diff --git a/Demo/App.config b/Demo/App.config deleted file mode 100644 index 5b023ab1..00000000 --- a/Demo/App.config +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/Demo/Demo.csproj b/Demo/Demo.csproj index af7f04ee..d42373e5 100644 --- a/Demo/Demo.csproj +++ b/Demo/Demo.csproj @@ -1,14 +1,15 @@ - Exe net8.0 - AnyCPU;x64;x86 + Demo + Demo + enable + enable - diff --git a/Demo/GlobalUsings.cs b/Demo/GlobalUsings.cs deleted file mode 100644 index 83493d33..00000000 --- a/Demo/GlobalUsings.cs +++ /dev/null @@ -1,21 +0,0 @@ -global using System; -global using System.Collections.Generic; -global using System.IO; -global using System.Linq; -global using System.Text; -global using System.Threading; -global using mupdf; -global using MuPDF.NET; -global using PDF4LLM; -global using static global::PDF4LLM.PdfExtractor; -global using PDF4LLM.Helpers; -global using PDF4LLM.Llama; -global using PDF4LLM.Ocr; -global using SkiaSharp; -global using Box = MuPDF.NET.Box; -global using Encoding = System.Text.Encoding; -global using File = System.IO.File; -global using Font = MuPDF.NET.Font; -global using Morph = MuPDF.NET.Morph; -global using TextWriter = MuPDF.NET.TextWriter; -global using Utils = MuPDF.NET.Utils; diff --git a/Demo/Program.cs b/Demo/Program.cs index c5d29c0a..4cad628f 100644 --- a/Demo/Program.cs +++ b/Demo/Program.cs @@ -1,15 +1,689 @@ -using System.Threading.Tasks; +// Demo - CLI tool ported from PyMuPDF's __main__.py +// Provides: show, clean, join, extract, gettext, embed-info/add/del/extract/copy commands. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using MuPDF.NET; namespace Demo { - /// - /// GitHub samples entry point. With no arguments, all samples run; see . - /// - internal partial class Program + internal static class Program { - private static void Main(string[] args) + static void TestAnnot() + { + Document doc = new Document(); + for (int i = 0; i < 1; i++) + doc.NewPage(); + + Page page = doc[0]; + var annot = page.AddCaretAnnot(new Point(100, 100)); + annot.Update(rotate: 20); + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on save. + doc.Save1(@"E:\Pdf\Tmp\Test\Annot_AddCaretAnnot.pdf", annot); + //doc.Save1(@"E:\Pdf\Tmp\Test\Annot_AddCaretAnnot.pdf", annot); + //annot.GetApnMatrix(); + + doc.Close(); + } + static int Main(string[] args) + { + TestAnnot(); + return 0; + /* + if (args.Length == 0) + { + PrintHelp(); + return 0; + } + */ + string command = "show"; //args[0].ToLower(); + + Console.WriteLine("\n========================================= Command: show =========================================\n"); + Console.WriteLine("Usage: show [-password PW] [-catalog] [-trailer] [-metadata] [-xrefs 1,5-7] [-pages 1,5-7]"); + string[] cmdArgs = new[] { @"E:\Pdf\Tmp\6025432\test.pdf", "-catalog", "-trailer", "-metadata-catalog", "-trailer", "-metadata" }; //args.Skip(1).ToArray(); + CommandShow(cmdArgs); + + Console.WriteLine("\n========================================= Command: clean =========================================\n"); + Console.WriteLine("Usage: clean [-password PW] [-pages 1,5-7] [-garbage 0-4] [-compress] [-linear] [-sanitize] [-pretty] [-ascii]"); + cmdArgs = new[] { @"E:\Pdf\Tmp\6025432\test.pdf", @"E:\Pdf\Tmp\6025432\2_.pdf", "-pages 1,5", "-compress", "-linear", "-sanitize", "-pretty", "-ascii" }; + CommandClean(cmdArgs); + + Console.WriteLine("\n========================================= Command: join =========================================\n"); + Console.WriteLine("Usage: join [input2.pdf ...] -output "); + cmdArgs = new[] { @"E:\Pdf\Tmp\6025432\test.pdf", @"E:\Pdf\Tmp\6025432\2_.pdf", "-output", @"E:\Pdf\Tmp\6025432\joined.pdf" }; + CommandJoin(cmdArgs); + + Console.WriteLine("\n========================================= Command: extract =========================================\n"); + Console.WriteLine("Usage: extract [-images] [-fonts] [-output DIR] [-password PW] [-pages 1,5-7]"); + cmdArgs = new[] { @"E:\Pdf\Tmp\6025432\magazine.pdf", "-images", "-fonts", "-output", @"E:\Pdf\Tmp\6025432\extracted" }; + CommandExtract(cmdArgs); + + Console.WriteLine("\n========================================= Command: gettext =========================================\n"); + Console.WriteLine("Usage: gettext [-password PW] [-mode simple|blocks|layout] [-pages 1,5-7,N] [-output FILE] [-grid N] [-fontsize N] [-noformfeed] [-skip-empty]"); + cmdArgs = new[] { @"E:\Pdf\Tmp\6025432\2.pdf", "-mode simple", "-pages 1", "-output", @"E:\Pdf\Tmp\6025432\output.txt", "-grid 4", "-fontsize 12", "-noformfeed", "-skip-empty" }; + CommandGetText(cmdArgs); + + Console.WriteLine("\n========================================= Command: embed-add =========================================\n"); + Console.WriteLine("Usage: embed-add -name NAME -path FILE [-desc TEXT] [-output OUT.pdf] [-password PW]"); + cmdArgs = new[] { @"E:\Pdf\Tmp\6025432\2.pdf", "-name", "My_PDF", "-path", @"E:\Pdf\Tmp\6025432\image.png", "-desc Cover_Image", "-output", @"E:\Pdf\Tmp\6025432\output.pdf" }; + CommandEmbedAdd(cmdArgs); + + Console.WriteLine("\n========================================= Command: embed-copy =========================================\n"); + Console.WriteLine("Usage: embed-copy -source [-name NAME ...] [-output OUT.pdf] [-password PW] [-pwdsource PW]"); + cmdArgs = new[] { @"E:\Pdf\Tmp\6025432\test.pdf", "-source", @"E:\Pdf\Tmp\6025432\output.pdf", "-name", "My_PDF", "-output", @"E:\Pdf\Tmp\6025432\output3.pdf" }; + CommandEmbedCopy(cmdArgs); + + Console.WriteLine("\n========================================= Command: embed-info =========================================\n"); + Console.WriteLine("Usage: embed-info [-name NAME] [-detail] [-password PW]"); + cmdArgs = new[] { @"E:\Pdf\Tmp\6025432\output.pdf", "-name", "My_PDF", "-detail" }; + CommandEmbedInfo(cmdArgs); + + Console.WriteLine("\n========================================= Command: embed-extract =========================================\n"); + Console.WriteLine("Usage: embed-extract -name NAME [-output FILE] [-password PW]"); + cmdArgs = new[] { @"E:\Pdf\Tmp\6025432\output.pdf", "-name", "My_PDF", "-output", @"E:\Pdf\Tmp\6025432\output1.png" }; + CommandEmbedExtract(cmdArgs); + + Console.WriteLine("\n========================================= Command: embed-del =========================================\n"); + Console.WriteLine("Usage: embed-del -name NAME [-output OUT.pdf] [-password PW]"); + cmdArgs = new[] { @"E:\Pdf\Tmp\6025432\output.pdf", "-name", "My_PDF", "-output", @"E:\Pdf\Tmp\6025432\output.pdf" }; + CommandEmbedDel(cmdArgs); + + Console.WriteLine("\n========================================= Command: embed-info =========================================\n"); + Console.WriteLine("Usage: embed-info [-name NAME] [-detail] [-password PW]"); + cmdArgs = new[] { @"E:\Pdf\Tmp\6025432\output.pdf", "-name", "My_PDF", "-detail" }; + CommandEmbedInfo(cmdArgs); + + return 0; + + try + { + return command switch + { + "show" => CommandShow(cmdArgs), + "clean" => CommandClean(cmdArgs), + "join" => CommandJoin(cmdArgs), + "extract" => CommandExtract(cmdArgs), + "gettext" => CommandGetText(cmdArgs), + "embed-info" => CommandEmbedInfo(cmdArgs), + "embed-add" => CommandEmbedAdd(cmdArgs), + "embed-del" => CommandEmbedDel(cmdArgs), + "embed-extract" => CommandEmbedExtract(cmdArgs), + "embed-copy" => CommandEmbedCopy(cmdArgs), + "-h" or "--help" or "help" => PrintHelp(), + _ => Error($"Unknown command: '{command}'. Use --help for usage.") + }; + } + catch (Exception ex) + { + Console.Error.WriteLine($"Error: {ex.Message}"); + return 1; + } + } + + // ─── Helpers ──────────────────────────────────────────────────── + + static string Center(string text, int width = 75, char pad = '-') + { + string s = $" {text} "; + int total = Math.Max(width, s.Length); + int left = (total - s.Length) / 2; + int right = total - s.Length - left; + return new string(pad, left) + s + new string(pad, right); + } + + static int Error(string message) + { + Console.Error.WriteLine(message); + return 1; + } + + static int PrintHelp() + { + Console.WriteLine(Center("MuPDF.NET Demo CLI")); + Console.WriteLine(); + Console.WriteLine("Usage: Demo [options]"); + Console.WriteLine(); + Console.WriteLine("Commands:"); + Console.WriteLine(" show Display PDF document information"); + Console.WriteLine(" clean Optimize PDF or create sub-PDF"); + Console.WriteLine(" join Join pages from multiple PDFs"); + Console.WriteLine(" extract Extract images and/or fonts to disk"); + Console.WriteLine(" gettext Extract text in various formatting modes"); + Console.WriteLine(" embed-info List embedded files"); + Console.WriteLine(" embed-add Add an embedded file"); + Console.WriteLine(" embed-del Delete an embedded file"); + Console.WriteLine(" embed-extract Extract an embedded file to disk"); + Console.WriteLine(" embed-copy Copy embedded files between PDFs"); + Console.WriteLine(); + Console.WriteLine("Use ' --help' for command-specific help."); + return 0; + } + + static Document OpenFile(string filename, string? password = null, bool requirePdf = true) + { + var doc = new Document(filename); + if (!doc.IsPdf && requirePdf) + throw new InvalidOperationException("This command supports PDF files only."); + if (doc.NeedsPass) + { + if (string.IsNullOrEmpty(password)) + throw new InvalidOperationException($"'{doc.Name}' requires a password."); + if (!doc.Authenticate(password)) + throw new InvalidOperationException("Authentication unsuccessful."); + } + return doc; + } + + static List ParsePageList(string spec, int pageCount) + { + int limit = pageCount + 1; + string resolved = spec.Replace("N", (pageCount).ToString()).Replace(" ", ""); + var result = new List(); + foreach (string item in resolved.Split(',', StringSplitOptions.RemoveEmptyEntries)) + { + if (int.TryParse(item, out int single)) + { + if (single >= 1 && single < limit) + result.Add(single); + else + throw new ArgumentException($"Bad page specification: {item}"); + } + else if (item.Contains('-')) + { + var parts = item.Split('-', 2); + int i1 = int.Parse(parts[0]); + int i2 = int.Parse(parts[1]); + if (i1 < 1 || i1 >= limit || i2 < 1 || i2 >= limit) + throw new ArgumentException($"Bad page range: {item}"); + if (i1 <= i2) + for (int i = i1; i <= i2; i++) result.Add(i); + else + for (int i = i1; i >= i2; i--) result.Add(i); + } + else + throw new ArgumentException($"Bad specification: {item}"); + } + return result; + } + + static (Dictionary named, List positional) ParseArgs( + string[] args, params string[] flags) + { + var named = new Dictionary(StringComparer.OrdinalIgnoreCase); + var positional = new List(); + var flagSet = new HashSet(flags, StringComparer.OrdinalIgnoreCase); + + for (int i = 0; i < args.Length; i++) + { + if (args[i].StartsWith("-")) + { + string key = args[i].TrimStart('-').ToLower(); + if (flagSet.Contains(key)) + { + named[key] = "true"; + } + else if (i + 1 < args.Length && !args[i + 1].StartsWith("-")) + { + named[key] = args[++i]; + } + else + { + named[key] = "true"; + } + } + else + { + positional.Add(args[i]); + } + } + return (named, positional); + } + + static string? Get(Dictionary d, string key, string? def = null) + => d.TryGetValue(key, out var v) ? v : def; + + static bool Flag(Dictionary d, string key) + => d.ContainsKey(key); + + // ─── show ─────────────────────────────────────────────────────── + + static int CommandShow(string[] args) + { + var (opts, pos) = ParseArgs(args, "catalog", "trailer", "metadata", "help", "h"); + if (Flag(opts, "help") || Flag(opts, "h") || pos.Count == 0) + { + Console.WriteLine("Usage: show [-password PW] [-catalog] [-trailer] [-metadata] [-xrefs 1,5-7] [-pages 1,5-7]"); + return 0; + } + + string input = pos[0]; + string? password = Get(opts, "password"); + using var doc = OpenFile(input, password, true); + + double size = new FileInfo(input).Length / 1024.0; + string flag = "KB"; + if (size > 1000) { size /= 1024; flag = "MB"; } + + var meta = doc.GetMetadata(); + Console.WriteLine($"'{input}', pages: {doc.PageCount}, objects: {doc.XrefLength - 1}, {size:F1} {flag}, {meta.GetValueOrDefault("format", "")}, encryption: {meta.GetValueOrDefault("encryption", "None")}"); + + if (Flag(opts, "metadata")) + { + Console.WriteLine(Center("PDF metadata")); + foreach (var kv in doc.GetMetadata()) + Console.WriteLine($" {kv.Key}: {kv.Value}"); + Console.WriteLine(); + } + + if (Flag(opts, "catalog")) + { + Console.WriteLine(Center("PDF catalog")); + int xref = doc.PdfCatalog; + PrintXref(doc, xref); + Console.WriteLine(); + } + + if (Flag(opts, "trailer")) + { + Console.WriteLine(Center("PDF trailer")); + Console.WriteLine(doc.PdfTrailer()); + Console.WriteLine(); + } + + if (opts.ContainsKey("xrefs")) + { + Console.WriteLine(Center("object information")); + var xrefs = ParsePageList(opts["xrefs"]!, doc.XrefLength); + foreach (int xref in xrefs) + { + PrintXref(doc, xref); + Console.WriteLine(); + } + } + + if (opts.ContainsKey("pages")) + { + Console.WriteLine(Center("page information")); + var pages = ParsePageList(opts["pages"]!, doc.PageCount); + foreach (int pno in pages) + { + int xref = doc.PageXref(pno - 1); + Console.WriteLine($"Page {pno}:"); + PrintXref(doc, xref); + Console.WriteLine(); + } + } + + return 0; + } + + static void PrintXref(Document doc, int xref) + { + Console.WriteLine($"{xref} 0 obj"); + var keys = doc.XrefGetKeys(xref); + string xrefStr = keys.Count > 0 ? string.Join("\n", keys.Select(k => $" /{k} {doc.XrefGetKey(xref, k).value}")) : ""; + Console.WriteLine(xrefStr); + if (doc.XrefIsStream(xref)) + Console.WriteLine("stream\n...bytes\nendstream"); + Console.WriteLine("endobj"); + } + + // ─── clean ────────────────────────────────────────────────────── + + static int CommandClean(string[] args) + { + var (opts, pos) = ParseArgs(args, "compress", "ascii", "linear", "sanitize", "pretty", "help", "h"); + if (Flag(opts, "help") || Flag(opts, "h") || pos.Count < 2) + { + Console.WriteLine("Usage: clean [-password PW] [-pages 1,5-7] [-garbage 0-4] [-compress] [-linear] [-sanitize] [-pretty] [-ascii]"); + return 0; + } + + string input = pos[0], output = pos[1]; + string? password = Get(opts, "password"); + using var doc = OpenFile(input, password, true); + + int garbage = int.Parse(Get(opts, "garbage", "0")!); + bool compress = Flag(opts, "compress"); + bool clean = Flag(opts, "sanitize"); + bool linear = Flag(opts, "linear"); + + string? pagesSpec = Get(opts, "pages"); + if (pagesSpec == null) + { + doc.Save(output, garbage: garbage>0?1:0, clean: clean?1:0, deflate: compress?1:0); + } + else + { + var pages = ParsePageList(pagesSpec, doc.PageCount); + using var outDoc = new Document(); + foreach (int pno in pages) + outDoc.InsertPdf(doc, fromPage: pno - 1, toPage: pno - 1); + outDoc.Save(output, garbage: garbage > 0 ? 1 : 0, clean: clean ? 1 : 0, deflate: compress ? 1 : 0); + } + + Console.WriteLine($"Saved to '{output}'"); + return 0; + } + + // ─── join ─────────────────────────────────────────────────────── + + static int CommandJoin(string[] args) + { + var (opts, pos) = ParseArgs(args, "help", "h"); + if (Flag(opts, "help") || Flag(opts, "h") || pos.Count == 0 || !opts.ContainsKey("output")) + { + Console.WriteLine("Usage: join [input2.pdf ...] -output "); + return 0; + } + + string output = opts["output"]!; + using var outDoc = new Document(); + + foreach (string srcItem in pos) + { + var parts = srcItem.Split(','); + string filename = parts[0]; + string? password = parts.Length > 1 ? parts[1] : null; + using var src = OpenFile(filename, password, true); + + List pageList; + if (parts.Length > 2) + pageList = ParsePageList(string.Join(",", parts.Skip(2)), src.PageCount); + else + pageList = Enumerable.Range(1, src.PageCount).ToList(); + + foreach (int pno in pageList) + outDoc.InsertPdf(src, fromPage: pno - 1, toPage: pno - 1); + } + + outDoc.Save(output, garbage: 1, deflate: 1); + Console.WriteLine($"Joined to '{output}'"); + return 0; + } + + // ─── extract ──────────────────────────────────────────────────── + + static int CommandExtract(string[] args) + { + var (opts, pos) = ParseArgs(args, "images", "fonts", "help", "h"); + if (Flag(opts, "help") || Flag(opts, "h") || pos.Count == 0) + { + Console.WriteLine("Usage: extract [-images] [-fonts] [-output DIR] [-password PW] [-pages 1,5-7]"); + return 0; + } + + bool doImages = Flag(opts, "images"); + bool doFonts = Flag(opts, "fonts"); + if (!doImages && !doFonts) + return Error("Neither -images nor -fonts requested."); + + string input = pos[0]; + string? password = Get(opts, "password"); + using var doc = OpenFile(input, password, true); + + string outDir = Get(opts, "output") ?? Directory.GetCurrentDirectory(); + if (!Directory.Exists(outDir)) + return Error($"Output directory '{outDir}' does not exist."); + + var pagesSpec = Get(opts, "pages"); + var pages = pagesSpec != null + ? ParsePageList(pagesSpec, doc.PageCount) + : Enumerable.Range(1, doc.PageCount).ToList(); + + var fontXrefs = new HashSet(); + var imageXrefs = new HashSet(); + + foreach (int pno in pages) + { + if (doFonts) + { + var fonts = doc.GetPageFonts(pno - 1); + foreach (var item in fonts) + { + int xref = item.Item1; + if (fontXrefs.Add(xref)) + { + var (fontname, ext, _, buffer) = doc.ExtractFont(xref); + if (ext == "n/a" || buffer == null || buffer.Length == 0) continue; + string outname = Path.Combine(outDir, $"{fontname.Replace(' ', '-')}-{xref}.{ext}"); + File.WriteAllBytes(outname, buffer); + } + } + } + + if (doImages) + { + var images = doc.GetPageImages(pno - 1); + foreach (var item in images) + { + int xref = item.Item1; + if (imageXrefs.Add(xref)) + { + var imgData = doc.ExtractImage(xref); + if (imgData != null && imgData.ContainsKey("image")) + { + string ext = imgData.ContainsKey("ext") ? imgData["ext"].ToString()! : "png"; + string outname = Path.Combine(outDir, $"img-{xref}.{ext}"); + File.WriteAllBytes(outname, (byte[])imgData["image"]); + } + } + } + } + } + + if (doFonts) Console.WriteLine($"Saved {fontXrefs.Count} fonts to '{outDir}'"); + if (doImages) Console.WriteLine($"Saved {imageXrefs.Count} images to '{outDir}'"); + return 0; + } + + // ─── gettext ──────────────────────────────────────────────────── + + static int CommandGetText(string[] args) + { + var (opts, pos) = ParseArgs(args, "noligatures", "convert-white", "extra-spaces", + "noformfeed", "skip-empty", "help", "h"); + if (Flag(opts, "help") || Flag(opts, "h") || pos.Count == 0) + { + Console.WriteLine("Usage: gettext [-password PW] [-mode simple|blocks|layout] [-pages 1,5-7,N] [-output FILE] [-grid N] [-fontsize N] [-noformfeed] [-skip-empty]"); + return 0; + } + + string input = pos[0]; + string? password = Get(opts, "password"); + using var doc = OpenFile(input, password, requirePdf: false); + + string pagesSpec = Get(opts, "pages", "1-N")!; + var pages = ParsePageList(pagesSpec, doc.PageCount); + string mode = Get(opts, "mode", "layout")!; + + string? outputFile = Get(opts, "output"); + if (outputFile == null) + outputFile = Path.ChangeExtension(input, ".txt"); + + using var outStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write); + byte eop = Flag(opts, "noformfeed") ? (byte)'\n' : (byte)12; + bool skipEmpty = Flag(opts, "skip-empty"); + + foreach (int pno in pages) + { + var page = doc[pno - 1]; + string text; + if (mode == "blocks") + { + var blocks = page.GetTextBlocks(); + if (blocks == null || blocks.Count == 0) + { + if (!skipEmpty) outStream.WriteByte(eop); + continue; + } + text = string.Join("", blocks.Select(b => b.Item5)); + } + else + { + text = page.GetText("text"); + } + + if (string.IsNullOrEmpty(text)) + { + if (!skipEmpty) outStream.WriteByte(eop); + continue; + } + var bytes = System.Text.Encoding.UTF8.GetBytes(text); + outStream.Write(bytes, 0, bytes.Length); + outStream.WriteByte(eop); + } + + Console.WriteLine($"Text saved to '{outputFile}'"); + return 0; + } + + // ─── embed-info ───────────────────────────────────────────────── + + static int CommandEmbedInfo(string[] args) { - SampleMenu.Run(args); + var (opts, pos) = ParseArgs(args, "detail", "help", "h"); + if (Flag(opts, "help") || Flag(opts, "h") || pos.Count == 0) + { + Console.WriteLine("Usage: embed-info [-name NAME] [-detail] [-password PW]"); + return 0; + } + + using var doc = OpenFile(pos[0], Get(opts, "password"), true); + var names = doc.EmbfileNames(); + if (names.Count == 0) + { + Console.WriteLine($"'{doc.Name}' contains no embedded files."); + return 0; + } + + string? filterName = Get(opts, "name"); + if (filterName != null) + { + if (!names.Contains(filterName)) + return Error($"No such embedded file '{filterName}'"); + Console.WriteLine($"Printing 1 of {names.Count} embedded file(s):"); + Console.WriteLine($" {filterName}"); + return 0; + } + + Console.WriteLine($"'{doc.Name}' contains {names.Count} embedded file(s):"); + foreach (var name in names) + Console.WriteLine($" {name}"); + return 0; + } + + // ─── embed-add ────────────────────────────────────────────────── + + static int CommandEmbedAdd(string[] args) + { + var (opts, pos) = ParseArgs(args, "help", "h"); + if (Flag(opts, "help") || Flag(opts, "h") || pos.Count == 0 + || !opts.ContainsKey("name") || !opts.ContainsKey("path")) + { + Console.WriteLine("Usage: embed-add -name NAME -path FILE [-desc TEXT] [-output OUT.pdf] [-password PW]"); + return 0; + } + + string input = pos[0]; + using var doc = OpenFile(input, Get(opts, "password"), true); + string name = opts["name"]!; + string path = opts["path"]!; + if (!File.Exists(path)) + return Error($"No such file '{path}'"); + + byte[] data = File.ReadAllBytes(path); + string desc = Get(opts, "desc", path)!; + doc.EmbfileAdd(name, data, filename: path, desc: desc); + + string? output = Get(opts, "output"); + if (output != null && output != input) + doc.Save(output); + else + doc.SaveIncr(); + + Console.WriteLine($"Added embedded file '{name}'"); + return 0; + } + + // ─── embed-del ────────────────────────────────────────────────── + + static int CommandEmbedDel(string[] args) + { + var (opts, pos) = ParseArgs(args, "help", "h"); + if (Flag(opts, "help") || Flag(opts, "h") || pos.Count == 0 || !opts.ContainsKey("name")) + { + Console.WriteLine("Usage: embed-del -name NAME [-output OUT.pdf] [-password PW]"); + return 0; + } + + using var doc = OpenFile(pos[0], Get(opts, "password"), true); + doc.EmbfileDel(opts["name"]!); + + string? output = Get(opts, "output"); + if (output != null && output != pos[0]) + doc.Save(output, garbage: 1); + else + doc.SaveIncr(); + + Console.WriteLine($"Deleted embedded file '{opts["name"]}'"); + return 0; + } + + // ─── embed-extract ────────────────────────────────────────────── + + static int CommandEmbedExtract(string[] args) + { + var (opts, pos) = ParseArgs(args, "help", "h"); + if (Flag(opts, "help") || Flag(opts, "h") || pos.Count == 0 || !opts.ContainsKey("name")) + { + Console.WriteLine("Usage: embed-extract -name NAME [-output FILE] [-password PW]"); + return 0; + } + + using var doc = OpenFile(pos[0], Get(opts, "password"), true); + string name = opts["name"]!; + byte[] data = doc.EmbfileGet(name); + string? outputFile = Get(opts, "output") ?? name; + File.WriteAllBytes(outputFile, data); + Console.WriteLine($"Saved embedded file '{name}' as '{outputFile}'"); + return 0; + } + + // ─── embed-copy ───────────────────────────────────────────────── + + static int CommandEmbedCopy(string[] args) + { + var (opts, pos) = ParseArgs(args, "help", "h"); + if (Flag(opts, "help") || Flag(opts, "h") || pos.Count == 0 || !opts.ContainsKey("source")) + { + Console.WriteLine("Usage: embed-copy -source [-name NAME ...] [-output OUT.pdf] [-password PW] [-pwdsource PW]"); + return 0; + } + + using var doc = OpenFile(pos[0], Get(opts, "password"), true); + using var src = OpenFile(opts["source"]!, Get(opts, "pwdsource"), true); + + var srcNames = new HashSet(src.EmbfileNames()); + var filterName = Get(opts, "name"); + var names = filterName != null ? new HashSet { filterName } : srcNames; + + foreach (var name in names) + { + byte[] data = src.EmbfileGet(name); + doc.EmbfileAdd(name, data); + Console.WriteLine($"Copied entry '{name}'"); + } + + string? output = Get(opts, "output"); + if (output != null && output != pos[0]) + doc.Save(output, garbage: 1); + else + doc.SaveIncr(); + + return 0; } } } diff --git a/Demo/SampleMenu.cs b/Demo/SampleMenu.cs deleted file mode 100644 index 3a4b1af4..00000000 --- a/Demo/SampleMenu.cs +++ /dev/null @@ -1,176 +0,0 @@ -namespace Demo -{ - /// - /// Demo samples grouped by MuPDF.NET / PDF4LLM feature areas. With no arguments, runs every sample. - /// Use dotnet run -- help for the list, or dotnet run -- <name> for one sample. - /// - public static class SampleMenu - { - /// Library-facing group (matches folders under Samples/ and major API surfaces). - private sealed record Sample(string Category, string Name, string Description, Action Run); - - /// Order matches Samples/ layout; PDF4LLM extras live in Samples/Llm/Program.Llm.*.Fixtures.cs. - private static readonly Sample[] Samples = - { - // —— Document & I/O (MuPDF.NET Document, open/save, streams) —— Samples/Document - new("Document & I/O", "hello-new-pdf", "Hello World on a new PDF", a => Program.TestHelloWorldToNewDocument(a)), - new("Document & I/O", "hello-existing-pdf", "Hello World on existing Blank.pdf", a => Program.TestHelloWorldToExistingDocument(a)), - new("Document & I/O", "join-pdf", "Insert pages from another PDF", a => Program.TestJoinPdfPages(a)), - new("Document & I/O", "metadata", "Print document metadata", _ => Program.TestMetadata()), - new("Document & I/O", "move-file", "Save through MemoryStream and move output", _ => Program.TestMoveFile()), - new("Document & I/O", "unicode-doc", "Save PDF with unicode filename", _ => Program.TestUnicodeDocument()), - new("Document & I/O", "memory-leak", "Open/close documents in a loop", _ => Program.TestMemoryLeak()), - - // —— Text, story & vector drawing (Page, Story, TextWriter, Shape) —— Samples/TextDrawing - new("Text, story & drawing", "insert-htmlbox", "Insert HTML story box into a new page", _ => Program.TestInsertHtmlbox()), - new("Text, story & drawing", "text-font", "FillTextbox with fonts", a => Program.TestTextFont(a)), - new("Text, story & drawing", "morph", "TextWriter with morph / rotation", _ => Program.TestMorph()), - new("Text, story & drawing", "gettext", "GetText dict dump per page", _ => Program.TestGetText()), - new("Text, story & drawing", "extract-text-layout", "Extract text with reading order (columns.pdf)", a => Program.TestExtractTextWithLayout(a)), - new("Text, story & drawing", "draw-line", "Draw dashed lines on a page", _ => Program.TestDrawLine()), - new("Text, story & drawing", "draw-shape", "Copy vector paths between PDFs", _ => Program.TestDrawShape()), - - // —— Annotations —— Samples/Annotations - new("Annotations", "line-annot", "Create and modify line annotations", _ => Program.TestLineAnnot()), - new("Annotations", "annot-freetext1", "Free-text annotation sample (1)", a => Program.TestAnnotationsFreeText1(a)), - new("Annotations", "annot-freetext2", "Free-text annotation sample (2)", a => Program.TestAnnotationsFreeText2(a)), - new("Annotations", "new-annots", "Caret, markers, shapes, stamp, redaction, etc.", a => NewAnnots.Run(a)), - new("Annotations", "annot-doc", "Rectangle annotation + text", _ => Program.CreateAnnotDocument()), - new("Annotations", "freetext-annot", "Add free-text annotation (unicode)", a => Program.TestFreeTextAnnot(a)), - - // —— Pages, widgets, images & color —— Samples/PageContent - new("Pages, widgets, images & color", "widget", "Inspect form widgets", a => Program.TestWidget(a)), - new("Pages, widgets, images & color", "color", "Recolor page images", a => Program.TestColor(a)), - new("Pages, widgets, images & color", "cmyk-recolor", "CMYK recolor", a => Program.TestCMYKRecolor(a)), - new("Pages, widgets, images & color", "svg-recolor", "SVG / RGB recolor", a => Program.TestSVGRecolor(a)), - new("Pages, widgets, images & color", "replace-image", "Replace embedded images", a => Program.TestReplaceImage(a)), - new("Pages, widgets, images & color", "insert-image", "Insert images from pixmaps and files", a => Program.TestInsertImage(a)), - new("Pages, widgets, images & color", "get-image-info", "Dump image xref info", a => Program.TestGetImageInfo(a)), - new("Pages, widgets, images & color", "page-ocr", "OCR text page with image filter pipeline", a => Program.TestGetTextPageOcr(a)), - new("Pages, widgets, images & color", "create-image-page", "New PDF page from PNG pixmap", a => Program.TestCreateImagePage(a)), - - // —— Image filters (Skia) —— Samples/ImageFilters - new("Image filters (Skia)", "image-filter", "Skia pipeline on table.jpg → output.png", _ => Program.TestImageFilter()), - new("Image filters (Skia)", "image-filter-ocr", "Pixmap OCR with filter pipeline", _ => Program.TestImageFilterOcr()), - - // —— Barcodes —— Samples/Barcodes - new("Barcodes", "read-barcode", "Read barcodes from image and PDF", a => Program.TestReadBarcode(a)), - new("Barcodes", "read-datamatrix", "Read Data Matrix from PDF", _ => Program.TestReadDataMatrix()), - //new("Barcodes", "read-qrcode", "Render PDF page and read QR from PNG", a => Program.TestReadQrCode(a)), - new("Barcodes", "write-barcode", "Write many barcode types to PDF and PNG", a => Program.TestWriteBarcode(a)), - new("Barcodes", "write-barcode1", "Write CODE39/CODE128/DM with Units rects", _ => Program.TestWriteBarcode1()), - - // —— PDF4LLM —— Samples/Llm - new("PDF4LLM", "rag-markdown", "Legacy RAG: PDF4LLM.ToMarkdown with UseLayout=false (Magazine.pdf)", _ => Program.TestMuPdfRagToMarkdown()), - new("PDF4LLM", "table", "Detect tables and export markdown", _ => Program.TestTable()), - new("PDF4LLM", "table-extract-1", "Dump detected tables by page to console", _ => Program.TestTableExtract1()), - new("PDF4LLM", "table-extract-2", "Export detected tables to tables.csv", _ => Program.TestTableExtract2()), - new("PDF4LLM", "table-extract-3", "Merge continued table pages by column count", _ => Program.TestTableExtract3()), - new("PDF4LLM", "table-ocr", "Extract OCR text from Ocr.pdf", _ => Program.TestOcr()), - new("PDF4LLM", "llm-reader-save-pages", "Load markdown chunks and save per-page .md files", _ => Program.TestLLM2()), - new("PDF4LLM", "markdown-reader", "LlamaIndex PDFMarkdownReader", _ => Program.TestMarkdownReader()), - new("PDF4LLM", "llm-to-markdown-fixture-370", "ToMarkdown vs tests/test_370_expected.md (needs tests/test_370.pdf)", a => Program.Test4LlmToMarkdownCompareExpected370(a)), - new("PDF4LLM", "llm-to-markdown-ocr-1", "ToMarkdown + U+FFFD fixture (tests/test_ocr_loremipsum_FFFD.pdf)", a => Program.Test4LlmToMarkdownOcrFixture1(a)), - new("PDF4LLM", "llm-to-markdown-ocr-2", "ToMarkdown useOcr=false on FFFD fixture", a => Program.Test4LlmToMarkdownOcrFixture2(a)), - new("PDF4LLM", "llm-to-markdown-ocr-3", "ToMarkdown OCR on/off on SVG fixture", a => Program.Test4LlmToMarkdownOcrFixture3(a)), - new("PDF4LLM", "llm-pdf-reader-empty", "PDFMarkdownReader: new PDF, one blank page", a => Program.Test4LlmPdfMarkdownReaderEmptyPage(a)), - new("PDF4LLM", "llm-pdf-reader-missing-file", "PDFMarkdownReader: missing path → FileNotFoundException", a => Program.Test4LlmPdfMarkdownReaderMissingFile(a)), - - // —— Regression & diagnostics —— Samples/Regression - new("Regression & diagnostics", "issue-213", "Repro: drawing paths / line width", _ => Program.TestIssue213()), - new("Regression & diagnostics", "issue-1880", "Repro: read Data Matrix barcodes", _ => Program.TestIssue1880()), - new("Regression & diagnostics", "issue-234", "Repro: pixmap scale + insert image", _ => Program.TestIssue234()), - new("Regression & diagnostics", "pixmap-parallel", "Repro: parallel Pixmap.ToBytes rendering", _ => Program.TestPixmapParallel()), - new("Regression & diagnostics", "jbig2", "Rewrite images with FAX recompression", _ => Program.TestRecompressJBIG2()), - }; - - private static readonly Dictionary ByName = BuildIndex(); - - private static Dictionary BuildIndex() - { - var d = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var s in Samples) - { - d[s.Name] = s; - } - return d; - } - - public static void Run(string[] args) - { - if (args.Length > 0 && IsHelp(args[0])) - { - PrintUsage(); - return; - } - - if (args.Length == 0 || IsRunAllSwitch(args[0])) - { - RunAll(); - return; - } - - if (!ByName.TryGetValue(args[0], out var sample)) - { - Console.Error.WriteLine($"Unknown sample: {args[0]}"); - PrintUsage(); - Environment.ExitCode = 1; - return; - } - - Console.WriteLine($"--- Sample: {sample.Name} ({sample.Category}) ---"); - sample.Run(args); - } - - private static bool IsHelp(string a) => - a is "-h" or "-?" or "/?" or "help" or "--help"; - - private static bool IsRunAllSwitch(string a) => - string.Equals(a, "all", StringComparison.OrdinalIgnoreCase) - || string.Equals(a, "-all", StringComparison.OrdinalIgnoreCase) - || string.Equals(a, "--all", StringComparison.OrdinalIgnoreCase); - - private static void RunAll() - { - var sampleArgs = Array.Empty(); - foreach (var s in Samples) - { - Console.WriteLine(); - Console.WriteLine($"========== {s.Category} / {s.Name} =========="); - try - { - s.Run(sampleArgs); - } - catch (Exception ex) - { - Console.Error.WriteLine($"FAILED {s.Name}: {ex.Message}"); - } - } - } - - private static void PrintUsage() - { - Console.WriteLine("MuPDF.NET Demo — samples mirror library areas under Demo/Samples/. Default: run all."); - Console.WriteLine(); - Console.WriteLine(" dotnet run (or: dotnet run -- -all)"); - Console.WriteLine(" dotnet run -- "); - Console.WriteLine(" dotnet run -- help"); - Console.WriteLine(); - Console.WriteLine("Samples by category:"); - var lastCat = ""; - foreach (var s in Samples) - { - if (s.Category != lastCat) - { - Console.WriteLine(); - Console.WriteLine($" [{s.Category}]"); - lastCat = s.Category; - } - - Console.WriteLine($" {s.Name,-22} {s.Description}"); - } - - Console.WriteLine(); - } - } -} diff --git a/Demo/Samples/Annotations/NewAnnots.cs b/Demo/Samples/Annotations/NewAnnots.cs deleted file mode 100644 index c0cfcd66..00000000 --- a/Demo/Samples/Annotations/NewAnnots.cs +++ /dev/null @@ -1,164 +0,0 @@ -/* - * ------------------------------------------------------------------------------- - * Demo script showing how annotations can be added to a PDF using MuPDF.NET. - * - * It contains the following annotation types: - * Caret, Text, FreeText, text markers (underline, strike-out, highlight, - * squiggle), Circle, Square, Line, PolyLine, Polygon, FileAttachment, Stamp - * and Redaction. - * There is some effort to vary appearances by adding colors, line ends, - * opacity, rotation, dashed lines, etc. - * -------------------------------------------------------------------------------- -*/ - -using MuPDF.NET; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Demo -{ - public static class NewAnnots - { - private static void print_descr(Annot annot) - { - // Print a short description to the right of each annot rect. - string description = annot.Type.Item2 + " annotation"; - annot.GetParent().InsertText( - annot.Rect.BottomRight + new Point(10, -5), description, color: Constants.red); - } - - // Use rich text for FreeText annotations - public static void Run(string[] args) - { - Console.WriteLine("\n=== NewAnnots ======================="); - Rect r = Constants.r; // use the rectangle defined in Constants.cs - - Document doc = new Document(); - Page page = doc.NewPage(); - - page.SetRotation(0); // no rotation - - Annot annot = page.AddCaretAnnot(r.TopLeft); - print_descr(annot); - - r = r + Constants.displ; - annot = page.AddFreeTextAnnot( - r, - Constants.t1, - fontSize: 10, - rotate: 90, - textColor: Constants.blue, - fillColor: Constants.gold, - align: (int)TextAlign.TEXT_ALIGN_CENTER - ); - annot.SetBorder(width: 0.3f, dashes: new int[] { 2 }); - annot.Update(textColor: Constants.blue, fillColor: Constants.gold); - print_descr(annot); - - r = r + Constants.displ; - annot = page.AddTextAnnot( - r.TopLeft, - Constants.t1 - ); - print_descr(annot); - - // Adding text marker annotations: - // first insert a unique text, then search for it, then mark it - Point pos = annot.Rect.TopLeft + Constants.displ.TopLeft; - page.InsertText( - pos, // insertion point - Constants.highlight, // inserted text - morph: new Morph(pos, new Matrix(-5)) // rotate around insertion point - ); - List rl = page.SearchFor(Constants.highlight, quads: true); // need a quad b/o tilted text - annot = page.AddHighlightAnnot(rl[0]); - print_descr(annot); - - pos = annot.Rect.BottomLeft; // next insertion point - page.InsertText(pos, Constants.underline, morph: new Morph(pos, new Matrix(-10))); - rl = page.SearchFor(Constants.underline, quads: true); - annot = page.AddUnderlineAnnot(rl[0]); - print_descr(annot); - - pos = annot.Rect.BottomLeft; - page.InsertText(pos, Constants.strikeout, morph: new Morph(pos, new Matrix(-15))); - rl = page.SearchFor(Constants.strikeout, quads: true); - annot = page.AddStrikeoutAnnot(rl[0]); - print_descr(annot); - - pos = annot.Rect.BottomLeft; - page.InsertText(pos, Constants.squiggled, morph: new Morph(pos, new Matrix(-20))); - rl = page.SearchFor(Constants.squiggled, quads: true); - annot = page.AddSquigglyAnnot(rl[0]); - print_descr(annot); - - pos = annot.Rect.BottomLeft; - r = new Rect(pos, pos.X + 75, pos.Y + 35) + new Rect(0, 20, 0, 20); - annot = page.AddPolylineAnnot(new List { r.BottomLeft, r.TopRight, r.BottomRight, r.TopLeft }); // 'Polyline' - annot.SetBorder(width: 0.3f, dashes: new int[] { 2 }); - annot.SetColors(stroke: Constants.blue, fill: Constants.green); - annot.SetLineEnds(PdfLineEnding.PDF_ANNOT_LE_CLOSED_ARROW, PdfLineEnding.PDF_ANNOT_LE_R_CLOSED_ARROW); - annot.Update(fillColor: new float[] { 1, 1, 0 }); - print_descr(annot); - - r += Constants.displ; - annot = page.AddPolygonAnnot(new List { r.BottomLeft, r.TopRight, r.BottomRight, r.TopLeft }); // 'Polygon' - annot.SetBorder(width: 0.3f, dashes: new int[] { 2 }); - annot.SetColors(stroke: Constants.blue, fill: Constants.gold); - annot.SetLineEnds(PdfLineEnding.PDF_ANNOT_LE_DIAMOND, PdfLineEnding.PDF_ANNOT_LE_CIRCLE); - annot.Update(); - print_descr(annot); - - r += Constants.displ; - annot = page.AddLineAnnot(r.TopRight, r.BottomLeft); // 'Line' - annot.SetBorder(width: 0.3f, dashes: new int[] { 2 }); - annot.SetColors(stroke: Constants.blue, fill: Constants.gold); - annot.SetLineEnds(PdfLineEnding.PDF_ANNOT_LE_DIAMOND, PdfLineEnding.PDF_ANNOT_LE_CIRCLE); - annot.Update(); - print_descr(annot); - - r += Constants.displ; - annot = page.AddRectAnnot(r); // 'Square' - annot.SetBorder(width: 1f, dashes: new int[] { 1, 2 }); - annot.SetColors(stroke: Constants.blue, fill: Constants.gold); - annot.Update(opacity: 0.5f); - print_descr(annot); - - r += Constants.displ; - annot = page.AddCircleAnnot(r); // 'Circle' - annot.SetBorder(width: 0.3f, dashes: new int[] { 2 }); - annot.SetColors(stroke: Constants.blue, fill: Constants.gold); - annot.Update(); - print_descr(annot); - - r += Constants.displ; - annot = page.AddFileAnnot( - r.TopLeft, Encoding.UTF8.GetBytes("just anything for testing"), "testdata.txt"); // 'FileAttachment' - print_descr(annot); // annot.rect - - r += Constants.displ; - annot = page.AddStampAnnot(r, stamp: 10); // 'Stamp' - annot.SetColors(stroke: Constants.green); - annot.Update(); - print_descr(annot); - - r += Constants.displ + new Rect(0, 0, 50, 10); - float rc = page.InsertTextbox( - r, - "This content will be removed upon applying the redaction.", - color: Constants.blue, - align: (int)TextAlign.TEXT_ALIGN_CENTER - ); - annot = page.AddRedactAnnot(r.Quad); - print_descr(annot); - - doc.Save(typeof(NewAnnots).Name + ".pdf", deflate:1); - - doc.Close(); - - Console.WriteLine("Saved to " + typeof(NewAnnots).Name + ".pdf"); - } - } -} diff --git a/Demo/Samples/Annotations/Program.Annotations.FreeText.cs b/Demo/Samples/Annotations/Program.Annotations.FreeText.cs deleted file mode 100644 index 4f398e5f..00000000 --- a/Demo/Samples/Annotations/Program.Annotations.FreeText.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace Demo -{ - internal partial class Program - { - /// Three stacked FreeText annotations (plain text, fonts, rotation). - internal static void TestAnnotationsFreeText1(string[] args) - { - _ = args; - Console.WriteLine("\n=== TestAnnotationsFreeText1 ======================="); - - Document doc = new Document(); - Page page = doc.NewPage(); - - Rect r1 = new Rect(100, 100, 200, 150); - Rect r2 = r1 + new Rect(0, 75, 0, 75); - Rect r3 = r2 + new Rect(0, 75, 0, 75); - - string t = "¡Un pequeño texto para practicar!"; - - Annot a1 = page.AddFreeTextAnnot(r1, t, textColor: Constants.red); - Annot a2 = page.AddFreeTextAnnot(r2, t, fontName: "Ti", textColor: Constants.blue); - Annot a3 = page.AddFreeTextAnnot(r3, t, fontName: "Co", textColor: Constants.blue, rotate: 90); - a3.SetBorder(width: 0); - a3.Update(fontSize: 8, fillColor: Constants.gold); - - doc.Save("a-freetext.pdf"); - doc.Close(); - - Console.WriteLine("Saved to a-freetext.pdf"); - } - - /// FreeText with rich text, styling, and callout line. - internal static void TestAnnotationsFreeText2(string[] args) - { - _ = args; - Console.WriteLine("\n=== TestAnnotationsFreeText2 ======================="); - - string ds = "font-size: 11pt; font-family: sans-serif;"; - string bullet = "\u2610\u2611\u2612"; - - string text = $@"

-MuPDF.NET འདི་ ཡིག་ཆ་བཀྲམ་སྤེལ་གྱི་དོན་ལུ་ པའི་ཐོན་ཐུམ་སྒྲིལ་དྲག་ཤོས་དང་མགྱོགས་ཤོས་ཅིག་ཨིན། -Here is some bold and italic text, followed by bold-italic. Text-based check boxes: {bullet}. -

"; - - Document doc = new Document(); - Page page = doc.NewPage(); - - Rect rect = new Rect(100, 100, 350, 200); - Point p2 = rect.TopRight + new Point(50, 30); - Point p3 = p2 + new Point(0, 30); - - Annot annot = page.AddFreeTextAnnot( - rect, - text, - fillColor: Constants.gold, - opacity: 1, - rotate: 0, - borderWidth: 1, - dashes: null, - richtext: true, - style: ds, - callout: new Point[] { p3, p2, rect.TopRight }, - lineEnd: PdfLineEnding.PDF_ANNOT_LE_OPEN_ARROW, - borderColor: Constants.green - ); - - const string outName = "AnnotationsFreeText2.pdf"; - doc.Save(outName, pretty: 1); - doc.Close(); - - Console.WriteLine("Saved to " + outName); - } - } -} diff --git a/Demo/Samples/Barcodes/Program.Barcodes.cs b/Demo/Samples/Barcodes/Program.Barcodes.cs deleted file mode 100644 index 792c161a..00000000 --- a/Demo/Samples/Barcodes/Program.Barcodes.cs +++ /dev/null @@ -1,341 +0,0 @@ -namespace Demo -{ - internal partial class Program - { - internal static void TestWriteBarcode1() - { - Console.WriteLine("\n=== TestWriteBarcode1 ====================="); - - string testFilePath = Path.GetFullPath("../../../TestDocuments/Blank.pdf"); - Document doc = new Document(testFilePath); - - Page page = doc[0]; - - // CODE39 - Rect rect = new Rect( - X0: Units.MmToPoints(50), - X1: Units.MmToPoints(80), - Y0: Units.MmToPoints(70), - Y1: Units.MmToPoints(85)); - - page.WriteBarcode(rect, "JJBEA6500", BarcodeFormat.CODE39, forceFitToRect: true, pureBarcode: true, narrowBarWidth:1); - - rect = new Rect( - X0: Units.MmToPoints(50), - X1: Units.MmToPoints(160), - Y0: Units.MmToPoints(100), - Y1: Units.MmToPoints(105)); - - page.WriteBarcode(rect, "JJBEA6500", BarcodeFormat.CODE39, forceFitToRect: true, pureBarcode: true, narrowBarWidth: 2); - - // CODE128 - Rect rect1 = new Rect( - X0: Units.MmToPoints(50), - X1: Units.MmToPoints(100), - Y0: Units.MmToPoints(50), - Y1: Units.MmToPoints(60)); - - page.WriteBarcode(rect1, "JJBEA6500063000000177922", BarcodeFormat.CODE128, forceFitToRect: false, pureBarcode: true, narrowBarWidth: 1); - - rect1 = new Rect( - X0: Units.MmToPoints(50), - X1: Units.MmToPoints(200), - Y0: Units.MmToPoints(80), - Y1: Units.MmToPoints(120)); - - page.WriteBarcode(rect1, "JJBEA6500063000000177922", BarcodeFormat.CODE128, forceFitToRect: true, pureBarcode: true, narrowBarWidth: 1); - - Rect rect2 = new Rect( - X0: Units.MmToPoints(100), - X1: Units.MmToPoints(140), - Y0: Units.MmToPoints(40), - Y1: Units.MmToPoints(80)); - - page.WriteBarcode(rect2, "01030000110444408000", BarcodeFormat.DM, forceFitToRect: false, pureBarcode: true, narrowBarWidth: 3); - - Pixmap pxmp = Utils.GetBarcodePixmap("JJBEA6500063000000177922", BarcodeFormat.CODE128, width: 500, pureBarcode: true, marginLeft:0, marginTop:0, marginRight:0, marginBottom:0, narrowBarWidth: 1); - - pxmp.Save(@"PxmpBarcode3.png"); - - byte[] imageBytes = pxmp.ToBytes(); - - using var stream = new SKMemoryStream(imageBytes); - using var codec = SKCodec.Create(stream); - var info = codec.Info; - var bitmap = SKBitmap.Decode(codec); - - using var data = bitmap.Encode(SKEncodedImageFormat.Png, 100); // 100 = quality - using var stream1 = File.OpenWrite(@"output.png"); - data.SaveTo(stream1); - - doc.Save(@"TestWriteBarcode1.pdf"); - - page.Dispose(); - doc.Close(); - - Console.WriteLine("TestWriteBarcode1 completed."); - } - - internal static void TestReadDataMatrix() - { - int i = 0; - - Console.WriteLine("\n=== TestReadDataMatrix ======================="); - - string testFilePath = Path.GetFullPath("../../../TestDocuments/Barcodes/datamatrix.pdf"); - Document doc = new Document(testFilePath); - - Page page = doc[0]; - - List barcodes = page.ReadBarcodes(decodeEmbeddedOnly: false); - - foreach (Barcode barcode in barcodes) - { - BarcodePoint[] points = barcode.ResultPoints; - Console.WriteLine($"Page {i++} - Type: {barcode.BarcodeFormat} - Value: {barcode.Text} - Rect: [{points[0]},{points[1]}]"); - } - /* - List blocks = page.GetImageInfo(); - - foreach (Block block in blocks) - { - Rect blockRect = block.Bbox; - barcodes = page.ReadBarcodes(clip:blockRect); - foreach (Barcode barcode in barcodes) - { - BarcodePoint[] points = barcode.ResultPoints; - if (points.Length == 2) - { - Console.WriteLine($"Page {i++} - Type: {barcode.BarcodeFormat} - Value: {barcode.Text} - Rect: [{points[0]},{points[1]}]"); - } - else if (points.Length == 4) - { - Console.WriteLine($"Page {i++} - Type: {barcode.BarcodeFormat} - Value: {barcode.Text} - Rect: [{points[0]},{points[2]}]"); - } - } - } - */ - /* - List imlist = page.GetImages(); - foreach (Entry im in imlist) - { - ImageInfo img = doc.ExtractImage(im.Xref); - File.WriteAllBytes(@"copy.png", img.Image); - - List barcodes = Utils.ReadBarcodes(@"copy.png", new Rect(0,0,img.Width,img.Height)); - - foreach (Barcode barcode in barcodes) - { - BarcodePoint[] points = barcode.ResultPoints; - Console.WriteLine($"Page {i++} - Type: {barcode.BarcodeFormat} - Value: {barcode.Text} - Rect: [{points[0]},{points[1]}]"); - } - } - */ - - page.Dispose(); - doc.Close(); - } - - - internal static void TestReadBarcode(string[] args) - { - int i = 0; - - Console.WriteLine("\n=== TestReadBarcode ======================="); - - Console.WriteLine("--- Read from image file ----------"); - string testFilePath1 = Path.GetFullPath("../../../TestDocuments/Barcodes/rendered.bmp"); - - Rect rect1 = new Rect(1260, 390, 1720, 580); - List barcodes2 = Utils.ReadBarcodes(testFilePath1, clip:rect1); - - i = 0; - foreach (Barcode barcode in barcodes2) - { - BarcodePoint[] points = barcode.ResultPoints; - Console.WriteLine($"Page {i++} - Type: {barcode.BarcodeFormat} - Value: {barcode.Text} - Rect: [{points[0]},{points[1]}]"); - } - - Console.WriteLine("--- Read from pdf file ----------"); - - string testFilePath = Path.GetFullPath("../../../TestDocuments/Barcodes/Samples.pdf"); - Document doc = new Document(testFilePath); - - Page page = doc[0]; - //Rect rect = new Rect(290, 590, 420, 660); - List barcodes = page.ReadBarcodes(); - - foreach (Barcode barcode in barcodes) - { - BarcodePoint[] points = barcode.ResultPoints; - Console.WriteLine($"Page {i++} - Type: {barcode.BarcodeFormat} - Value: {barcode.Text} - Rect: [{points[0]},{points[1]}]"); - } - doc.Close(); - } - - internal static void TestReadQrCode(string[] args) - { - Console.WriteLine("\n=== TestReadQrCode ======================="); - int i = 0; - /* - Console.WriteLine("=== Read from image file ====================="); - string testFilePath1 = Path.GetFullPath("../../../TestDocuments/Barcodes/2.png"); - - List barcodes2 = Utils.ReadBarcodes(testFilePath1, autoRotate:true); - - i = 0; - foreach (Barcode barcode in barcodes2) - { - BarcodePoint[] points = barcode.ResultPoints; - Console.WriteLine($"Page {i++} - Type: {barcode.BarcodeFormat} - Value: {barcode.Text} - Rect: [{points[0]},{points[1]}]"); - } - */ - ///* - Console.WriteLine("--- Read from pdf file ----------"); - - string testImagePath = @"test.png"; - string testFilePath = Path.GetFullPath("../../../TestDocuments/Barcodes/input.pdf"); - Document doc = new Document(testFilePath); - - Page page = doc[0]; - page.RemoveRotation(); // remove rotation to read barcodes correctly - - // Apply 2x scale (both X and Y) - var matrix = new Matrix(3.0f, 3.0f); - - // Render the page using the scaled matrix - var pixmap = page.GetPixmap(matrix); - - pixmap.GammaWith(3.2f); // apply gamma correction to improve barcode detection - - pixmap.Save(testImagePath); - - /* - Rect rect = new Rect(400, 700, page.Rect.X1, page.Rect.Y1); - List barcodes = page.ReadBarcodes(rect); - - foreach (Barcode barcode in barcodes) - { - BarcodePoint[] points = barcode.ResultPoints; - Console.WriteLine($"Page {i++} - Type: {barcode.BarcodeFormat} - Value: {barcode.Text} - Rect: [{points[0]},{points[1]}]"); - } - */ - - pixmap.Dispose(); - doc.Close(); - - List barcodes2 = Utils.ReadBarcodes(testImagePath); - - i = 0; - foreach (Barcode barcode in barcodes2) - { - BarcodePoint[] points = barcode.ResultPoints; - Console.WriteLine($"Page {i++} - Type: {barcode.BarcodeFormat} - Value: {barcode.Text} - Rect: [{points[0]},{points[1]}]"); - } - //*/ - } - - internal static void TestWriteBarcode(string[] args) - { - Console.WriteLine("\n=== TestWriteBarcode ======================="); - Console.WriteLine("--- Write to pdf file ----------"); - string testFilePath = Path.GetFullPath("../../../TestDocuments/Blank.pdf"); - Document doc = new Document(testFilePath); - Page page = doc[0]; - - MuPDF.NET.TextWriter writer = new MuPDF.NET.TextWriter(page.Rect); - Font font = new Font("cour", isBold: 1); - writer.FillTextbox(page.Rect, "QR_CODE", font, pos: new Point(0, 10)); - writer.FillTextbox(page.Rect, "EAN_8", font, pos: new Point(0, 110)); - writer.FillTextbox(page.Rect, "EAN_13", font, pos: new Point(0, 165)); - writer.FillTextbox(page.Rect, "UPC_A", font, pos: new Point(0, 220)); - writer.FillTextbox(page.Rect, "CODE_39", font, pos: new Point(0, 275)); - writer.FillTextbox(page.Rect, "CODE_128", font, pos: new Point(0, 330)); - writer.FillTextbox(page.Rect, "ITF", font, pos: new Point(0, 385)); - writer.FillTextbox(page.Rect, "PDF_417", font, pos: new Point(0, 440)); - writer.FillTextbox(page.Rect, "CODABAR", font, pos: new Point(0, 520)); - writer.FillTextbox(page.Rect, "DATA_MATRIX", font, pos: new Point(0, 620)); - writer.WriteText(page); - - // QR_CODE - Rect rect = new Rect(100, 20, 300, 80); - page.WriteBarcode(rect, "Hello World!", BarcodeFormat.QR, forceFitToRect:false, pureBarcode:false, marginLeft:0); - - // EAN_8 - rect = new Rect(100, 100, 300, 120); - page.WriteBarcode(rect, "1234567", BarcodeFormat.EAN8, forceFitToRect: false, pureBarcode: false, marginBottom: 20); - - // EAN_13 - rect = new Rect(100, 155, 300, 200); - page.WriteBarcode(rect, "123456789012", BarcodeFormat.EAN13, forceFitToRect: false, pureBarcode: true, marginBottom: 0); - - // UPC_A - rect = new Rect(100, 210, 300, 255); - page.WriteBarcode(rect, "123456789012", BarcodeFormat.UPC_A, forceFitToRect: false, pureBarcode: true, marginBottom: 0); - - // CODE_39 - rect = new Rect(100, 265, 600, 285); - page.WriteBarcode(rect, "Hello World!", BarcodeFormat.CODE39, forceFitToRect: false, pureBarcode: false, marginBottom: 0); - - // CODE_128 - rect = new Rect(100, 320, 400, 355); - page.WriteBarcode(rect, "Hello World!", BarcodeFormat.CODE128, forceFitToRect: true, pureBarcode: true, marginBottom: 0); - - // ITF - rect = new Rect(100, 385, 300, 420); - page.WriteBarcode(rect, "12345678901234567890", BarcodeFormat.I2OF5, forceFitToRect: false, pureBarcode: false, marginBottom: 0); - - // PDF_417 - rect = new Rect(100, 430, 400, 435); - page.WriteBarcode(rect, "Hello World!", BarcodeFormat.PDF417, forceFitToRect: false, pureBarcode: true, marginBottom: 0); - - // CODABAR - rect = new Rect(100, 540, 400, 580); - page.WriteBarcode(rect, "12345678901234567890", BarcodeFormat.CODABAR, forceFitToRect: false, pureBarcode: true, marginBottom: 0); - - // DATA_MATRIX - rect = new Rect(100, 620, 140, 660); - page.WriteBarcode(rect, "01100000110419257000", BarcodeFormat.DM, forceFitToRect: false, pureBarcode: false, marginBottom: 0); - - doc.Save("barcode.pdf"); - - Console.WriteLine($"Barcodes written to 'barcode.pdf' in: {page.Rect}"); - doc.Close(); - - Console.WriteLine("--- Write to image file ----------"); - - // QR_CODE - Utils.WriteBarcode("QR_CODE.png", "Hello World!", BarcodeFormat.QR, width: 600, height: 600, forceFitToRect: true, pureBarcode: false, marginBottom: 0); - - // EAN_8 - Utils.WriteBarcode("EAN_8.png", "1234567", BarcodeFormat.EAN8, width: 300, height: 20, forceFitToRect: false, pureBarcode: false, marginBottom: 4); - - // EAN_13 - Utils.WriteBarcode("EAN_13.png", "123456789012", BarcodeFormat.EAN13, width: 300, height: 0, forceFitToRect: false, pureBarcode: false, marginBottom: 10); - - // UPC_A - Utils.WriteBarcode("UPC_A.png", "123456789012", BarcodeFormat.UPC_A, width: 300, height: 20, forceFitToRect: false, pureBarcode: false, marginBottom: 10); - - // CODE_39 - Utils.WriteBarcode("CODE_39.png", "Hello World!", BarcodeFormat.CODE39, width: 300, height: 70, forceFitToRect: false, pureBarcode: false, marginBottom: 20); - - // CODE_128 - Utils.WriteBarcode("CODE_128.png", "Hello World!", BarcodeFormat.CODE128, width: 300, height: 150, forceFitToRect: false, pureBarcode: false, marginBottom: 20); - - // ITF - Utils.WriteBarcode("ITF.png", "12345678901234567890", BarcodeFormat.I2OF5, width: 300, height: 120, forceFitToRect: false, pureBarcode: false, marginBottom: 20); - - // PDF_417 - Utils.WriteBarcode("PDF_417.png", "Hello World!", BarcodeFormat.PDF417, width: 300, height: 10, forceFitToRect: false, pureBarcode: false, marginBottom: 0); - - // CODABAR - Utils.WriteBarcode("CODABAR.png", "12345678901234567890", BarcodeFormat.CODABAR, width: 300, height: 150, forceFitToRect: false, pureBarcode: false, marginBottom: 20); - - // DATA_MATRIX - Utils.WriteBarcode("DATA_MATRIX.png", "01100000110419257000", BarcodeFormat.DM, width: 300, height: 300, forceFitToRect: false, pureBarcode: true, marginBottom: 1); - - Console.WriteLine("Barcodes written to image files in the current directory."); - } - - } -} diff --git a/Demo/Samples/Document/Program.Document.cs b/Demo/Samples/Document/Program.Document.cs deleted file mode 100644 index 0e23500a..00000000 --- a/Demo/Samples/Document/Program.Document.cs +++ /dev/null @@ -1,125 +0,0 @@ -namespace Demo -{ - internal partial class Program - { - internal static void TestMoveFile() - { - string origfilename = @"../../../TestDocuments/Blank.pdf"; - - string filePath = @"testmove.pdf"; - - File.Copy(origfilename, filePath, true); - - Document d = new Document(filePath); - - Page page = d[0]; - - Point tl = new Point(100, 120); - Point br = new Point(300, 150); - - Rect rect = new Rect(tl, br); - - TextWriter pw = new TextWriter(page.TrimBox); - /* - Font font = new Font(fontName: "tiro"); - - List<(string, float)> ret = pw.FillTextbox(rect, "This is a test to overwrite the original file and move it", font, fontSize: 24); - */ - pw.WriteText(page); - - page.Dispose(); - - MemoryStream tmp = new MemoryStream(); - - d.Save(tmp, garbage: 3, deflateFonts: 1, deflate: 1); - - d.Close(); - - File.WriteAllBytes(filePath, tmp.ToArray()); - - tmp.Dispose(); - - File.Move(filePath, @"moved.pdf", true); - } - - internal static void TestMetadata() - { - Console.WriteLine("\n=== TestMetadata ====================="); - - string testFilePath = @"../../../TestDocuments/Annot.pdf"; - - Document doc = new Document(testFilePath); - - Dictionary metaDict = doc.MetaData; - - foreach (string key in metaDict.Keys) - { - Console.WriteLine(key + ": " + metaDict[key]); - } - - doc.Close(); - - Console.WriteLine("TestMetadata completed."); - } - - internal static void TestMorph() - { - Console.WriteLine("\n=== TestMorph ====================="); - - string testFilePath = @"../../../TestDocuments/Morph.pdf"; - - Document doc = new Document(testFilePath); - Page page = doc[0]; - Rect printrect = new Rect(180, 30, 650, 60); - int pagerot = page.Rotation; - TextWriter pw = new TextWriter(page.TrimBox); - string txt = "Origin 100.100"; - pw.Append(new Point(100, 100), txt, new Font("tiro"), fontSize: 24); - pw.WriteText(page); - - txt = "rotated 270 - 100.100"; - Matrix matrix = new IdentityMatrix(); - matrix.Prerotate(270); - Morph mo = new Morph(new Point(100, 100), matrix); - pw = new TextWriter(page.TrimBox); - pw.Append(new Point(100, 100), txt, new Font("tiro"), fontSize: 24); - pw.WriteText(page, morph:mo); - page.SetRotation(270); - - page.Dispose(); - doc.Save(@"morph.pdf"); - doc.Close(); - } - - internal static void TestUnicodeDocument() - { - Console.WriteLine("\n=== TestUnicodeDocument ====================="); - - string testFilePath = @"../../../TestDocuments/Σ╜áσÑ╜.pdf"; - - Document doc = new Document(testFilePath); - - doc.Save(@"Σ╜áσÑ╜_.pdf"); - doc.Close(); - - Console.WriteLine("TestUnicodeDocument completed."); - } - - internal static void TestMemoryLeak() - { - Console.WriteLine("\n=== TestMemoryLeak ======================="); - string testFilePath = Path.GetFullPath("../../../TestDocuments/Blank.pdf"); - - for (int i = 0; i < 100; i++) - { - Document doc = new Document(testFilePath); - Page page = doc.NewPage(); - page.Dispose(); - doc.Close(); - } - - Console.WriteLine("Memory leak test completed. No leaks should be detected."); - } - - } -} diff --git a/Demo/Samples/ImageFilters/Program.ImageFilters.cs b/Demo/Samples/ImageFilters/Program.ImageFilters.cs deleted file mode 100644 index f51e3d7a..00000000 --- a/Demo/Samples/ImageFilters/Program.ImageFilters.cs +++ /dev/null @@ -1,86 +0,0 @@ -namespace Demo -{ - internal partial class Program - { - internal static void TestImageFilter() - { - const string inputPath = @"../../../TestDocuments/Image/table.jpg"; - const string outputPath = @"output.png"; - - // Load the image file into SKBitmap - using (var bitmap = SKBitmap.Decode(inputPath)) - { - if (bitmap == null) - { - Console.WriteLine("Failed to load image."); - return; - } - - SKBitmap inputBitmap = bitmap.Copy(); - - // build the pipeline - var pipeline = new ImageFilterPipeline(); - - // clear any defaults if youΓÇÖre reusing the instance - pipeline.Clear(); - - // add filters one-by-one - pipeline.AddDeskew(minAngle: 0.5); // replaces any existing deskew step - pipeline.AddRemoveHorizontalLines(); // also replaces existing horizontal-removal step - pipeline.AddRemoveVerticalLines(); - pipeline.AddGrayscale(); - //pipeline.AddMedian(blockSize: 2, replaceExisting: true); - //pipeline.AddGamma(gamma: 1.2); // brighten slightly - //pipeline.AddContrast(contrast: 100); - //pipeline.AddFit(100); - //pipeline.AddDilation(); - //pipeline.AddScale(scaleFactor: 1.75, quality: SKFilterQuality.Medium); - pipeline.AddInvert(); - - // apply the pipeline (bitmap is modified in place) - pipeline.Apply(ref inputBitmap); - - using (var data = inputBitmap.Encode(SKEncodedImageFormat.Png, 100)) // 100 = quality - { - using (var stream = File.OpenWrite(outputPath)) - { - data.SaveTo(stream); - } - } - - Console.WriteLine($"Loaded image: {bitmap.Width}x{bitmap.Height} pixels"); - } - } - - internal static void TestImageFilterOcr() - { - const string inputPath = @"../../../TestDocuments/Image/boxedpage.jpg"; - - using (Pixmap pxmp = new Pixmap(inputPath)) - { - // build the pipeline - var pipeline = new ImageFilterPipeline(); - - // clear any defaults if youΓÇÖre reusing the instance - pipeline.Clear(); - - // add filters one-by-one - //pipeline.AddDeskew(minAngle: 0.5); // replaces any existing deskew step - //pipeline.AddRemoveHorizontalLines(); // also replaces existing horizontal-removal step - //pipeline.AddRemoveVerticalLines(); - //pipeline.AddGrayscale(); - //pipeline.AddMedian(blockSize: 2, replaceExisting: true); - pipeline.AddGamma(gamma: 1.2); // brighten slightly - //pipeline.AddContrast(contrast: 100); - //pipeline.AddScaleFit(100); - //pipeline.AddDilation(); - pipeline.AddScale(scaleFactor: 1.75, quality: SKFilterQuality.High); - //pipeline.AddInvert(); - - string txt = pxmp.GetTextFromOcr(pipeline); - Console.WriteLine(txt); - } - } - - } -} diff --git a/Demo/Samples/Llm/Program.Llm.PdfMarkdownReader.Fixtures.cs b/Demo/Samples/Llm/Program.Llm.PdfMarkdownReader.Fixtures.cs deleted file mode 100644 index 57719380..00000000 --- a/Demo/Samples/Llm/Program.Llm.PdfMarkdownReader.Fixtures.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace Demo -{ - /// - /// demos aligned with PDF4LLM / repository reader tests. - /// - internal partial class Program - { - /// Empty in-memory PDF, save, then (one page → one Llama document). - internal static void Test4LlmPdfMarkdownReaderEmptyPage(string[] args) - { - _ = args; - Console.WriteLine("\n=== Test4LlmPdfMarkdownReaderEmptyPage ======================="); - - string path = Path.Combine(AppContext.BaseDirectory, "llm_reader_empty_page.pdf"); - Document document = new Document(); - try - { - document.NewPage(); - document.Save(path); - } - finally - { - document.Close(); - } - - var reader = new PDFMarkdownReader(); - var documents = reader.LoadData(path); - Console.WriteLine($"Loaded {documents.Count} document(s)."); - } - - /// with a non-existent path → . - internal static void Test4LlmPdfMarkdownReaderMissingFile(string[] args) - { - _ = args; - Console.WriteLine("\n=== Test4LlmPdfMarkdownReaderMissingFile ======================="); - - var reader = new PDFMarkdownReader(); - try - { - reader.LoadData(Path.Combine(LlmRepositoryTestsDirectory(), "fake", "path", "nope.pdf")); - Console.WriteLine("Unexpected: LoadData should throw for missing file."); - } - catch (FileNotFoundException ex) - { - Console.WriteLine($"OK: FileNotFoundException — {ex.Message}"); - } - } - } -} diff --git a/Demo/Samples/Llm/Program.Llm.TableExtract.cs b/Demo/Samples/Llm/Program.Llm.TableExtract.cs deleted file mode 100644 index fd738f60..00000000 --- a/Demo/Samples/Llm/Program.Llm.TableExtract.cs +++ /dev/null @@ -1,135 +0,0 @@ -using Newtonsoft.Json.Linq; - -namespace Demo -{ - internal partial class Program - { - internal static void TestTableExtract1() - { - JArray pages = GetPagesFromJson(PdfExtractor.ToJson(@"..\..\..\TestDocuments\national-capitals.pdf")); - - foreach (JObject page in pages) - { - int pageNum = page["page_number"]!.Value(); - Console.WriteLine($"\nPage {pageNum}"); - - foreach (JObject box in (page["boxes"] as JArray)?.OfType() ?? Enumerable.Empty()) - { - if (!string.Equals(box["boxclass"]?.Value(), "table", StringComparison.Ordinal)) continue; - - var rows = ParseTableRows(box["table"]); - int rowCount = rows.Count; - int columnCount = rowCount > 0 ? rows.Max(r => r?.Count ?? 0) : 0; - Console.WriteLine($"Table: {rowCount} rows x {columnCount} columns"); - - foreach (var row in rows) - Console.WriteLine(string.Join(" | ", row ?? [])); - } - } - } - - internal static void TestTableExtract2() - { - JArray pages = GetPagesFromJson(PdfExtractor.ToJson(@"..\..\..\TestDocuments\national-capitals.pdf")); - var csvLines = new List(); - - foreach (JObject page in pages) - { - foreach (JObject box in (page["boxes"] as JArray)?.OfType() ?? Enumerable.Empty()) - { - if (!string.Equals(box["boxclass"]?.Value(), "table", StringComparison.Ordinal)) continue; - - var rows = ParseTableRows(box["table"]); - foreach (var row in rows) - { - var escaped = (row ?? []).Select(cell => - cell.Contains(',') || cell.Contains('"') - ? $"\"{cell.Replace("\"", "\"\"")}\"" - : cell - ); - csvLines.Add(string.Join(",", escaped)); - } - - csvLines.Add(string.Empty); - } - } - - File.WriteAllLines("tables.csv", csvLines, Encoding.UTF8); - } - - internal static void TestTableExtract3() - { - JArray pages = GetPagesFromJson(PdfExtractor.ToJson(@"..\..\..\TestDocuments\national-capitals.pdf")); - var mergedRows = new List>(); - int? prevColCount = null; - - foreach (JObject page in pages) - { - foreach (JObject box in (page["boxes"] as JArray)?.OfType() ?? Enumerable.Empty()) - { - if (!string.Equals(box["boxclass"]?.Value(), "table", StringComparison.Ordinal)) continue; - - var rows = ParseTableRows(box["table"]); - if (rows.Count == 0) - { - prevColCount = null; - continue; - } - - int colCount = rows.Max(r => r?.Count ?? 0); - if (colCount > 0 && colCount == prevColCount) - mergedRows.AddRange(rows.Skip(1)); - else - mergedRows.AddRange(rows); - - prevColCount = colCount > 0 ? colCount : null; - } - } - - Console.WriteLine($"Merged table: {mergedRows.Count} rows"); - foreach (var row in mergedRows) - Console.WriteLine(string.Join(" | ", row ?? [])); - } - - internal static void TestOcr() - { - PdfExtractor.ToMarkdown(@"..\..\..\TestDocuments\Ocr.pdf", useOcr: true, writeImages: false, embedImages: false); - string text = PdfExtractor.ToText(@"..\..\..\TestDocuments\Ocr.pdf", useOcr: true); - Console.WriteLine(text); - } - - internal static void TestLLM2() - { - var reader = PdfExtractor.LlamaMarkdownReader(); - var chunks = reader.LoadData(@"..\..\..\TestDocuments\magazine.pdf"); - - Directory.CreateDirectory("Output"); - foreach (var chunk in chunks) - { - int pageNum = (int)chunk.ExtraInfo["page"]; - Console.WriteLine(pageNum); - string filePath = $"output/page-{pageNum}.md"; - File.WriteAllText(filePath, chunk.Text, Encoding.UTF8); - } - } - - private static JArray GetPagesFromJson(string json) - { - JToken root = JToken.Parse(json); - return root switch - { - JArray arr => arr, - JObject obj when obj["pages"] is JArray arr => arr, - _ => throw new InvalidOperationException("Expected a JSON array or an object containing a 'pages' array.") - }; - } - - private static List> ParseTableRows(JToken tableToken) => - tableToken switch - { - JArray arr => arr.ToObject>>() ?? [], - JObject obj when obj["extract"] is JArray extract => extract.ToObject>>() ?? [], - _ => [] - }; - } -} diff --git a/Demo/Samples/Llm/Program.Llm.ToMarkdown.Fixtures.cs b/Demo/Samples/Llm/Program.Llm.ToMarkdown.Fixtures.cs deleted file mode 100644 index d7661dfa..00000000 --- a/Demo/Samples/Llm/Program.Llm.ToMarkdown.Fixtures.cs +++ /dev/null @@ -1,211 +0,0 @@ -namespace Demo -{ - /// - /// PDF4LLM demos aligned with repository tests/ fixtures (golden markdown, OCR behavior). - /// PDFs live under repo tests/; samples skip if files are missing. - /// - internal partial class Program - { - private static string LlmRepositoryRootFromAppBase() => - Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..")); - - private static string LlmRepositoryTestsDirectory() => - Path.Combine(LlmRepositoryRootFromAppBase(), "tests"); - - private static bool LlmOcrEnvironmentLikelyAvailable() => - !string.IsNullOrEmpty(Utils.TESSDATA_PREFIX); - - /// ToMarkdown with fixed flags vs tests/test_370_expected.md (fixture: tests/test_370.pdf). - internal static void Test4LlmToMarkdownCompareExpected370(string[] args) - { - _ = args; - Console.WriteLine("\n=== Test4LlmToMarkdownCompareExpected370 (PDF4LLM) ======================="); - - string testsDir = LlmRepositoryTestsDirectory(); - string pdfPath = Path.Combine(testsDir, "test_370.pdf"); - string expectedPath = Path.Combine(testsDir, "test_370_expected.md"); - if (!File.Exists(pdfPath) || !File.Exists(expectedPath)) - { - Console.WriteLine($"Skip: need test_370.pdf and test_370_expected.md in: {testsDir}"); - return; - } - - string expected = File.ReadAllText(expectedPath, Encoding.UTF8); - Document document = new Document(pdfPath); - try - { - string actual = ToMarkdown( - document, - header: false, - footer: false, - writeImages: false, - embedImages: false, - imageFormat: "jpg", - showProgress: true, - forceText: true, - pageSeparators: true); - - string actualPath = Path.Combine(AppContext.BaseDirectory, "llm_fixture_370_actual.md"); - File.WriteAllText(actualPath, actual, Encoding.UTF8); - Console.WriteLine($"Wrote actual markdown: {actualPath}"); - - if (!string.Equals(actual, expected, StringComparison.Ordinal)) - { - Console.WriteLine("Mismatch vs tests/test_370_expected.md (first differences):"); - LlmPrintLineDiff(expected, actual, maxLines: 40); - } - else - { - Console.WriteLine("OK: actual matches test_370_expected.md"); - } - } - finally - { - document.Close(); - } - } - - /// Default ToMarkdown on FFFD fixture; U+FFFD vs TESSDATA_PREFIX (fixture: tests/test_ocr_loremipsum_FFFD.pdf). - internal static void Test4LlmToMarkdownOcrFixture1(string[] args) - { - _ = args; - Console.WriteLine("\n=== Test4LlmToMarkdownOcrFixture1 ======================="); - - string pdfPath = Path.Combine(LlmRepositoryTestsDirectory(), "test_ocr_loremipsum_FFFD.pdf"); - if (!File.Exists(pdfPath)) - { - Console.WriteLine($"Skip: missing {pdfPath}"); - return; - } - - Document doc = new Document(pdfPath); - string md; - try - { - md = ToMarkdown(doc); - } - finally - { - doc.Close(); - } - - File.WriteAllText(Path.Combine(AppContext.BaseDirectory, "llm_ocr_fixture_1.md"), md, Encoding.UTF8); - bool ocr = LlmOcrEnvironmentLikelyAvailable(); - Console.WriteLine($"TESSDATA_PREFIX set: {ocr}"); - bool hasReplacement = md.Contains(TesseractApi.ReplacementUnicode); - if (ocr && hasReplacement) - Console.WriteLine("Note: U+FFFD still present—check tessdata / language / PDF."); - else if (ocr && !hasReplacement) - Console.WriteLine("OK: no U+FFFD when tessdata is configured."); - else if (!ocr && hasReplacement) - Console.WriteLine("OK: U+FFFD present without tessdata."); - else - Console.WriteLine("Note: no U+FFFD without tessdata—compare llm_ocr_fixture_1.md."); - } - - /// ToMarkdown(..., useOcr: false) on FFFD fixture. - internal static void Test4LlmToMarkdownOcrFixture2(string[] args) - { - _ = args; - Console.WriteLine("\n=== Test4LlmToMarkdownOcrFixture2 ======================="); - - string pdfPath = Path.Combine(LlmRepositoryTestsDirectory(), "test_ocr_loremipsum_FFFD.pdf"); - if (!File.Exists(pdfPath)) - { - Console.WriteLine($"Skip: missing {pdfPath}"); - return; - } - - Document doc = new Document(pdfPath); - string md; - try - { - md = ToMarkdown(doc, useOcr: false); - } - finally - { - doc.Close(); - } - - File.WriteAllText(Path.Combine(AppContext.BaseDirectory, "llm_ocr_fixture_2.md"), md, Encoding.UTF8); - bool hasReplacement = md.Contains(TesseractApi.ReplacementUnicode); - Console.WriteLine(hasReplacement - ? "OK: U+FFFD present with useOcr=false." - : "Note: no U+FFFD with OCR off—fixture-dependent."); - } - - /// SVG text fixture: compare default vs useOcr: false output size (fixture: tests/test_ocr_loremipsum_svg.pdf). - internal static void Test4LlmToMarkdownOcrFixture3(string[] args) - { - _ = args; - Console.WriteLine("\n=== Test4LlmToMarkdownOcrFixture3 ======================="); - - string pdfPath = Path.Combine(LlmRepositoryTestsDirectory(), "test_ocr_loremipsum_svg.pdf"); - if (!File.Exists(pdfPath)) - { - Console.WriteLine($"Skip: missing {pdfPath}"); - return; - } - - Document doc = new Document(pdfPath); - string md; - string mdNoOcr; - try - { - md = ToMarkdown(doc); - mdNoOcr = ToMarkdown(doc, useOcr: false); - } - finally - { - doc.Close(); - } - - string baseDir = AppContext.BaseDirectory; - File.WriteAllText(Path.Combine(baseDir, "llm_ocr_fixture_3.md"), md, Encoding.UTF8); - File.WriteAllText(Path.Combine(baseDir, "llm_ocr_fixture_3_no_ocr.md"), mdNoOcr, Encoding.UTF8); - - bool ocr = LlmOcrEnvironmentLikelyAvailable(); - if (ocr) - { - if (mdNoOcr.Length < md.Length) - Console.WriteLine($"OK: with tessdata, no-OCR shorter ({mdNoOcr.Length} < {md.Length})."); - else - Console.WriteLine($"Note: lengths OCR={md.Length}, no-OCR={mdNoOcr.Length} (environment-dependent)."); - } - else - { - Console.WriteLine(string.Equals(md, mdNoOcr, StringComparison.Ordinal) - ? "OK: without tessdata, OCR on/off often match." - : "Note: outputs differ; compare llm_ocr_fixture_3*.md."); - } - } - - private static void LlmPrintLineDiff(string expected, string actual, int maxLines) - { - string[] a = expected.Replace("\r\n", "\n").Split('\n'); - string[] b = actual.Replace("\r\n", "\n").Split('\n'); - int n = Math.Max(a.Length, b.Length); - int printed = 0; - for (int i = 0; i < n && printed < maxLines; i++) - { - string lineA = i < a.Length ? a[i] : ""; - string lineB = i < b.Length ? b[i] : ""; - if (lineA == lineB) - continue; - Console.WriteLine($" line {i + 1}:"); - Console.WriteLine($" expected: {LlmTruncateForConsole(lineA)}"); - Console.WriteLine($" actual: {LlmTruncateForConsole(lineB)}"); - printed++; - } - if (printed >= maxLines) - Console.WriteLine(" ... (truncated)"); - } - - private static string LlmTruncateForConsole(string s, int max = 200) - { - if (string.IsNullOrEmpty(s)) - return s; - return s.Length <= max ? s : s.Substring(0, max) + "…"; - } - } -} diff --git a/Demo/Samples/Llm/Program.Llm.cs b/Demo/Samples/Llm/Program.Llm.cs deleted file mode 100644 index 8686b497..00000000 --- a/Demo/Samples/Llm/Program.Llm.cs +++ /dev/null @@ -1,387 +0,0 @@ -namespace Demo -{ - internal partial class Program - { - internal static void TestMarkdownReader() - { - Console.WriteLine("\n=== TestMarkdownReader ======================="); - - var reader = new PDFMarkdownReader(); - string testFilePath = Path.GetFullPath("../../../TestDocuments/columns.pdf"); - - var docs = reader.LoadData(testFilePath); - - foreach (var doc in docs) - { - Console.WriteLine(doc.Text); - } - } - - internal static void TestGetText() - { - Console.WriteLine("\n=== TestGetText ======================="); - - var reader = new PDFMarkdownReader(); - string testFilePath = Path.GetFullPath("../../../TestDocuments/columns.pdf"); - - Document doc = new Document(testFilePath); - - for (int i = 0; i < doc.PageCount; i++) - { - Page page = doc[i]; - - var text = Utils.GetText(page, option: "dict"); - - Console.WriteLine(text); - - page.Dispose(); - } - - doc.Close(); - } - - internal static void TestTable() - { - Console.WriteLine("\n=== TestTable ======================="); - - try - { - string testFilePath = Path.GetFullPath("../../../TestDocuments/err_table.pdf"); - - if (!File.Exists(testFilePath)) - { - Console.WriteLine($"Error: Test file not found: {testFilePath}"); - return; - } - - Console.WriteLine($"Loading PDF: {testFilePath}"); - Document doc = new Document(testFilePath); - Console.WriteLine($"Document loaded: {doc.PageCount} page(s)"); - - // Test on first page - Page page = doc[0]; - Console.WriteLine($"\nPage 0 - Rect: {page.Rect}"); - - // Test 1: Get tables with default strategy - Console.WriteLine("\n--- Test 1: Get tables with 'lines_strict' strategy ---"); - List tables = Utils.GetTables( - page, - clip: page.Rect, - vertical_strategy: "lines_strict", - horizontal_strategy: "lines_strict"); - - Console.WriteLine($"Found {tables.Count} table(s) on page 0"); - - if (tables.Count > 0) - { - for (int i = 0; i < tables.Count; i++) - { - Table table = tables[i]; - Console.WriteLine($"\n Table {i + 1}:"); - Console.WriteLine($" Rows: {table.row_count}"); - Console.WriteLine($" Columns: {table.col_count}"); - if (table.bbox != null) - { - Console.WriteLine($" BBox: ({table.bbox.X0:F2}, {table.bbox.Y0:F2}, {table.bbox.X1:F2}, {table.bbox.Y1:F2})"); - } - - // Display header information - if (table.header != null) - { - Console.WriteLine($" Header:"); - Console.WriteLine($" External: {table.header.external}"); - if (table.header.names != null && table.header.names.Count > 0) - { - Console.WriteLine($" Column names: {string.Join(", ", table.header.names)}"); - } - } - - // Extract table data - Console.WriteLine($"\n Extracting table data..."); - List> tableData = table.Extract(); - if (tableData != null && tableData.Count > 0) - { - Console.WriteLine($" Extracted {tableData.Count} row(s) of data"); - // Show first few rows as preview - int previewRows = Math.Min(3, tableData.Count); - for (int row = 0; row < previewRows; row++) - { - var rowData = tableData[row]; - if (rowData != null) - { - Console.WriteLine($" Row {row + 1}: {string.Join(" | ", rowData.Take(5))}"); // Show first 5 columns - } - } - if (tableData.Count > previewRows) - { - Console.WriteLine($" ... and {tableData.Count - previewRows} more row(s)"); - } - } - - // Convert to markdown - Console.WriteLine($"\n Converting to Markdown..."); - try - { - string markdown = table.ToMarkdown(clean: false, fillEmpty: true); - if (!string.IsNullOrEmpty(markdown)) - { - Console.WriteLine($" Markdown length: {markdown.Length} characters"); - // Save markdown to file - string markdownFile = $"table_{i + 1}_page0.md"; - File.WriteAllText(markdownFile, markdown, Encoding.UTF8); - Console.WriteLine($" Markdown saved to: {markdownFile}"); - - // Show preview - int previewLength = Math.Min(200, markdown.Length); - Console.WriteLine($" Preview (first {previewLength} chars):"); - Console.WriteLine($" {markdown.Substring(0, previewLength)}..."); - } - } - catch (Exception ex) - { - Console.WriteLine($" Error converting to markdown: {ex.Message}"); - } - } - } - else - { - Console.WriteLine("No tables found. Trying with 'lines' strategy..."); - - // Test 2: Try with 'lines' strategy (less strict) - Console.WriteLine("\n--- Test 2: Get tables with 'lines' strategy ---"); - tables = Utils.GetTables( - page, - clip: page.Rect, - vertical_strategy: "lines", - horizontal_strategy: "lines"); - - Console.WriteLine($"Found {tables.Count} table(s) with 'lines' strategy"); - } - - // Test 3: Try with 'text' strategy - Console.WriteLine("\n--- Test 3: Get tables with 'text' strategy ---"); - List
textTables = Utils.GetTables( - page, - clip: page.Rect, - vertical_strategy: "text", - horizontal_strategy: "text"); - - Console.WriteLine($"Found {textTables.Count} table(s) with 'text' strategy"); - - // Test 4: Get tables from all pages - Console.WriteLine("\n--- Test 4: Get tables from all pages ---"); - int totalTables = 0; - for (int pageNum = 0; pageNum < doc.PageCount; pageNum++) - { - Page currentPage = doc[pageNum]; - List
pageTables = Utils.GetTables( - currentPage, - clip: currentPage.Rect, - vertical_strategy: "lines_strict", - horizontal_strategy: "lines_strict"); - - if (pageTables.Count > 0) - { - Console.WriteLine($" Page {pageNum}: {pageTables.Count} table(s)"); - totalTables += pageTables.Count; - } - currentPage.Dispose(); - } - Console.WriteLine($"Total tables found across all pages: {totalTables}"); - - page.Dispose(); - doc.Close(); - - Console.WriteLine("\n=== TestTable completed successfully ==="); - } - catch (Exception ex) - { - Console.WriteLine($"Error in TestTable: {ex.Message}"); - Console.WriteLine($"Stack trace: {ex.StackTrace}"); - throw; - } - } - - internal static void TestMuPdfRagToMarkdown() - { - Console.WriteLine("\n=== TestMuPdfRagToMarkdown (legacy RAG via PDF4LLM.UseLayout=false) ======================="); - - try - { - // Find a test PDF file - //string testFilePath = Path.GetFullPath("../../../TestDocuments/national-capitals.pdf"); - string testFilePath = Path.GetFullPath("../../../TestDocuments/Magazine.pdf"); - - Document doc = new Document(testFilePath); - Console.WriteLine($"Document loaded: {doc.PageCount} page(s)"); - Console.WriteLine($"Document name: {doc.Name}"); - - // Test 1: legacy MuPdf_rag-style path (MuPdfRag is internal; use public API only) - Console.WriteLine("\n--- Test 1: PDF4LLM.ToMarkdown with UseLayout=false (same stack as internal MuPdfRag) ---"); - try - { - List pages = new List(); - pages.Add(0); - bool prev = UseLayout; - string markdown; - try - { - UseLayout = false; - markdown = ToMarkdown( - doc, - pages: pages, - writeImages: false, - embedImages: false, - imagePath: "", - imageFormat: "png", - filename: testFilePath, - forceText: true, - pageChunks: false, - pageSeparators: false, - dpi: 150, - pageWidth: 612, - pageHeight: null, - ignoreCode: false, - showProgress: false); - } - finally - { - UseLayout = prev; - } - - string markdownFile = "TestMuPdfRag_Output.md"; - File.WriteAllText(markdownFile, markdown, Encoding.UTF8); - Console.WriteLine($"Markdown output saved to: {markdownFile}"); - Console.WriteLine($"Markdown length: {markdown.Length} characters"); - if (markdown.Length > 0) - { - int previewLength = Math.Min(300, markdown.Length); - Console.WriteLine($"Preview (first {previewLength} chars):\n{markdown.Substring(0, previewLength)}..."); - } - } - catch (Exception ex) - { - Console.WriteLine($"Error in basic ToMarkdown: {ex.Message}"); - } - /* - // Test 2: ToMarkdown with IdentifyHeaders - Console.WriteLine("\n--- Test 2: ToMarkdown with IdentifyHeaders ---"); - try - { - var identifyHeaders = new IdentifyHeaders(doc, pages: null, bodyLimit: 12.0f, maxLevels: 6); - string markdown = MuPdfRag.ToMarkdown( - doc, - pages: new List { 0 }, // First page only - hdrInfo: identifyHeaders, - writeImages: false, - embedImages: false, - ignoreImages: false, - filename: testFilePath, - forceText: true, - showProgress: false - ); - - string markdownFile = "TestMuPdfRag_WithHeaders.md"; - File.WriteAllText(markdownFile, markdown, Encoding.UTF8); - Console.WriteLine($"Markdown with headers saved to: {markdownFile}"); - Console.WriteLine($"Markdown length: {markdown.Length} characters"); - } - catch (Exception ex) - { - Console.WriteLine($"Error in ToMarkdown with IdentifyHeaders: {ex.Message}"); - } - - // Test 3: ToMarkdown with TocHeaders - Console.WriteLine("\n--- Test 3: ToMarkdown with TocHeaders ---"); - try - { - var tocHeaders = new TocHeaders(doc); - string markdown = MuPdfRag.ToMarkdown( - doc, - pages: new List { 0 }, // First page only - hdrInfo: tocHeaders, - writeImages: false, - embedImages: false, - ignoreImages: false, - filename: testFilePath, - forceText: true, - showProgress: false - ); - - string markdownFile = "TestMuPdfRag_WithToc.md"; - File.WriteAllText(markdownFile, markdown, Encoding.UTF8); - Console.WriteLine($"Markdown with TOC headers saved to: {markdownFile}"); - Console.WriteLine($"Markdown length: {markdown.Length} characters"); - } - catch (Exception ex) - { - Console.WriteLine($"Error in ToMarkdown with TocHeaders: {ex.Message}"); - } - - // Test 4: ToMarkdown with page separators - Console.WriteLine("\n--- Test 4: ToMarkdown with page separators ---"); - try - { - string markdown = MuPdfRag.ToMarkdown( - doc, - pages: null, // All pages - hdrInfo: null, - writeImages: false, - embedImages: false, - ignoreImages: false, - filename: testFilePath, - forceText: true, - pageSeparators: true, // Add page separators - showProgress: false - ); - - string markdownFile = "TestMuPdfRag_WithSeparators.md"; - File.WriteAllText(markdownFile, markdown, Encoding.UTF8); - Console.WriteLine($"Markdown with page separators saved to: {markdownFile}"); - Console.WriteLine($"Markdown length: {markdown.Length} characters"); - } - catch (Exception ex) - { - Console.WriteLine($"Error in ToMarkdown with page separators: {ex.Message}"); - } - - // Test 5: ToMarkdown with progress bar - Console.WriteLine("\n--- Test 5: ToMarkdown with progress bar ---"); - try - { - string markdown = MuPdfRag.ToMarkdown( - doc, - pages: null, // All pages - hdrInfo: null, - writeImages: false, - embedImages: false, - ignoreImages: false, - filename: testFilePath, - forceText: true, - showProgress: true, // Show progress bar - pageSeparators: false - ); - - string markdownFile = "TestMuPdfRag_WithProgress.md"; - File.WriteAllText(markdownFile, markdown, Encoding.UTF8); - Console.WriteLine($"\nMarkdown with progress saved to: {markdownFile}"); - Console.WriteLine($"Markdown length: {markdown.Length} characters"); - } - catch (Exception ex) - { - Console.WriteLine($"Error in ToMarkdown with progress: {ex.Message}"); - } - */ - doc.Close(); - } - catch (Exception ex) - { - Console.WriteLine($"An unexpected error occurred during MuPdfRag test: {ex.Message}"); - Console.WriteLine($"Stack trace: {ex.StackTrace}"); - } - - Console.WriteLine("\n=== TestMuPdfRagToMarkdown Completed ======================="); - } - - } -} diff --git a/Demo/Samples/PageContent/Program.PageContent.cs b/Demo/Samples/PageContent/Program.PageContent.cs deleted file mode 100644 index 89f60423..00000000 --- a/Demo/Samples/PageContent/Program.PageContent.cs +++ /dev/null @@ -1,291 +0,0 @@ -namespace Demo -{ - internal partial class Program - { - internal static void TestExtractTextWithLayout(string[] args) - { - Console.WriteLine("\n=== TestExtractTextWithLayout ====================="); - string testFilePath = Path.GetFullPath("../../../TestDocuments/columns.pdf"); - Document doc = new Document(testFilePath); - - FileStream wstream = File.Create("columns.txt"); - - for (int i = 0; i < 1/*doc.PageCount*/; i++) - { - Page page = doc[i]; - string textWithLayout = page.GetTextWithLayout(tolerance: 3); - if (!string.IsNullOrEmpty(textWithLayout)) - { - byte[] bytes = Encoding.UTF8.GetBytes(textWithLayout); - wstream.Write(bytes, 0, bytes.Length); - } - } - - wstream.Close(); - - doc.Close(); - - Console.WriteLine("Created columns.txt file"); - } - - internal static void TestWidget(string[] args) - { - Console.WriteLine("\n=== TestWidget ====================="); - - string testFilePath = Path.GetFullPath("../../../TestDocuments/Widget.pdf"); - Document doc = new Document(testFilePath); - for (int i = 0; i < 1; i++) - { - var page = doc[i]; - - List entries = page.GetXObjects(); - - Widget fWidget = page.FirstWidget; - while (fWidget != null) - { - Console.WriteLine($"Widget: {fWidget}"); - Console.WriteLine($"FieldName: {fWidget.FieldName}"); - Console.WriteLine($"FieldType: {fWidget.FieldType}"); - Console.WriteLine($"FieldValue: {fWidget.FieldValue}"); - Console.WriteLine($"FieldFlags: {fWidget.FieldFlags}"); - Console.WriteLine($"FieldLabel: {fWidget.FieldLabel}"); - Console.WriteLine($"TextFont: {fWidget.TextFont}"); - Console.WriteLine($"TextFontSize: {fWidget.TextFontSize}"); - Console.WriteLine($"TextColor: {string.Join(",", fWidget.TextColor)}"); - fWidget = (Widget)fWidget.Next; - } - - foreach (var widget in page.GetWidgets()) - { - Console.WriteLine($"Widget: {widget}"); - Console.WriteLine($"FieldName: {widget.FieldName}"); - Console.WriteLine($"FieldType: {widget.FieldType}"); - Console.WriteLine($"FieldValue: {widget.FieldValue}"); - Console.WriteLine($"FieldFlags: {widget.FieldFlags}"); - Console.WriteLine($"FieldLabel: {widget.FieldLabel}"); - Console.WriteLine($"TextFont: {widget.TextFont}"); - Console.WriteLine($"TextFontSize: {widget.TextFontSize}"); - Console.WriteLine($"TextColor: {string.Join(",", widget.TextColor)}"); - - } - } - - doc.Close(); - Console.WriteLine("Widget test completed."); - } - - internal static void TestColor(string[] args) - { - Console.WriteLine("\n=== TestColor ====================="); - - string testFilePath = Path.GetFullPath("../../../TestDocuments/Color.pdf"); - Document doc = new Document(testFilePath); - List images = doc.GetPageImages(0); - Console.WriteLine($"CaName: {images[0].CsName}"); - doc.Recolor(0, 4); - images = doc.GetPageImages(0); - Console.WriteLine($"CaName: {images[0].AltCsName}"); - doc.Save("ReColor.pdf"); - doc.Close(); - - Console.WriteLine("Color test completed."); - } - - internal static void TestCMYKRecolor(string[] args) - { - Console.WriteLine("\n=== TestCMYKRecolor ====================="); - - string testFilePath = Path.GetFullPath("../../../TestDocuments/CMYK_Recolor.pdf"); - Document doc = new Document(testFilePath); - //List images = doc.GetPageImages(0); - //Console.WriteLine($"CaName: {images[0].CsName}"); - doc.Recolor(0, "CMYK"); - //images = doc.GetPageImages(0); - //Console.WriteLine($"CaName: {images[0].AltCsName}"); - doc.Save("CMYKRecolor.pdf"); - doc.Close(); - - Console.WriteLine("CMYK Recolor test completed."); - } - - internal static void TestSVGRecolor(string[] args) - { - Console.WriteLine("\n=== TestSVGRecolor ====================="); - - string testFilePath = Path.GetFullPath("../../../TestDocuments/SvgTest.pdf"); - Document doc = new Document(testFilePath); - doc.Recolor(0, "RGB"); - doc.Save("SVGRecolor.pdf"); - doc.Close(); - - Console.WriteLine("SVG Recolor test completed."); - } - - internal static void TestReplaceImage(string[] args) - { - Console.WriteLine("\n=== TestReplaceImage ====================="); - - string testFilePath = Path.GetFullPath("../../../TestDocuments/Color.pdf"); - Document doc = new Document(testFilePath); - Page page = doc[0]; - List images = page.GetImages(true); - List imgs = page.GetImageRects(images[0].Xref); - - List infos = page.GetImageInfo(xrefs: true); - - page.ReplaceImage(images[0].Xref, "../../../TestDocuments/Image/_apple.png"); - page.ReplaceImage(images[0].Xref, "../../../TestDocuments/Image/_bb-logo.png"); - - infos = page.GetImageInfo(xrefs: true); - //page.DeleteImage(images[0].Xref); - - //int newXref = page.InsertImage(imgs[0].Rect, "../../../TestDocuments/Sample.png"); - - //images = page.GetImages(true); - //imgs = page.GetImageRects(images[0].Xref); - - //page.ReplaceImage(infos[0].Xref, "../../../TestDocuments/Sample.png"); - //page.DeleteImage(images[0].Xref); - - //page.InsertImage(imgs[0].Rect, "../../../TestDocuments/Sample.jpg"); - - doc.Save("ReplaceImage.pdf"); - doc.Close(); - - Console.WriteLine("Image replacement test completed."); - } - - internal static void TestInsertImage(string[] args) - { - Console.WriteLine("\n=== TestInsertImage ====================="); - - string testFilePath = Path.GetFullPath("../../../TestDocuments/Image/test.pdf"); - Document doc = new Document(testFilePath); - Page page = doc[0]; - - var pixmap1 = new Pixmap("../../../TestDocuments/Image/_apple.png"); - //var pixmap1 = new Pixmap("../../../TestDocuments/Image/30mb.jpg"); - var pixmap2 = new Pixmap("../../../TestDocuments/Image/_bb-logo.png"); - var imageRect1 = new Rect(0, 0, 100, 100); - var imageRect2 = new Rect(100, 100, 200, 200); - var imageRect3 = new Rect(100, 200, 200, 300); - var imageRect4 = new Rect(100, 300, 200, 400); - var imageRect5 = new Rect(100, 400, 200, 500); - var imageRect6 = new Rect(100, 500, 200, 600); - - var img_xref = page.InsertImage(imageRect1, pixmap: pixmap1); - Console.WriteLine(img_xref); - - //img_xref = page.InsertImage(imageRect2, "../../../TestDocuments/Image/_apple.png"); - img_xref = page.InsertImage(imageRect2, pixmap: pixmap1); - Console.WriteLine(img_xref); - img_xref = page.InsertImage(imageRect3, pixmap: pixmap2); - Console.WriteLine(img_xref); - img_xref = page.InsertImage(imageRect4, "../../../TestDocuments/Image/_bb-logo.png"); - Console.WriteLine(img_xref); - page.InsertImage(imageRect5, xref: img_xref); - Console.WriteLine(img_xref); - page.InsertImage(imageRect6, xref: img_xref); - - doc.Save("TestInsertImage.pdf"); - doc.Close(); - - Console.WriteLine("Image insertion test completed."); - } - - internal static void TestGetImageInfo(string[] args) - { - Console.WriteLine("\n=== TestGetImageInfo ====================="); - - string testFilePath = Path.GetFullPath("../../../TestDocuments/Image/TestInsertImage.pdf"); - Document doc = new Document(testFilePath); - Page page = doc[0]; - - List infos = page.GetImageInfo(xrefs: true); - - doc.Close(); - - Console.WriteLine("Image info test completed."); - } - - internal static void TestGetTextPageOcr(string[] args) - { - Console.WriteLine("\n=== TestGetTextPageOcr ====================="); - - string testFilePath = Path.GetFullPath(@"../../../TestDocuments/Ocr.pdf"); - Document doc = new Document(testFilePath); - Page page = doc[0]; - - page.RemoveRotation(); - Pixmap pixmap = page.GetPixmap(); - - List blocks = page.GetText("dict", flags: (int)TextFlags.TEXT_PRESERVE_IMAGES)?.Blocks; - foreach (Block block in blocks) - { - Console.WriteLine(block.Image.Length); - } - - // build the pipeline - var pipeline = new ImageFilterPipeline(); - pipeline.Clear(); - //pipeline.AddDeskew(minAngle: 0.5); // replaces any existing deskew step - //pipeline.AddRemoveHorizontalLines(); // also replaces existing horizontal-removal step - //pipeline.AddRemoveVerticalLines(); - //pipeline.AddGrayscale(); - //pipeline.AddMedian(blockSize: 2, replaceExisting: true); - pipeline.AddGamma(gamma: 1.2); // brighten slightly - //pipeline.AddScaleFit(100); - pipeline.AddScale(scaleFactor: 3f, quality: SKFilterQuality.High); - //pipeline.AddContrast(contrast: 100); - //pipeline.AddDilation(); - //pipeline.AddInvert(); - - TextPage tp = page.GetTextPageOcr((int)TextFlags.TEXT_PRESERVE_SPANS, full: true, imageFilters: pipeline); - string txt = tp.ExtractText(); - Console.WriteLine(txt); - - doc.Close(); - - Console.WriteLine("OCR text extraction test completed."); - } - - internal static void TestCreateImagePage(string[] args) - { - Console.WriteLine("\n=== TestCreateImagePage ====================="); - - Pixmap pxmp = new Pixmap("../../../TestDocuments/Image/_bb-logo.png"); - - Document doc = new Document(); - Page page = doc.NewPage(width:pxmp.W, height:pxmp.H); - - page.InsertImage(page.Rect, pixmap: pxmp); - - pxmp.Dispose(); - - doc.Save("_bb-logo.pdf", pretty: 1); - doc.Close(); - - Console.WriteLine("Image page creation test completed."); - } - - internal static void TestJoinPdfPages(string[] args) - { - Console.WriteLine("\n=== TestJoinPdfPages ====================="); - - string testFilePath1 = Path.GetFullPath(@"../../../TestDocuments/Widget.pdf"); - Document doc1 = new Document(testFilePath1); - string testFilePath2 = Path.GetFullPath(@"../../../TestDocuments/Color.pdf"); - Document doc2 = new Document(testFilePath2); - - doc1.InsertPdf(doc2, 0, 0, 2); - - doc1.Save("Joined.pdf", pretty: 1); - - doc2.Close(); - doc1.Close(); - - Console.WriteLine("PDF pages joined successfully into 'Joined.pdf'."); - } - - } -} diff --git a/Demo/Samples/Regression/Program.Regression.cs b/Demo/Samples/Regression/Program.Regression.cs deleted file mode 100644 index 26842f5d..00000000 --- a/Demo/Samples/Regression/Program.Regression.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System.Threading.Tasks; - -namespace Demo -{ - internal partial class Program - { - internal static void TestIssue234() - { - Console.WriteLine("\n=== TestIssue234 ======================="); - - var pix = new Pixmap("../../../TestDocuments/Image/boxedpage.jpg"); // 629x1000 image - var scaled = new Pixmap(pix, 943, 1500, null); // scale up - byte[] jpeg = scaled.ToBytes("jpg", 65); - - using var doc = new Document(); - Page page = doc.NewPage(0, 943, 1500); - page.InsertImage(page.Rect, stream: jpeg); - page.Dispose(); - doc.Save("issue_234.pdf"); - doc.Close(); - - Console.WriteLine("Saved issue_234.pdf"); - } - - internal static void TestRecompressJBIG2() - { - Console.WriteLine("\n=== TestJBIG2 ======================="); - - string testFilePath = Path.GetFullPath("../../../TestDocuments/Jbig2.pdf"); - - Document doc = new Document(testFilePath); - - PdfImageRewriterOptions opts = new PdfImageRewriterOptions(); - - opts.bitonal_image_recompress_method = mupdf.mupdf.FZ_RECOMPRESS_FAX; - opts.recompress_when = mupdf.mupdf.FZ_RECOMPRESS_WHEN_ALWAYS; - - doc.RewriteImage(options: opts); - - doc.Save(@"e:\TestRecompressJBIG2.pdf"); - doc.Close(); - - Console.WriteLine("Saved e:\\TestRecompressJBIG2.pdf"); - } - - internal static void TestIssue1880() - { - Console.WriteLine("\n=== TestIssue1880 ======================="); - - string testFilePath = Path.GetFullPath(@"../../../TestDocuments/issue_1880.pdf"); - - Document doc = new Document(testFilePath); - - for (int i = 0; i < doc.PageCount; i++) - { - Page page = doc[i]; - - List barcodes = page.ReadBarcodes(barcodeFormat: BarcodeFormat.DM, pureBarcode:true); - foreach (Barcode barcode in barcodes) - { - BarcodePoint[] points = barcode.ResultPoints; - Console.WriteLine($"Page {i++} - Type: {barcode.BarcodeFormat} - Value: {barcode.Text} - Rect: [{points[0]},{points[1]}]"); - } - - page.Dispose(); - } - - doc.Close(); - } - - internal static void TestIssue213() - { - Console.WriteLine("\n=== TestIssue213 ======================="); - - string origfilename = @"../../../TestDocuments/issue_213.pdf"; - string outfilename = @"../../../TestDocuments/Blank.pdf"; - float newWidth = 0.5f; - - Document inputDoc = new Document(origfilename); - Document outputDoc = new Document(outfilename); - - if (inputDoc.PageCount != outputDoc.PageCount) - { - return; - } - - for (int pagNum = 0; pagNum < inputDoc.PageCount; pagNum++) - { - Page page = inputDoc.LoadPage(pagNum); - - Pixmap pxmp = page.GetPixmap(); - pxmp.Save(@"output.png"); - pxmp.Dispose(); - - Page outPage = outputDoc.LoadPage(pagNum); - List paths = page.GetDrawings(extended: false); - int totalPaths = paths.Count; - - int i = 0; - foreach (PathInfo pathInfo in paths) - { - Shape shape = outPage.NewShape(); - foreach (Item item in pathInfo.Items) - { - if (item != null) - { - if (item.Type == "l") - { - shape.DrawLine(item.P1, item.LastPoint); - //writer.Write($"{i:000}\\] line: {item.Type} >>> {item.P1}, {item.LastPoint}\\n"); - } - else if (item.Type == "re") - { - shape.DrawRect(item.Rect, item.Orientation); - //writer.Write($"{i:000}\\] rect: {item.Type} >>> {item.Rect}, {item.Orientation}\\n"); - } - else if (item.Type == "qu") - { - shape.DrawQuad(item.Quad); - //writer.Write($"{i:000}\\] quad: {item.Type} >>> {item.Quad}\\n"); - } - else if (item.Type == "c") - { - shape.DrawBezier(item.P1, item.P2, item.P3, item.LastPoint); - //writer.Write($"{i:000}\\] curve: {item.Type} >>> {item.P1}, {item.P2}, {item.P3}, {item.LastPoint}\\n"); - } - else - { - throw new Exception("unhandled drawing. Aborting..."); - } - } - } - - //pathInfo.Items.get - float newLineWidth = pathInfo.Width; - if (pathInfo.Width <= newWidth) - { - newLineWidth = newWidth; - } - - int lineCap = 0; - if (pathInfo.LineCap != null && pathInfo.LineCap.Count > 0) - lineCap = (int)pathInfo.LineCap[0]; - shape.Finish( - fill: pathInfo.Fill, - color: pathInfo.Color, //this.\_m_DEFAULT_COLOR, - evenOdd: pathInfo.EvenOdd, - closePath: pathInfo.ClosePath, - lineJoin: (int)pathInfo.LineJoin, - lineCap: lineCap, - width: newLineWidth, - strokeOpacity: pathInfo.StrokeOpacity, - fillOpacity: pathInfo.FillOpacity, - dashes: pathInfo.Dashes - ); - - // file_export.write(f'Path {i:03}\] width: {lwidth}, dashes: {path\["dashes"\]}, closePath: {path\["closePath"\]}\\n') - //writer.Write($"Path {i:000}\\] with: {newLineWidth}, dashes: {pathInfo.Dashes}, closePath: {pathInfo.ClosePath}\\n"); - - i++; - shape.Commit(); - } - } - - inputDoc.Close(); - - outputDoc.Save(@"output.pdf"); - outputDoc.Close(); - - //writer.Close(); - } - - internal static void TestPixmapParallel() - { - const int iterations = 300; - const int degreeOfParallelism = 10; - - var pdfPath = Path.Combine(@"..\..\..\TestDocuments\TestPdf1.pdf"); - var pdf = File.ReadAllBytes(pdfPath); - - Console.WriteLine($"MuPDF.NET parallel Pixmap.ToBytes repro"); - Console.WriteLine($"PDF: {pdfPath}"); - Console.WriteLine($"Iterations: {iterations}"); - Console.WriteLine($"Degree of parallelism: {degreeOfParallelism}"); - Console.WriteLine(); - - Parallel.ForEach( - Enumerable.Range(0, iterations), - new ParallelOptions { MaxDegreeOfParallelism = degreeOfParallelism }, - iteration => - { - using var document = new Document(stream: pdf, fileType: "pdf"); - using var page = document[0]; - using var pixmap = page.GetPixmap(new Matrix(2, 2)); - - var png = pixmap.ToBytes("png"); - Console.WriteLine($"Iteration {iteration + 1}: rendered {png.Length} bytes"); - }); - - Console.WriteLine("Completed without crashing."); - } - } -} diff --git a/Demo/Samples/TextDrawing/Program.TextDrawing.cs b/Demo/Samples/TextDrawing/Program.TextDrawing.cs deleted file mode 100644 index d927a168..00000000 --- a/Demo/Samples/TextDrawing/Program.TextDrawing.cs +++ /dev/null @@ -1,366 +0,0 @@ -namespace Demo -{ - internal partial class Program - { - internal static void CreateAnnotDocument() - { - Console.WriteLine("\n=== CreateAnnotDocument ======================="); - Rect r = Constants.r; // use the rectangle defined in Constants.cs - - Document doc = new Document(); - Page page = doc.NewPage(); - - page.SetRotation(0); // no rotation - - TextWriter pw = new TextWriter(page.TrimBox); - string txt = "Origin 100.100"; - pw.Append(new Point(100, 500), txt, new Font("tiro"), fontSize: 24); - pw.WriteText(page, new float[]{0,0.4f,1}, oc: 0); - - - - Annot annot = page.AddRectAnnot(r); // 'Square' - annot.SetBorder(width: 1f, dashes: new int[] { 1, 2 }); - annot.SetColors(stroke: Constants.blue, fill: Constants.gold); - annot.Update(opacity: 0.5f); - - doc.Save(@"CreateAnnotDocument.pdf"); - - doc.Close(); - } - - internal static void TestDrawShape() - { - string origfilename = @"../../../TestDocuments/NewAnnots.pdf"; - string outfilename = @"../../../TestDocuments/Blank.pdf"; - float newWidth = 0.5f; - - Document inputDoc = new Document(origfilename); - Document outputDoc = new Document(outfilename); - - //string filePath = @"D:\\Vectorlab\\Jobs\\2025\\PACE\\pdf_fix\\assets\\exported_paths_net.txt"; - //StreamWriter writer = new StreamWriter(filePath); - - if (inputDoc.PageCount != outputDoc.PageCount) - { - return; - } - - for (int pagNum = 0; pagNum < inputDoc.PageCount; pagNum++) - { - Page page = inputDoc.LoadPage(pagNum); - Page outPage = outputDoc.LoadPage(pagNum); - List paths = page.GetDrawings(extended: false); - int totalPaths = paths.Count; - - int i = 0; - foreach (PathInfo pathInfo in paths) - { - Shape shape = outPage.NewShape(); - foreach (Item item in pathInfo.Items) - { - if (item != null) - { - if (item.Type == "l") - { - shape.DrawLine(item.P1, item.LastPoint); - //writer.Write($"{i:000}\\] line: {item.Type} >>> {item.P1}, {item.LastPoint}\\n"); - } - else if (item.Type == "re") - { - shape.DrawRect(item.Rect, item.Orientation); - //writer.Write($"{i:000}\\] rect: {item.Type} >>> {item.Rect}, {item.Orientation}\\n"); - } - else if (item.Type == "qu") - { - shape.DrawQuad(item.Quad); - //writer.Write($"{i:000}\\] quad: {item.Type} >>> {item.Quad}\\n"); - } - else if (item.Type == "c") - { - shape.DrawBezier(item.P1, item.P2, item.P3, item.LastPoint); - //writer.Write($"{i:000}\\] curve: {item.Type} >>> {item.P1}, {item.P2}, {item.P3}, {item.LastPoint}\\n"); - } - else - { - throw new Exception("unhandled drawing. Aborting..."); - } - } - } - - //pathInfo.Items.get - float newLineWidth = pathInfo.Width; - if (pathInfo.Width <= newWidth) - { - newLineWidth = newWidth; - } - - int lineCap = 0; - if (pathInfo.LineCap != null && pathInfo.LineCap.Count > 0) - lineCap = (int)pathInfo.LineCap[0]; - shape.Finish( - fill: pathInfo.Fill, - color: pathInfo.Color, //this.\_m_DEFAULT_COLOR, - evenOdd: pathInfo.EvenOdd, - closePath: pathInfo.ClosePath, - lineJoin: (int)pathInfo.LineJoin, - lineCap: lineCap, - width: newLineWidth, - strokeOpacity: pathInfo.StrokeOpacity, - fillOpacity: pathInfo.FillOpacity, - dashes: pathInfo.Dashes - ); - - // file_export.write(f'Path {i:03}\] width: {lwidth}, dashes: {path\["dashes"\]}, closePath: {path\["closePath"\]}\\n') - //writer.Write($"Path {i:000}\\] with: {newLineWidth}, dashes: {pathInfo.Dashes}, closePath: {pathInfo.ClosePath}\\n"); - - i++; - shape.Commit(); - } - } - - inputDoc.Close(); - - outputDoc.Save(@"TestDrawShape.pdf"); - outputDoc.Close(); - - //writer.Close(); - } - - internal static void DrawLine(Page page, float startX, float startY, float endX, float endY, Color lineColor = null, float lineWidth = 1, bool dashed = false) - { - Console.WriteLine("\n=== DrawLine ======================="); - - if (lineColor == null) - { - lineColor = new Color(); // Default to black - lineColor.Stroke = new float[] { 0, 0, 0 }; // RGB black - } - Shape img = page.NewShape(); - Point startPoint = new Point(startX, startY); - Point endPoint = new Point(endX, endY); - - String dashString = ""; - if (dashed == true) - { - dashString = "[2] 0"; // Example dash pattern - } - - img.DrawLine(startPoint, endPoint); - img.Finish(width: lineWidth, color: lineColor.Stroke, dashes: dashString); - img.Commit(); - - Console.WriteLine($"Line drawn from ({startX}, {startY}) to ({endX}, {endY}) with color {lineColor.Stroke} and width {lineWidth}."); - } - - internal static void TestDrawLine() - { - Console.WriteLine("\n=== TestDrawLine ======================="); - - Document doc = new Document(); - - Page page = doc.NewPage(); - - string fontDir = Environment.GetFolderPath(Environment.SpecialFolder.Fonts); - - page.DrawLine(new Point(45, 50), new Point(80, 50), width: 0.5f, dashes: "[5] 0"); - page.DrawLine(new Point(90, 50), new Point(150, 50), width: 0.5f, dashes: "[5] 0"); - page.DrawLine(new Point(45, 80), new Point(180, 80), width: 0.5f, dashes: "[5] 0"); - page.DrawLine(new Point(45, 100), new Point(180, 100), width: 0.5f, dashes: "[5] 0"); - - //DrawLine(page, 45, 50, 80, 50, lineWidth: 0.5f, dashed: true); - //DrawLine(page, 90, 60, 150, 60, lineWidth: 0.5f, dashed: true); - //DrawLine(page, 45, 80, 180, 80, lineWidth: 0.5f, dashed: true); - //DrawLine(page, 45, 100, 180, 100, lineWidth: 0.5f, dashed: true); - - doc.Save(@"TestDrawLine.pdf"); - - page.Dispose(); - doc.Close(); - - Console.WriteLine("Write to TestDrawLine.pdf"); - } - - internal static void TestTextFont(string[] args) - { - Console.WriteLine("\n=== TestTextFont ======================="); - //for (int i = 0; i < 100; i++) - { - Document doc = new Document(); - - Page page0 = doc.NewPage(); - Page page1 = doc.NewPage(pno: -1, width: 595, height: 842); - - string fontDir = Environment.GetFolderPath(Environment.SpecialFolder.Fonts); - - float[] blue = new float[] { 0.0f, 0.0f, 1.0f }; - float[] red = new float[] { 1.0f, 0.0f, 0.0f }; - - Rect rect1 = new Rect(100, 100, 510, 200); - Rect rect2 = new Rect(100, 250, 300, 400); - - MuPDF.NET.Font font1 = new MuPDF.NET.Font("asdfasdf"); - //MuPDF.NET.Font font1 = new MuPDF.NET.Font("arial", fontDir+"\\arial_0.ttf"); - MuPDF.NET.Font font2 = new MuPDF.NET.Font("times", fontDir + "\\times.ttf"); - - string text1 = "This is a test of the FillTextbox method with Arial font."; - string text2 = "This is another test with Times New Roman font."; - - MuPDF.NET.TextWriter tw1 = new MuPDF.NET.TextWriter(page0.Rect); - tw1.FillTextbox(rect: rect1, text: text1, font: font1, fontSize:20); - font1.Dispose(); - tw1.WriteText(page0); - - MuPDF.NET.TextWriter tw2 = new MuPDF.NET.TextWriter(page0.Rect, color: red); - tw2.FillTextbox(rect: rect2, text: text2, font: font2, fontSize: 10, align: (int)TextAlign.TEXT_ALIGN_LEFT); - font2.Dispose(); - tw2.WriteText(page0); - - doc.Save(@"TestTextFont.pdf"); - - page0.Dispose(); - doc.Close(); - - Console.WriteLine("Write to TestTextFont.pdf"); - } - - } - - internal static void TestInsertHtmlbox() - { - Console.WriteLine("\n=== TestInsertHtmlbox ======================="); - - Rect rect = new Rect(100, 100, 550, 2250); - Document doc = new Document(); - Page page = doc.NewPage(); - - string htmlString = "

生产准备:

1. 每日生产进行维护保养,请参照并填写Philips 自动螺丝起点检表《WI-Screw assembly-Makita DF010&Kilews

SKD-B512L-F01》

2 .扭力计UNIT选择‘lbf.in’,‘P-P’模式,每四小时检查一次,每次检查5组数据,只有合格才可以生产;并填写

力扭矩记录表,表单号 : F-EN-34 。

1.τö╡σè¿Φ╡╖σ¡Éσè¢τƒ⌐∩╝Ü 5±1 in-lbs∩╝îτö╡σè¿Φ₧║Σ╕¥Φ╡╖τ╝ûσÅ╖∩╝Ü5.0πÇé

2.τö╡σè¿Φ╡╖σ¡Éσè¢τƒ⌐∩╝Ü10±1 in-lbs∩╝îτö╡σè¿Φ₧║Σ╕¥Φ╡╖τ╝ûσÅ╖∩╝Ü10.0πÇé

3.τö╡σè¿Φ╡╖σ¡Éσè¢τƒ⌐∩╝Ü12±1 in-lbs∩╝îτö╡σè¿Φ₧║Σ╕¥Φ╡╖τ╝ûσÅ╖∩╝Ü12.0πÇé

Colten - line break

生产准备:

1. 每日生产进行维护保养,请参照并填写Philips 自动螺丝起点检表《WI-Screw assembly-Makita DF010&Kilews

SKD-B512L-F01》

2 .扭力计UNIT选择‘lbf.in’,‘P-P’模式,每四小时检查一次,每次检查5组数据,只有合格才可以生产;并填写

力扭矩记录表,表单号 : F-EN-34 。

1.τö╡σè¿Φ╡╖σ¡Éσè¢τƒ⌐∩╝Ü 5±1 in-lbs∩╝îτö╡σè¿Φ₧║Σ╕¥Φ╡╖τ╝ûσÅ╖∩╝Ü5.0πÇé

2.τö╡σè¿Φ╡╖σ¡Éσè¢τƒ⌐∩╝Ü10±1 in-lbs∩╝îτö╡σè¿Φ₧║Σ╕¥Φ╡╖τ╝ûσÅ╖∩╝Ü10.0πÇé

3.τö╡σè¿Φ╡╖σ¡Éσè¢τƒ⌐∩╝Ü12±1 in-lbs∩╝îτö╡σè¿Φ₧║Σ╕¥Φ╡╖τ╝ûσÅ╖∩╝Ü12.0πÇé

Colten - line break

生产准备:

1. 每日生产进行维护保养,请参照并填写Philips 自动螺丝起点检表《WI-Screw assembly-Makita DF010&Kilews

SKD-B512L-F01》

2 .扭力计UNIT选择‘lbf.in’,‘P-P’模式,每四小时检查一次,每次检查5组数据,只有合格才可以生产;并填写

力扭矩记录表,表单号 : F-EN-34 。

1.τö╡σè¿Φ╡╖σ¡Éσè¢τƒ⌐∩╝Ü 5±1 in-lbs∩╝îτö╡σè¿Φ₧║Σ╕¥Φ╡╖τ╝ûσÅ╖∩╝Ü5.0πÇé

2.τö╡σè¿Φ╡╖σ¡Éσè¢τƒ⌐∩╝Ü10±1 in-lbs∩╝îτö╡σè¿Φ₧║Σ╕¥Φ╡╖τ╝ûσÅ╖∩╝Ü10.0πÇé

3.τö╡σè¿Φ╡╖σ¡Éσè¢τƒ⌐∩╝Ü12±1 in-lbs∩╝îτö╡σè¿Φ₧║Σ╕¥Φ╡╖τ╝ûσÅ╖∩╝Ü12.0πÇé

Colten - line break

生产准备:

1. 每日生产进行维护保养,请参照并填写Philips 自动螺丝起点检表《WI-Screw assembly-Makita DF010&Kilews

SKD-B512L-F01》

2 .扭力计UNIT选择‘lbf.in’,‘P-P’模式,每四小时检查一次,每次检查5组数据,只有合格才可以生产;并填写

力扭矩记录表,表单号 : F-EN-34 。

1.τö╡σè¿Φ╡╖σ¡Éσè¢τƒ⌐∩╝Ü 5±1 in-lbs∩╝îτö╡σè¿Φ₧║Σ╕¥Φ╡╖τ╝ûσÅ╖∩╝Ü5.0πÇé

2.τö╡σè¿Φ╡╖σ¡Éσè¢τƒ⌐∩╝Ü10±1 in-lbs∩╝îτö╡σè¿Φ₧║Σ╕¥Φ╡╖τ╝ûσÅ╖∩╝Ü10.0πÇé

3.τö╡σè¿Φ╡╖σ¡Éσè¢τƒ⌐∩╝Ü12±1 in-lbs∩╝îτö╡σè¿Φ₧║Σ╕¥Φ╡╖τ╝ûσÅ╖∩╝Ü12.0πÇé

"; - (float s, float scale) = page.InsertHtmlBox(rect, htmlString, scaleLow: 0f); - doc.Save(@"TestInsertHtmlbox.pdf"); - - page.Dispose(); - doc.Close(); - - Console.WriteLine($"Inserted HTML box with scale: {scale} and size: {s}"); - } - - internal static void TestLineAnnot() - { - Console.WriteLine("\n=== TestLineAnnot ======================="); - Document newDoc = new Document(); - Page newPage = newDoc.NewPage(); - - newPage.AddLineAnnot(new Point(100, 100), new Point(300, 300)); - - newDoc.Save(@"TestLineAnnot1.pdf"); - newDoc.Close(); - - Document doc = new Document(@"TestLineAnnot1.pdf"); // open a document - List annotationsToUpdate = new List(); - Page page = doc[0]; - // Fix: Correctly handle the IEnumerable returned by GetAnnots() - IEnumerable annots = page.GetAnnots(); - foreach (Annot annot in annots) - { - Console.WriteLine("Annotation on page width before modified: " + annot.Border.Width); - annot.SetBorder(width: 8); - annot.Update(); - Console.WriteLine("Annotation on page width after modified: " + annot.Border.Width); - } - annotationsToUpdate.Clear(); - doc.Save(@"TestLineAnnot2.pdf"); // Save the modified document - doc.Close(); // Close the document - } - - internal static void TestHelloWorldToNewDocument(string[] args) - { - Console.WriteLine("\n=== TestHelloWorldToNewDocument ======================="); - Document doc = new Document(); - Page page = doc.NewPage(); - - //{ "helv", "Helvetica" }, - //{ "heit", "Helvetica-Oblique" }, - //{ "hebo", "Helvetica-Bold" }, - //{ "hebi", "Helvetica-BoldOblique" }, - //{ "cour", "Courier" }, - //{ "cobo", "Courier-Bold" }, - //{ "cobi", "Courier-BoldOblique" }, - //{ "tiro", "Times-Roman" }, - //{ "tibo", "Times-Bold" }, - //{ "tiit", "Times-Italic" }, - //{ "tibi", "Times-BoldItalic" }, - //{ "symb", "Symbol" }, - //{ "zadb", "ZapfDingbats" } - MuPDF.NET.TextWriter writer = new MuPDF.NET.TextWriter(page.Rect); - var ret = writer.FillTextbox(page.Rect, "Hello World!", new MuPDF.NET.Font(fontName: "helv"), rtl: true); - writer.WriteText(page); - doc.Save("text.pdf", pretty: 1); - doc.Close(); - - Console.WriteLine($"Text written to 'text.pdf' in: {page.Rect}"); - } - - internal static void TestHelloWorldToExistingDocument(string[] args) - { - Console.WriteLine("\n=== TestHelloWorldToExistingDocument ======================="); - string testFilePath = Path.GetFullPath("../../../TestDocuments/Blank.pdf"); - Document doc = new Document(testFilePath); - - Page page = doc[0]; - - Rect rect = new Rect(100, 100, 510, 210); - page.DrawRect(rect); - - MuPDF.NET.TextWriter writer = new MuPDF.NET.TextWriter(page.Rect); - //Font font = new Font("kenpixel", "../../../kenpixel.ttf", isBold: 1); - Font font = new Font("cobo", isBold: 0); - var ret = writer.FillTextbox(page.Rect, "123456789012345678901234567890Peter Test- this is a string that is too long to fit into the TextBox", font, rtl: false); - writer.WriteText(page); - - doc.Save("text1.pdf", pretty: 1); - - doc.Close(); - - Console.WriteLine($"Text written to 'text1.pdf' in: {page.Rect}"); - } - - internal static void TestFreeTextAnnot(string[] args) - { - Console.WriteLine("\n=== TestFreeTextAnnot ====================="); - - Rect r = new Rect(72, 72, 220, 100); - string t1 = "t├¬xt ├╝s├¿s L├ñti├▒ char├ƒ,\nEUR: Γé¼, mu: ┬╡, super scripts: ┬▓┬│!"; - Rect rect = new Rect(100,100,200,200); - float[] red = new float[] { 1, 0, 0 }; - float[] blue = new float[] { 0, 0, 1 }; - float[] gold = new float[] { 1, 1, 0 }; - float[] green = new float[] { 0, 1, 0 }; - float[] white = new float[] { 1, 1, 1 }; - - Document doc = new Document(); - Page page = doc.NewPage(); - - Annot annot = page.AddFreeTextAnnot( - rect, - t1, - fontSize: 10, - rotate: 90, - textColor: red, - fillColor: gold, - align: (int)TextAlign.TEXT_ALIGN_CENTER, - dashes: new int[] { 2 } - ); - - annot.SetBorder(border: null, width: 0.3f, dashes: new int[] { 2 }); - annot.Update(textColor: blue); - //annot.Update(textColor: red, fillColor: blue); - - doc.Save("FreeTextAnnot.pdf"); - - doc.Close(); - - Console.WriteLine("Free text annotation created and saved to 'FreeTextAnnot.pdf'."); - } - - } -} diff --git a/Demo/Support/Constants.cs b/Demo/Support/Constants.cs deleted file mode 100644 index 7c354a14..00000000 --- a/Demo/Support/Constants.cs +++ /dev/null @@ -1,39 +0,0 @@ -using MuPDF.NET; -using SkiaSharp; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; - -namespace Demo -{ - public static class Constants - { - // some colors - public static float[] red = new float[] { 1, 0, 0 }; - public static float[] blue = new float[] { 0, 0, 1 }; - public static float[] gold = new float[] { 1, 1, 0 }; - public static float[] green = new float[] { 0, 1, 0 }; - public static float[] white = new float[] { 1, 1, 1 }; - public static float[] black = new float[] { 0, 0, 0 }; - - // rectangles and points - public static Rect displ = new Rect(0, 50, 0, 50); - public static Rect r = new Rect(72, 72, 220, 100); - public static Rect rect = new Rect(100, 100, 200, 200); - - // string - public static string t1 = "têxt üsès Lätiñ charß,\nEUR: €, mu: µ, super scripts: ²³!"; - public static string highlight = "this text is highlighted"; - public static string underline = "this text is underlined"; - public static string strikeout = "this text is striked out"; - public static string squiggled = "this text is zigzag-underlined"; - - public static Func FILENAME = () => - { - return System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; - }; - } -} diff --git a/Demo/Support/Units.cs b/Demo/Support/Units.cs deleted file mode 100644 index 730f7d3e..00000000 --- a/Demo/Support/Units.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Demo -{ - public static class Units - { - public const float InchesPerMm = 1.0f / 25.4f; - public const float PointsPerInch = 72.0f; - - public static float MmToPoints(float mm) => mm * InchesPerMm * PointsPerInch; - public static float PointsToMm(float points) => points / PointsPerInch / InchesPerMm; - - public static float MmToPixels(float mm, float dpi) => mm * InchesPerMm * dpi; - public static float PixelsToMm(float px, float dpi) => px / dpi / InchesPerMm; - } -} diff --git a/Demo/TestDocuments/Annot.pdf b/Demo/TestDocuments/Annot.pdf deleted file mode 100644 index f3897e58..00000000 Binary files a/Demo/TestDocuments/Annot.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/2.png b/Demo/TestDocuments/Barcodes/2.png deleted file mode 100644 index e23d2c42..00000000 Binary files a/Demo/TestDocuments/Barcodes/2.png and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/Input.pdf b/Demo/TestDocuments/Barcodes/Input.pdf deleted file mode 100644 index 32620a1c..00000000 Binary files a/Demo/TestDocuments/Barcodes/Input.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/Low/drawing.pdf b/Demo/TestDocuments/Barcodes/Low/drawing.pdf deleted file mode 100644 index befc3242..00000000 Binary files a/Demo/TestDocuments/Barcodes/Low/drawing.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/Low/fiamm.pdf b/Demo/TestDocuments/Barcodes/Low/fiamm.pdf deleted file mode 100644 index e1277543..00000000 Binary files a/Demo/TestDocuments/Barcodes/Low/fiamm.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/Low/ocr-me.pdf b/Demo/TestDocuments/Barcodes/Low/ocr-me.pdf deleted file mode 100644 index 6da6f056..00000000 Binary files a/Demo/TestDocuments/Barcodes/Low/ocr-me.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/Sample1.jpg b/Demo/TestDocuments/Barcodes/Sample1.jpg deleted file mode 100644 index 5249d2d3..00000000 Binary files a/Demo/TestDocuments/Barcodes/Sample1.jpg and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/Sample1.pdf b/Demo/TestDocuments/Barcodes/Sample1.pdf deleted file mode 100644 index 542b6814..00000000 Binary files a/Demo/TestDocuments/Barcodes/Sample1.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/Samplepng.png b/Demo/TestDocuments/Barcodes/Samplepng.png deleted file mode 100644 index 7f079d20..00000000 Binary files a/Demo/TestDocuments/Barcodes/Samplepng.png and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/Samples.pdf b/Demo/TestDocuments/Barcodes/Samples.pdf deleted file mode 100644 index 321c62e2..00000000 Binary files a/Demo/TestDocuments/Barcodes/Samples.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/datamatrix.pdf b/Demo/TestDocuments/Barcodes/datamatrix.pdf deleted file mode 100644 index 9ddfadca..00000000 Binary files a/Demo/TestDocuments/Barcodes/datamatrix.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/output.png b/Demo/TestDocuments/Barcodes/output.png deleted file mode 100644 index 1aab7ebf..00000000 Binary files a/Demo/TestDocuments/Barcodes/output.png and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/qr.png b/Demo/TestDocuments/Barcodes/qr.png deleted file mode 100644 index fe983667..00000000 Binary files a/Demo/TestDocuments/Barcodes/qr.png and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/rendered.bmp b/Demo/TestDocuments/Barcodes/rendered.bmp deleted file mode 100644 index b19e28a9..00000000 Binary files a/Demo/TestDocuments/Barcodes/rendered.bmp and /dev/null differ diff --git a/Demo/TestDocuments/Barcodes/samplejpg.jpg b/Demo/TestDocuments/Barcodes/samplejpg.jpg deleted file mode 100644 index 947a0184..00000000 Binary files a/Demo/TestDocuments/Barcodes/samplejpg.jpg and /dev/null differ diff --git a/Demo/TestDocuments/Blank.pdf b/Demo/TestDocuments/Blank.pdf deleted file mode 100644 index 6baef93d..00000000 Binary files a/Demo/TestDocuments/Blank.pdf and /dev/null differ diff --git a/Demo/TestDocuments/CMYK_Recolor.pdf b/Demo/TestDocuments/CMYK_Recolor.pdf deleted file mode 100644 index 1d427b8e..00000000 Binary files a/Demo/TestDocuments/CMYK_Recolor.pdf and /dev/null differ diff --git a/Demo/TestDocuments/CMYK_Recolor1.pdf b/Demo/TestDocuments/CMYK_Recolor1.pdf deleted file mode 100644 index e7637642..00000000 Binary files a/Demo/TestDocuments/CMYK_Recolor1.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Color.pdf b/Demo/TestDocuments/Color.pdf deleted file mode 100644 index 6086ec95..00000000 Binary files a/Demo/TestDocuments/Color.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Html/test.html b/Demo/TestDocuments/Html/test.html deleted file mode 100644 index 9cdece9c..00000000 --- a/Demo/TestDocuments/Html/test.html +++ /dev/null @@ -1,31 +0,0 @@ - - -

- Lorem Ipsum -

-

- Lorem Ipsum -

-

- Lorem Ipsum -

-

- Lorem Ipsum -

-

- Lorem Ipsum -

-

- Lorem Ipsum -

-

- Lorem Ipsum -

-

- Lorem Ipsum -

-

- Lorem Ipsum -

- - \ No newline at end of file diff --git a/Demo/TestDocuments/Image/TestInsertImage.pdf b/Demo/TestDocuments/Image/TestInsertImage.pdf deleted file mode 100644 index 23357d22..00000000 Binary files a/Demo/TestDocuments/Image/TestInsertImage.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Image/_apple.png b/Demo/TestDocuments/Image/_apple.png deleted file mode 100644 index 6d81042d..00000000 Binary files a/Demo/TestDocuments/Image/_apple.png and /dev/null differ diff --git a/Demo/TestDocuments/Image/_bb-logo.png b/Demo/TestDocuments/Image/_bb-logo.png deleted file mode 100644 index 971607d7..00000000 Binary files a/Demo/TestDocuments/Image/_bb-logo.png and /dev/null differ diff --git a/Demo/TestDocuments/Image/boxedpage.jpg b/Demo/TestDocuments/Image/boxedpage.jpg deleted file mode 100644 index 1b7030f1..00000000 Binary files a/Demo/TestDocuments/Image/boxedpage.jpg and /dev/null differ diff --git a/Demo/TestDocuments/Image/table.jpg b/Demo/TestDocuments/Image/table.jpg deleted file mode 100644 index d8cad3cd..00000000 Binary files a/Demo/TestDocuments/Image/table.jpg and /dev/null differ diff --git a/Demo/TestDocuments/Image/test.pdf b/Demo/TestDocuments/Image/test.pdf deleted file mode 100644 index 5411b2a5..00000000 Binary files a/Demo/TestDocuments/Image/test.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Jbig2.pdf b/Demo/TestDocuments/Jbig2.pdf deleted file mode 100644 index d8c9a130..00000000 Binary files a/Demo/TestDocuments/Jbig2.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Magazine.pdf b/Demo/TestDocuments/Magazine.pdf deleted file mode 100644 index c8e166e0..00000000 Binary files a/Demo/TestDocuments/Magazine.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Morph.pdf b/Demo/TestDocuments/Morph.pdf deleted file mode 100644 index a26cc1a3..00000000 Binary files a/Demo/TestDocuments/Morph.pdf and /dev/null differ diff --git a/Demo/TestDocuments/NewAnnots.pdf b/Demo/TestDocuments/NewAnnots.pdf deleted file mode 100644 index 5a055ff1..00000000 Binary files a/Demo/TestDocuments/NewAnnots.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Ocr.pdf b/Demo/TestDocuments/Ocr.pdf deleted file mode 100644 index 3f28b991..00000000 Binary files a/Demo/TestDocuments/Ocr.pdf and /dev/null differ diff --git a/Demo/TestDocuments/ReColor.pdf b/Demo/TestDocuments/ReColor.pdf deleted file mode 100644 index fdf43744..00000000 Binary files a/Demo/TestDocuments/ReColor.pdf and /dev/null differ diff --git a/Demo/TestDocuments/SvgTest.pdf b/Demo/TestDocuments/SvgTest.pdf deleted file mode 100644 index 822a211f..00000000 Binary files a/Demo/TestDocuments/SvgTest.pdf and /dev/null differ diff --git a/Demo/TestDocuments/TestPdf1.pdf b/Demo/TestDocuments/TestPdf1.pdf deleted file mode 100644 index ae0f872e..00000000 Binary files a/Demo/TestDocuments/TestPdf1.pdf and /dev/null differ diff --git a/Demo/TestDocuments/TestResolveNames.pdf b/Demo/TestDocuments/TestResolveNames.pdf deleted file mode 100644 index dd523186..00000000 Binary files a/Demo/TestDocuments/TestResolveNames.pdf and /dev/null differ diff --git a/Demo/TestDocuments/Widget.pdf b/Demo/TestDocuments/Widget.pdf deleted file mode 100644 index f903200a..00000000 Binary files a/Demo/TestDocuments/Widget.pdf and /dev/null differ diff --git a/Demo/TestDocuments/columns.pdf b/Demo/TestDocuments/columns.pdf deleted file mode 100644 index 18f5f159..00000000 Binary files a/Demo/TestDocuments/columns.pdf and /dev/null differ diff --git a/Demo/TestDocuments/err_table.pdf b/Demo/TestDocuments/err_table.pdf deleted file mode 100644 index f7f19b3a..00000000 Binary files a/Demo/TestDocuments/err_table.pdf and /dev/null differ diff --git a/Demo/TestDocuments/issue_1880.pdf b/Demo/TestDocuments/issue_1880.pdf deleted file mode 100644 index 306deab6..00000000 Binary files a/Demo/TestDocuments/issue_1880.pdf and /dev/null differ diff --git a/Demo/TestDocuments/issue_213.pdf b/Demo/TestDocuments/issue_213.pdf deleted file mode 100644 index 5263abd3..00000000 Binary files a/Demo/TestDocuments/issue_213.pdf and /dev/null differ diff --git a/Demo/TestDocuments/kenpixel.ttf b/Demo/TestDocuments/kenpixel.ttf deleted file mode 100644 index e3dc57ec..00000000 Binary files a/Demo/TestDocuments/kenpixel.ttf and /dev/null differ diff --git a/Demo/TestDocuments/national-capitals.pdf b/Demo/TestDocuments/national-capitals.pdf deleted file mode 100644 index d2b47219..00000000 Binary files a/Demo/TestDocuments/national-capitals.pdf and /dev/null differ diff --git a/Demo/TestDocuments/test_widget_parse.pdf b/Demo/TestDocuments/test_widget_parse.pdf deleted file mode 100644 index 70328c72..00000000 Binary files a/Demo/TestDocuments/test_widget_parse.pdf and /dev/null differ diff --git "a/Demo/TestDocuments/\344\275\240\345\245\275.pdf" "b/Demo/TestDocuments/\344\275\240\345\245\275.pdf" deleted file mode 100644 index 0b81fba8..00000000 Binary files "a/Demo/TestDocuments/\344\275\240\345\245\275.pdf" and /dev/null differ diff --git a/MuPDF.NET.Test/AnnotTest.cs b/MuPDF.NET.Test/AnnotTest.cs deleted file mode 100644 index 9573c38d..00000000 --- a/MuPDF.NET.Test/AnnotTest.cs +++ /dev/null @@ -1,506 +0,0 @@ -using ICSharpCode.SharpZipLib.Zip.Compression; -using Newtonsoft.Json; -using NUnit.Framework; -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using MuPDF.NET; - -namespace MuPDF.NET.Test -{ - public class AnnotTest - { - [Test] - public void Annot_CleanContents() - { - Document doc = new Document(); - Page page = doc.NewPage(); - Annot annot = page.AddHighlightAnnot(new Rect(10, 10, 20, 20)); - - annot.CleanContents(); - - Assert.That(Encoding.UTF8.GetString(annot.GetAP()).StartsWith("q"), Is.EqualTo(true)); - } - - [Test] - public void Test_PdfString() - { - Utils.GetPdfNow(); - Utils.GetPdfString("Beijing, chinesisch 北京"); - Utils.GetTextLength("Beijing, chinesisch 北京", "null", fontName: "china-s"); - Utils.GetPdfString("Latin characters êßöäü"); - } - - [Test] - public void TestCaret() - { - Document doc = new Document(); - Page page = doc.NewPage(); - Rect r = new Rect(72, 72, 220, 100); - Annot annot = page.AddCaretAnnot(r.TopLeft); - - Assert.That(annot.Type.Item2, Is.EqualTo("Caret")); - Assert.That((int)annot.Type.Item1, Is.EqualTo(14)); - - annot.Update(rotate: 20); - - page.GetAnnotNames(); - page.GetAnnotXrefs(); - } - - [Test] - public void TestFreeText1() - { - Document doc = new Document(); - Page page = doc.NewPage(); - Annot annot = page.AddFreeTextAnnot( - Constants.r, - Constants.t1, - fontSize: 10, - rotate: 90, - textColor: new float[] { 0, 0, 1 }, - align: (int)TextAlign.TEXT_ALIGN_CENTER - ); - - annot.SetBorder(border: null, width: 0.3f, dashes: new int[] { 2 }); - annot.Update(textColor: new float[] { 0, 0, 1 }, fillColor: new float[] { 0, 1, 1 }); - - Assert.That((int)annot.Type.Item1, Is.EqualTo(2)); - Assert.That(annot.Type.Item2, Is.EqualTo("FreeText")); - - page.Dispose(); - doc.Save(@"TestFreeText1.pdf"); - doc.Close(); - } - - [Test] - public void TestFreeText2() - { - string ds = "font-size: 11pt; font-family: sans-serif;"; - // some special characters - string bullet = "\u2610\u2611\u2612"; // Output: ☐☑☒ - - // the annotation text with HTML and styling syntax - string text = $@"

-MuPDF.NET འདི་ ཡིག་ཆ་བཀྲམ་སྤེལ་གྱི་དོན་ལུ་ པའི་ཐོན་ཐུམ་སྒྲིལ་དྲག་ཤོས་དང་མགྱོགས་ཤོས་ཅིག་ཨིན། -Here is some bold and italic text, followed by bold-italic. Text-based check boxes: {bullet}. -

"; - - Document doc = new Document(); - Page page = doc.NewPage(); - - // 3 rectangles, same size, above each other - Rect rect = new Rect(100, 100, 350, 200); - - // define some points for callout lines - Point p2 = rect.TopRight + new Point(50, 30); - Point p3 = p2 + new Point(0, 30); - - // define the annotation - Annot annot = page.AddFreeTextAnnot( - rect, - text, - fillColor: Constants.gold, // fill color - opacity: 1, // non-transparent - rotate: 0, // no rotation - borderWidth: 1, // border and callout line width - dashes: null, // no dashing - richtext: true, // this is rich text - style: ds, // my styling default - callout: new Point[] { p3, p2, rect.TopRight }, // define end, knee, start points - lineEnd: PdfLineEnding.PDF_ANNOT_LE_OPEN_ARROW, // symbol shown at p3 - borderColor: Constants.green - ); - - Assert.That(annot.GetText(page).Length, Is.EqualTo(206)); - - page.Dispose(); - doc.Save(@"TestFreeText2.pdf"); - doc.Close(); - } - - [Test] - public void AddPolyLine() - { - Document doc = new Document(); - Page page = doc.NewPage(); - Rect r = new Rect(72, 72, 220, 100); - Annot annot = page.AddFileAnnot( - r.TopLeft, - Encoding.UTF8.GetBytes("just anything for testing"), - "testdata.txt" - ); - - Assert.That((int)annot.Type.Item1, Is.EqualTo(17)); - } - - [Test] - public void Redact1() - { - Document doc = new Document(); - Page page = doc.NewPage(); - Annot annot = page.AddRedactAnnot(new Rect(72, 72, 200, 200).Quad, text: "Hello"); - annot.Update(rotate: -1); - Assert.That((int)annot.Type.Item1, Is.EqualTo(12)); - - annot.GetPixmap(); - AnnotInfo info = annot.Info; - annot.SetInfo(info); - Assert.That(annot.HasPopup, Is.False); - - annot.SetPopup(new Rect(72, 72, 100, 100)); - Rect s = annot.PopupRect; - - Assert.That(s.Abs(), Is.EqualTo(new Rect(72, 72, 100, 100).Abs())); - page.ApplyRedactions(); - } - - [Test] - public void Redact2() - { - Document doc = new Document("../../../resources/symbol-list.pdf"); - Page page = doc[0]; - List allText = page.GetText("words"); - page.AddRedactAnnot(page.Rect.Quad); - page.ApplyRedactions(text: 0); - List t = page.GetText("words"); - - Assert.That(t.Count, Is.EqualTo(0)); - Assert.That(page.GetDrawings().Count, Is.Zero); - } - - [Test] - public void Redact3() - { - Document doc = new Document("../../../resources/symbol-list.pdf"); - Page page = doc[0]; - List arts = page.GetDrawings(); - page.AddRedactAnnot(page.Rect.Quad); - page.ApplyRedactions(graphics: 0); - - Assert.That(page.GetText("words").Count, Is.Zero); - Assert.That(arts.Count, Is.EqualTo(page.GetDrawings().Count)); - } - - [Test] - public void FirstAnnot() - { - Document doc = new Document("../../../resources/annots.pdf"); - Page page = doc[0]; - Annot firstAnnot = (new List(page.GetAnnots()))[0]; - Annot next = firstAnnot.Next; - } - - [Test] - public void AddLineAnnot() - { - Document doc = new Document(); - Page page = doc.NewPage(); - - page.AddLineAnnot(new Point(0, 0), new Point(100, 100)); - page.AddLineAnnot(new Point(100, 0), new Point(0, 100)); - - IEnumerable annots = page.GetAnnots(); - foreach (Annot annot in annots) - { - annot.SetBorder(width: 8); - annot.Update(); - - Assert.That(annot.Type.Item1, Is.EqualTo(PdfAnnotType.PDF_ANNOT_LINE)); - } - page.Dispose(); - doc.Save(@"AddLineAnnot.pdf"); // Save the modified document - doc.Close(); - } - - /* - * Test fix for #1645. - * The expected output files assume annot_stem is 'jorj'. We need to always - * restore this before returning (this is checked by conftest.py). - */ - [Test] - public void Test1645() - { - string annot_stem = Utils.ANNOT_ID_STEM; - Utils.SetAnnotStem("jorj"); - try - { - string path_in = "../../../resources/symbol-list.pdf"; - string path_expected = "../../../resources/test_1645_expected.pdf"; - string path_out = "test_1645_out.pdf"; - Document doc = new Document(path_in); - Page page = doc[0]; - Rect page_bounds = page.GetBound(); - Rect annot_loc = new Rect(page_bounds.X0, page_bounds.Y0, page_bounds.X0 + 75, page_bounds.Y0 + 15); - - page.AddFreeTextAnnot( - annot_loc * page.DerotationMatrix, - "TEST", - fontSize: 18, - fillColor: Utils.GetColor("FIREBRICK1"), - rotate: page.Rotation - ); - - doc.Save(path_out, garbage: 1, deflate: 1, noNewId: 1); - Console.WriteLine($@"Have created {path_out}. comparing with {path_expected}."); - - byte[] outBytes = File.ReadAllBytes(path_out); - byte[] expectedBytes = File.ReadAllBytes(path_expected); - - Assert.IsTrue(outBytes.SequenceEqual(expectedBytes), "Byte arrays are not equal"); - } - finally - { - Utils.SetAnnotStem(annot_stem); - } - } - - /* - * Test fix for #4254. - * Ensure that both annotations are fully created - * We do this by asserting equal top-used colors in respective pixmaps. - */ - [Test] - public void Test4254() - { - int GetHashCode(byte[] obj) - { - if (obj == null) return 0; - unchecked - { - int hash = 17; - foreach (byte b in obj) - hash = hash * 31 + b; - return hash; - } - } - - Document doc = new Document(); - Page page = doc.NewPage(); - - Rect rect = new Rect(100, 100, 200, 150); - Annot annot = page.AddFreeTextAnnot(rect, "Test Annotation from minimal example"); - annot.SetBorder(width: 1, dashes: new int[] { 3, 3 }); - annot.SetOpacity(0.5f); - try - { - annot.SetColors(stroke: new float[] { 1, 0, 0 }); - } - catch (Exception e) {} - - annot.Update(); - - rect = new Rect(200, 200, 400, 400); - Annot annot2 = page.AddFreeTextAnnot(rect, "Test Annotation from minimal example pt 2"); - annot2.SetBorder(width: 1, dashes: new int[] { 3, 3 }); - annot2.SetOpacity(0.5f); - try - { - annot.SetColors(stroke: new float[] { 1, 0, 0 }); - } - catch (Exception e) { } - - annot.Update(); - annot2.Update(); - - doc.Save("test_4254.pdf"); - - // stores top color for each pixmap - HashSet top_colors = new HashSet(); - foreach (var _annot in page.GetAnnots()) - { - Pixmap pix = _annot.GetPixmap(); - top_colors.Add(GetHashCode(pix.ColorTopUsage().Item2)); - } - - // only one color must exist - Assert.IsTrue(top_colors.Count == 1, "No colors found in annotations pixmaps."); - } - - /* - * Test creation of rich text FreeText annotations. - * We create the same annotation on different pages in different ways, - * with and without using Annotation.update(), and then assert equality - * of the respective images. - * We do this by asserting equal top-used colors in respective pixmaps. - */ - [Test] - public void TestRichText() - { - string ds = "font-size: 11pt; font-family: sans-serif;"; - string bullet = "\u2610\u2611\u2612"; // Output: ☐☑☒; - - string text = $@"

-MuPDF.NET འདི་ ཡིག་ཆ་བཀྲམ་སྤེལ་གྱི་དོན་ལུ་ པའི་ཐོན་ཐུམ་སྒྲིལ་དྲག་ཤོས་དང་མགྱོགས་ཤོས་ཅིག་ཨིན། -Here is some bold and italic text, followed by bold-italic. Text-based check boxes: {bullet}. -

"; - - Document doc = new Document(); - - // first page - Page page = doc.NewPage(); - - Rect rect = new Rect(100, 100, 350, 200); - Point p2 = rect.TopRight + new Point(50, 30); - Point p3 = p2 + new Point(0, 30); - Annot annot = page.AddFreeTextAnnot( - rect, - text, - fillColor: Constants.gold, - opacity: 0.5f, - rotate: 90, - borderWidth: 1, - dashes: null, - richtext: true, - callout: new Point[] { p3, p2, rect.TopRight } - ); - Pixmap pix1 = page.GetPixmap(); - - // # Second page. - // the annotation is created with minimal parameters, which are supplied - // in a separate call to the .update() method. - page = doc.NewPage(); - annot = page.AddFreeTextAnnot( - rect, - text, - borderWidth: 1, - dashes: null, - richtext: true, - callout: new Point[] { p3, p2, rect.TopRight } - ); - annot.Update(fillColor: Constants.gold, opacity: 0.5f, rotate: 90); - Pixmap pix2 = page.GetPixmap(); - - doc.Save("test_rich_text.pdf"); - doc.Close(); - - Assert.That(pix1.SAMPLES, Is.EqualTo(pix2.SAMPLES)); - - pix1.Dispose(); - pix2.Dispose(); - } - - /* - * Test fix for #4447. - */ - [Test] - public void Test4447() - { - Document doc = new Document(); - Page page = doc.NewPage(); - - float[] text_color = Constants.red; - float[] fill_color = Constants.green; - float[] border_color = Constants.blue; - - Rect annot_rect = new Rect(90.1f, 486.73f, 139.26f, 499.46f); - - try - { - Annot annot = page.AddFreeTextAnnot( - annot_rect, - "AETERM", - fontName: "Arial", - fontSize: 10, - textColor: text_color, - fillColor: fill_color, - borderColor: border_color, - borderWidth: 1 - ); - } - catch (Exception e) - { - Assert.That(true, $@"cannot set border_color if rich_text is False {e.Message}"); - } - - try - { - Annot annot = page.AddFreeTextAnnot( - new Rect(30, 400, 100, 450), - "Two", - fontName: "Arial", - fontSize: 10, - textColor: text_color, - fillColor: fill_color, - borderColor: border_color, - borderWidth: 1 - ); - } - catch (Exception e) - { - Assert.That(true, $@"cannot set border_color if rich_text is False {e.Message}"); - } - - { - Annot annot = page.AddFreeTextAnnot( - new Rect(30, 500, 100, 550), - "Three", - fontName: "Arial", - fontSize: 10, - textColor: text_color, - borderWidth: 1 - ); - annot.Update(textColor: text_color, fillColor: fill_color); - try - { - annot.Update(borderColor: border_color); - } - catch (Exception e) - { - Assert.That(true, e.Message, Does.Contain("cannot set border_color if rich_text is False")); - } - } - - doc.Save("test_4447.pdf"); - doc.Close(); - } - - /* - * Test Stamp. - */ - [Test] - public void TestStamp() - { - Document doc = new Document(); - Page page = doc.NewPage(); - - Rect r = new Rect(72, 72, 220, 100); - - Annot annot = page.AddStampAnnot(r, stamp: 0); - Assert.That(annot.Type.Item1, Is.EqualTo(PdfAnnotType.PDF_ANNOT_STAMP)); - Assert.That(annot.Type.Item2, Is.EqualTo("Stamp")); - Assert.That(annot.Info.Content, Is.EqualTo("Approved")); - string annot_id = annot.Info.Id; - int annot_xref = annot.Xref; - Annot annot1 = page.LoadAnnot(annot_id); - Annot annot2 = page.LoadAnnot(annot_xref); - Assert.That(annot1.Xref, Is.EqualTo(annot2.Xref)); - page = doc.ReloadPage(page); - - doc.Save("test_stamp.pdf"); - doc.Close(); - } - - /* - * Test Image Stamp. - */ - [Test] - public void TestImageStamp() - { - Document doc = new Document(); - Page page = doc.NewPage(); - - Rect r = new Rect(72, 72, 220, 100); - - string filename = "../../../resources/nur-ruhig.jpg"; - Annot annot = page.AddStampAnnot(r, stamp: filename); - Assert.That(annot.Info.Content, Is.EqualTo("Image Stamp")); - - doc.Save("test_image_stamp.pdf"); - doc.Close(); - } - } -} diff --git a/MuPDF.NET.Test/AnnotTests.cs b/MuPDF.NET.Test/AnnotTests.cs new file mode 100644 index 00000000..6cc6d05f --- /dev/null +++ b/MuPDF.NET.Test/AnnotTests.cs @@ -0,0 +1,462 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Xunit; + +namespace MuPDF.NET.Test +{ + /// + /// Tests for the Annot class. + /// Ported from tests/test_annots.py. + /// + public class AnnotTests + { + private float[] red = { 1, 0, 0 }; + private float[] blue = { 0, 0, 1 }; + private float[] gold = { 1, 1, 0 }; + private float[] green = { 0, 1, 0 }; + + private Rect displ = new Rect(0, 50, 0, 50); + private Rect r = new Rect(72, 72, 220, 100); + private string t1 = "têxt üsès Lätiñ charß,\nEUR: €, mu: µ, super scripts: ²³!"; + private Rect rect = new Rect(100, 100, 200, 200); + + private (Document doc, Page page) CreateDocWithPage() + { + var doc = new Document(); + var page = doc.NewPage(); + return (doc, page); + } + + // ─── Add annotations ──────────────────────────────────────────── + + [Fact] + public void Annot_AddTextAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddTextAnnot(r.TL, t1); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddTextAnnot.pdf"); + Assert.NotNull(annot); + Assert.Equal("Text", annot.TypeString); + Assert.True(annot.Xref > 0); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on + } + } + + [Fact] + public void Annot_AddFreeTextAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + // `borderWidth` exists only on the full AddFreeTextAnnot overload (disambiguates from compact). + var annot = page.AddFreeTextAnnot( + rect, + t1, + fontsize: 10, + rotate: 90, + textColor: blue, + fillColor: gold, + align: Constants.TEXT_ALIGN_CENTER + ); + annot.SetBorder(width: 0.3f, dashes: new float[] { 2 }); + annot.Update(textColor: blue, fillColor: gold); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddFreeTextAnnot.pdf"); + Assert.NotNull(annot); + Assert.Equal("FreeText", annot.TypeString); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on + } + } + + [Fact] + public void Annot_AddHighlightAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddHighlightAnnot(new[] { new Quad(rect) }); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddHighlightAnnot.pdf"); + Assert.NotNull(annot); + Assert.Equal("Highlight", annot.TypeString); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on + } + } + + [Fact] + public void Annot_AddUnderlineAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddUnderlineAnnot(new[] { new Quad(rect) }); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddUnderlineAnnot.pdf"); + Assert.NotNull(annot); + Assert.Equal("Underline", annot.TypeString); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on + } + } + + [Fact] + public void Annot_AddStrikeoutAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddStrikeoutAnnot(new[] { new Quad(rect) }); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddStrikeoutAnnot.pdf"); + Assert.NotNull(annot); + Assert.Equal("StrikeOut", annot.TypeString); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on + } + } + + [Fact] + public void Annot_AddSquigglyAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddSquigglyAnnot(new[] { new Quad(rect) }); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddSquigglyAnnot.pdf"); + Assert.NotNull(annot); + Assert.Equal("Squiggly", annot.TypeString); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on + } + } + + [Fact] + public void Annot_AddLineAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddLineAnnot(new Point(50, 50), new Point(200, 200)); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddLineAnnot.pdf"); + Assert.NotNull(annot); + Assert.Equal("Line", annot.TypeString); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on + } + } + + [Fact] + public void Annot_AddRectAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddRectAnnot(new Rect(50, 50, 200, 200)); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddRectAnnot.pdf"); + Assert.NotNull(annot); + Assert.Equal("Square", annot.TypeString); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on + } + } + + [Fact] + public void Annot_AddCircleAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddCircleAnnot(new Rect(50, 50, 200, 200)); + annot.Update(fillColor: new float[] { 1, 0, 0 }, rotate: 20, opacity: 0.1f); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddCircleAnnot.pdf"); + Assert.NotNull(annot); + Assert.Equal("Circle", annot.TypeString); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on + } + } + + [Fact] + public void Annot_AddPolylineAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + Rect rect = page.Rect + new Rect(100, 36, -100, -36); + + var cell = Utils.MakeTable(rect, rows: 10); + + for (int i = 0; i < 10; i++) + { + // (cell[i][0].bl, cell[i][0].br) + var points = new[] + { + cell[i][0].BottomLeft, + cell[i][0].BottomRight + }; + + var annot_ = page.AddPolylineAnnot(points); + + annot_.SetLineEnds(i, i); + annot_.Update(); + } + + int index = 0; + + foreach (var annot__ in page.annots()) + { + var lineEnds = annot__.line_ends; + + //Debug.Assert(lineEnds.Item1 == index); + //Debug.Assert(lineEnds.Item2 == index); + + index++; + } + + // last annot from enumeration + var lastAnnot = page.annots().Last(); + + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddPolylineAnnot.pdf"); + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on + } + } + + [Fact] + public void Annot_AddPolygonAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var points = new List + { + rect.BL, rect.TR, rect.BR, rect.TL + }; + var annot = page.AddPolygonAnnot(points.ToArray()); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddPolygonAnnot.pdf"); + Assert.NotNull(annot); + Assert.Equal("Polygon", annot.TypeString); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on + } + } + + [Fact] + public void Annot_AddInkAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var strokes = new List> + { + new List + { + new Point(50, 50), new Point(100, 100), new Point(150, 50) + } + }; + var annot = page.AddInkAnnot(strokes.Select(s => s.ToArray()).ToArray()); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddInkAnnot.pdf"); + Assert.NotNull(annot); + Assert.Equal("Ink", annot.TypeString); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on + } + } + + [Fact] + public void Annot_AddCaretAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddCaretAnnot(new Point(100, 100)); + annot.Update(rotate: 20); + Assert.NotNull(annot); + Assert.Equal("Caret", annot.TypeString); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + List names = page.AnnotNames(); + var xrefs = page.AnnotXrefs(); + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on save. + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddCaretAnnot.pdf"); + } + } + + [Fact] + public void Annot_AddStampAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddStampAnnot(new Rect(100, 100, 300, 200)); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddStampAnnot.pdf"); + Assert.NotNull(annot); + Assert.Equal("Stamp", annot.TypeString); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on save. + } + } + + [Fact] + public void Annot_AddRedactAnnot() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddRedactAnnot(new Quad(new Rect(50, 50, 200, 100))); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_AddRedactAnnot.pdf"); + Assert.NotNull(annot); + Assert.Equal("Redact", annot.TypeString); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on + } + } + + // ─── Annotation properties ────────────────────────────────────── + + [Fact] + public void Annot_RectProperty() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddRectAnnot(new Rect(50, 50, 200, 200)); + var rect = annot.Rect; + Assert.True(rect.Width > 0); + Assert.True(rect.Height > 0); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_RectProperty.pdf"); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on save. + } + } + + [Fact] + public void Annot_Contents() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddTextAnnot(new Point(100, 100), "My Contents"); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_Contents.pdf"); + Assert.Equal("My Contents", annot.Contents); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on save. + } + } + + [Fact] + public void Annot_Flags() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddTextAnnot(new Point(100, 100), "Flags"); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_Flags.pdf"); + Assert.True(annot.Flags >= 0); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on save. + } + } + + [Fact] + public void Annot_Opacity() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot1 = page.AddTextAnnot(new Point(100, 100), "Flags"); + //var annot = page.AddRectAnnot(new Rect(50, 50, 200, 200)); + var annot = page.AddRedactAnnot(new Quad(new Rect(50, 50, 200, 200))); + //annot.SetOpacity(0.5f); + Assert.False(TestHelper.IsClose(0.5, annot.Opacity, 0.01)); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_Opacity.pdf"); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on save. + } + } + + // ─── Iteration ────────────────────────────────────────────────── + + [Fact] + public void Page_AnnotsEnumeration() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + page.AddTextAnnot(new Point(50, 50), "A1"); + page.AddRectAnnot(new Rect(100, 100, 200, 200)); + page.AddCircleAnnot(new Rect(200, 200, 300, 300)); + int count = page.Annots().Count(); + Assert.Equal(3, count); + doc.Save(@"E:\Pdf\Tmp\Test\Page_AnnotsEnumeration.pdf"); + page.Dispose(); + } + } + + [Fact] + public void Page_NoAnnots() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + int count = page.Annots().Count(); + Assert.Equal(0, count); + } + } + + // ─── Delete ───────────────────────────────────────────────────── + + [Fact] + public void Annot_Delete() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddTextAnnot(new Point(50, 50), "Delete Me"); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_Delete_Before.pdf"); + Assert.NotNull(page.FirstAnnot); + page.DeleteAnnot(annot); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_Delete.pdf"); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on save. + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on save. + } + } + + // ─── Update ───────────────────────────────────────────────────── + + [Fact] + public void Annot_Update() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddRectAnnot(new Rect(50, 50, 200, 200)); + annot.SetColors(new float[] { 0, 1, 1 }); + //annot.Update(default(float[]), opacity: 0); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_Update.pdf"); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on save. + } + } + + // ─── SetInfo / GetInfo ────────────────────────────────────────── + + [Fact] + public void Annot_SetAndGetInfo() + { + var (doc, page) = CreateDocWithPage(); + using (doc) + { + var annot = page.AddTextAnnot(new Point(50, 50), "Info Test"); + annot.SetInfo(new Dictionary { ["title"] = "Test Author" }); + var result = annot.GetInfo(); + Assert.Contains("title", result.Keys); + Assert.Equal("Test Author", result["title"]); + doc.Save(@"E:\Pdf\Tmp\Test\Annot_SetAndGetInfo.pdf"); + annot.Dispose(); // Dispose annot before page to avoid "Page still in use" error on + page.Dispose(); // Dispose page before doc to avoid "Page still in use" error on save. + } + } + } +} diff --git a/MuPDF.NET.Test/ArchiveTest.cs b/MuPDF.NET.Test/ArchiveTest.cs deleted file mode 100644 index 25825704..00000000 --- a/MuPDF.NET.Test/ArchiveTest.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MuPDF.NET.Test -{ - public class ArchiveTest - { - - } -} diff --git a/MuPDF.NET.Test/BarcodeTest.cs b/MuPDF.NET.Test/BarcodeTest.cs deleted file mode 100644 index 99336642..00000000 --- a/MuPDF.NET.Test/BarcodeTest.cs +++ /dev/null @@ -1,274 +0,0 @@ -using NUnit.Framework; -using mupdf; - -namespace MuPDF.NET.Test -{ - public class BarcodeTest - { - [Test] - public void ReadBarcodeFromImage() - { - string imageFilePath = Path.GetFullPath("../../../resources/Barcodes/rendered.bmp"); - - // Code 128 - List code128s = Utils.ReadBarcodes(imageFilePath, barcodeFormat:BarcodeFormat.CODE128); - Assert.IsTrue(code128s.Count == 3); - Assert.IsTrue(code128s[0].Text == "Hello World!"); - Assert.IsTrue(code128s[1].Text == "0123456789"); - Assert.IsTrue(code128s[2].Text == "010123456789012810012345678915041220"); - - // I2OF5 - List i2of5s = Utils.ReadBarcodes(imageFilePath, clip: new Rect(1245, 690, 2200, 1235), barcodeFormat: BarcodeFormat.I2OF5); - Assert.IsTrue(i2of5s.Count == 2); - Assert.IsTrue(i2of5s[0].Text == "0123456789"); - Assert.IsTrue(i2of5s[1].Text == "15400141288763"); - - // Code 39 - List code39s = Utils.ReadBarcodes(imageFilePath, clip: new Rect(1245, 1295, 2200, 1520), barcodeFormat: BarcodeFormat.CODE39); - Assert.IsTrue(code39s.Count == 1); - Assert.IsTrue(code39s[0].Text == "0123456789"); - - // UPC-A - List upcAs = Utils.ReadBarcodes(imageFilePath, clip: new Rect(1245, 2490, 2200, 2720), barcodeFormat: BarcodeFormat.UPC_A); - Assert.IsTrue(upcAs.Count == 1); - Assert.IsTrue(upcAs[0].Text == "001234567895"); - - // UPC-E - List upcEs = Utils.ReadBarcodes(imageFilePath, clip: new Rect(1245, 2780, 2200, 3020), barcodeFormat: BarcodeFormat.UPC_E); - Assert.IsTrue(upcEs.Count == 1); - Assert.IsTrue(upcEs[0].Text == "01234133"); - } - - [Test] - public void ReadBarcodeFromPdf() - { - string pdfFilePath = Path.GetFullPath("../../../resources/Barcodes/Samples.pdf"); - - Document doc = new Document(pdfFilePath); - - Page page = doc[0]; - - // EAN 8 - List ean8s = page.ReadBarcodes(barcodeFormat: BarcodeFormat.EAN8); - Assert.IsTrue(ean8s.Count == 1); - Assert.IsTrue(ean8s[0].Text == "40123455"); - - // EAN 13 - List ean13s = page.ReadBarcodes(clip: new Rect(212, 243, 510, 575), barcodeFormat: BarcodeFormat.EAN13); - Assert.IsTrue(ean13s.Count == 3); - Assert.IsTrue(ean13s[0].Text == "4012345678901"); - Assert.IsTrue(ean13s[1].Text == "9783161484100"); - Assert.IsTrue(ean13s[2].Text == "9771234567058"); - - // UPC-A - List upcAs = page.ReadBarcodes(clip: new Rect(212, 575, 510, 690), barcodeFormat: BarcodeFormat.UPC_A); - Assert.IsTrue(upcAs.Count == 1); - Assert.IsTrue(upcAs[0].Text == "042287061527"); - - // UPC-E - List upcEs = page.ReadBarcodes(clip: new Rect(212, 690, 510, 790), barcodeFormat: BarcodeFormat.UPC_E); - Assert.IsTrue(upcEs.Count == 1); - Assert.IsTrue(upcEs[0].Text == "12345670"); - - // 2/5 Interleaved - List i2of5s = page.ReadBarcodes(clip: new Rect(800, 160, 1100, 370), barcodeFormat: BarcodeFormat.I2OF5); - Assert.IsTrue(i2of5s.Count == 2); - Assert.IsTrue(i2of5s[0].Text == "012345678905"); - Assert.IsTrue(i2of5s[1].Text == "40123456789010"); - - // PHARMA - List pharmas = page.ReadBarcodes(clip: new Rect(800, 230, 1100, 295), barcodeFormat: BarcodeFormat.PHARMA); - Assert.IsTrue(pharmas.Count == 1); - Assert.IsTrue(pharmas[0].Text == "-1043773788"); - - // Code 39 - List code39s = page.ReadBarcodes(clip: new Rect(800, 370, 1100, 435), barcodeFormat: BarcodeFormat.CODE39); - Assert.IsTrue(code39s.Count == 1); - Assert.IsTrue(code39s[0].Text == "123ABC$"); - - // Code 39 Extended - List code39Exs = page.ReadBarcodes(clip: new Rect(800, 435, 1100, 505), barcodeFormat: BarcodeFormat.CODE39_EX); - Assert.IsTrue(code39Exs.Count == 1); - Assert.IsTrue(code39Exs[0].Text == "123+A+B+CX"); - - // Code 128 - List code128s = page.ReadBarcodes(clip: new Rect(800, 505, 1100, 645), barcodeFormat: BarcodeFormat.CODE128); - Assert.IsTrue(code128s.Count == 2); - Assert.IsTrue(code128s[0].Text == "1234567890"); - Assert.IsTrue(code128s[1].Text == "014012345678901"); - - // PHARMA - pharmas = page.ReadBarcodes(clip: new Rect(800, 645, 1100, 775), barcodeFormat: BarcodeFormat.PHARMA); - Assert.IsTrue(pharmas.Count == 2); - Assert.IsTrue(pharmas[0].Text == "1319299"); - Assert.IsTrue(pharmas[1].Text == "12345"); - - // TRIOPTIC - List trioptics = page.ReadBarcodes(clip: new Rect(800, 775, 1100, 845), barcodeFormat: BarcodeFormat.TRIOPTIC); - Assert.IsTrue(trioptics.Count == 1); - Assert.IsTrue(trioptics[0].Text == "-1234567"); - - // Datamatrix - List datamatrixes = page.ReadBarcodes(clip: new Rect(1400, 150, 1660, 357), barcodeFormat: BarcodeFormat.DM); - //Assert.IsTrue(datamatrixes.Count == 1); - //Assert.IsTrue(datamatrixes[0].Text == "-1234567"); - - // QR - List qrs = page.ReadBarcodes(clip: new Rect(1400, 357, 1660, 500), barcodeFormat: BarcodeFormat.QR); - Assert.IsTrue(qrs.Count == 1); - Assert.IsTrue(qrs[0].Text == "https://softmatic.com"); - - // PDF417 - List pdf417s = page.ReadBarcodes(clip: new Rect(1400, 500, 1660, 580), barcodeFormat: BarcodeFormat.PDF417); - Assert.IsTrue(pdf417s.Count == 1); - Assert.IsTrue(pdf417s[0].Text == "1234567890ABCDEF"); - - // Aztec - List aztecs = page.ReadBarcodes(clip: new Rect(1400, 580, 1660, 720), barcodeFormat: BarcodeFormat.AZTEC); - Assert.IsTrue(aztecs.Count == 1); - Assert.IsTrue(aztecs[0].Text == "1234567890ABCDEF"); - - page.Dispose(); - doc.Close(); - } - - [Test] - public void ReadDatamatrixFromPdf() - { - string pdfFilePath = Path.GetFullPath("../../../resources/Barcodes/datamatrix.pdf"); - - Document doc = new Document(pdfFilePath); - - Page page = doc[0]; - - List barcodes = page.ReadBarcodes(barcodeFormat: BarcodeFormat.DM); - Assert.IsTrue(barcodes.Count == 2); - Assert.IsTrue(barcodes[0].Text == "01020000110435177000"); - Assert.IsTrue(barcodes[1].Text == "1100630047057533"); - - page.Dispose(); - doc.Close(); - } - - [Test] - public void ReadQrFromPdf() - { - string pdfFilePath = Path.GetFullPath("../../../resources/Barcodes/qr.pdf"); - - Document doc = new Document(pdfFilePath); - - Page page = doc[0]; - - List barcodes = page.ReadBarcodes(barcodeFormat: BarcodeFormat.QR); - Assert.IsTrue(barcodes.Count == 1); - Assert.IsTrue(barcodes[0].Text == "$001-52-20250520#MRC"); - - page.Dispose(); - doc.Close(); - } - - [Test] - public void TestBarcode() - { - string testFilePath = @"TestBarcode.pdf"; - - Document doc = new Document(); - Page page = doc.NewPage(); - - MuPDF.NET.TextWriter writer = new MuPDF.NET.TextWriter(page.Rect); - Font font = new Font("cour", isBold: 1); - writer.FillTextbox(page.Rect, "QR_CODE", font, pos: new Point(0, 10)); - writer.FillTextbox(page.Rect, "EAN_8", font, pos: new Point(0, 110)); - writer.FillTextbox(page.Rect, "EAN_13", font, pos: new Point(0, 165)); - writer.FillTextbox(page.Rect, "UPC_A", font, pos: new Point(0, 220)); - writer.FillTextbox(page.Rect, "CODE_39", font, pos: new Point(0, 275)); - writer.FillTextbox(page.Rect, "CODE_128", font, pos: new Point(0, 330)); - writer.FillTextbox(page.Rect, "ITF", font, pos: new Point(0, 385)); - writer.FillTextbox(page.Rect, "PDF_417", font, pos: new Point(0, 440)); - writer.FillTextbox(page.Rect, "CODABAR", font, pos: new Point(0, 520)); - writer.FillTextbox(page.Rect, "DATA_MATRIX", font, pos: new Point(0, 620)); - writer.WriteText(page); - - // QR_CODE - Rect rect = new Rect(100, 20, 300, 80); - page.WriteBarcode(rect, "Hello World!", BarcodeFormat.QR, forceFitToRect: false, pureBarcode: false, marginLeft: 0); - - // EAN_8 - rect = new Rect(100, 100, 300, 120); - page.WriteBarcode(rect, "1234567", BarcodeFormat.EAN8, forceFitToRect: false, pureBarcode: false, marginBottom: 20); - - // EAN_13 - rect = new Rect(100, 155, 300, 200); - page.WriteBarcode(rect, "123456789012", BarcodeFormat.EAN13, forceFitToRect: false, pureBarcode: true, marginBottom: 0); - - // UPC_A - rect = new Rect(100, 210, 300, 255); - page.WriteBarcode(rect, "123456789012", BarcodeFormat.UPC_A, forceFitToRect: false, pureBarcode: true, marginBottom: 0); - - // CODE_39 - rect = new Rect(100, 265, 600, 285); - page.WriteBarcode(rect, "Hello!", BarcodeFormat.CODE39, forceFitToRect: false, pureBarcode: false, marginBottom: 0); - - // CODE_128 - rect = new Rect(100, 320, 400, 355); - page.WriteBarcode(rect, "Hello World!", BarcodeFormat.CODE128, forceFitToRect: true, pureBarcode: true, marginBottom: 0); - - // ITF - rect = new Rect(100, 385, 300, 420); - page.WriteBarcode(rect, "12345678901234567890", BarcodeFormat.I2OF5, forceFitToRect: false, pureBarcode: false, marginBottom: 0); - - // PDF_417 - rect = new Rect(100, 430, 400, 435); - page.WriteBarcode(rect, "Hello World!", BarcodeFormat.PDF417, forceFitToRect: false, pureBarcode: true, marginBottom: 0); - - // CODABAR - rect = new Rect(100, 540, 400, 580); - page.WriteBarcode(rect, "12345678901234567890", BarcodeFormat.CODABAR, forceFitToRect: true, pureBarcode: true, marginBottom: 0); - - // DATA_MATRIX - rect = new Rect(100, 620, 140, 660); - page.WriteBarcode(rect, "01100000110419257000", BarcodeFormat.DM, forceFitToRect: false, pureBarcode: false, marginBottom: 0); - - page.Dispose(); - doc.Save(testFilePath); - doc.Close(); - - // read barcodes in the new pdf document - doc = new Document(testFilePath); - page = doc[0]; - - List barcodes = page.ReadBarcodes(barcodeFormat: BarcodeFormat.QR); - Assert.IsTrue(barcodes.Count == 1 && barcodes[0].Text == "Hello World!"); - - barcodes = page.ReadBarcodes(barcodeFormat: BarcodeFormat.EAN8); - Assert.IsTrue(barcodes.Count == 1 && barcodes[0].Text == "12345670"); - - barcodes = page.ReadBarcodes(barcodeFormat: BarcodeFormat.EAN13); - Assert.IsTrue(barcodes.Count == 1 && barcodes[0].Text == "0123456789012"); - - barcodes = page.ReadBarcodes(barcodeFormat: BarcodeFormat.UPC_A); - Assert.IsTrue(barcodes.Count == 1 && barcodes[0].Text == "123456789012"); - - barcodes = page.ReadBarcodes(barcodeFormat: BarcodeFormat.CODE39); - Assert.IsTrue(barcodes.Count == 1 && barcodes[0].Text == "H+E+L+L+O/A"); - - barcodes = page.ReadBarcodes(clip: new Rect(100, 320, 400, 355), barcodeFormat: BarcodeFormat.CODE128); - Assert.IsTrue(barcodes.Count == 1 && barcodes[0].Text == "Hello World!"); - - barcodes = page.ReadBarcodes(barcodeFormat: BarcodeFormat.I2OF5); - Assert.IsTrue(barcodes.Count == 1 && barcodes[0].Text == "12345678901234567890"); - - barcodes = page.ReadBarcodes(barcodeFormat: BarcodeFormat.PDF417); - Assert.IsTrue(barcodes.Count == 1 && barcodes[0].Text == "Hello World!"); - - barcodes = page.ReadBarcodes(clip: new Rect(80, 500, 450, 700), barcodeFormat: BarcodeFormat.CODABAR); - //Assert.IsTrue(barcodes.Count == 1 && barcodes[0].Text == "12345678901234567890"); - - barcodes = page.ReadBarcodes(clip: new Rect(100, 620, 140, 660), barcodeFormat: BarcodeFormat.DM); - //Assert.IsTrue(barcodes.Count == 1 && barcodes[0].Text == "01100000110419257000"); - - page.Dispose(); - doc.Close(); - } - } -} diff --git a/MuPDF.NET.Test/ColorspaceTests.cs b/MuPDF.NET.Test/ColorspaceTests.cs new file mode 100644 index 00000000..d8e81877 --- /dev/null +++ b/MuPDF.NET.Test/ColorspaceTests.cs @@ -0,0 +1,39 @@ +using Xunit; + +namespace MuPDF.NET.Test +{ + /// + /// Tests for the Colorspace class. + /// + public class ColorspaceTests + { + [Fact] + public void Colorspace_RGB() + { + var cs = Colorspace.CsRGB; + Assert.Equal(3, cs.N); + Assert.Contains("RGB", cs.Name, System.StringComparison.OrdinalIgnoreCase); + } + + [Fact] + public void Colorspace_Gray() + { + var cs = Colorspace.CsGRAY; + Assert.Equal(1, cs.N); + } + + [Fact] + public void Colorspace_CMYK() + { + var cs = Colorspace.CsCMYK; + Assert.Equal(4, cs.N); + } + + [Fact] + public void Colorspace_NameNotEmpty() + { + var cs = Colorspace.CsRGB; + Assert.False(string.IsNullOrEmpty(cs.Name)); + } + } +} diff --git a/MuPDF.NET.Test/DisposePatternTest.cs b/MuPDF.NET.Test/DisposePatternTest.cs deleted file mode 100644 index 80fdc4b6..00000000 --- a/MuPDF.NET.Test/DisposePatternTest.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.IO; -using NUnit.Framework; -using MuPDF.NET; - -namespace MuPDF.NET.Test -{ - public class DisposePatternTest - { - private const string TocPath = "../../../resources/toc.pdf"; - - [Test] - public void Document_Dispose_MultipleTimes_DoesNotThrow() - { - var doc = new Document(TocPath); - - doc.Dispose(); - - Assert.DoesNotThrow(() => doc.Dispose()); - } - - [Test] - public void Page_Dispose_MultipleTimes_DoesNotThrow() - { - var doc = new Document(TocPath); - var page = doc[0]; - - page.Dispose(); - - Assert.DoesNotThrow(() => page.Dispose()); - - doc.Dispose(); - } - - [Test] - public void TextPage_Dispose_MultipleTimes_DoesNotThrow() - { - var doc = new Document(TocPath); - var page = doc[0]; - var textPage = page.GetTextPage(); - - textPage.Dispose(); - - Assert.DoesNotThrow(() => textPage.Dispose()); - - page.Dispose(); - doc.Dispose(); - } - - [Test] - public void Story_Dispose_MultipleTimes_DoesNotThrow() - { - var story = new Story("

Hello

"); - - story.Dispose(); - - Assert.DoesNotThrow(() => story.Dispose()); - } - - [Test] - public void DisplayList_Dispose_MultipleTimes_DoesNotThrow() - { - var rect = new Rect(0, 0, 100, 100); - var dl = new DisplayList(rect); - - dl.Dispose(); - - Assert.DoesNotThrow(() => dl.Dispose()); - } - - [Test] - public void DocumentWriter_Dispose_MultipleTimes_DoesNotThrow() - { - string path = Path.GetTempFileName(); - - try - { - var writer = new DocumentWriter(path); - - writer.Dispose(); - - Assert.DoesNotThrow(() => writer.Dispose()); - } - finally - { - if (File.Exists(path)) - File.Delete(path); - } - } - - [Test] - public void Font_Dispose_MultipleTimes_DoesNotThrow() - { - var font = new Font(); - - font.Dispose(); - - Assert.DoesNotThrow(() => font.Dispose()); - } - - [Test] - public void GraftMap_Dispose_MultipleTimes_DoesNotThrow() - { - var doc = new Document(TocPath); - var map = new GraftMap(doc); - - map.Dispose(); - - Assert.DoesNotThrow(() => map.Dispose()); - - doc.Dispose(); - } - - // Outline is constructed internally from native MuPDF outline structures and - // not exposed as a public constructor. Its disposal semantics are exercised - // indirectly via Document/Document.GetToc tests, so we skip a direct idempotency test. - } -} - diff --git a/MuPDF.NET.Test/DocumentTest.cs b/MuPDF.NET.Test/DocumentTest.cs deleted file mode 100644 index 7141ce01..00000000 --- a/MuPDF.NET.Test/DocumentTest.cs +++ /dev/null @@ -1,245 +0,0 @@ -using mupdf; -using System; -using System.IO; -using System.Collections.Generic; -using System.Data.Common; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NUnit.Framework; - -namespace MuPDF.NET.Test -{ - public class DocumentTest : PdfTestBase - { - [Test] - public void CopyFullPage() - { - Document doc = new Document("../../../resources/toc.pdf"); - int oldLen = doc.PageCount; - doc.CopyFullPage(0); - - Assert.AreEqual(doc.PageCount, oldLen + 1); - doc.Close(); - } - - [Test] - public void CopyPage() - { - Document doc = new Document("../../../resources/toc.pdf"); - int oldLen = doc.PageCount; - doc.CopyPage(0); - - Assert.AreEqual(doc.PageCount, oldLen + 1); - doc.Close(); - } - - [Test] - public void ColorTest() - { - string testFilePath = Path.GetFullPath("../../../resources/DocumentTest/Color.pdf"); - Document doc = new Document(testFilePath); - List images = doc.GetPageImages(0); - Assert.IsTrue(images[0].CsName == "DeviceRGB"); - - doc.Recolor(0, 4); - images = doc.GetPageImages(0); - Assert.IsTrue(images[0].CsName == "ICCBased"); - - doc.Close(); - } - - [Test] - public void DeletePage() - { - Document doc = new Document("../../../resources/toc.pdf"); - int oldLen = doc.PageCount; - doc.DeletePage(0); - Assert.AreEqual(doc.PageCount, oldLen - 1); - doc.Close(); - } - - [Test] - public void DeletePages() - { - Document doc = new Document("../../../resources/toc.pdf"); - int oldLen = doc.PageCount; - - doc.DeletePages(0); // delete one page - - doc.DeletePages(new int[] { 0, }); // delete 2 pages - - Assert.AreEqual(doc.PageCount, oldLen - 1); - doc.Close(); - } - - [Test] - public void XmlMetadata() - { - Document doc = new Document("../../../resources/toc.pdf"); - doc.DeleteXmlMetadata(); - - Assert.That(doc.GetXmlMetadata(), Is.EqualTo("")); - doc.Close(); - } - - [Test] - public void GetXrefLen() - { - Document doc = new Document("../../../resources/toc.pdf"); - doc.GetXrefLength(); - //Assert.Pass(); - doc.Close(); - } - - [Test] - public void GetPageImage_ExtractImage() - { - Document doc = new Document("../../../resources/toc.pdf"); - int n = doc.GetPageImages(0).Count; - - Assert.That(n, Is.EqualTo(15)); // in case of current input pdf, if other file, real count should be fixed - - n = doc.ExtractImage(doc.GetPageImages(0)[0].Xref).Image.Length; - - //Assert.Pass(); - doc.Close(); - } - - [Test] - public void GetToc() - { - Document doc = new Document("../../../resources/toc.pdf"); - doc.GetToc(true); - } - - [Test] - public void EraseToc() - { - Document doc = new Document("../../../resources/toc.pdf"); - doc.SetToc(null); - Assert.That(doc.GetToc().Count, Is.EqualTo(0)); - doc.Close(); - } - - [Test] - public void Embedded() - { - Document doc = new Document(); - byte[] buffer = Encoding.UTF8.GetBytes("123456678790qwexcvnmhofbnmfsdg4589754uiofjkb-"); - doc.AddEmbfile("file1", buffer, "testfile.txt", "testfile-u.txt", "Description of some sort"); - } - - [Test] - public void Test_IsNoPDF() - { - Document doc = new Document("../../../resources/DocumentTest/Bezier.epub"); - Assert.That(doc.IsPDF, Is.False); - } - - [Test] - public void Test_PageIds() - { - Document doc = new Document("../../../resources/DocumentTest/Bezier.epub"); - - Assert.That(doc.ChapterCount, Is.EqualTo(7)); - Assert.That(doc.LastLocation.Item1, Is.EqualTo(6)); - } - - [Test] - public void OC1() - { - Document doc = new Document(); - int ocg1 = doc.AddOcg("ocg1"); - int ocg2 = doc.AddOcg("ocg2"); - doc.SetOCMD(xref: 0, ocgs: new int[] { ocg1, ocg2 }); - doc.SetLayer(-1); - doc.AddLayer("layer1"); - - doc.GetLayer(); - doc.GetLayers(); - doc.GetOcgs(); - doc.LayerUIConfigs(); - doc.SwitchLayer(0); - } - - [Test] - public void OpenUnicodeDocument() - { - Document doc = new Document("../../../resources/DocumentTest/你好.pdf"); - Assert.That(doc.PageCount, Is.EqualTo(1)); - doc.Close(); - } - - [Test] - public void TestRewriteImages() - { - // Example for decreasing file size by more than 30%. - string filePath = "../../../resources/DocumentTest/test-rewrite-images.pdf"; - Document doc = new Document(filePath); - int size0 = File.ReadAllBytes(filePath).Length; - doc.RewriteImage(dpiThreshold: 100, dpiTarget: 72, quality: 33); - byte[] data = doc.Write(garbage: true, deflate: true); - int size1 = data.Length; - - Assert.That((1-(size1/size0)) > 0.3); - } - - [Test] - public void TestJoinPdfPages() - { - string testFilePath1 = Path.GetFullPath(@"../../../resources/DocumentTest/Widget.pdf"); - Document doc1 = new Document(testFilePath1); - string testFilePath2 = Path.GetFullPath(@"../../../resources/DocumentTest/Color.pdf"); - Document doc2 = new Document(testFilePath2); - - doc1.InsertPdf(doc2, 0, 0, 2); - - doc1.Save("Joined.pdf", pretty: 1); - - Assert.IsTrue(doc1.PageCount == 7); - - doc2.Close(); - doc1.Close(); - } - - [Test] - public void TestMoveFile() - { - string testFilePath1 = Path.GetFullPath(@"../../../resources/DocumentTest/Widget.pdf"); - string testFilePath2 = Path.GetFullPath(@"TestMoveOrig.pdf"); - string testFilePath3 = Path.GetFullPath(@"TestMoveNew.pdf"); - - File.Copy(testFilePath1, testFilePath2, true); - - Document doc = new Document(testFilePath2); - Page page = doc[0]; - - Point tl = new Point(100, 120); - Point br = new Point(300, 150); - - Rect rect = new Rect(tl, br); - TextWriter pw = new TextWriter(page.TrimBox); - Font font = new Font(fontName: "tiro"); - List<(string, float)> ret = pw.FillTextbox(rect, "This is a test to overwrite the original file and move it", font, fontSize: 24); - - pw.WriteText(page); - page.Dispose(); - - MemoryStream tmp = new MemoryStream(); - - doc.Save(tmp, garbage: 3, deflateFonts: 1, deflate: 1); - doc.Close(); - - File.WriteAllBytes(testFilePath2, tmp.ToArray()); - - tmp.Dispose(); - - File.Move(testFilePath2, testFilePath3, true); - - Document newDoc = new Document(testFilePath3); - Assert.IsTrue(newDoc.PageCount == 6); - newDoc.Close(); - } - } -} diff --git a/MuPDF.NET.Test/DocumentTests.cs b/MuPDF.NET.Test/DocumentTests.cs new file mode 100644 index 00000000..fa6c8e61 --- /dev/null +++ b/MuPDF.NET.Test/DocumentTests.cs @@ -0,0 +1,918 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using Xunit; + +namespace MuPDF.NET.Test +{ + /// + /// Tests for the Document class. + /// Ported from tests/test_general.py, tests/test_metadata.py. + /// + public class DocumentTests + { + // ─── Construction ─────────────────────────────────────────────── + + [Fact] + public void Document_NewEmptyPdf() + { + using var doc = new Document(); + Assert.True(doc.IsPdf); + Assert.Equal(0, doc.PageCount); + Assert.False(doc.IsClosed); + } + + [Fact] + public void Document_init_doc_OpenPdf_NoThrow() + { + using var doc = new Document(); + doc.init_doc(); + Assert.False(doc.IsEncrypted); + } + + [Fact] + public void Document_OpenNonExistent_Throws() + { + Assert.Throws(() => new Document("nonexistent.pdf")); + } + + [Fact] + public void Document_OpenEmptyFile_Throws() + { + var path = Path.GetTempFileName(); + try + { + Assert.Throws(() => new Document(path)); + } + finally + { + File.Delete(path); + } + } + + [Fact] + public void Document_OpenEmptyStream_Throws() + { + Assert.Throws(() => new Document(Array.Empty())); + } + + [Fact] + public void Document_Close() + { + var doc = new Document(); + Assert.False(doc.IsClosed); + doc.Close(); + Assert.True(doc.IsClosed); + } + + [Fact] + public void Document_CloseTwiceNoError() + { + var doc = new Document(); + doc.Close(); + doc.Close(); + Assert.True(doc.IsClosed); + } + + [Fact] + public void Document_AccessAfterClose_Throws() + { + var doc = new Document(); + doc.Close(); + var ex = Assert.Throws(() => _ = doc.PageCount); + Assert.Equal("document closed", ex.Message); + } + + [Fact] + public void Document_DisposeClosesDocument() + { + var doc = new Document(); + doc.Dispose(); + Assert.True(doc.IsClosed); + } + + // ─── New page creation ────────────────────────────────────────── + + [Fact] + public void Document_NewPage() + { + using var doc = new Document(); + var page = doc.NewPage(); + Assert.Equal(1, doc.PageCount); + Assert.Equal(595, page.Width); + Assert.Equal(842, page.Height); + } + + [Fact] + public void Document_NewPageCustomSize() + { + using var doc = new Document(); + var page = doc.NewPage(width: 200, height: 300); + Assert.True(TestHelper.IsClose(200, page.Width)); + Assert.True(TestHelper.IsClose(300, page.Height)); + } + + [Fact] + public void Document_NewPage_WhenClosed_ThrowsLikePython() + { + var doc = new Document(); + doc.Close(); + var ex = Assert.Throws(() => doc.NewPage()); + Assert.Equal("document closed or encrypted", ex.Message); + } + + [Fact] + public void Document_NewPage_InsertBeforeLessThanMinusOne_ThrowsLikePython() + { + using var doc = new Document(); + doc.NewPage(); + Assert.Throws(() => doc.NewPage(-2)); + } + + [Fact] + public void Document_MultiplePages() + { + using var doc = new Document(); + doc.NewPage(); + doc.NewPage(); + doc.NewPage(); + Assert.Equal(3, doc.PageCount); + } + + // ─── Page loading ─────────────────────────────────────────────── + + [Fact] + public void Document_LoadPage() + { + using var doc = new Document(); + doc.NewPage(); + var page = doc.LoadPage(0); + Assert.NotNull(page); + Assert.Equal(0, page.Number); + } + + [Fact] + public void Document_LoadPageNegativeIndex() + { + using var doc = new Document(); + doc.NewPage(); + doc.NewPage(); + var page = doc.LoadPage(-1); + Assert.Equal(1, page.Number); + } + + [Fact] + public void Document_LoadPageOutOfRange_Throws() + { + using var doc = new Document(); + doc.NewPage(); + var ex = Assert.Throws(() => doc.LoadPage(5)); + Assert.Equal("page not in document", ex.Message); + } + + [Fact] + public void Document_LoadPage_WhenClosed_ThrowsLikePython() + { + var doc = new Document(); + doc.NewPage(); + doc.Close(); + var ex = Assert.Throws(() => doc.LoadPage(0)); + Assert.Equal("document closed or encrypted", ex.Message); + } + + [Fact] + public void Document_Indexer() + { + using var doc = new Document(); + doc.NewPage(); + var page = doc[0]; + Assert.NotNull(page); + } + + [Fact] + public void Document_Indexer_OutOfRange_ThrowsIndexErrorLikePython() + { + using var doc = new Document(); + doc.NewPage(); + var ex = Assert.Throws(() => _ = doc[5]); + Assert.Contains("page 5", ex.Message, StringComparison.Ordinal); + Assert.Contains("not in document", ex.Message, StringComparison.Ordinal); + } + + // ─── Page enumeration ─────────────────────────────────────────── + + [Fact] + public void Document_Enumerate() + { + using var doc = new Document(); + doc.NewPage(); + doc.NewPage(); + int count = 0; + foreach (var page in doc) + count++; + Assert.Equal(2, count); + } + + [Fact] + public void Document_PagesGenerator() + { + using var doc = new Document(); + doc.NewPage(); + doc.NewPage(); + doc.NewPage(); + int count = 0; + foreach (var page in doc.Pages(start: 0, stop: 2)) + count++; + Assert.Equal(2, count); + } + + // ─── Properties ───────────────────────────────────────────────── + + [Fact] + public void Document_IsPdf() + { + using var doc = new Document(); + Assert.True(doc.IsPdf); + } + + /// Matches PyMuPDF tests/test_general.py::test_isdirty (opened file, not a fresh in-memory PDF). + [Fact] + public void Document_IsDirty_FalseOnOpenPdf() + { + var path = TestHelper.GetResource("test_4043.pdf"); + Assert.True(File.Exists(path), $"missing test PDF: {path}"); + using var doc = new Document(path); + Assert.False(doc.IsDirty); + } + + [Fact] + public void Document_NeedsPass_FalseOnNew() + { + using var doc = new Document(); + Assert.False(doc.NeedsPass); + } + + [Fact] + public void Document_IsReflowable_FalseForPdf() + { + using var doc = new Document(); + Assert.False(doc.IsReflowable); + } + + [Fact] + public void Document_ChapterCount() + { + using var doc = new Document(); + Assert.True(doc.ChapterCount >= 1); + } + + [Fact] + public void Document_ContainsPage() + { + using var doc = new Document(); + doc.NewPage(); + Assert.True(doc.ContainsPage(0)); + Assert.False(doc.ContainsPage(5)); + } + + // ─── Metadata ─────────────────────────────────────────────────── + + [Fact] + public void Document_GetMetadata() + { + using var doc = new Document(); + var meta = doc.GetMetadata(); + Assert.NotNull(meta); + Assert.True(meta.ContainsKey("format")); + } + + [Fact] + public void Document_SetMetadata() + { + using var doc = new Document(); + doc.SetMetadata(new Dictionary + { + ["title"] = "Test Title", + ["author"] = "Test Author" + }); + var meta = doc.GetMetadata(); + Assert.Contains("Test Title", meta.GetValueOrDefault("title", "")); + } + + [Fact] + public void Document_SetMetadata_WhenClosed_ThrowsLikePython() + { + var doc = new Document(); + doc.NewPage(); + doc.Close(); + var ex = Assert.Throws(() => + doc.SetMetadata(new Dictionary { ["title"] = "x" })); + Assert.Equal("document closed or encrypted", ex.Message); + } + + // ─── Table of Contents ────────────────────────────────────────── + + [Fact] + public void Document_GetToc_EmptyOnNew() + { + using var doc = new Document(); + doc.NewPage(); + var toc = doc.GetToc(); + Assert.Empty(toc); + } + + // ─── Page Operations ──────────────────────────────────────────── + + [Fact] + public void Document_DeletePagesBySlice_WhenClosed_ThrowsLikePython() + { + var doc = new Document(); + doc.NewPage(); + doc.Close(); + var ex = Assert.Throws(() => doc.DeletePagesBySlice(0, 1)); + Assert.Equal("document closed", ex.Message); + } + + [Fact] + public void Document_DeletePage() + { + using var doc = new Document(); + doc.NewPage(); + doc.NewPage(); + Assert.Equal(2, doc.PageCount); + doc.DeletePage(0); + Assert.Equal(1, doc.PageCount); + } + + [Fact] + public void Document_DeletePage_WhenClosed_ThrowsDocumentClosedLikePython() + { + var doc = new Document(); + doc.NewPage(); + doc.Close(); + var ex = Assert.Throws(() => doc.DeletePage(0)); + Assert.Equal("document closed", ex.Message); + } + + [Fact] + public void Document_FullcopyPage_WhenClosed_ThrowsDocumentClosedLikePython() + { + var doc = new Document(); + doc.NewPage(); + doc.Close(); + var ex = Assert.Throws(() => doc.FullcopyPage(0)); + Assert.Equal("document closed", ex.Message); + } + + [Fact] + public void Document_CopyPage() + { + using var doc = new Document(); + doc.NewPage(); + Assert.Equal(1, doc.PageCount); + doc.CopyPage(0); + Assert.Equal(2, doc.PageCount); + } + + [Fact] + public void Document_MovePage() + { + using var doc = new Document(); + doc.NewPage(width: 100, height: 100); + doc.NewPage(width: 200, height: 200); + doc.MovePage(0, 2); + Assert.Equal(2, doc.PageCount); + } + + [Fact] + public void Document_CopyPage_WhenClosed_ThrowsDocumentClosedLikePython() + { + var doc = new Document(); + doc.NewPage(); + doc.Close(); + var ex = Assert.Throws(() => doc.CopyPage(0)); + Assert.Equal("document closed", ex.Message); + } + + [Fact] + public void Document_MovePage_WhenClosed_ThrowsDocumentClosedLikePython() + { + var doc = new Document(); + doc.NewPage(); + doc.Close(); + var ex = Assert.Throws(() => doc.MovePage(0, -1)); + Assert.Equal("document closed", ex.Message); + } + + // ─── Write / ToBytes ──────────────────────────────────────────── + + [Fact] + public void Document_Write() + { + using var doc = new Document(); + doc.NewPage(); + var bytes = doc.Write(); + Assert.NotNull(bytes); + Assert.True(bytes.Length > 0); + } + + [Fact] + public void Document_ToBytes() + { + using var doc = new Document(); + doc.NewPage(); + var bytes = doc.ToBytes(); + Assert.True(bytes.Length > 0); + } + + [Fact] + public void Document_Save() + { + var tmpDir = Path.GetTempPath(); + var path = Path.Combine(tmpDir, $"mupdfnet_test_{Guid.NewGuid()}.pdf"); + try + { + using var doc = new Document(); + doc.NewPage(); + doc.Save(path); + Assert.True(File.Exists(path)); + Assert.True(new FileInfo(path).Length > 0); + } + finally + { + if (File.Exists(path)) File.Delete(path); + } + } + + // ─── Xref ─────────────────────────────────────────────────────── + + [Fact] + public void Document_XrefLength() + { + using var doc = new Document(); + doc.NewPage(); + Assert.True(doc.XrefLength > 0); + } + + [Fact] + public void Document_PageXref() + { + using var doc = new Document(); + doc.NewPage(); + int xref = doc.PageXref(0); + Assert.True(xref > 0); + } + + /// PyMuPDF xref_object uses pdf_print_obj; binding pdf_to_str_buf was empty for many dicts. + [Fact] + public void Document_XrefObject_PageDictNonEmpty() + { + using var doc = new Document(); + doc.NewPage(595, 842); + int xref = doc.PageXref(0); + var text = doc.XrefObject(xref); + Assert.False(string.IsNullOrWhiteSpace(text)); + Assert.Contains("/Type", text, StringComparison.Ordinal); + Assert.Contains("/Page", text, StringComparison.Ordinal); + Assert.Contains("MediaBox", text, StringComparison.Ordinal); + } + + [Fact] + public void Document_XrefObject_ContainsCropBoxAfterSet() + { + using var doc = new Document(); + doc.NewPage(595, 842); + doc[0].SetCropBox(new Rect(100, 200, 400, 700)); + int xref = doc.PageXref(0); + var text = doc.XrefObject(xref); + Assert.Contains("/CropBox", text, StringComparison.Ordinal); + // pdf_print_obj (compress=false) may add spaces, e.g. "[ 100 200 400 700 ]". + var compact = Regex.Replace(text, @"\s+", ""); + Assert.Contains("100200400700", compact); + } + + [Fact] + public void Document_PdfTrailer_NonEmpty() + { + using var doc = new Document(); + doc.NewPage(); + var t = doc.PdfTrailer(); + Assert.False(string.IsNullOrWhiteSpace(t)); + Assert.Contains("Root", t, StringComparison.Ordinal); + } + + [Fact] + public void Document_XrefObject_AfterClose_Throws() + { + var doc = new Document(); + doc.NewPage(); + int xref = doc.PageXref(0); + doc.Close(); + var ex = Assert.Throws(() => _ = doc.XrefObject(xref)); + Assert.Equal("document closed", ex.Message); + } + + [Fact] + public void Document_XrefGetKey_XrefZero_Throws() + { + using var doc = new Document(); + doc.NewPage(); + Assert.Throws(() => doc.XrefGetKey(0, "Type")); + } + + [Fact] + public void Document_XrefGetKey_OutOfRange_Throws() + { + using var doc = new Document(); + doc.NewPage(); + int bad = doc.XrefLength + 50; + Assert.Throws(() => doc.XrefGetKey(bad, "Type")); + } + + [Fact] + public void Document_XrefGetKeys_TrailerMinusOne() + { + using var doc = new Document(); + doc.NewPage(); + var keys = doc.XrefGetKeys(-1); + Assert.NotNull(keys); + Assert.Contains("Root", keys); + } + + [Fact] + public void Document_XrefStream_XrefZero_Throws() + { + using var doc = new Document(); + doc.NewPage(); + Assert.Throws(() => doc.XrefStream(0)); + } + + [Fact] + public void Document_XrefStream_TrailerMinusOne_NoThrow() + { + using var doc = new Document(); + doc.NewPage(); + // Trailer is usually not a stream; PyMuPDF returns None. + _ = doc.XrefStream(-1); + } + + [Fact] + public void Document_XrefStreamRaw_XrefZero_Throws() + { + using var doc = new Document(); + doc.NewPage(); + Assert.Throws(() => doc.XrefStreamRaw(0)); + } + + [Fact] + public void Document_UpdateObject_XrefZero_Throws() + { + using var doc = new Document(); + doc.NewPage(); + Assert.Throws(() => doc.UpdateObject(0, "<<>>")); + } + + [Fact] + public void Document_UpdateStream_XrefZero_Throws() + { + using var doc = new Document(); + doc.NewPage(); + Assert.Throws(() => doc.UpdateStream(0, new byte[] { 1, 2, 3 })); + } + + [Fact] + public void Document_UpdateStream_NonDictXref_Throws() + { + using var doc = new Document(); + doc.NewPage(); + int xref = doc.GetNewXref(); + doc.UpdateObject(xref, "42"); + var ex = Assert.Throws(() => doc.UpdateStream(xref, new byte[] { 1, 2, 3 })); + Assert.Contains("dict", ex.Message, StringComparison.OrdinalIgnoreCase); + } + + [Fact] + public void Document_update_stream_FourArgPythonOverload_IgnoresNewUsesCompressInt() + { + using var doc = new Document(); + doc.NewPage(); + int xref = doc.GetNewXref(); + doc.UpdateObject(xref, "<<>>"); + doc.update_stream(xref, new byte[] { 0xAB, 0xCD, 0xEF }, new_: 999, compress: 0); + var raw = doc.XrefStreamRaw(xref); + Assert.NotNull(raw); + } + + [Fact] + public void Document_XrefSetKey_NullKeyword_RemovesDictEntry() + { + using var doc = new Document(); + doc.NewPage(); + int xref = doc.GetNewXref(); + doc.UpdateObject(xref, "<< /Y 1 >>"); + doc.XrefSetKey(xref, "Y", "null"); + Assert.DoesNotContain("Y", doc.XrefGetKeys(xref)); + var y = doc.XrefGetKey(xref, "Y"); + Assert.Equal("null", y.type); + Assert.Equal("null", y.value); + } + + [Fact] + public void Document_XrefCopy_CopiesDictKeysAndRespectsKeep() + { + using var doc = new Document(); + doc.NewPage(); + int a = doc.GetNewXref(); + int b = doc.GetNewXref(); + doc.UpdateObject(a, "<< /K1 1 /K2 2 >>"); + doc.UpdateObject(b, "<< /K2 99 /K3 3 >>"); + doc.XrefCopy(a, b, new[] { "K3" }); + Assert.Equal("int", doc.XrefGetKey(b, "K1").type); + Assert.Equal("1", doc.XrefGetKey(b, "K1").value); + Assert.Equal("2", doc.XrefGetKey(b, "K2").value); + Assert.Equal("3", doc.XrefGetKey(b, "K3").value); + } + + [Fact] + public void Document_XrefCopy_StaticOverload_MatchesInstance() + { + using var doc = new Document(); + doc.NewPage(); + int a = doc.GetNewXref(); + int b = doc.GetNewXref(); + doc.UpdateObject(a, "<< /X 42 >>"); + doc.UpdateObject(b, "<< /Y 1 >>"); + Document.XrefCopy(doc, a, b); + Assert.Equal("42", doc.XrefGetKey(b, "X").value); + Assert.Equal("null", doc.XrefGetKey(b, "Y").type); + } + + [Fact] + public void Document_xref_copy_PythonCompat() + { + using var doc = new Document(); + doc.NewPage(); + int a = doc.GetNewXref(); + int b = doc.GetNewXref(); + doc.UpdateObject(a, "<< /P 7 >>"); + doc.UpdateObject(b, "<< /Q 8 >>"); + Document.xref_copy(doc, a, b, new List { "Q" }); + Assert.Equal("7", doc.XrefGetKey(b, "P").value); + Assert.Equal("8", doc.XrefGetKey(b, "Q").value); + } + + [Fact] + public void Document_GetNewXref() + { + using var doc = new Document(); + int xref = doc.GetNewXref(); + Assert.True(xref > 0); + } + + // ─── ConvertToPdf ─────────────────────────────────────────────── + + [Fact] + public void Document_ConvertToPdf() + { + using var doc = new Document(); + doc.NewPage(); + var pdfBytes = doc.ConvertToPdf(); + Assert.True(pdfBytes.Length > 0); + } + + // ─── Journal ──────────────────────────────────────────────────── + + [Fact] + public void Document_JournalEnable() + { + using var doc = new Document(); + doc.JournalEnable(); + Assert.True(doc.JournalIsEnabled); + } + + [Fact] + public void Document_Journal_WhenClosed_ThrowsValueErrorLikePython() + { + var doc = new Document(); + doc.Close(); + var ex = Assert.Throws(() => doc.JournalEnable()); + Assert.Equal("document closed or encrypted", ex.Message); + var ex2 = Assert.Throws(() => doc.JournalCanDo()); + Assert.Equal("document closed or encrypted", ex2.Message); + } + + [Fact] + public void Document_MakeBookmark_WhenClosed_ThrowsValueErrorLikePython() + { + var doc = new Document(); + doc.NewPage(); + doc.Close(); + var ex = Assert.Throws(() => doc.MakeBookmark((0, 0))); + Assert.Equal("document closed or encrypted", ex.Message); + } + + [Fact] + public void Document_FindBookmark_WhenClosed_ThrowsValueErrorLikePython() + { + var doc = new Document(); + doc.Close(); + var ex = Assert.Throws(() => doc.FindBookmark(0UL)); + Assert.Equal("document closed or encrypted", ex.Message); + } + + // ─── Select / scrub (Python ValueError messages) ──────────────── + + [Fact] + public void Document_Select_WhenClosed_ThrowsCombinedMessageLikePython() + { + var doc = new Document(); + doc.NewPage(); + doc.Close(); + var ex = Assert.Throws(() => doc.Select(new[] { 0 })); + Assert.Equal("document closed or encrypted", ex.Message); + } + + [Fact] + public void Document_Select_Null_ThrowsSequenceRequiredLikePython() + { + using var doc = new Document(); + doc.NewPage(); + var ex = Assert.Throws(() => doc.Select(null!)); + Assert.Equal("sequence required", ex.Message); + } + + [Fact] + public void Document_Select_Empty_ThrowsBadPageNumbersLikePython() + { + using var doc = new Document(); + doc.NewPage(); + var ex = Assert.Throws(() => doc.Select(Array.Empty())); + Assert.Equal("bad page number(s)", ex.Message); + } + + [Fact] + public void Document_Select_OutOfRange_ThrowsBadPageNumbersLikePython() + { + using var doc = new Document(); + doc.NewPage(); + var ex = Assert.Throws(() => doc.Select(new[] { 5 })); + Assert.Equal("bad page number(s)", ex.Message); + } + + [Fact] + public void Document_Select_ReordersPages() + { + using var doc = new Document(); + doc.NewPage(); + doc.NewPage(); + doc.NewPage(); + doc.Select(new[] { 2, 0, 1 }); + Assert.Equal(3, doc.PageCount); + } + + [Fact] + public void Document_Scrub_WhenClosed_ThrowsValueErrorLikePython() + { + var doc = new Document(); + doc.NewPage(); + doc.Close(); + var ex = Assert.Throws(() => doc.Scrub()); + Assert.Equal("closed or encrypted doc", ex.Message); + } + + [Fact] + public void Document_Scrub_MinimalPdf_NoThrow() + { + using var doc = new Document(); + doc.NewPage(); + doc.Scrub(); + Assert.Equal(1, doc.PageCount); + } + + // ─── TOC (set_toc / set_toc_item ValueError parity) ───────────── + + [Fact] + public void Document_SetToc_FirstLevelNot1_ThrowsValueErrorLikePython() + { + using var doc = new Document(); + doc.NewPage(); + var toc = new List { new List { 2, "x", 1 } }; + var ex = Assert.Throws(() => doc.SetToc(toc)); + Assert.Equal("hierarchy level of item 0 must be 1", ex.Message); + } + + [Fact] + public void Document_SetToc_PageOutOfRange_ThrowsValueErrorLikePython() + { + using var doc = new Document(); + doc.NewPage(); + var toc = new List + { + new List { 1, "a", 100 }, + new List { 1, "b", 1 }, + }; + var ex = Assert.Throws(() => doc.SetToc(toc)); + Assert.Equal("row 0: page number out of range", ex.Message); + } + + [Fact] + public void Document_SetTocItem_BadPno_ThrowsValueErrorLikePython() + { + using var doc = new Document(); + doc.NewPage(); + doc.SetToc(new List { new List { 1, "t", 1 } }); + var ex = Assert.Throws(() => + doc.SetTocItem(0, destDict: default, Constants.LINK_GOTO, 5)); + Assert.Equal("bad page number", ex.Message); + } + + // ─── Embedded files / page_annot_xrefs (ValueError parity) ───── + + [Fact] + public void Document_EmbfileAdd_DuplicateName_ThrowsValueErrorLikePython() + { + using var doc = new Document(); + doc.NewPage(); + doc.EmbfileAdd("a", new byte[] { 1, 2, 3 }); + var ex = Assert.Throws(() => doc.EmbfileAdd("a", new byte[] { 4 })); + Assert.Equal("Name 'a' already exists.", ex.Message); + } + + [Fact] + public void Document_EmbfileGet_MissingName_ThrowsValueErrorLikePython() + { + using var doc = new Document(); + doc.NewPage(); + var ex = Assert.Throws(() => doc.EmbfileGet("missing")); + Assert.Equal("'missing' not in EmbeddedFiles array.", ex.Message); + } + + [Fact] + public void Document_PageAnnotXrefs_BadPage_ThrowsValueErrorLikePython() + { + using var doc = new Document(); + doc.NewPage(); + var ex = Assert.Throws(() => doc.PageAnnotXrefs(5)); + Assert.Equal("bad page number(s)", ex.Message); + } + + [Fact] + public void Document_InsertPdf_SameInstance_ThrowsValueErrorLikePython() + { + using var doc = new Document(); + doc.NewPage(); + var ex = Assert.Throws(() => doc.InsertPdf(doc)); + Assert.Equal("source and target cannot be same object", ex.Message); + } + + [Fact] + public void Document_InsertFile_BadInfile_ThrowsValueErrorLikePython() + { + using var doc = new Document(); + doc.NewPage(); + var ex = Assert.Throws(() => doc.InsertFile(123)); + Assert.Equal("bad infile parameter", ex.Message); + } + + [Fact] + public void Document_SetPageLayout_Invalid_ThrowsValueErrorLikePython() + { + using var doc = new Document(); + doc.NewPage(); + var ex = Assert.Throws(() => doc.SetPageLayout("Nope")); + Assert.Equal("bad PageLayout value", ex.Message); + } + + [Fact] + public void Document_PageCropBox_WhenClosed_ThrowsValueErrorLikePython() + { + var doc = new Document(); + doc.NewPage(); + doc.Close(); + var ex = Assert.Throws(() => doc.PageCropBox(0)); + Assert.Equal("document closed", ex.Message); + } + + // ─── Bake ─────────────────────────────────────────────────────── + + [Fact] + public void Document_Bake_NoError() + { + using var doc = new Document(); + doc.NewPage(); + doc.Bake(); + Assert.Equal(1, doc.PageCount); + } + + // ─── ToString ─────────────────────────────────────────────────── + + [Fact] + public void Document_ToString() + { + using var doc = new Document(); + var s = doc.ToString(); + Assert.Contains("Document", s); + } + } +} diff --git a/MuPDF.NET.Test/FontTest.cs b/MuPDF.NET.Test/FontTest.cs deleted file mode 100644 index e59e0a07..00000000 --- a/MuPDF.NET.Test/FontTest.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using NUnit.Framework; - -namespace MuPDF.NET.Test -{ - public class FontTest - { - [Test] - public void Flags() - { - Font font = new Font("kenpixel", "../../../resources/kenpixel.ttf", isBold: 1); - - //Assert.Pass(); - } - - [Test] - public void TextLength() - { - Font font = new Font("kenpixel", "../../../resources/kenpixel.ttf", isBold: 1); - - float length = font.TextLength("hello world", 15); - - Assert.That(length, Is.Not.Zero); - } - - [Test] - public void SubsetFonts() - { - Document doc = new Document("../../../resources/subset.pdf"); - - int n = doc.PageCount; - - mupdf.mupdf.pdf_subset_fonts2(Document.AsPdfDocument(doc), new mupdf.vectori(Enumerable.Range(0, n / 2).Select(i => i * 2))); - } - } -} diff --git a/MuPDF.NET.Test/FontTests.cs b/MuPDF.NET.Test/FontTests.cs new file mode 100644 index 00000000..3b8872ef --- /dev/null +++ b/MuPDF.NET.Test/FontTests.cs @@ -0,0 +1,182 @@ +using System; +using System.Linq; +using Xunit; + +namespace MuPDF.NET.Test +{ + /// + /// Tests for the Font class. + /// Ported from tests/test_font.py. + /// + public class FontTests + { + // ─── Construction ─────────────────────────────────────────────── + + [Fact] + public void Font_DefaultHelvetica() + { + using var font = new Font("helv"); + Assert.Contains("Helvetica", font.Name, StringComparison.OrdinalIgnoreCase); + } + + [Fact] + public void Font_Courier() + { + using var font = new Font("cour"); + Assert.True(font.IsMonospaced); + } + + [Fact] + public void Font_TimesRoman() + { + using var font = new Font("tiro"); + Assert.True(font.IsSerif); + } + + // ─── Properties ───────────────────────────────────────────────── + + [Fact] + public void Font_Name() + { + using var font = new Font("helv"); + Assert.False(string.IsNullOrEmpty(font.Name)); + } + + [Fact] + public void Font_GlyphCount() + { + using var font = new Font("helv"); + Assert.True(font.GlyphCount > 0); + } + + [Fact] + public void Font_BBox() + { + using var font = new Font("helv"); + var bbox = font.BBox; + Assert.True(bbox.Width > 0); + Assert.True(bbox.Height > 0); + } + + [Fact] + public void Font_Ascender() + { + using var font = new Font("helv"); + Assert.True(font.Ascender > 0); + } + + [Fact] + public void Font_Descender() + { + using var font = new Font("helv"); + Assert.True(font.Descender < 0); + } + + [Fact] + public void Font_IsBold() + { + using var font = new Font("helv"); + Assert.False(font.IsBold); + } + + [Fact] + public void Font_IsItalic() + { + using var font = new Font("helv"); + Assert.False(font.IsItalic); + } + + [Fact] + public void Font_FontBuffer() + { + using var font = new Font("helv"); + var buffer = font.FontBuffer; + Assert.NotNull(buffer); + Assert.True(buffer.Length > 0); + } + + // ─── Glyph operations ─────────────────────────────────────────── + + [Fact] + public void Font_HasGlyph() + { + using var font = new Font("helv"); + Assert.True(font.HasGlyph('A')); + } + + [Fact] + public void Font_GlyphAdvance() + { + using var font = new Font("helv"); + float advance = font.GlyphAdvance('A'); + Assert.True(advance > 0); + } + + [Fact] + public void Font_GlyphBbox() + { + using var font = new Font("helv"); + var bbox = font.GlyphBbox('A'); + Assert.True(bbox.Width > 0); + } + + [Fact] + public void Font_GlyphName() + { + using var font = new Font("helv"); + string name = font.GlyphName('A'); + Assert.False(string.IsNullOrEmpty(name)); + } + + [Fact] + public void Font_TextLength() + { + using var font = new Font("helv"); + float length = font.TextLength("Hello World", fontsize: 11); + Assert.True(length > 0); + } + + [Fact] + public void Font_CharToGid() + { + using var font = new Font("helv"); + int gid = font.CharToGid('A'); + Assert.True(gid >= 0); + } + + [Fact] + public void Font_CharLengths() + { + using var font = new Font("helv"); + var lengths = font.CharLengths("ABC", fontsize: 11); + Assert.Equal(3, lengths.Length); + Assert.True(lengths.All(l => l > 0)); + } + + [Fact] + public void Font_ValidCodepoints() + { + using var font = new Font("helv"); + var cps = font.ValidCodepoints(); + Assert.NotNull(cps); + Assert.True(cps.Count > 0); + } + + // ─── Dispose ──────────────────────────────────────────────────── + + [Fact] + public void Font_Dispose() + { + var font = new Font("helv"); + font.Dispose(); + Assert.Throws(() => _ = font.Name); + } + + [Fact] + public void Font_ToString() + { + using var font = new Font("helv"); + Assert.Contains("Font", font.ToString()); + } + } +} diff --git a/MuPDF.NET.Test/GeneralTest.cs b/MuPDF.NET.Test/GeneralTest.cs deleted file mode 100644 index 9e73b249..00000000 --- a/MuPDF.NET.Test/GeneralTest.cs +++ /dev/null @@ -1,497 +0,0 @@ -using MuPDF.NET.enums; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Linq; - -namespace MuPDF.NET.Test -{ - internal class GeneralTest - { - - [Test] - public void SearchText() - { - Document doc = new Document("../../../resources/test_2533.pdf"); - Page page = doc[0]; - string needle = "盐"; - int iNeedle = Convert.ToInt32(needle[0]); - Rect bbox = new Rect(); - foreach (SpanInfo span in page.GetTextTrace()) - { - foreach (Char ch in span.Chars) - { - if (ch.UCS == iNeedle) - bbox = new Rect(ch.Bbox); - } - } - - Rect bbox1 = page.SearchFor("盐", page.GetBound())[0].Rect; - IRect ibbox = bbox.Round(); - IRect ibbox1 = bbox1.Round(); - Assert.That(ibbox.Equals(ibbox)); - - Assert.That(page.SearchFor("偿力很务").Count == 0); - doc.Close(); - } - - [Test] - public void Test_Opacity() - { - Document doc = new Document(); - Page page = doc.NewPage(); - - Annot annot1 = page.AddCircleAnnot(new Rect(50, 50, 100, 100)); - annot1.SetColors(fill: new float[] { 1, 0, 0 }, stroke: new float[] { 1, 0, 0 }); - annot1.SetOpacity(2 / 3.0f); - annot1.Update(blendMode: "Multiply"); - - Annot annot2 = page.AddCircleAnnot(new Rect(75, 75, 125, 125)); - annot2.SetColors(fill: new float[] { 0, 0, 1 }, stroke: new float[] { 0, 0, 1 }); - annot2.SetOpacity(1 / 3.0f); - annot2.Update(blendMode: "Multiply"); - - doc.Save("output.pdf", expand: 1, pretty: 1); - doc.Close(); - } - /* - [Test] - public void TestWrapContents() - { - Document doc = new Document("../../../resources/toc.pdf"); - Page page = doc[0]; - page.WrapContents(); - int xref = page.GetContents()[0]; - byte[] cont = page.ReadContents(); - - doc.UpdateStream(xref, cont); - page.SetContents(xref); - Assert.That(page.GetContents().Count, Is.EqualTo(1));; - page.CleanContetns(); - doc.Close(); - } - - [Test] - public void TestPageCleanContents() - { - Document doc = new Document(); - Page page = doc.NewPage(); - page.DrawRect(new Rect(10, 10, 20, 20)); - page.DrawRect(new Rect(20, 20, 30, 30)); - Assert.That(page.GetContents().Count, Is.EqualTo(2)); - Assert.That(Encoding.UTF8.GetString(page.ReadContents()).StartsWith("q"), Is.False); - - page.CleanContetns(); - Assert.That(page.GetContents().Count, Is.EqualTo(1)); - Assert.That(Encoding.UTF8.GetString(page.ReadContents()).StartsWith("q"), Is.True); - } - - [Test] - public void TestGetText() - { - string[] files = { "test_2645_1.pdf", "test_2645_2.pdf", "test_2645_3.pdf" }; - foreach (string name in files) - { - Document doc = new Document("../../../resources/" + name); - Page page = doc[0]; - float size0 = page.GetTextTrace()[0].Size; - float size1 = page.GetText("dict", flags: (int)TextFlagsExtension.TEXTFLAGS_TEXT).Blocks[0].Lines[0].Spans[0].Size; - - Assert.That(Math.Abs(size0 - size1), Is.LessThan(1e-5)); - doc.Close(); - } - } - */ - [Test] - public void TestFontSize() - { - Document doc = new Document(); - Page page = doc.NewPage(); - Point point = new Point(100, 300); - float fontSize = 11f; - string text = "Hello"; - int[] angles = { 0, 30, 60, 90, 120 }; - - foreach (int angle in angles) - { - page.InsertText(point, text, fontFile: "../../../resources/kenpixel.ttf", fontSize: fontSize, morph: new Morph(point, new Matrix(angle))); - } - - foreach (SpanInfo span in page.GetTextTrace()) - { - Assert.That(span.Size, Is.EqualTo(fontSize)); - } - - foreach (Block block in page.GetText("dict").Blocks) - { - foreach (Line line in block.Lines) - { - foreach (Span span in line.Spans) - { - Assert.That(span.Size, Is.EqualTo(fontSize)); - } - } - } - doc.Close(); - } - - [Test] - public void Reload() - { - Document doc = new Document("../../../resources/test_2596.pdf"); - Page page = doc[0]; - Pixmap pix0 = page.GetPixmap(); - doc.Write(garbage: true); - - page = doc.ReloadPage(page); - Pixmap pix1 = page.GetPixmap(); - Assert.That(pix1.SAMPLES.SequenceEqual(pix0.SAMPLES), Is.True); - } - - [Test] - public void Cropbox() - { - Document doc = new Document(); - Page page = doc.NewPage(); - - doc.SetKeyXRef(page.Xref, "MediaBox", "[-30 -20 595 842]"); - Assert.That(page.CropBox.EqualTo(new Rect(-30, 0, 595, 862))); - Assert.That(page.Rect.EqualTo(new Rect(0, 0, 625, 862))); - - page.SetCropBox(new Rect(-20, 0, 595, 852)); - Assert.That(doc.GetKeyXref(page.Xref, "CropBox").Item2, Is.EqualTo("[-20 -10 595 842]")); - - bool error = false; - string text = ""; - try - { - page.SetCropBox(new Rect(-35, -10, 595, 852)); - } - catch (Exception ex) - { - text = ex.Message; - error = true; - } - Assert.That(error); - Assert.That(text, Is.EqualTo("CropBox not in Mediabox")); - } - - [Test] - public void Insert() - { - Document doc = new Document(); - Page page = doc.NewPage(); - - Rect r1 = new Rect(50, 50, 100, 100); - Rect r2 = new Rect(50, 150, 200, 400); - page.InsertImage(r1, filename: "../../../resources/nur-ruhig.jpg"); - page.InsertImage(r1, filename: "../../../resources/nur-ruhig.jpg", rotate: 270); - List lists = page.GetImageInfo(); - Assert.That(lists.Count, Is.EqualTo(2)); - - Rect bbox1 = new Rect(lists[0].Bbox); - Rect bbox2 = new Rect(lists[1].Bbox); - } - - [Test] - public void Compress() - { - Document doc = new Document("../../../resources/2.pdf"); - Document npdf = new Document(); - for (int i = 0; i < doc.PageCount; i++) - { - Pixmap pixmap = doc[i].GetPixmap(colorSpace: "RGB", dpi: 72, annots: false); - Page pageNew = npdf.NewPage(); - pageNew.InsertImage(rect: pageNew.GetBound(), pixmap: pixmap); - } - npdf.Save("2.pdf.compress.pdf", garbage: 3, deflate: 1, deflateImages: 1, deflateFonts: 1, pretty: 1); - } - - [Test] - public void PageLinksGenerator() - { - Document doc = new Document("../../../resources/2.pdf"); - Page page = doc[-1]; - - List links = page.GetLinks(); - Assert.That(links.Count, Is.EqualTo(7)); - } - - [Test] - public void Deletion() - { - Document doc = new Document(); - LinkInfo link = new LinkInfo() - { - From = new Rect(100, 100, 120, 120), - Kind = LinkType.LINK_GOTO, - Page = 5, - To = new Point(100, 100) - }; - - List tocs = new List(); - - for (int i = 0; i < 100; i ++) - { - Page page = doc.NewPage(); - page.InsertText(new Point(100, 100), $"{i}", fontFile: "../../../resources/kenpixel.ttf"); - if (i > 5) - page.InsertLink(link); - tocs.Add(new Toc() { Level = 1, Title = $"{i}", Page = i + 1 }); - } - doc.SetToc(tocs); - Assert.That(doc.HasLinks()); - } - - [Test] - public void DeletePages() - { - Document doc = new Document("../../../resources/cython.pdf"); - int[] pages = { 3, 3, 3, 2, 3, 1, 0, 0 }; - doc.Select(new List(pages)); - Assert.That(doc.PageCount, Is.EqualTo(8)); - } - - [Test] - public void SetLabels() - { - Document doc = new Document(); - for (int i = 0; i < 10; i++) - doc.NewPage(); - - List
tables = Utils.GetTables( - page, - clip: page.Rect, - vertical_strategy: "lines_strict", - horizontal_strategy: "lines_strict"); - - Assert.That(tables, Is.Not.Null); - - if (tables.Count == 0) - { - // Test 2: Fallback with 'lines' strategy (as in Demo) - tables = Utils.GetTables( - page, - clip: page.Rect, - vertical_strategy: "lines", - horizontal_strategy: "lines"); - } - - // Test 3: Get tables with 'text' strategy (as in Demo) - List
textTables = Utils.GetTables( - page, - clip: page.Rect, - vertical_strategy: "text", - horizontal_strategy: "text"); - - Assert.That(textTables, Is.Not.Null); - - // For each table found with lines_strict/lines: validate structure and Extract/ToMarkdown - for (int i = 0; i < tables.Count; i++) - { - Table table = tables[i]; - Assert.That(table.row_count, Is.GreaterThanOrEqualTo(0)); - Assert.That(table.col_count, Is.GreaterThanOrEqualTo(0)); - - List> tableData = table.Extract(); - Assert.That(tableData, Is.Not.Null); - - string markdown = table.ToMarkdown(clean: false, fillEmpty: true); - Assert.That(markdown, Is.Not.Null); - } - - // Test 4: Get tables from all pages (as in Demo) - int totalTables = 0; - for (int pageNum = 0; pageNum < doc.PageCount; pageNum++) - { - Page currentPage = doc[pageNum]; - List
pageTables = Utils.GetTables( - currentPage, - clip: currentPage.Rect, - vertical_strategy: "lines_strict", - horizontal_strategy: "lines_strict"); - if (pageTables.Count > 0) - totalTables += pageTables.Count; - currentPage.Dispose(); - } - - Assert.That(totalTables, Is.GreaterThanOrEqualTo(0)); - page.Dispose(); - } - finally - { - doc.Close(); - } - } - - /* - [Test] - public void BorderedTable() - { - Document doc = new Document("../../../resources/bordered-table.pdf"); - Rect clip = new Rect(20, 100, 580, 300); - Page page = doc[0]; - int cellCount = 0; - - List
tables = page.GetTables(clip:clip); - foreach (var table in tables) - { - List> text = table.Extract(); - foreach (var row in text) - { - foreach (var cell in row) - { - cellCount++; - } - } - } - - doc.Close(); - - Assert.That(cellCount, Is.EqualTo(18)); - } - - [Test] - public void NonBorderedTable() - { - Document doc = new Document("../../../resources/non-bordered-table.pdf"); - Page page = doc[0]; - int cellCount = 0; - - List
tables = page.GetTables(vertical_strategy: "text", horizontal_strategy: "text"); - foreach (var table in tables) - { - List> text = table.Extract(); - foreach (var row in text) - { - foreach (var cell in row) - { - cellCount++; - } - } - } - - doc.Close(); - - Assert.That(cellCount, Is.EqualTo(54)); - } - */ - } -} diff --git a/MuPDF.NET.Test/TestHelper.cs b/MuPDF.NET.Test/TestHelper.cs new file mode 100644 index 00000000..24edeb21 --- /dev/null +++ b/MuPDF.NET.Test/TestHelper.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; + +namespace MuPDF.NET.Test +{ + /// + /// Shared helpers for locating test resources and common assertions. + /// + internal static class TestHelper + { + private static readonly string ProjectDir = FindProjectDir(); + + /// + /// Returns the absolute path to a file inside tests/resources. + /// + internal static string GetResource(string filename) + { + return Path.Combine(ProjectDir, "tests", "resources", filename); + } + + /// + /// Check that two floating-point values are approximately equal. + /// + internal static bool IsClose(double a, double b, double eps = 1e-5) + { + return Math.Abs(a - b) < eps; + } + + private static string FindProjectDir() + { + // Walk up from the output directory to find the repo root (contains tests/) + string dir = AppDomain.CurrentDomain.BaseDirectory; + for (int i = 0; i < 10; i++) + { + if (Directory.Exists(Path.Combine(dir, "tests", "resources"))) + return dir; + var parent = Directory.GetParent(dir); + if (parent == null) break; + dir = parent.FullName; + } + // Fallback: use current directory + return Directory.GetCurrentDirectory(); + } + } +} diff --git a/MuPDF.NET.Test/TextExtractionTests.cs b/MuPDF.NET.Test/TextExtractionTests.cs new file mode 100644 index 00000000..1eaedc63 --- /dev/null +++ b/MuPDF.NET.Test/TextExtractionTests.cs @@ -0,0 +1,240 @@ +using System; +using Xunit; + +namespace MuPDF.NET.Test +{ + /// + /// Tests for text extraction (TextPage, page.GetText, search). + /// Ported from tests/test_textextract.py, tests/test_textsearch.py. + /// + public class TextExtractionTests + { + // ─── GetText variants ─────────────────────────────────────────── + + [Fact] + public void GetText_PlainText() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "Hello World"); + string text = page.GetText("text"); + Assert.Contains("Hello", text); + } + + [Fact] + public void GetText_Html() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "Test HTML"); + string html = page.GetText("html"); + Assert.NotNull(html); + } + + [Fact] + public void GetText_Xhtml() + { + using var doc = new Document(); + var page = doc.NewPage(); + string xhtml = page.GetText("xhtml"); + Assert.NotNull(xhtml); + } + + [Fact] + public void GetText_Xml() + { + using var doc = new Document(); + var page = doc.NewPage(); + string xml = page.GetText("xml"); + Assert.NotNull(xml); + } + + // ─── TextPage ─────────────────────────────────────────────────── + + [Fact] + public void TextPage_ExtractText() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "TextPage Test"); + using var tp = page.GetTextPage(); + string text = tp.ExtractText(); + Assert.Contains("TextPage", text); + } + + [Fact] + public void TextPage_ExtractBlocks() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "Block Test"); + using var tp = page.GetTextPage(); + var blocks = tp.ExtractBlocks(); + Assert.NotNull(blocks); + } + + [Fact] + public void TextPage_ExtractWords() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "Word One Two"); + using var tp = page.GetTextPage(); + var words = tp.ExtractWords(); + Assert.NotNull(words); + } + + [Fact] + public void TextPage_ExtractHtml() + { + using var doc = new Document(); + var page = doc.NewPage(); + using var tp = page.GetTextPage(); + string html = tp.ExtractHtml(); + Assert.NotNull(html); + } + + [Fact] + public void TextPage_Rect() + { + using var doc = new Document(); + var page = doc.NewPage(); + using var tp = page.GetTextPage(); + Assert.NotNull(tp.Rect); + } + + [Fact] + public void TextPage_Dispose() + { + using var doc = new Document(); + var page = doc.NewPage(); + var tp = page.GetTextPage(); + tp.Dispose(); + Assert.Throws(() => tp.ExtractText()); + } + + // ─── Search ───────────────────────────────────────────────────── + + [Fact] + public void Search_FindsText() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "Findable Text"); + var results = page.SearchFor("Findable"); + Assert.NotEmpty(results); + } + + [Fact] + public void Search_NoMatch() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "Some text here"); + var results = page.SearchFor("ZZZZZ"); + Assert.Empty(results); + } + + [Fact] + public void Search_MaxHits() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "AAA AAA AAA AAA"); + var results = page.SearchFor("AAA", maxHits: 2); + Assert.True(results.Count <= 2); + } + + // ─── Document-level convenience ───────────────────────────────── + + [Fact] + public void Document_SearchPageFor() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "DocSearch"); + var results = doc.SearchPageFor(0, "DocSearch"); + Assert.NotEmpty(results); + } + + [Fact] + public void Page_SearchFor_WrongTextPageParent_Throws() + { + using var doc = new Document(); + doc.NewPage(); + doc.NewPage(); + // Second NewPage resets page wrappers; reload before use. + var p0 = doc.LoadPage(0); + var p1 = doc.LoadPage(1); + p0.InsertText(new Point(72, 72), "OnlyHere"); + using var tpOther = p1.GetTextPage(); + Assert.Throws(() => p0.SearchFor("OnlyHere", textpage: tpOther)); + } + + [Fact] + public void Document_SearchPageFor_WithClip_FindsTextInsideClip() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "NearOrigin"); + page.InsertText(new Point(400, 400), "FarCorner"); + var clip = new Quad(new Rect(0, 0, 200, 200)); + var near = doc.SearchPageFor(0, "NearOrigin", clip: clip); + Assert.NotEmpty(near); + } + + [Fact] + public void TextPage_SearchRects_ReturnsRects() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "RectSearch"); + using var tp = page.GetTextPage(); + var rects = tp.SearchRects("RectSearch"); + Assert.NotEmpty(rects); + Assert.All(rects, r => Assert.False(r.IsEmpty)); + } + + [Fact] + public void Document_SearchPageForRects_ReturnsRects() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "DocRectSearch"); + var rects = doc.SearchPageForRects(0, "DocRectSearch"); + Assert.NotEmpty(rects); + Assert.All(rects, r => Assert.False(r.IsEmpty)); + } + + [Fact] + public void Page_search_for_rects_PythonCompat_ReturnsRects() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "SnakeRect"); + var rects = page.search_for_rects("SnakeRect"); + Assert.NotEmpty(rects); + } + + [Fact] + public void TextPage_search_rects_PythonCompat_ReturnsRects() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "TpRect"); + using var tp = page.GetTextPage(); + var rects = tp.search_rects("TpRect"); + Assert.NotEmpty(rects); + } + + [Fact] + public void Document_GetPageText() + { + using var doc = new Document(); + var page = doc.NewPage(); + page.InsertText(new Point(72, 72), "PageText"); + string text = doc.GetPageText(0); + Assert.Contains("PageText", text); + } + } +} diff --git a/MuPDF.NET.Test/TextPageTest.cs b/MuPDF.NET.Test/TextPageTest.cs deleted file mode 100644 index 93ee122d..00000000 --- a/MuPDF.NET.Test/TextPageTest.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MuPDF.NET.Test -{ - public class TextPageTest : PdfTestBase - { - /* - [SetUp] - public void Setup() - { - doc = new Document("../../../resources/cython.pdf"); - - textPage = doc.LoadPage(0).GetTextPage(); - } - - [Test] - public void Constructor() - { - textPage = new TextPage(new mupdf.FzRect()); - //Assert.Pass(); - - textPage = new TextPage(doc.LoadPage(0).GetTextPage()); - //Assert.Pass(); - } - - [Test] - public void ExtractHtml() - { - textPage.ExtractHtml(); - //Assert.Pass(); - } - - [Test] - public void ExtractText() - { - textPage.ExtractText(); - //Assert.Pass(); - } - - [Test] - public void ExtractXml() - { - textPage.ExtractXML(); - //Assert.Pass(); - } - - [Test] - public void ExtractBlocks() - { - List blocks = textPage.ExtractBlocks(); - Assert.That(blocks.Count, Is.EqualTo(4)); - } - - [Test] - public void ExtractXHtml() - { - textPage.ExtractXHtml(); - //Assert.Pass(); - } - - [Test] - public void ExtractDict() - { - textPage.ExtractDict(new Rect(0, 0, 300, 300)); - //Assert.Pass(); - } - - [Test] - public void ExtractRAWDict() - { - textPage.ExtractRAWDict(new Rect(0, 0, 300, 300)); - //Assert.Pass(); - - textPage.ExtractRAWDict(null); - //Assert.Pass(); - } - - [Test] - public void ExtractSelection() - { - textPage.ExtractSelection(new Point(20, 20), new Point(100, 100)); - //Assert.Pass(); - - textPage.ExtractSelection(null, null); - //Assert.Pass(); - - string ret = textPage.ExtractSelection(new Point(-5, -15), null); - //Assert.Pass(); - } - - [Test] - public void SearchTest() - { - //Document doc = new Document("input.pdf"); - - Page page = doc[0]; - - TextPage tpage = page.GetTextPage(page.Rect); - - List matches = TextPage.Search(textPage, "2018"); - - if (matches.Count > 0) - { - page.AddHighlightAnnot(matches); - } - - Assert.That(page.FirstAnnot.Type.Item1, Is.EqualTo(PdfAnnotType.PDF_ANNOT_HIGHLIGHT)); - } - - [Test] - public void GetText() - { - Document doc = new Document("../../../resources/test_3650.pdf"); - List blocks = doc[0].GetText("blocks"); - - string t = ""; - foreach (TextBlock bt in blocks) - t += bt.Text; - - Assert.That(t.Equals("RECUEIL DES ACTES ADMINISTRATIFSn° 78 du 28 avril 2023")); - } - */ - } -} diff --git a/MuPDF.NET.Test/TextWriterTests.cs b/MuPDF.NET.Test/TextWriterTests.cs new file mode 100644 index 00000000..7d966d01 --- /dev/null +++ b/MuPDF.NET.Test/TextWriterTests.cs @@ -0,0 +1,114 @@ +using Xunit; + +namespace MuPDF.NET.Test +{ + /// + /// Tests for the TextWriter class. + /// + public class TextWriterTests + { + // ─── Construction ─────────────────────────────────────────────── + + [Fact] + public void TextWriter_Create() + { + var tw = new TextWriter(new Rect(0, 0, 595, 842)); + Assert.NotNull(tw); + Assert.Equal(0, tw.SpanCount); + } + + // ─── Append ───────────────────────────────────────────────────── + + [Fact] + public void TextWriter_Append() + { + var tw = new TextWriter(new Rect(0, 0, 595, 842)); + tw.Append(new Point(72, 72), "Hello World"); + Assert.True(tw.SpanCount > 0); + } + + [Fact] + public void TextWriter_AppendMultiple() + { + var tw = new TextWriter(new Rect(0, 0, 595, 842)); + tw.Append(new Point(72, 72), "Line 1"); + tw.Append(new Point(72, 90), "Line 2"); + Assert.True(tw.SpanCount >= 2); + } + + // ─── FillTextbox ──────────────────────────────────────────────── + + [Fact] + public void TextWriter_FillTextbox() + { + var tw = new TextWriter(new Rect(0, 0, 595, 842)); + tw.FillTextbox( + new Rect(50, 50, 300, 200), + "This is a longer text that should fill the textbox area." + ); + Assert.True(tw.SpanCount > 0); + } + + // ─── WriteText to page ────────────────────────────────────────── + + [Fact] + public void TextWriter_WriteTextToPage() + { + using var doc = new Document(); + var page = doc.NewPage(); + var tw = new TextWriter(page.Rect); + tw.Append(new Point(72, 72), "Written by TextWriter"); + tw.WriteText(page); + + string text = page.GetText("text"); + Assert.Contains("Written", text); + } + + // ─── Properties ───────────────────────────────────────────────── + + [Fact] + public void TextWriter_Rect() + { + var tw = new TextWriter(new Rect(0, 0, 595, 842)); + Assert.Equal(595, tw.Rect.Width); + Assert.Equal(842, tw.Rect.Height); + } + + [Fact] + public void TextWriter_TextRect() + { + var tw = new TextWriter(new Rect(0, 0, 595, 842)); + tw.Append(new Point(72, 72), "Test"); + var tr = tw.TextRect; + Assert.True(tr.Width > 0); + } + + [Fact] + public void TextWriter_LastPoint() + { + var tw = new TextWriter(new Rect(0, 0, 595, 842)); + tw.Append(new Point(72, 72), "Hello"); + var lp = tw.LastPoint; + Assert.True(lp.X > 72); + } + + [Fact] + public void TextWriter_OpacityAndColor() + { + var tw = new TextWriter(new Rect(0, 0, 595, 842), opacity: 0.5f, color: new float[] { 1, 0, 0 }); + Assert.True(TestHelper.IsClose(0.5, tw.Opacity, 0.01)); + } + + // ─── Reset ────────────────────────────────────────────────────── + + [Fact] + public void TextWriter_Reset() + { + var tw = new TextWriter(new Rect(0, 0, 595, 842)); + tw.Append(new Point(72, 72), "Some text"); + Assert.True(tw.SpanCount > 0); + tw.Reset(); + Assert.Equal(0, tw.SpanCount); + } + } +} diff --git a/MuPDF.NET.Test/UtilsTest.cs b/MuPDF.NET.Test/UtilsTest.cs deleted file mode 100644 index 2aeb0e7f..00000000 --- a/MuPDF.NET.Test/UtilsTest.cs +++ /dev/null @@ -1,50 +0,0 @@ -using NUnit.Framework; -using MuPDF.NET; - -namespace MuPDF.NET.Test -{ - public class UtilsTest - { - [Test] - public void FloatToString_NoScientificNotation() - { - Assert.That(Utils.FloatToString(1.5f), Is.EqualTo("1.5")); - Assert.That(Utils.FloatToString(0f), Is.EqualTo("0")); - Assert.That(Utils.FloatToString(-123.456f), Is.EqualTo("-123.456")); - Assert.That(Utils.FloatToString(1000000f), Is.EqualTo("1000000")); - - // Values that would use scientific notation with default ToString - string small = Utils.FloatToString(0.0000123f); - Assert.That(small.Contains("E") || small.Contains("e"), Is.False, "Should not use scientific notation"); - Assert.That(small.Contains("0.00001") || small.Contains("0.000012"), Is.True); - } - - [Test] - public void FloatToString_InvariantCulture() - { - string result = Utils.FloatToString(1.5f); - Assert.That(result.Contains("."), Is.True); - Assert.That(result.Contains(","), Is.False); - } - - [Test] - public void DoubleToString_NoScientificNotation() - { - Assert.That(Utils.DoubleToString(1.5), Is.EqualTo("1.5")); - Assert.That(Utils.DoubleToString(0), Is.EqualTo("0")); - Assert.That(Utils.DoubleToString(-123.456), Is.EqualTo("-123.456")); - Assert.That(Utils.DoubleToString(1000000), Is.EqualTo("1000000")); - - string small = Utils.DoubleToString(1.23e-10); - Assert.That(small.Contains("E") || small.Contains("e"), Is.False, "Should not use scientific notation"); - } - - [Test] - public void DoubleToString_InvariantCulture() - { - string result = Utils.DoubleToString(1.5); - Assert.That(result.Contains("."), Is.True); - Assert.That(result.Contains(","), Is.False); - } - } -} diff --git a/MuPDF.NET.Test/WidgetTest.cs b/MuPDF.NET.Test/WidgetTest.cs deleted file mode 100644 index aa88d8a5..00000000 --- a/MuPDF.NET.Test/WidgetTest.cs +++ /dev/null @@ -1,134 +0,0 @@ -using mupdf; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MuPDF.NET.Test -{ - public class WidgetTest - { - [Test] - public void Text() - { - Document doc = new Document(); - Page page = doc.NewPage(); - Widget w = new Widget(page); - - w.BorderColor = new float[] { 0, 0, 1 }; - w.BorderWidth = 0.3f; - w.BorderStyle = "d"; - w.BorderDashes = new int[] { 2, 3 }; - w.FieldName = "Textfield-1"; - w.FieldLabel = "arbitray text - e.g. to help filling the field"; - w.FieldType = (int)PdfWidgetType.PDF_WIDGET_TYPE_TEXT; - w.FillColor = new float[] { 1, 1, 0 }; - w.Rect = new Rect(50, 72, 400, 200); - w.TextColor = new float[] { 0, 0, 1 }; - w.TextFont = "TiRo"; - w.TextFontSize = 11.0f; - w.TextMaxLen = 50; - w.FieldValue = "Times-Roman"; - page.AddWidget(w); - Widget first = page.FirstWidget; - - Assert.That(first.FieldTypeString, Is.EqualTo("Text")); - } - - [Test] - public void Test4505() - { - // Copy field flags to Parent widget and all of its kids. - Document doc = new Document("../../../resources/WidgetTest/test_4505.pdf"); - Page page = doc[0]; - - Dictionary text1_flags_before = new Dictionary(); - Dictionary text1_flags_after = new Dictionary(); - - // extract all widgets having the same field name - foreach (Widget _w in page.GetWidgets()) - { - if (_w.FieldName != "text_1") - continue; - - text1_flags_before[_w.Xref] = _w.FieldFlags; - } - Assert.That(text1_flags_before, Is.EqualTo(new Dictionary{{ 8, 1 },{ 10, 0 },{ 33, 0 }})); - - Widget w = page.LoadWidget(8); // first of these widgets - // give all connected widgets that field flags value - w.Update(syncFlags: true); - // confirm that all connected widgets have the same field flags - foreach (Widget _w in page.GetWidgets()) - { - if (_w.FieldName != "text_1") - continue; - - text1_flags_after[_w.Xref] = _w.FieldFlags; - } - Assert.That(text1_flags_after, Is.EqualTo(new Dictionary { { 8, 1 }, { 10, 1 }, { 33, 1 } })); - } - - [Test] - public void ShouldNotThrowOnGetWidgets() - { - Document doc = new Document("../../../resources/WidgetTest/test_widget_parse.pdf"); - - var widgets = doc[0].GetWidgets().ToList(); - Assert.That(widgets.Count, Is.EqualTo(85)); - - widgets = doc[1].GetWidgets().ToList(); - Assert.That(widgets.Count, Is.EqualTo(20)); - } - - [Test] - public void Checkbox() - { - Document doc = new Document(); - Page page = doc.NewPage(); - Widget w = new Widget(page); - - w.BorderStyle = "b"; - w.FieldName = "Button-1"; - w.FieldLabel = "a simple check box button"; - w.FieldType = (int)PdfWidgetType.PDF_WIDGET_TYPE_CHECKBOX; - w.FillColor = new float[] { 0, 1, 1 }; - w.Rect = new Rect(50, 72, 100, 77); - w.TextColor = new float[] { 0, 0, 1 }; - w.TextFont = "ZaDb"; - w.FieldValue = "true"; - page.AddWidget(w); - - Widget field = page.FirstWidget; - Assert.That(field.FieldTypeString, Is.EqualTo("CheckBox")); - - w.FieldFlags |= (int)FormFlags.PDF_FIELD_IS_READ_ONLY; - w.Update(); - doc.Save("output.pdf"); - //Assert.Pass(); - } - - [Test] - public void TestWidget() - { - string testFilePath = Path.GetFullPath("../../../resources/WidgetTest/Widget.pdf"); - Document doc = new Document(testFilePath); - - Page page = doc[0]; - Widget fWidget = page.FirstWidget; - - Assert.IsTrue(fWidget.FieldName == "partlyDetail"); - Assert.IsTrue(fWidget.FieldType == 7); - Assert.IsTrue(fWidget.FieldValue == ""); - Assert.IsTrue(fWidget.FieldFlags == 8392704); - Assert.IsTrue(fWidget.FieldLabel == "undefined"); - Assert.IsTrue(fWidget.TextFont == "SimSun"); - Assert.IsTrue(fWidget.TextFontSize == 12); - - page.Dispose(); - doc.Close(); - } - } -} diff --git a/MuPDF.NET.Test/WidgetTests.cs b/MuPDF.NET.Test/WidgetTests.cs new file mode 100644 index 00000000..1d8d6cb4 --- /dev/null +++ b/MuPDF.NET.Test/WidgetTests.cs @@ -0,0 +1,152 @@ +using System.IO; +using System.Linq; +using Xunit; + +namespace MuPDF.NET.Test +{ + /// + /// Tests for the Widget class. + /// Ported from tests/test_widgets.py. + /// Widgets require native PDF form fields - we test by using existing + /// PDF resources or by testing via the Page.FirstWidget / Widgets() API. + /// + public class WidgetTests + { + /// + /// Attempt to load the interfield-calculation.pdf resource that contains widgets. + /// + private static string GetInterFieldPdf() + { + return TestHelper.GetResource("interfield-calculation.pdf"); + } + + // ─── Widget presence ──────────────────────────────────────────── + + [Fact] + public void Widget_NoWidgetsOnEmptyPage() + { + using var doc = new Document(); + var page = doc.NewPage(); + Assert.Null(page.FirstWidget); + } + + [Fact] + public void Widget_WidgetsEnumeration_EmptyPage() + { + using var doc = new Document(); + var page = doc.NewPage(); + int count = page.Widgets().Count(); + Assert.Equal(0, count); + } + + [Fact] + public void Widget_IsFormPdf_FalseOnNew() + { + using var doc = new Document(); + Assert.False(doc.IsFormPdf); + } + + // ─── Widget from PDF with form fields ─────────────────────────── + + [Fact] + public void Widget_LoadFromInterFieldPdf() + { + var path = GetInterFieldPdf(); + if (!File.Exists(path)) + return; // skip if resource not available + + using var doc = new Document(path); + Assert.True(doc.IsFormPdf); + var page = doc[0]; + var first = page.FirstWidget; + Assert.NotNull(first); + } + + [Fact] + public void Widget_EnumerateFromPdf() + { + var path = GetInterFieldPdf(); + if (!File.Exists(path)) + return; + + using var doc = new Document(path); + var page = doc[0]; + int count = page.Widgets().Count(); + Assert.True(count > 0); + } + + [Fact] + public void Widget_Properties() + { + var path = GetInterFieldPdf(); + if (!File.Exists(path)) + return; + + using var doc = new Document(path); + var page = doc[0]; + var w = page.FirstWidget; + Assert.NotNull(w); + Assert.False(string.IsNullOrEmpty(w.FieldTypeString)); + Assert.True(w.Xref > 0); + Assert.True(w.Rect.Width > 0); + } + + [Fact] + public void Widget_FieldName() + { + var path = GetInterFieldPdf(); + if (!File.Exists(path)) + return; + + using var doc = new Document(path); + var page = doc[0]; + var w = page.FirstWidget; + Assert.NotNull(w); + Assert.NotNull(w.FieldName); + } + + [Fact] + public void Widget_Next() + { + var path = GetInterFieldPdf(); + if (!File.Exists(path)) + return; + + using var doc = new Document(path); + var page = doc[0]; + var w = page.FirstWidget; + Assert.NotNull(w); + // The interfield-calculation.pdf typically has multiple widgets + var next = w.Next; + // Next may or may not be null depending on the PDF + } + + [Fact] + public void Widget_Dispose() + { + var path = GetInterFieldPdf(); + if (!File.Exists(path)) + return; + + using var doc = new Document(path); + var page = doc[0]; + var w = page.FirstWidget; + Assert.NotNull(w); + w.Dispose(); + } + + [Fact] + public void Widget_ToString() + { + var path = GetInterFieldPdf(); + if (!File.Exists(path)) + return; + + using var doc = new Document(path); + var page = doc[0]; + var w = page.FirstWidget; + Assert.NotNull(w); + Assert.Contains("Widget", w.ToString()); + } + } +} diff --git a/MuPDF.NET.Test/_Constants.cs b/MuPDF.NET.Test/_Constants.cs deleted file mode 100644 index dd143fa6..00000000 --- a/MuPDF.NET.Test/_Constants.cs +++ /dev/null @@ -1,35 +0,0 @@ -using MuPDF.NET; -using SkiaSharp; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; - -namespace MuPDF.NET.Test -{ - public static class Constants - { - // some colors - public static float[] red = new float[] { 1, 0, 0 }; - public static float[] blue = new float[] { 0, 0, 1 }; - public static float[] gold = new float[] { 1, 1, 0 }; - public static float[] green = new float[] { 0, 1, 0 }; - public static float[] white = new float[] { 1, 1, 1 }; - public static float[] black = new float[] { 0, 0, 0 }; - - // rectangles and points - public static Rect displ = new Rect(0, 50, 0, 50); - public static Rect r = new Rect(72, 72, 220, 100); - public static Rect rect = new Rect(100, 100, 200, 200); - - // string - public static string t1 = "têxt üsès Lätiñ charß,\nEUR: €, mu: µ, super scripts: ²³!"; - - public static Func FILENAME = () => - { - return System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; - }; - } -} diff --git a/MuPDF.NET.Test/resources/151/drawing.pdf b/MuPDF.NET.Test/resources/151/drawing.pdf deleted file mode 100644 index befc3242..00000000 Binary files a/MuPDF.NET.Test/resources/151/drawing.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/151/quad.pdf b/MuPDF.NET.Test/resources/151/quad.pdf deleted file mode 100644 index f960329b..00000000 Binary files a/MuPDF.NET.Test/resources/151/quad.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/2.pdf b/MuPDF.NET.Test/resources/2.pdf deleted file mode 100644 index 1e10b459..00000000 Binary files a/MuPDF.NET.Test/resources/2.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/Barcodes/Samples.pdf b/MuPDF.NET.Test/resources/Barcodes/Samples.pdf deleted file mode 100644 index 321c62e2..00000000 Binary files a/MuPDF.NET.Test/resources/Barcodes/Samples.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/Barcodes/datamatrix.pdf b/MuPDF.NET.Test/resources/Barcodes/datamatrix.pdf deleted file mode 100644 index 9ddfadca..00000000 Binary files a/MuPDF.NET.Test/resources/Barcodes/datamatrix.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/Barcodes/qr.pdf b/MuPDF.NET.Test/resources/Barcodes/qr.pdf deleted file mode 100644 index 32620a1c..00000000 Binary files a/MuPDF.NET.Test/resources/Barcodes/qr.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/Barcodes/rendered.bmp b/MuPDF.NET.Test/resources/Barcodes/rendered.bmp deleted file mode 100644 index b19e28a9..00000000 Binary files a/MuPDF.NET.Test/resources/Barcodes/rendered.bmp and /dev/null differ diff --git a/MuPDF.NET.Test/resources/DocumentTest/Bezier.epub b/MuPDF.NET.Test/resources/DocumentTest/Bezier.epub deleted file mode 100644 index 0a9ec16f..00000000 Binary files a/MuPDF.NET.Test/resources/DocumentTest/Bezier.epub and /dev/null differ diff --git a/MuPDF.NET.Test/resources/DocumentTest/CMYK_Recolor.pdf b/MuPDF.NET.Test/resources/DocumentTest/CMYK_Recolor.pdf deleted file mode 100644 index e7637642..00000000 Binary files a/MuPDF.NET.Test/resources/DocumentTest/CMYK_Recolor.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/DocumentTest/Color.pdf b/MuPDF.NET.Test/resources/DocumentTest/Color.pdf deleted file mode 100644 index 6086ec95..00000000 Binary files a/MuPDF.NET.Test/resources/DocumentTest/Color.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/DocumentTest/Widget.pdf b/MuPDF.NET.Test/resources/DocumentTest/Widget.pdf deleted file mode 100644 index f903200a..00000000 Binary files a/MuPDF.NET.Test/resources/DocumentTest/Widget.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/DocumentTest/test-rewrite-images.pdf b/MuPDF.NET.Test/resources/DocumentTest/test-rewrite-images.pdf deleted file mode 100644 index d1d74239..00000000 Binary files a/MuPDF.NET.Test/resources/DocumentTest/test-rewrite-images.pdf and /dev/null differ diff --git "a/MuPDF.NET.Test/resources/DocumentTest/\344\275\240\345\245\275.pdf" "b/MuPDF.NET.Test/resources/DocumentTest/\344\275\240\345\245\275.pdf" deleted file mode 100644 index 0b81fba8..00000000 Binary files "a/MuPDF.NET.Test/resources/DocumentTest/\344\275\240\345\245\275.pdf" and /dev/null differ diff --git a/MuPDF.NET.Test/resources/GeneralTest/columns.pdf b/MuPDF.NET.Test/resources/GeneralTest/columns.pdf deleted file mode 100644 index 18f5f159..00000000 Binary files a/MuPDF.NET.Test/resources/GeneralTest/columns.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/PageTest/Color.pdf b/MuPDF.NET.Test/resources/PageTest/Color.pdf deleted file mode 100644 index 6086ec95..00000000 Binary files a/MuPDF.NET.Test/resources/PageTest/Color.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/PageTest/Ocr.pdf b/MuPDF.NET.Test/resources/PageTest/Ocr.pdf deleted file mode 100644 index 3f28b991..00000000 Binary files a/MuPDF.NET.Test/resources/PageTest/Ocr.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/PageTest/_apple.png b/MuPDF.NET.Test/resources/PageTest/_apple.png deleted file mode 100644 index 6d81042d..00000000 Binary files a/MuPDF.NET.Test/resources/PageTest/_apple.png and /dev/null differ diff --git a/MuPDF.NET.Test/resources/WidgetTest/Widget.pdf b/MuPDF.NET.Test/resources/WidgetTest/Widget.pdf deleted file mode 100644 index f903200a..00000000 Binary files a/MuPDF.NET.Test/resources/WidgetTest/Widget.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/WidgetTest/test_4505.pdf b/MuPDF.NET.Test/resources/WidgetTest/test_4505.pdf deleted file mode 100644 index 038b34d8..00000000 Binary files a/MuPDF.NET.Test/resources/WidgetTest/test_4505.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/WidgetTest/test_widget_parse.pdf b/MuPDF.NET.Test/resources/WidgetTest/test_widget_parse.pdf deleted file mode 100644 index 70328c72..00000000 Binary files a/MuPDF.NET.Test/resources/WidgetTest/test_widget_parse.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/annots.pdf b/MuPDF.NET.Test/resources/annots.pdf deleted file mode 100644 index 4778ea36..00000000 Binary files a/MuPDF.NET.Test/resources/annots.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/bordered-table.pdf b/MuPDF.NET.Test/resources/bordered-table.pdf deleted file mode 100644 index 939ff512..00000000 Binary files a/MuPDF.NET.Test/resources/bordered-table.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/cython.pdf b/MuPDF.NET.Test/resources/cython.pdf deleted file mode 100644 index 13a473d4..00000000 Binary files a/MuPDF.NET.Test/resources/cython.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/drawings.pdf b/MuPDF.NET.Test/resources/drawings.pdf deleted file mode 100644 index b5126fe1..00000000 Binary files a/MuPDF.NET.Test/resources/drawings.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/err_table.pdf b/MuPDF.NET.Test/resources/err_table.pdf deleted file mode 100644 index f7f19b3a..00000000 Binary files a/MuPDF.NET.Test/resources/err_table.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/github_sample.pdf b/MuPDF.NET.Test/resources/github_sample.pdf deleted file mode 100644 index 58f38b89..00000000 Binary files a/MuPDF.NET.Test/resources/github_sample.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/image-file1.pdf b/MuPDF.NET.Test/resources/image-file1.pdf deleted file mode 100644 index 5b896b03..00000000 Binary files a/MuPDF.NET.Test/resources/image-file1.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/images.pdf b/MuPDF.NET.Test/resources/images.pdf deleted file mode 100644 index 6c952255..00000000 Binary files a/MuPDF.NET.Test/resources/images.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/img-transparent.png b/MuPDF.NET.Test/resources/img-transparent.png deleted file mode 100644 index ad6d6ddb..00000000 Binary files a/MuPDF.NET.Test/resources/img-transparent.png and /dev/null differ diff --git a/MuPDF.NET.Test/resources/kenpixel.ttf b/MuPDF.NET.Test/resources/kenpixel.ttf deleted file mode 100644 index e3dc57ec..00000000 Binary files a/MuPDF.NET.Test/resources/kenpixel.ttf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/kenpixel.zip b/MuPDF.NET.Test/resources/kenpixel.zip deleted file mode 100644 index 0638bb19..00000000 Binary files a/MuPDF.NET.Test/resources/kenpixel.zip and /dev/null differ diff --git a/MuPDF.NET.Test/resources/non-bordered-table.pdf b/MuPDF.NET.Test/resources/non-bordered-table.pdf deleted file mode 100644 index f32acb9f..00000000 Binary files a/MuPDF.NET.Test/resources/non-bordered-table.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/nur-ruhig.jpg b/MuPDF.NET.Test/resources/nur-ruhig.jpg deleted file mode 100644 index d6b3357a..00000000 Binary files a/MuPDF.NET.Test/resources/nur-ruhig.jpg and /dev/null differ diff --git a/MuPDF.NET.Test/resources/quad-calc-0.pdf b/MuPDF.NET.Test/resources/quad-calc-0.pdf deleted file mode 100644 index 0b81fba8..00000000 Binary files a/MuPDF.NET.Test/resources/quad-calc-0.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/subset.pdf b/MuPDF.NET.Test/resources/subset.pdf deleted file mode 100644 index 1e10b459..00000000 Binary files a/MuPDF.NET.Test/resources/subset.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/symbol-list.pdf b/MuPDF.NET.Test/resources/symbol-list.pdf deleted file mode 100644 index 5f9058fe..00000000 Binary files a/MuPDF.NET.Test/resources/symbol-list.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test-2812.pdf b/MuPDF.NET.Test/resources/test-2812.pdf deleted file mode 100644 index 793aba5f..00000000 Binary files a/MuPDF.NET.Test/resources/test-2812.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test-3591.pdf b/MuPDF.NET.Test/resources/test-3591.pdf deleted file mode 100644 index 005d9ad9..00000000 Binary files a/MuPDF.NET.Test/resources/test-3591.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test-707673.pdf b/MuPDF.NET.Test/resources/test-707673.pdf deleted file mode 100644 index 85f385aa..00000000 Binary files a/MuPDF.NET.Test/resources/test-707673.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test_1645_expected.pdf b/MuPDF.NET.Test/resources/test_1645_expected.pdf deleted file mode 100644 index f6805572..00000000 Binary files a/MuPDF.NET.Test/resources/test_1645_expected.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test_2533.pdf b/MuPDF.NET.Test/resources/test_2533.pdf deleted file mode 100644 index cf88301f..00000000 Binary files a/MuPDF.NET.Test/resources/test_2533.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test_2548.pdf b/MuPDF.NET.Test/resources/test_2548.pdf deleted file mode 100644 index 4b543158..00000000 Binary files a/MuPDF.NET.Test/resources/test_2548.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test_2596.pdf b/MuPDF.NET.Test/resources/test_2596.pdf deleted file mode 100644 index 96498a0f..00000000 Binary files a/MuPDF.NET.Test/resources/test_2596.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test_2645_1.pdf b/MuPDF.NET.Test/resources/test_2645_1.pdf deleted file mode 100644 index 5c177a09..00000000 Binary files a/MuPDF.NET.Test/resources/test_2645_1.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test_2645_2.pdf b/MuPDF.NET.Test/resources/test_2645_2.pdf deleted file mode 100644 index 0a9317a5..00000000 Binary files a/MuPDF.NET.Test/resources/test_2645_2.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test_2645_3.pdf b/MuPDF.NET.Test/resources/test_2645_3.pdf deleted file mode 100644 index 19908998..00000000 Binary files a/MuPDF.NET.Test/resources/test_2645_3.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test_2885.pdf b/MuPDF.NET.Test/resources/test_2885.pdf deleted file mode 100644 index b5a0f3e1..00000000 Binary files a/MuPDF.NET.Test/resources/test_2885.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test_3450.pdf b/MuPDF.NET.Test/resources/test_3450.pdf deleted file mode 100644 index e993d00f..00000000 Binary files a/MuPDF.NET.Test/resources/test_3450.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test_3615.epub b/MuPDF.NET.Test/resources/test_3615.epub deleted file mode 100644 index 94ab28db..00000000 Binary files a/MuPDF.NET.Test/resources/test_3615.epub and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test_3650.pdf b/MuPDF.NET.Test/resources/test_3650.pdf deleted file mode 100644 index 50afea04..00000000 Binary files a/MuPDF.NET.Test/resources/test_3650.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/test_3727.pdf b/MuPDF.NET.Test/resources/test_3727.pdf deleted file mode 100644 index b12bf9b4..00000000 Binary files a/MuPDF.NET.Test/resources/test_3727.pdf and /dev/null differ diff --git a/MuPDF.NET.Test/resources/toc.pdf b/MuPDF.NET.Test/resources/toc.pdf deleted file mode 100644 index a505dbbd..00000000 Binary files a/MuPDF.NET.Test/resources/toc.pdf and /dev/null differ diff --git a/MuPDF.NET/.editorconfig b/MuPDF.NET/.editorconfig deleted file mode 100644 index acec1f83..00000000 --- a/MuPDF.NET/.editorconfig +++ /dev/null @@ -1,243 +0,0 @@ -# Remove the line below if you want to inherit .editorconfig settings from higher directories -root = true - -# C# files -[*.cs] - -#### Core EditorConfig Options #### - -# Indentation and spacing -indent_size = 4 -indent_style = space -tab_width = 4 - -# New line preferences -end_of_line = crlf -insert_final_newline = false - -#### .NET Coding Conventions #### - -# Organize usings -dotnet_separate_import_directive_groups = false -dotnet_sort_system_directives_first = false -file_header_template = unset - -# this. and Me. preferences -dotnet_style_qualification_for_event = false -dotnet_style_qualification_for_field = false -dotnet_style_qualification_for_method = false -dotnet_style_qualification_for_property = false - -# Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true -dotnet_style_predefined_type_for_member_access = true - -# Parentheses preferences -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity -dotnet_style_parentheses_in_other_operators = never_if_unnecessary -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity - -# Modifier preferences -dotnet_style_require_accessibility_modifiers = for_non_interface_members - -# Expression-level preferences -dotnet_style_coalesce_expression = true -dotnet_style_collection_initializer = true -dotnet_style_explicit_tuple_names = true -dotnet_style_namespace_match_folder = true -dotnet_style_null_propagation = true -dotnet_style_object_initializer = true -dotnet_style_operator_placement_when_wrapping = beginning_of_line -dotnet_style_prefer_auto_properties = true -dotnet_style_prefer_collection_expression = when_types_loosely_match -dotnet_style_prefer_compound_assignment = true -dotnet_style_prefer_conditional_expression_over_assignment = true -dotnet_style_prefer_conditional_expression_over_return = true -dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed -dotnet_style_prefer_inferred_anonymous_type_member_names = true -dotnet_style_prefer_inferred_tuple_names = true -dotnet_style_prefer_is_null_check_over_reference_equality_method = true -dotnet_style_prefer_simplified_boolean_expressions = true -dotnet_style_prefer_simplified_interpolation = true - -# Field preferences -dotnet_style_readonly_field = true - -# Parameter preferences -dotnet_code_quality_unused_parameters = all - -# Suppression preferences -dotnet_remove_unnecessary_suppression_exclusions = none - -# New line preferences -dotnet_style_allow_multiple_blank_lines_experimental = true -dotnet_style_allow_statement_immediately_after_block_experimental = true - -#### C# Coding Conventions #### - -# var preferences -csharp_style_var_elsewhere = false -csharp_style_var_for_built_in_types = false -csharp_style_var_when_type_is_apparent = false - -# Expression-bodied members -csharp_style_expression_bodied_accessors = true:silent -csharp_style_expression_bodied_constructors = false:silent -csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_lambdas = true:silent -csharp_style_expression_bodied_local_functions = false:silent -csharp_style_expression_bodied_methods = false:silent -csharp_style_expression_bodied_operators = false:silent -csharp_style_expression_bodied_properties = true:silent - -# Pattern matching preferences -csharp_style_pattern_matching_over_as_with_null_check = true -csharp_style_pattern_matching_over_is_with_cast_check = true -csharp_style_prefer_extended_property_pattern = true -csharp_style_prefer_not_pattern = true -csharp_style_prefer_pattern_matching = true -csharp_style_prefer_switch_expression = true - -# Null-checking preferences -csharp_style_conditional_delegate_call = true - -# Modifier preferences -csharp_prefer_static_local_function = true -csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async -csharp_style_prefer_readonly_struct = true -csharp_style_prefer_readonly_struct_member = true - -# Code-block preferences -csharp_prefer_braces = true:silent -csharp_prefer_simple_using_statement = true:suggestion -csharp_style_namespace_declarations = block_scoped:silent -csharp_style_prefer_method_group_conversion = true:silent -csharp_style_prefer_primary_constructors = true:suggestion -csharp_style_prefer_top_level_statements = true:silent - -# Expression-level preferences -csharp_prefer_simple_default_expression = true -csharp_style_deconstructed_variable_declaration = true -csharp_style_implicit_object_creation_when_type_is_apparent = true -csharp_style_inlined_variable_declaration = true -csharp_style_prefer_index_operator = true -csharp_style_prefer_local_over_anonymous_function = true -csharp_style_prefer_null_check_over_type_check = true -csharp_style_prefer_range_operator = true -csharp_style_prefer_tuple_swap = true -csharp_style_prefer_utf8_string_literals = true -csharp_style_throw_expression = true -csharp_style_unused_value_assignment_preference = discard_variable -csharp_style_unused_value_expression_statement_preference = discard_variable - -# 'using' directive preferences -csharp_using_directive_placement = outside_namespace:silent - -# New line preferences -csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true -csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true -csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true -csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true -csharp_style_allow_embedded_statements_on_same_line_experimental = true - -#### C# Formatting Rules #### - -# New line preferences -csharp_new_line_before_catch = true -csharp_new_line_before_else = true -csharp_new_line_before_finally = true -csharp_new_line_before_members_in_anonymous_types = true -csharp_new_line_before_members_in_object_initializers = true -csharp_new_line_before_open_brace = all -csharp_new_line_between_query_expression_clauses = true - -# Indentation preferences -csharp_indent_block_contents = true -csharp_indent_braces = false -csharp_indent_case_contents = true -csharp_indent_case_contents_when_block = true -csharp_indent_labels = one_less_than_current -csharp_indent_switch_labels = true - -# Space preferences -csharp_space_after_cast = false -csharp_space_after_colon_in_inheritance_clause = true -csharp_space_after_comma = true -csharp_space_after_dot = false -csharp_space_after_keywords_in_control_flow_statements = true -csharp_space_after_semicolon_in_for_statement = true -csharp_space_around_binary_operators = before_and_after -csharp_space_around_declaration_statements = false -csharp_space_before_colon_in_inheritance_clause = true -csharp_space_before_comma = false -csharp_space_before_dot = false -csharp_space_before_open_square_brackets = false -csharp_space_before_semicolon_in_for_statement = false -csharp_space_between_empty_square_brackets = false -csharp_space_between_method_call_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_between_method_call_parameter_list_parentheses = false -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -csharp_space_between_method_declaration_name_and_open_parenthesis = false -csharp_space_between_method_declaration_parameter_list_parentheses = false -csharp_space_between_parentheses = false -csharp_space_between_square_brackets = false - -# Wrapping preferences -csharp_preserve_single_line_blocks = true -csharp_preserve_single_line_statements = true - -#### Naming styles #### - -# Naming rules - -dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion -dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface -dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i - -dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.types_should_be_pascal_case.symbols = types -dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case - -dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members -dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case - -# Symbol specifications - -dotnet_naming_symbols.interface.applicable_kinds = interface -dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = - -dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum -dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = - -dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method -dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = - -# Naming styles - -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = -dotnet_naming_style.pascal_case.capitalization = pascal_case - -dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = -dotnet_naming_style.begins_with_i.capitalization = pascal_case - -[*.{cs,vb}] -dotnet_style_operator_placement_when_wrapping = beginning_of_line -tab_width = 4 -indent_size = 4 -end_of_line = crlf -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion -dotnet_style_prefer_auto_properties = true:silent -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion \ No newline at end of file diff --git a/MuPDF.NET/Annot.cs b/MuPDF.NET/Annot.cs index 37729945..8a5b4dd5 100644 --- a/MuPDF.NET/Annot.cs +++ b/MuPDF.NET/Annot.cs @@ -1,2714 +1,1542 @@ -using mupdf; using System; using System.Collections.Generic; -using Microsoft.CSharp.RuntimeBinder; -using System.Linq; -using System.Runtime.InteropServices; +using System.Diagnostics; using System.Text; +using System.Linq; +using System.Threading; +using mupdf; namespace MuPDF.NET { - public class Annot + /// + /// Represents a PDF annotation. + /// + public class Annot : IDisposable { - static Annot() - { - Utils.InitApp(); - } - - internal PdfAnnot _nativeAnnotion; - - public bool IsOwner { get; set; } - - public bool ThisOwn { get; set; } - - public bool Yielded { get; set; } + private static int _nextAnnotRefId; + private mupdf.PdfAnnot _nativeAnnot; + private bool _disposed; + internal Page Parent { get; } - delegate string LE_FUNCTION( - Annot annot, - Point p1, - Point p2, - bool lr, - float[] fillColor - ); - - private Page _parent; + /// + /// Stable identity for annot-ref bookkeeping (Python uses id(annot) keys in Page._annot_refs). + /// + internal int AnnotRefId { get; } - public Rect Rect + internal mupdf.PdfAnnot NativeAnnot { - get { return new Rect(mupdf.mupdf.pdf_bound_annot(_nativeAnnotion)); } + get + { + if (_disposed) throw new ObjectDisposedException(nameof(Annot)); + return _nativeAnnot; + } } - internal Page Parent + internal Annot(mupdf.PdfAnnot annot, Page page) { - get { return _parent; } - set { _parent = value; } + _nativeAnnot = annot; + Parent = page; + AnnotRefId = Interlocked.Increment(ref _nextAnnotRefId); + page.RegisterAnnotRef(this); } - public Rect ApnBbox - { - get - { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfObj ap = Utils.pdf_dict_getl(annotObj, new string[] { "AP", "N" }); - - FzRect val = null; - if (ap.m_internal == null) - val = new FzRect(FzRect.Fixed.Fixed_EMPTY); - else - val = ap.pdf_dict_get_rect(new PdfObj("BBOX")); - - Rect ret = new Rect(val); - ret = ret * Parent.TransformationMatrix; - ret = ret * Parent.DerotationMatrix; + private mupdf.PdfObj AnnotObj => mupdf.mupdf.pdf_annot_obj(NativeAnnot); + private mupdf.PdfDocument ParentPdfDocument => Parent.Parent.NativePdfDocument; + private Matrix RotatePageMatrix => Helpers.RotatePageMatrix(Parent); + private Matrix DerotatePageMatrix => Helpers.DerotatePageMatrix(Parent); + private Matrix VertexMatrix => Helpers.AnnotVertexMatrix(Parent); + private bool IsWidgetAnnot => Type == AnnotationType.Widget; - return ret; - } - } + // ─── Properties ───────────────────────────────────────────────── - public Matrix ApnMatrix + /// + /// Annotation type as enum. + /// + public AnnotationType Type { get { - PdfAnnot annot = _nativeAnnotion; - PdfObj ap = Utils.pdf_dict_getl(annot.pdf_annot_obj(), new string[] { "AP", "N" }); - if (ap.m_internal == null) - { - return new Matrix(); - } - - FzMatrix mat = ap.pdf_dict_get_matrix(new PdfObj("MATRIX")); - - return new Matrix(mat); + var t = mupdf.mupdf.pdf_annot_type(NativeAnnot); + return (AnnotationType)(int)t; } } - public string BlendMode + /// + /// Annotation type as string. + /// + public string TypeString => mupdf.mupdf.pdf_string_from_annot_type( + (mupdf.pdf_annot_type)(int)Type); + + /// + /// Annotation rectangle. + /// + public Rect Rect { get { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfObj obj = annotObj.pdf_dict_get(new PdfObj("BM")); - string blendMode = ""; - - if (obj.m_internal != null) - { - blendMode = obj.pdf_to_name(); - return blendMode; - } + var r = mupdf.mupdf.pdf_bound_annot(NativeAnnot); + // Python: "val *= p.derotation_matrix" + return Helpers.TransformRect(new Rect(r), DerotatePageMatrix); + } + } - obj = Utils.pdf_dict_getl( - annotObj, - new string[] { "AP", "N", "Resources", "ExtGState" } - ); - - if (obj.pdf_is_dict() != 0) - { - int n = obj.pdf_dict_len(); - for (int i = 0; i < n; i++) - { - PdfObj obj1 = obj.pdf_dict_get_val(i); - if (obj1.pdf_is_dict() != 0) - { - int m = obj1.pdf_dict_len(); - for (int j = 0; j < m; j++) - { - PdfObj obj2 = obj1.pdf_dict_get_key(j); - if (obj2.pdf_objcmp(new PdfObj("BM")) == 0) - { - blendMode = obj1.pdf_dict_get_val(j).pdf_to_name(); - return blendMode; - } - } - } - } - } + /// + /// Annotation xref number. + /// + public int Xref => mupdf.mupdf.pdf_to_num(mupdf.mupdf.pdf_annot_obj(NativeAnnot)); - return blendMode; - } + /// + /// Flags field. + /// + public int Flags + { + get => mupdf.mupdf.pdf_annot_flags(NativeAnnot); + set => mupdf.mupdf.pdf_set_annot_flags(NativeAnnot, value); } - public FileInfo FileInfo + /// + /// Annotation contents. + /// + public string Contents { - get + get => mupdf.mupdf.pdf_annot_contents(NativeAnnot); + set { - FileInfo ret = new FileInfo(); - int length = -1; - int size = -1; - string filename = ""; - string desc = ""; - - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - pdf_annot_type type = annot.pdf_annot_type(); - - if ((PdfAnnotType)type != PdfAnnotType.PDF_ANNOT_FILE_ATTACHMENT) - { - throw new Exception("bad annot type"); - } - - PdfObj stream = Utils.pdf_dict_getl(annotObj, new string[] { "FS", "EF", "F" }); - if (stream == null) - { - throw new Exception("bad PDF: file entry not found"); - } - - PdfObj fs = annotObj.pdf_dict_get(new PdfObj("FS")); - PdfObj o = fs.pdf_dict_get(new PdfObj("UF")); - if (o != null) - { - filename = o.pdf_to_text_string(); - } - else - { - o = fs.pdf_dict_get(new PdfObj("F")); - if (o != null) - { - filename = o.pdf_to_text_string(); - } - } - - o = fs.pdf_dict_get(new PdfObj("Desc")); - if (o != null) - desc = o.pdf_to_text_string(); - - o = stream.pdf_dict_get(new PdfObj("Length")); - if (o != null) - { - length = o.pdf_to_int(); - } - - o = Utils.pdf_dict_getl(stream, new string[] { "Params", "Size" }); - if (o != null) - { - size = o.pdf_to_int(); - } - - ret.FileName = EscapeStrFromStr(filename); - ret.Desc = EscapeStrFromStr(desc); - ret.Size = size; - ret.Length = length; - - return ret; + mupdf.mupdf.pdf_set_annot_contents(NativeAnnot, value); + mupdf.mupdf.pdf_update_annot(NativeAnnot); } } + /// + /// Check if annotation has a Popup. + /// public bool HasPopup { get { - PdfAnnot annot = _nativeAnnotion; - PdfObj obj = annot.pdf_annot_obj().pdf_dict_get(new PdfObj("Popup")); - - if (obj.m_internal != null) - return true; - - return false; + var popup = mupdf.mupdf.pdf_dict_gets(AnnotObj, "Popup"); + return popup.m_internal != null; } } - public AnnotInfo Info + /// + /// Annotation 'Popup' rectangle. + /// + public Rect PopupRect { get { - AnnotInfo res = new AnnotInfo(); - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - - res.Content = annot.pdf_annot_contents(); - - PdfObj o = annotObj.pdf_dict_get(new PdfObj("Name")); - res.Name = o.pdf_to_name(); - - o = annotObj.pdf_dict_get(new PdfObj("T")); - res.Title = o.pdf_to_text_string(); - - o = annotObj.pdf_dict_gets("CreationDate"); - res.CreationDate = o.pdf_to_text_string(); - - o = annotObj.pdf_dict_get(new PdfObj("M")); - res.ModDate = o.pdf_to_text_string(); - - o = annotObj.pdf_dict_gets("Subj"); - res.Subject = o.pdf_to_text_string(); - - o = annotObj.pdf_dict_gets("NM"); - res.Id = o.pdf_to_text_string(); - - return res; + var rect = Rect.Infinite; + var popup = mupdf.mupdf.pdf_dict_gets(AnnotObj, "Popup"); + if (popup.m_internal != null) + rect = new Rect(mupdf.mupdf.pdf_dict_get_rect(popup, mupdf.mupdf.pdf_new_name("Rect"))); + // Python: val = Rect(val) * transformation_matrix; val *= derotation_matrix + rect = Helpers.TransformRect(rect, Parent.TransformationMatrix); + rect = Helpers.TransformRect(rect, DerotatePageMatrix); + return rect; } } - public int Flags + /// + /// Annotation 'Popup' xref. + /// + public int PopupXref { - get { return _nativeAnnotion.pdf_annot_flags(); } + get + { + if (!HasPopup) return 0; + var popup = mupdf.mupdf.pdf_dict_gets(AnnotObj, "Popup"); + return mupdf.mupdf.pdf_to_num(popup); + } } + /// + /// Get 'open' status of annotation or its Popup. + /// public bool IsOpen - { - get { return _nativeAnnotion.pdf_annot_is_open() == 0 ? false : true; } - } - - public string Language { get { - fz_text_language lang; - PdfAnnot annot = _nativeAnnotion; - lang = mupdf.mupdf.pdf_annot_language(annot); - - if (lang == fz_text_language.FZ_LANG_UNSET) - return null; - - return mupdf.mupdf.fz_string_from_text_language("", lang); + return mupdf.mupdf.pdf_annot_is_open(NativeAnnot) != 0; } + set => mupdf.mupdf.pdf_set_annot_is_open(NativeAnnot, value ? 1 : 0); } - public int Xref + /// + /// Opacity value. + /// + public float Opacity { get { - PdfAnnot annot = _nativeAnnotion; - return annot.pdf_annot_obj().pdf_to_num(); + var ca = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("CA")); + return mupdf.mupdf.pdf_is_number(ca) != 0 ? mupdf.mupdf.pdf_to_real(ca) : -1; + } + set + { + SetOpacity(value); } } - public (PdfLineEnding, PdfLineEnding) LineEnds + /// + /// Border width. + /// + public float Border_Width { - get - { - PdfAnnot annot = _nativeAnnotion; - if (annot.pdf_annot_has_line_ending_styles() == 0) - return (0, 0); - - PdfLineEnding lstart = (PdfLineEnding)annot.pdf_annot_line_start_style(); - PdfLineEnding lend = (PdfLineEnding)annot.pdf_annot_line_end_style(); - - return (lstart, lend); - } + get => Border.TryGetValue("width", out var width) && width != null ? Convert.ToSingle(width) : -1; } /// - /// return MuPDFAnnot or Widget + /// Annotation unique id (NM key). /// - public dynamic Next + public string Id { get { - try - { - PdfAnnot annot = _nativeAnnotion; - pdf_annot_type type = annot.pdf_annot_type(); - - if (type != pdf_annot_type.PDF_ANNOT_WIDGET) - annot = annot.pdf_next_annot(); - else - annot = annot.pdf_next_widget(); - - Annot val = (annot == null ? null : new Annot(annot, _parent)); - if (val == null) - { - return null; - } - - val.IsOwner = true; - if (val.GetParent() == null) - throw new Exception("null parent"); - - val.Parent.AnnotRefs[val.GetHashCode()] = val; - - if (val.Type.Item1 == PdfAnnotType.PDF_ANNOT_WIDGET) - { - Widget widget = new Widget(Parent); - Utils.FillWidget(val, widget); - return widget; - } - else if (val.Type.Item1 == PdfAnnotType.PDF_ANNOT_UNKNOWN) - { - return null; - } - - return val; - } - catch(Exception e) - { - throw new Exception(e.Message); - } + var nm = mupdf.mupdf.pdf_dict_gets(mupdf.mupdf.pdf_annot_obj(NativeAnnot), "NM"); + return nm.m_internal != null ? mupdf.mupdf.pdf_to_text_string(nm) : null; } } - public float Opacity + /// + /// Next annotation. + /// + public Annot Next { get { - float opy = -1.0f; - PdfAnnot annot = _nativeAnnotion; - PdfObj ca = annot.pdf_annot_obj().pdf_dict_get(new PdfObj("CA")); - - if (ca.pdf_is_number() != 0) - opy = ca.pdf_to_real(); - - return opy; + var next = IsWidgetAnnot + ? mupdf.mupdf.pdf_next_widget(NativeAnnot) + : mupdf.mupdf.pdf_next_annot(NativeAnnot); + return next.m_internal != null ? new Annot(next, Parent) : null; } } - public Rect PopupRect - { - get - { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - FzRect rect = new FzRect(FzRect.Fixed.Fixed_INFINITE); - PdfObj obj = annotObj.pdf_dict_get(new PdfObj("Popup")); - if (obj.m_internal != null) - { - rect = obj.pdf_dict_get_rect(new PdfObj("Rect")); - } + /// + /// Stroke color components. + /// + public float[] Colors + { + get => ColorInfo.TryGetValue("stroke", out var stroke) ? (float[])stroke : Array.Empty(); + } - Rect val = new Rect(rect); - val = val * Parent.TransformationMatrix; - val *= Parent.DerotationMatrix; + /// + /// Interior (fill) color components. + /// + public float[] InteriorColor + { + get => ColorInfo.TryGetValue("fill", out var fill) ? (float[])fill : Array.Empty(); + } - return val; - } + public Dictionary ColorInfo + { + get => Helpers.JM_annot_colors(AnnotObj); } - public int PopupXref + public Dictionary Border { get { - int xref = 0; - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfObj obj = annotObj.pdf_dict_get(new PdfObj("Popup")); - - if (obj.m_internal != null) - { - xref = obj.pdf_to_num(); - } - - return xref; + var annotType = Type; + if (annotType != AnnotationType.Circle + && annotType != AnnotationType.FreeText + && annotType != AnnotationType.Ink + && annotType != AnnotationType.Line + && annotType != AnnotationType.PolyLine + && annotType != AnnotationType.Polygon + && annotType != AnnotationType.Square) + return new Dictionary(); + return Helpers.JM_annot_border(AnnotObj); } } - public (float, float, float, float) RectDelta + public Rect ApnBBox { get { - PdfObj annotObj = _nativeAnnotion.pdf_annot_obj(); - PdfObj arr = annotObj.pdf_dict_get(new PdfObj("RD")); - - if (arr.pdf_array_len() == 4) - { - return ( - arr.pdf_array_get(0).pdf_to_real(), - arr.pdf_array_get(1).pdf_to_real(), - arr.pdf_array_get(2).pdf_to_real(), - arr.pdf_array_get(3).pdf_to_real() - ); - } - else - return (0, 0, 0, 0); + var ap = GetAppearanceStreamObject("N"); + Rect value = ap.m_internal == null + ? Rect.Infinite + : new Rect(mupdf.mupdf.pdf_dict_get_rect(ap, mupdf.mupdf.pdf_new_name("BBox"))); + value = Helpers.TransformRect(value, Parent.TransformationMatrix); + value = Helpers.TransformRect(value, DerotatePageMatrix); + return value; } } - public int Rotation + public Matrix ApnMatrix { get { - PdfAnnot annot = _nativeAnnotion; - PdfObj rotation = annot.pdf_annot_obj().pdf_dict_get(new PdfObj("Rotate")); - - if (rotation.m_internal == null) - return -1; - - return rotation.pdf_to_int(); + var ap = GetAppearanceStreamObject("N"); + if (ap.m_internal == null) return new Matrix(); + return Helpers.MatrixFromFz(mupdf.mupdf.pdf_dict_get_matrix(ap, mupdf.mupdf.pdf_new_name("Matrix"))); } } - public Border Border + public string Language { get { - PdfAnnotType atype = Type.Item1; - if ( - !( - new List() - { - PdfAnnotType.PDF_ANNOT_CIRCLE, - PdfAnnotType.PDF_ANNOT_FREE_TEXT, - PdfAnnotType.PDF_ANNOT_INK, - PdfAnnotType.PDF_ANNOT_LINE, - PdfAnnotType.PDF_ANNOT_POLY_LINE, - PdfAnnotType.PDF_ANNOT_POLYGON, - PdfAnnotType.PDF_ANNOT_SQUARE - } - ).Contains(atype) - ) - { - return new Border(); - } - - PdfObj annotObj = _nativeAnnotion.pdf_annot_obj(); - - return GetBorderFromAnnot(annotObj); + var lang = mupdf.mupdf.pdf_annot_language(NativeAnnot); + return lang == mupdf.fz_text_language.FZ_LANG_UNSET + ? null + : mupdf.mupdf.fz_string_from_text_language2(lang); } + set => SetLanguage(value); } - public Color Colors + public (int start, int end)? LineEnds { get { - try - { - PdfAnnot annot = _nativeAnnotion; - return GetColorFromAnnot(annot.pdf_annot_obj()); - } - catch - { - throw; - } + if (mupdf.mupdf.pdf_annot_has_line_ending_styles(NativeAnnot) == 0) + return null; + return ((int)mupdf.mupdf.pdf_annot_line_start_style(NativeAnnot), + (int)mupdf.mupdf.pdf_annot_line_end_style(NativeAnnot)); } } - public dynamic Vertices + public int Rotation { get { - PdfAnnot annot = _nativeAnnotion; - FzMatrix pageCtm = new FzMatrix(); - FzRect dummy = new FzRect(0); - annot.pdf_annot_page().pdf_page_transform(dummy, pageCtm); - FzMatrix derot = Utils.DerotatePageMatrix(annot.pdf_annot_page()); - pageCtm = mupdf.mupdf.fz_concat(pageCtm, derot); - - PdfObj obj = annot.pdf_annot_obj().pdf_dict_get(new PdfObj("Vertices")); - if (obj == null) - obj = annot.pdf_annot_obj().pdf_dict_get(new PdfObj("L")); - if (obj == null) - obj = annot.pdf_annot_obj().pdf_dict_get(new PdfObj("QuadPoints")); - if (obj == null) - obj = annot.pdf_annot_obj().pdf_dict_get(new PdfObj("CL")); - - if (obj.m_internal != null) - { - List ret = new List(); - for (int i = 0; i < obj.pdf_array_len(); i += 2) - { - float x = obj.pdf_array_get(i).pdf_to_real(); - float y = obj.pdf_array_get(i + 1).pdf_to_real(); - FzPoint p = new FzPoint(x, y); - p = mupdf.mupdf.fz_transform_point(p, pageCtm); - ret.Add(new Point(p)); - } - - return ret; - } - else - { - List> ret = new List>(); - for (int j = 0; j < obj.pdf_array_len(); j++) - { - List t = new List(); - PdfObj o = obj.pdf_array_get(j); - for (int i = 0; i < o.pdf_array_len(); i += 2) - { - float x = o.pdf_array_get(i).pdf_to_real(); - float y = o.pdf_array_get(i + 1).pdf_to_real(); - FzPoint p = new FzPoint(x, y); - p = mupdf.mupdf.fz_transform_point(p, pageCtm); - t.Add(new Point(p)); - } - ret.Add(t); - } - - return ret; - } + var rotation = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("Rotate")); + return rotation.m_internal == null ? -1 : mupdf.mupdf.pdf_to_int(rotation); } } - public static string EscapeStrFromStr(string c) - { - if (c == null || c == "") - return ""; - byte[] b = Encoding.UTF8.GetBytes(c); - string ret = ""; - foreach (byte bb in b) + // ─── Methods ──────────────────────────────────────────────────── + + /// + /// Set annotation rectangle. + /// + /// + /// Python returns None on success and False on failure; this returns / . + /// + public bool? SetRect(Rect rect) + { + // CheckParent(self) + var annot = NativeAnnot; + // pdfpage = _pdf_annot_page(annot) + var pdfpage = mupdf.mupdf.pdf_annot_page(annot); + // rot = JM_rotate_page_matrix(pdfpage) + var rot = Helpers.JM_rotate_page_matrix(pdfpage); + // r = mupdf.fz_transform_rect(JM_rect_from_py(rect), rot) + var r = mupdf.mupdf.fz_transform_rect(new Rect(rect).ToFzRect(), rot.ToFzMatrix()); + if (mupdf.mupdf.fz_is_empty_rect(r) != 0 || mupdf.mupdf.fz_is_infinite_rect(r) != 0) + throw new ValueErrorException(Constants.MSG_BAD_RECT); + try { - ret += (char)bb; + mupdf.mupdf.pdf_set_annot_rect(annot, r); + return null; + } + catch (Exception e) + { + Trace.WriteLine($"cannot set rect: {e}"); + return false; } - - return ret; } - public static string UnicodeFromStr(dynamic s) + /// + /// Set border properties. + /// + public void SetBorder(float width = -1, float[] dashes = null, string style = null) { - string ret = ""; - if (s is string && string.IsNullOrEmpty(s)) - return ""; - - if (s is byte[]) - ret = Encoding.UTF8.GetString(s); - - return ret; + SetBorder(null, width, style, dashes != null ? Array.ConvertAll(dashes, x => (int)x) : null, -1); } - public (PdfAnnotType, string, string) Type + public void SetBorder(Dictionary border = null, float width = -1, string style = null, int[] dashes = null, float clouds = -1) { - get - { - if (_nativeAnnotion.m_internal == null) - return (PdfAnnotType.PDF_ANNOT_UNKNOWN, null, null); + var annotType = Type; + if (annotType != AnnotationType.Circle + && annotType != AnnotationType.FreeText + && annotType != AnnotationType.Ink + && annotType != AnnotationType.Line + && annotType != AnnotationType.PolyLine + && annotType != AnnotationType.Polygon + && annotType != AnnotationType.Square) + return; - pdf_annot_type t = _nativeAnnotion.pdf_annot_type(); - string c = mupdf.mupdf.pdf_string_from_annot_type(t); - PdfObj obj = _nativeAnnotion.pdf_annot_obj().pdf_dict_gets("IT"); + if (annotType != AnnotationType.Circle + && annotType != AnnotationType.FreeText + && annotType != AnnotationType.Polygon + && annotType != AnnotationType.Square + && clouds > 0) + clouds = -1; - if (obj.m_internal == null || obj.pdf_is_name() != 0) - return ((PdfAnnotType)t, c, null); - string it = obj.pdf_to_name(); + border ??= new Dictionary(); + if (!border.ContainsKey("width")) border["width"] = width; + if (!border.ContainsKey("style")) border["style"] = style; + if (!border.ContainsKey("dashes")) border["dashes"] = dashes; + if (!border.ContainsKey("clouds")) border["clouds"] = clouds; + if (border["width"] == null) border["width"] = -1f; + if (border["clouds"] == null) border["clouds"] = -1f; + if (annotType != AnnotationType.Circle + && annotType != AnnotationType.FreeText + && annotType != AnnotationType.Polygon + && annotType != AnnotationType.Square + && border.TryGetValue("clouds", out var cloudObj) + && cloudObj != null + && Convert.ToSingle(cloudObj) > 0) + border["clouds"] = -1f; - return ((PdfAnnotType)t, c, it); + if (border.TryGetValue("dashes", out var dashObj) && dashObj is System.Collections.IEnumerable seq && dashObj is not string) + { + var parsed = new List(); + bool ok = true; + foreach (var item in seq) + { + if (item is int di) parsed.Add(di); + else { ok = false; break; } + } + border["dashes"] = ok ? parsed.ToArray() : null; } + Helpers.JM_annot_set_border(border, ParentPdfDocument, AnnotObj); } - public override string ToString() - { - return string.Format("'{0}' annotation on", 0); - } - - public Annot(PdfAnnot annotion, Page parent) + /// + /// Set 'stroke' and 'fill' colors. + /// + public void SetColors(float[] stroke = null, float[] fill = null) { - _nativeAnnotion = new PdfAnnot(annotion); - _parent = parent; - IsOwner = true; + SetColors((Dictionary)null, stroke, fill); } - public void Erase() + public void SetColors(Dictionary colors = null, object stroke = null, object fill = null) { - IsOwner = false; + colors ??= new Dictionary + { + ["fill"] = fill, + ["stroke"] = stroke, + }; + var strokeObj = colors.TryGetValue("stroke", out var s) ? s : stroke; + var fillObj = colors.TryGetValue("fill", out var f) ? f : fill; + SetColors(null, NormalizeColorSequence(strokeObj), NormalizeColorSequence(fillObj)); } - internal AnnotValues GetRedactValues() + public void SetColors(Dictionary colors = null, float[] stroke = null, float[] fill = null) { - PdfAnnot annot = _nativeAnnotion; + if (Type == AnnotationType.FreeText) + throw new ArgumentException("cannot be used for FreeText annotations"); - AnnotValues values = new AnnotValues(); - try + colors ??= new Dictionary { - PdfObj obj = annot.pdf_annot_obj().pdf_dict_gets("RO"); - if (obj.m_internal != null) - { - int xref = obj.pdf_to_num(); - values.Xref = xref; - } + ["fill"] = fill, + ["stroke"] = stroke, + }; - obj = annot.pdf_annot_obj().pdf_dict_gets("OverlayText"); - if (obj.m_internal != null) - { - string text = obj.pdf_to_text_string(); - values.Text = text; - } + fill = colors.TryGetValue("fill", out var fillColor) ? fillColor : null; + stroke = colors.TryGetValue("stroke", out var strokeColor) ? strokeColor : null; + + if (stroke != null) + { + if (stroke.Length == 0) + Parent.Parent.XrefSetKey(Xref, "C", "[]"); else { - values.Text = ""; + Helpers.CheckColor(stroke); + Parent.Parent.XrefSetKey(Xref, "C", Helpers.EscapePdfArray(stroke)); } + } + + bool allowFill = Type == AnnotationType.Circle + || Type == AnnotationType.Square + || Type == AnnotationType.Line + || Type == AnnotationType.PolyLine + || Type == AnnotationType.Polygon + || Type == AnnotationType.Redact; - int align = 0; - obj = annot.pdf_annot_obj().pdf_dict_gets("Q"); - if (obj.m_internal != null) + if (fill != null) + { + if (!allowFill) + return; + if (fill.Length == 0) + Parent.Parent.XrefSetKey(Xref, "IC", "[]"); + else { - align = obj.pdf_to_int(); + Helpers.CheckColor(fill); + Parent.Parent.XrefSetKey(Xref, "IC", Helpers.EscapePdfArray(fill)); } - values.Align = align; - } - catch (Exception) - { - return null; } - - if (values == null) - return values; - - values.Rect = this.Rect; - List tColor = new List(); - (tColor, values.FontName, values.FontSize) = ParseData(this); - values.TextColor = tColor.ToArray(); - values.Fill = new float[Colors.Fill.Length]; - Array.Copy(Colors.Fill, values.Fill, values.Fill.Length); - - return values; } - public static (List, string, float) ParseData(Annot annot) + /// + /// Set opacity. + /// + public void SetOpacity(float opacity) { - PdfObj obj = annot._nativeAnnotion.pdf_annot_obj(); - PdfDocument pdf = obj.pdf_get_bound_document(); - string da_string = ""; - try + if (!Helpers.InRange(opacity, 0.0, 1.0)) { - da_string = obj.pdf_dict_get_inheritable(new PdfObj("DA")).pdf_to_text_string(); - /* - PdfObj da = obj.pdf_dict_get_inheritable(new PdfObj("DA")); - if (da.m_internal != null) - { - PdfObj tailer = pdf.pdf_trailer(); - da = Utils.pdf_dict_getl(tailer, new string[] { "Root", "AcroForm", "DA" }); - da_string = da.pdf_to_text_string(); - } - */ + mupdf.mupdf.pdf_set_annot_opacity(NativeAnnot, 1); + return; } - catch (Exception e) + mupdf.mupdf.pdf_set_annot_opacity(NativeAnnot, opacity); + if (opacity < 1.0) { - Console.WriteLine($"Error: {e.Message}"); // Example logging - da_string = ""; + var page = mupdf.mupdf.pdf_annot_page(NativeAnnot); + var pdfPage = page.m_internal; + if (pdfPage != null) + pdfPage.transparency = 1; } + } - if (string.IsNullOrEmpty(da_string)) - { - return (new List(), "", 0.0f); - } - - string font = "Helv"; - float fsize = 12.0f; - List col = new List() { 0.0f, 0.0f, 0.0f }; - string[] dat = da_string.Split(); + /// + /// Set /Name (icon) of annotation. + /// + public void SetName(string name) + { + mupdf.mupdf.pdf_dict_put_name(AnnotObj, mupdf.mupdf.pdf_new_name("Name"), name); + } - for (int i = 0; i < dat.Length; i++) - { - string item = dat[i]; - if (item == "Tf") - { - font = dat[i - 2].Substring(1); - fsize = float.Parse(dat[i - 1], System.Globalization.CultureInfo.InvariantCulture); - dat[i] = dat[i - 1] = dat[i - 2] = ""; - continue; - } + /// + /// Get /Name (icon) of annotation. + /// + public string GetName() + { + var obj = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("Name")); + return obj.m_internal != null ? mupdf.mupdf.pdf_to_name(obj) : ""; + } - if (item == "g") - { - col = new List() { float.Parse(dat[i - 1], System.Globalization.CultureInfo.InvariantCulture) }; - dat[i] = dat[i - 1] = ""; - continue; - } + /// + /// Set annotation BlendMode. + /// + public void SetBlendMode(string mode) + { + mupdf.mupdf.pdf_dict_put_name(AnnotObj, mupdf.mupdf.pdf_new_name("BM"), mode); + } - if (item == "rg") - { - col = new List(); - for (int j = i - 3; j < i; j++) - { - col.Add(float.Parse(dat[j], System.Globalization.CultureInfo.InvariantCulture)); - } - dat[i] = dat[i - 1] = dat[i - 2] = dat[i - 3] = ""; - continue; - } + /// + /// Annotation BlendMode. + /// + public string BlendMode + { + get + { + var bm = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("BM")); + if (bm.m_internal != null) return mupdf.mupdf.pdf_to_name(bm); - if (item == "k") + var extState = GetNestedDict(AnnotObj, "AP", "N", "Resources", "ExtGState"); + if (mupdf.mupdf.pdf_is_dict(extState) != 0) { - col = new List(); - for (int j = i - 4; j < i; j++) + int n = mupdf.mupdf.pdf_dict_len(extState); + for (int i = 0; i < n; i++) { - col.Add(float.Parse(dat[j], System.Globalization.CultureInfo.InvariantCulture)); + var state = mupdf.mupdf.pdf_dict_get_val(extState, i); + if (mupdf.mupdf.pdf_is_dict(state) == 0) continue; + int m = mupdf.mupdf.pdf_dict_len(state); + for (int j = 0; j < m; j++) + { + var key = mupdf.mupdf.pdf_dict_get_key(state, j); + if (mupdf.mupdf.pdf_objcmp(key, mupdf.mupdf.pdf_new_name("BM")) == 0) + return mupdf.mupdf.pdf_to_name(mupdf.mupdf.pdf_dict_get_val(state, j)); + } } - - dat[i] = dat[i - 1] = dat[i - 2] = dat[i - 3] = dat[i - 4] = ""; - continue; } + return null; } - - return (col, font, fsize); } - public static void UpdateData(PdfAnnot annot_, string dataStr) + /// + /// Annotation author. + /// + public string Author { - try + get { - PdfAnnot annot = annot_; - annot.pdf_annot_obj().pdf_dict_put_text_string(new PdfObj("DA"), dataStr); - annot.pdf_annot_obj().pdf_dict_del(new PdfObj("DS")); - annot.pdf_annot_obj().pdf_dict_del(new PdfObj("RC")); + var title = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("T")); + return title.m_internal != null ? mupdf.mupdf.pdf_to_text_string(title) : ""; } - catch + set { - return; + mupdf.mupdf.pdf_set_annot_author(NativeAnnot, value); } } - public byte[] GetAP() + /// + /// Annotation creation date. + /// + public string CreationDate { - PdfObj obj = _nativeAnnotion.pdf_annot_obj(); - PdfObj ap = Utils.pdf_dict_getl(obj, new string[] { "AP", "N" }); - - FzBuffer ret = null; - if (ap.pdf_is_stream() != 0) + get { - ret = ap.pdf_load_stream(); + var cd = mupdf.mupdf.pdf_dict_gets(mupdf.mupdf.pdf_annot_obj(NativeAnnot), "CreationDate"); + return cd.m_internal != null ? mupdf.mupdf.pdf_to_text_string(cd) : ""; } - - byte[] r = null; - if (ret != null && ret.m_internal != null) - r = Utils.BinFromBuffer(ret); - - return r; } - public void SetAP(byte[] buffer, int rect = 0) + /// + /// Annotation modification date. + /// + public string ModDate { - try + get { - PdfObj annotObj = _nativeAnnotion.pdf_annot_obj(); - PdfPage page = _nativeAnnotion.pdf_annot_page(); - PdfObj apObj = Utils.pdf_dict_getl(annotObj, new string[] { "AP", "N" }); - - if (apObj.m_internal == null) - { - throw new Exception("bad or missing annot AP/N"); - } - if (apObj.pdf_is_stream() == 0) - { - throw new Exception("bad or missing annot AP/N"); - } - FzBuffer buf = Utils.BufferFromBytes(buffer); - if (buf.m_internal == null) - { - throw new Exception("bad type: 'buffer'"); - } - PdfDocument pageDoc = page.doc(); - Utils.UpdateStream(pageDoc, apObj, buf, 1); - if (rect != 0) - { - FzRect bbox = annotObj.pdf_dict_get_rect(new PdfObj("Rect")); - apObj.pdf_dict_put_rect(new PdfObj("Rect"), bbox); - } - pageDoc.Dispose(); + var md = mupdf.mupdf.pdf_dict_gets(mupdf.mupdf.pdf_annot_obj(NativeAnnot), "M"); + return md.m_internal != null ? mupdf.mupdf.pdf_to_text_string(md) : ""; } - catch (Exception) { } } - public static float[] ColorFromSequence(float[] seq) + /// + /// Set various annotation properties. + /// + public void SetInfo(string content = null, string title = null, string creationDate = null, string modDate = null, string subject = null) { - if (!(seq is float[]) || seq == null) - return null; - - if (!(new List() { 0, 1, 3, 4 }).Contains(seq.Length)) - return null; - - for (int i = 0; i < seq.Length; i++) - { - if (seq[i] < 0 || seq[i] > 1) - seq[i] = 1.0f; - } - - return seq; + SetInfo(null, content, title, creationDate, modDate, subject); } - private bool UpdateAppearance( - float opacity = -1.0f, - string blendMode = null, - float[] fillColor = null, - float rotate = -1.0f - ) + public void SetInfo(Dictionary info = null, string content = null, string title = null, string creationDate = null, string modDate = null, string subject = null) { - PdfObj annotObj = _nativeAnnotion.pdf_annot_obj(); - PdfPage page = _nativeAnnotion.pdf_annot_page(); - PdfDocument pageDoc = page.doc(); - pdf_annot_type type = _nativeAnnotion.pdf_annot_type(); - float[] cols = ColorFromSequence(fillColor); - int nCols = cols.Length; - - IntPtr colsPtr = Marshal.AllocHGlobal(nCols * sizeof(float)); - Marshal.Copy(cols, 0, colsPtr, nCols); - SWIGTYPE_p_float swigCols = new SWIGTYPE_p_float(colsPtr, false); - - try - { - if ( - nCols == 0 - || !( - new List() - { - PdfAnnotType.PDF_ANNOT_SQUARE, - PdfAnnotType.PDF_ANNOT_CIRCLE, - PdfAnnotType.PDF_ANNOT_LINE, - PdfAnnotType.PDF_ANNOT_POLY_LINE, - PdfAnnotType.PDF_ANNOT_POLYGON - } - ).Contains((PdfAnnotType)type) - ) - { - annotObj.pdf_dict_del(new PdfObj("IC")); - } - else if (nCols > 0) - { - _nativeAnnotion.pdf_set_annot_interior_color(nCols, swigCols); - } - - int insertRot = 0; - if (rotate >= 0) - insertRot = 1; - - if ( - !( - new List() - { - PdfAnnotType.PDF_ANNOT_CARET, - PdfAnnotType.PDF_ANNOT_CIRCLE, - PdfAnnotType.PDF_ANNOT_FREE_TEXT, - PdfAnnotType.PDF_ANNOT_FILE_ATTACHMENT, - PdfAnnotType.PDF_ANNOT_INK, - PdfAnnotType.PDF_ANNOT_LINE, - PdfAnnotType.PDF_ANNOT_POLY_LINE, - PdfAnnotType.PDF_ANNOT_POLYGON, - PdfAnnotType.PDF_ANNOT_SQUARE, - PdfAnnotType.PDF_ANNOT_STAMP, - PdfAnnotType.PDF_ANNOT_TEXT, - } - ).Contains((PdfAnnotType)type) - ) - { - insertRot = 0; - } - - if (insertRot != 0) - { - annotObj.pdf_dict_put_int(new PdfObj("Rotate"), (long)rotate); - } - - // insert fill color - if ((PdfAnnotType)type == PdfAnnotType.PDF_ANNOT_FREE_TEXT) - { - if (nCols > 0) - { - _nativeAnnotion.pdf_set_annot_color(nCols, swigCols); - } - } - else if (nCols > 0) - { - PdfObj col = pageDoc.pdf_new_array(nCols); - for (int i = 0; i < nCols; i++) - { - mupdf.mupdf.pdf_array_push_real(col, cols[i]); - } - annotObj.pdf_dict_put(new PdfObj("IC"), col); - } - _nativeAnnotion.pdf_dirty_annot(); - _nativeAnnotion.pdf_update_annot(); - pageDoc.m_internal.resynth_required = 0; - } - catch (Exception e) + if (info != null) { - pageDoc.Dispose(); - throw new Exception("cannot update annot:" + e.Message); + info.TryGetValue("content", out content); + info.TryGetValue("title", out title); + info.TryGetValue("creationDate", out creationDate); + info.TryGetValue("modDate", out modDate); + info.TryGetValue("subject", out subject); } - if ((opacity < 0 || opacity >= 1) && blendMode == null) // no opacity, no blend_mode + if (!string.IsNullOrEmpty(content)) + mupdf.mupdf.pdf_set_annot_contents(NativeAnnot, content); + bool isMarkup = mupdf.mupdf.pdf_annot_has_author(NativeAnnot) != 0; + if (isMarkup) { - pageDoc.Dispose(); - return true; + if (!string.IsNullOrEmpty(title)) Author = title; + if (!string.IsNullOrEmpty(creationDate)) + mupdf.mupdf.pdf_dict_put_text_string(AnnotObj, + mupdf.mupdf.pdf_new_name("CreationDate"), creationDate); + if (!string.IsNullOrEmpty(modDate)) + mupdf.mupdf.pdf_dict_put_text_string(AnnotObj, + mupdf.mupdf.pdf_new_name("M"), modDate); + if (!string.IsNullOrEmpty(subject)) + mupdf.mupdf.pdf_dict_puts(AnnotObj, + "Subj", mupdf.mupdf.pdf_new_text_string(subject)); } + } - try // create or update /ExtGState - { - PdfObj ap = Utils.pdf_dict_getl( - _nativeAnnotion.pdf_annot_obj(), - new string[] { "AP", "N" } - ); - if (ap.m_internal == null) - { - // should never happen - pageDoc.Dispose(); - throw new Exception("bad or missing annot AP/N"); - } - - PdfObj resources = ap.pdf_dict_get(new PdfObj("Resources")); - if (resources.m_internal == null) - resources = ap.pdf_dict_put_dict(new PdfObj("Resources"), 2); + /// + /// Various information details. + /// + public Dictionary GetInfo() + { + var res = new Dictionary(); - PdfObj alp0 = pageDoc.pdf_new_dict(3); - if (opacity >= 0 && opacity < 1) - { - alp0.pdf_dict_put_real(new PdfObj("CA"), opacity); - alp0.pdf_dict_put_real(new PdfObj("ca"), opacity); - annotObj.pdf_dict_put_real(new PdfObj("CA"), opacity); - } + res["content"] = Contents; - if (!string.IsNullOrEmpty(blendMode)) - { - alp0.pdf_dict_put_name(new PdfObj("BM"), blendMode); - annotObj.pdf_dict_put_name(new PdfObj("BM"), blendMode); - } + var nameObj = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("Name")); + res["name"] = nameObj.m_internal != null ? mupdf.mupdf.pdf_to_name(nameObj) : ""; - PdfObj extg = resources.pdf_dict_get(new PdfObj("ExtGState")); - if (extg.m_internal == null) // no ExtGState yet: make one - extg = resources.pdf_dict_put_dict(new PdfObj("ExtGState"), 2); + // Title (= author) + res["title"] = Author; - extg.pdf_dict_put(new PdfObj("H"), alp0); - } - catch (Exception) - { - Console.WriteLine("cannot set opacity or blend mode"); - } + // CreationDate + res["creationDate"] = CreationDate; - pageDoc.Dispose(); + // ModDate + res["modDate"] = ModDate; - return true; - } + // Subj + var subjObj = mupdf.mupdf.pdf_dict_gets(AnnotObj, "Subj"); + res["subject"] = subjObj.m_internal != null ? mupdf.mupdf.pdf_to_text_string(subjObj) : ""; - public void CleanContents(int sanitize = 1) - { - PdfAnnot annot = _nativeAnnotion; - PdfDocument pdf = annot.pdf_annot_obj().pdf_get_bound_document(); - PdfFilterOptions filter = Utils.MakePdfFilterOptions( - recurse: 1, - instanceForms: 0, - ascii: 0, - sanitize: sanitize - ); - pdf.pdf_filter_annot_contents(annot, filter); - } + // Identification (PDF key /NM) + var nmObj = mupdf.mupdf.pdf_dict_gets(AnnotObj, "NM"); + res["id"] = nmObj.m_internal != null ? mupdf.mupdf.pdf_to_text_string(nmObj) : ""; - public PdfAnnot ToPdfAnnot() - { - return _nativeAnnotion; + return res; } - public static PdfAnnot FindAnnotIRT(PdfAnnot annot) + /// + /// Annotation vertex points. + /// + public List Vertices { - PdfAnnot irtAnnot = null; - PdfObj annotObj = annot.pdf_annot_obj(); - int found = 0; - PdfPage page = annot.pdf_annot_page(); - irtAnnot = page.pdf_first_annot(); - - while (true) + get { - if (irtAnnot.m_internal == null) - { - break; - } + var result = new List(); + var source = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("Vertices")); + if (source.m_internal == null) source = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("L")); + if (source.m_internal == null) source = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("QuadPoints")); + if (source.m_internal == null) source = mupdf.mupdf.pdf_dict_gets(AnnotObj, "CL"); - PdfObj irtAnnotObj = irtAnnot.pdf_annot_obj(); - PdfObj irt = irtAnnotObj.pdf_dict_gets("IRT"); - if (irt.m_internal != null) + if (source.m_internal != null) { - if (irt.pdf_objcmp(annotObj) != 0) + for (int i = 0; i < mupdf.mupdf.pdf_array_len(source); i += 2) { - found = 1; - break; + double x = mupdf.mupdf.pdf_to_real(mupdf.mupdf.pdf_array_get(source, i)); + double y = mupdf.mupdf.pdf_to_real(mupdf.mupdf.pdf_array_get(source, i + 1)); + result.Add(Helpers.TransformPoint(new Point(x, y), VertexMatrix)); } + return result; } - irtAnnot = irtAnnot.pdf_next_annot(); - } - - if (found != 0) - return irtAnnot; - - return null; - } - - public void DeleteResponses() - { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfPage page = annot.pdf_annot_page(); - - while (true) - { - PdfAnnot irtAnnot = FindAnnotIRT(annot); - if (irtAnnot == null) - break; - page.pdf_delete_annot(irtAnnot); - } - annotObj.pdf_dict_del(new PdfObj("Popup")); - PdfObj annots = page.obj().pdf_dict_get(new PdfObj("Annots")); - int n = annots.pdf_array_len(); - int found = 0; - - for (int i = n - 1; i >= 0; i--) - { - PdfObj o = annots.pdf_array_get(i); - PdfObj p = o.pdf_dict_get(new PdfObj("Parent")); - if (o.m_internal == null) - { - continue; - } - if (p.pdf_objcmp(annotObj) == 0) + var inkList = mupdf.mupdf.pdf_dict_gets(AnnotObj, "InkList"); + if (inkList.m_internal != null) { - annots.pdf_array_delete(i); - found = 1; + for (int i = 0; i < mupdf.mupdf.pdf_array_len(inkList); i++) + { + var sub = mupdf.mupdf.pdf_array_get(inkList, i); + for (int j = 0; j < mupdf.mupdf.pdf_array_len(sub); j += 2) + { + double x = mupdf.mupdf.pdf_to_real(mupdf.mupdf.pdf_array_get(sub, j)); + double y = mupdf.mupdf.pdf_to_real(mupdf.mupdf.pdf_array_get(sub, j + 1)); + result.Add(Helpers.TransformPoint(new Point(x, y), VertexMatrix)); + } + } } + return result; } - - if (found != 0) - page.obj().pdf_dict_put(new PdfObj("Annots"), annots); } - public FzBuffer GetFile() - { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfAnnotType type = (PdfAnnotType)annot.pdf_annot_type(); - if (type != PdfAnnotType.PDF_ANNOT_FILE_ATTACHMENT) - { - throw new Exception("bad annot type"); - } - - PdfObj stream = Utils.pdf_dict_getl(annotObj, new string[] { "FS", "EF", "F" }); - if (stream.m_internal == null) + /// + /// Line annotation endpoints. + /// + public (Point, Point) Line + { + get { - throw new Exception("bad PDF: file entry not found"); + var p1 = new mupdf.FzPoint(); + var p2 = new mupdf.FzPoint(); + NativeAnnot.pdf_annot_line(p1, p2); + return (Helpers.TransformPoint(new Point(p1), VertexMatrix), Helpers.TransformPoint(new Point(p2), VertexMatrix)); } - - FzBuffer buf = stream.pdf_load_stream(); - - return buf; } - public int GetOC() + /// + /// Set line endpoints. + /// + public void SetLine(Point p1, Point p2) { - int oc = 0; - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfObj obj = annotObj.pdf_dict_get(new PdfObj("OC")); - if (obj.m_internal != null) - oc = obj.pdf_to_num(); - - return oc; + var rp1 = Helpers.TransformPoint(new Point(p1), RotatePageMatrix); + var rp2 = Helpers.TransformPoint(new Point(p2), RotatePageMatrix); + mupdf.mupdf.pdf_set_annot_line(NativeAnnot, rp1.ToFzPoint(), rp2.ToFzPoint()); } - public Pixmap GetPixmap( - Matrix matrix = null, - int dpi = 0, - ColorSpace colorSpace = null, - int alpha = 0 - ) + /// + /// Set annotation vertices. + /// + public void SetVertices(Point[] points) { - List colorSpaces = new List() + mupdf.mupdf.pdf_clear_annot_vertices(NativeAnnot); + foreach (var p in points) { - new ColorSpace(Utils.CS_GRAY), - new ColorSpace(Utils.CS_RGB), - new ColorSpace(Utils.CS_CMYK), - }; - - if (dpi != 0) - matrix = new Matrix(dpi / 72, dpi / 72); - - FzMatrix ctm = matrix == null ? new FzMatrix() : matrix.ToFzMatrix(); - FzColorspace cs = null; - if (colorSpace == null) - cs = mupdf.mupdf.fz_device_rgb(); - else - cs = colorSpace.ToFzColorspace(); - - FzPixmap pix = mupdf.mupdf.pdf_new_pixmap_from_annot( - _nativeAnnotion, - ctm, - cs, - new FzSeparations(0), - alpha - ); - - Pixmap ret = new Pixmap(pix); - if (dpi != 0) - ret.SetDpi(dpi, dpi); - - return ret; + var rp = Helpers.TransformPoint(new Point(p), RotatePageMatrix); + mupdf.mupdf.pdf_add_annot_vertex(NativeAnnot, rp.ToFzPoint()); + } } - public Sound GetSound() + /// + /// Update annot appearance. + /// + /// Notes: + /// Depending on the annot type, some parameters make no sense, + /// while others are only available in this method to achieve the + /// desired result. This is especially true for 'FreeText' annots. + /// + /// Args: + /// blend_mode: set the blend mode, all annotations. + /// opacity: set the opacity, all annotations. + /// fontsize: set fontsize, 'FreeText' only. + /// fontname: set the font, 'FreeText' only. + /// border_color: set border color, 'FreeText' only. + /// text_color: set text color, 'FreeText' only. + /// fill_color: set fill color, all annotations. + /// cross_out: draw diagonal lines, 'Redact' only. + /// rotate: set rotation, 'FreeText' and some others. + /// + public void Update( + string? blendMode = null, + float? opacity = null, + float fontsize = 0, + string? fontname = null, + float[]? textColor = null, + float[]? borderColor = null, + float[]? fillColor = null, + bool crossOut = true, + int rotate = -1) { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfAnnotType type = (PdfAnnotType)annot.pdf_annot_type(); - PdfObj sound = annotObj.pdf_dict_get(new PdfObj("Sound")); - - if (type != PdfAnnotType.PDF_ANNOT_SOUND || sound.m_internal == null) + var annotObj = AnnotObj; + if (borderColor != null) { - throw new Exception(Utils.ErrorMessages["MSG_BAD_ANNOT_TYPE"]); + var isRichText = mupdf.mupdf.pdf_dict_get(annotObj, mupdf.mupdf.pdf_new_name("RC")); + if (isRichText.m_internal == null) + throw new ArgumentException("cannot set border_color if rich_text is False"); } + UpdateTimingTest(); - if (sound.pdf_dict_get(new PdfObj("F")).m_internal != null) + var annotType = Type; + var border = Border; + var dashes = border.TryGetValue("dashes", out var dashValue) ? dashValue as int[] : null; + float borderWidth = border.TryGetValue("width", out var widthValue) && widthValue != null ? Convert.ToSingle(widthValue) : -1; + float[] stroke = Colors; + float[] fill = fillColor ?? InteriorColor; + Matrix apnMatrix = ApnMatrix; + Rect rect = null; + + if (rotate != -1) { - throw new Exception("Unsupported Sound Stream"); + while (rotate < 0) rotate += 360; + while (rotate >= 360) rotate -= 360; + if (annotType == AnnotationType.FreeText && rotate % 90 != 0) + rotate = 0; } - Sound ret = new Sound(); - PdfObj obj = sound.pdf_dict_get(new PdfObj("R")); - if (obj.m_internal != null) - ret.Rate = obj.pdf_to_real(); - obj = sound.pdf_dict_get(new PdfObj("C")); - if (obj.m_internal != null) - ret.Channels = obj.pdf_to_int(); - obj = sound.pdf_dict_get(new PdfObj("B")); - if (obj.m_internal != null) - ret.Bps = obj.pdf_to_int(); - obj = sound.pdf_dict_get(new PdfObj("E")); - if (obj.m_internal != null) - ret.Encoding = obj.pdf_to_name(); - obj = sound.pdf_dict_gets("CO"); - if (obj.m_internal != null) - ret.Compression = obj.pdf_to_name(); - - FzBuffer buf = sound.pdf_load_stream(); - byte[] stream = Utils.BinFromBuffer(buf); - ret.Stream = stream; + string effectiveBlendMode = blendMode ?? BlendMode; + float effectiveOpacity = opacity ?? Opacity; + string opaCode = (effectiveOpacity >= 0 && effectiveOpacity < 1) || !string.IsNullOrEmpty(effectiveBlendMode) + ? "/H gs\n" // Python: "then we must reference this 'gs'" + : ""; - return ret; - } - - public TextPage GetTextPage(Rect clip = null, int flags = 0) - { - FzStextOptions options = new FzStextOptions(); - PdfAnnot annot = _nativeAnnotion; - options.flags = flags; - FzStextPage stPage = new FzStextPage(annot, options); - - return new TextPage(stPage); - } - - public int IrtXref - { - get + if (annotType == AnnotationType.FreeText) { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfObj irt = annotObj.pdf_dict_get(new PdfObj("IRT")); - if (irt == null) - return 0; - return irt.pdf_to_num(); + Helpers.CheckColor(textColor); + Helpers.CheckColor(fillColor); + var (tcol, fname, fsize) = Helpers.ParseAnnotDefaultAppearance(NativeAnnot); + if (fsize <= 0) fsize = 12; + if (textColor != null) tcol = textColor; + if (!string.IsNullOrEmpty(fontname)) fname = fontname; + if (fontsize > 0) fsize = fontsize; + Helpers.JM_make_annot_DA(NativeAnnot, tcol?.Length ?? 0, tcol ?? Array.Empty(), fname, fsize); + effectiveBlendMode = null; // Python: not supported for FreeText. } - } - - private void SetApnBbox(Rect bbox) - { - Page page = Parent; - Matrix rot = page.DerotationMatrix; - Matrix mat = page.TransformationMatrix; - bbox = bbox * (rot * ~mat); - - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfObj ap = Utils.pdf_dict_getl(annotObj, new string[] { "AP", "N" }); - - if (ap.m_internal == null) - throw new Exception(Utils.ErrorMessages["MSG_BAD_APN"]); - - ap.pdf_dict_put_rect(new PdfObj("BBox"), bbox.ToFzRect()); - } - - private void SetApnMatrix(Matrix matrix) - { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfObj ap = Utils.pdf_dict_getl(annotObj, new string[] { "AP", "N" }); - - if (ap.m_internal == null) - throw new Exception(Utils.ErrorMessages["MSG_BAD_APN"]); - - ap.pdf_dict_put_matrix(new PdfObj("Matrix"), matrix.ToFzMatrix()); - } - - public void SetBlendMode(string blendMode) - { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - annotObj.pdf_dict_put_name(new PdfObj("BM"), blendMode); - } - public void SetBorder( - Border border = null, - float width = -1, - string style = null, - int[] dashes = null, - int clouds = -1 - ) - { - (PdfAnnotType atype, string atname, string _) = this.Type; + bool val = _update_appearance(effectiveOpacity, effectiveBlendMode, fill, rotate, annotType); + if (!val) + throw new InvalidOperationException("Error updating annotation."); - if ( - !( - new List() - { - PdfAnnotType.PDF_ANNOT_CIRCLE, - PdfAnnotType.PDF_ANNOT_FREE_TEXT, - PdfAnnotType.PDF_ANNOT_INK, - PdfAnnotType.PDF_ANNOT_LINE, - PdfAnnotType.PDF_ANNOT_POLY_LINE, - PdfAnnotType.PDF_ANNOT_POLYGON, - PdfAnnotType.PDF_ANNOT_SQUARE - } - ).Contains(atype) - ) - { - Console.WriteLine(string.Format("Cannot set cloudy border for '{0}'.", atname)); + // Python: "read contents as created by MuPDF" + byte[] ap = _getAP(); + if (ap == null || ap.Length == 0) return; - } - if ( - !( - new List() - { - PdfAnnotType.PDF_ANNOT_CIRCLE, - PdfAnnotType.PDF_ANNOT_FREE_TEXT, - PdfAnnotType.PDF_ANNOT_POLYGON, - PdfAnnotType.PDF_ANNOT_SQUARE - } - ).Contains(atype) - ) + if (annotType == AnnotationType.FreeText) { - if (clouds > 0) - { - Console.WriteLine(string.Format("Cannot set cloudy border for '{0}'.", atname)); - clouds = -1; - } - } - - Border border_ = new Border(); - if (border == null) - { - border_.Width = width; - border_.Style = style; - border_.Dashes = dashes; - border_.Clouds = clouds; + string apPrefixCheck = Encoding.UTF8.GetString(ap); + if (effectiveOpacity >= 0 && effectiveOpacity < 1 + && !apPrefixCheck.StartsWith("/H gs", StringComparison.Ordinal)) + _setAP(Encoding.UTF8.GetBytes("/H gs\n").Concat(ap).ToArray()); + return; } - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfDocument pdf = annotObj.pdf_get_bound_document(); - SetBorderAnnot(border_, pdf, annotObj); - } - - public static Border GetBorderFromAnnot(PdfObj annotObj) - { - List dashes = new List(); - string style = ""; - float width = -1.0f; - int clouds = -1; - PdfObj obj = annotObj.pdf_dict_get(new PdfObj("Border")); + string apText = Encoding.UTF8.GetString(ap); + bool apUpdated = false; - if (obj.pdf_is_array() != 0) - { - width = obj.pdf_array_get(2).pdf_to_real(); - if (obj.pdf_array_len() == 4) - { - PdfObj dash = obj.pdf_array_get(3); - for (int i = 0; i < dash.pdf_array_len(); i++) - { - int v = dash.pdf_array_get(i).pdf_to_int(); - dashes.Add(v); - } + if (annotType == AnnotationType.Redact && crossOut) + { + // Python: create crossed-out rect for redact annotations. + var lines = new List(apText.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None)); + if (lines.Count >= 6) + { + int n = lines.Count; + string ll = lines[n - 4]; + string lr = lines[n - 3]; + string ur = lines[n - 2]; + string ul = lines[n - 5]; + lines.RemoveAt(lines.Count - 1); + lines.Add(lr); + lines.Add(ll); + lines.Add(ur); + lines.Add(ll); + lines.Add(ul); + lines.Add("S"); + apText = string.Join("\n", lines); + apUpdated = true; } } - PdfObj bsObj = annotObj.pdf_dict_get(new PdfObj("BS")); - if (bsObj.m_internal != null) + string fillCode = Helpers.ColorCode(fill, "f"); + string strokeCode = Helpers.ColorCode(stroke, "c"); + if (dashes != null && dashes.Length > 0) { - width = bsObj.pdf_dict_get(new PdfObj("W")).pdf_to_real(); - style = bsObj.pdf_dict_get(new PdfObj("S")).pdf_to_name(); - - if (style == "") - style = null; - obj = bsObj.pdf_dict_get(new PdfObj("D")); - if (obj != null) - for (int i = 0; i < obj.pdf_array_len(); i++) - { - int v = obj.pdf_array_get(i).pdf_to_int(); - dashes.Add(v); - } + // Python: handle dashes and reset where appropriate. + apText = "[" + string.Join(" ", dashes) + "] 0 d\n" + apText; + apText = apText.Replace("\nS\n", "\nS\n[] 0 d\n"); + apUpdated = true; } - - obj = annotObj.pdf_dict_get(new PdfObj("BE")); - if (obj.m_internal != null) - clouds = obj.pdf_dict_get(new PdfObj("I")).pdf_to_int(); - - Border ret = new Border(); - ret.Width = width; - ret.Dashes = dashes.ToArray(); - ret.Style = style; - ret.Clouds = clouds; - - return ret; - } - - public static Color GetColorFromAnnot(PdfObj annotObj) - { - Color ret = new Color(); - List bc = new List(); - List fc = new List(); - - PdfObj obj = annotObj.pdf_dict_get(new PdfObj("C")); - if (obj.pdf_is_array() != 0) + if (!string.IsNullOrEmpty(opaCode)) { - int n = obj.pdf_array_len(); - for (int i = 0; i < n; i++) - { - float col = obj.pdf_array_get(i).pdf_to_real(); - bc.Add(col); - } + apText = opaCode + apText; + apUpdated = true; } - ret.Stroke = bc.ToArray(); - obj = annotObj.pdf_dict_gets("IC"); - if (obj.pdf_is_array() != 0) + if (annotType == AnnotationType.Polygon || annotType == AnnotationType.PolyLine) { - int n = obj.pdf_array_len(); - for (int i = 0; i < n; i++) - { - float col = obj.pdf_array_get(i).pdf_to_real(); - fc.Add(col); - } + var tab = apText.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None); + if (tab.Length > 0) + apText = string.Join("\n", tab.Take(tab.Length - 1)) + "\n"; + if (!string.IsNullOrEmpty(fillCode)) + apText += fillCode + (annotType == AnnotationType.Polygon ? "b" : "S"); + else + apText += annotType == AnnotationType.Polygon ? "s" : "S"; + apUpdated = true; } - ret.Fill = fc.ToArray(); - - return ret; - } - - public static PdfObj GetBorderStyle(string s) - { - PdfObj val = new PdfObj("S"); - if (string.IsNullOrEmpty(s)) - return val; - - if (s.StartsWith("b") || s.StartsWith("B")) - val = new PdfObj("B"); - else if (s.StartsWith("d") || s.StartsWith("D")) - val = new PdfObj("D"); - else if (s.StartsWith("i") || s.StartsWith("I")) - val = new PdfObj("I"); - else if (s.StartsWith("u") || s.StartsWith("U")) - val = new PdfObj("U"); - else if (s.StartsWith("s") || s.StartsWith("S")) - val = new PdfObj("S"); - return val; - } - - public static void SetBorderAnnot(Border border, PdfDocument doc, PdfObj annotObj) - { - int dashLen = 0; - float nWidth = border.Width; - int[] nDashes = border.Dashes; - string nStyle = border.Style; - float nClouds = border.Clouds; - - Border oldBorder = GetBorderFromAnnot(annotObj); - - annotObj.pdf_dict_del(new PdfObj("BS")); - annotObj.pdf_dict_del(new PdfObj("BE")); - annotObj.pdf_dict_del(new PdfObj("Border")); - - if (nWidth < 0) - nWidth = oldBorder.Width; - if (nDashes == null) - nDashes = oldBorder.Dashes; - if (nStyle == null) - nStyle = oldBorder.Style; - if (nClouds < 0) - nClouds = oldBorder.Clouds; - - if (nDashes != null && nDashes.Length > 0) + if (annotType == AnnotationType.Redact && (borderWidth > 0 || !string.IsNullOrEmpty(strokeCode))) { - dashLen = nDashes.Length; - PdfObj darr = doc.pdf_new_array(dashLen); - foreach (int b in nDashes) + var rebuilt = new StringBuilder(); + if (borderWidth > 0) + rebuilt.AppendLine(borderWidth.ToString("G", System.Globalization.CultureInfo.InvariantCulture) + " w"); + foreach (var line in apText.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None)) { - darr.pdf_array_push_int(b); + if (line.EndsWith("w", StringComparison.Ordinal)) continue; + rebuilt.AppendLine(line.EndsWith("RG", StringComparison.Ordinal) && !string.IsNullOrEmpty(strokeCode) + ? strokeCode.TrimEnd() + : line); } - Utils.pdf_dict_putl(annotObj, darr, new string[] { "BS", "D" }); - } - - Utils.pdf_dict_putl( - annotObj, - mupdf.mupdf.pdf_new_real(nWidth), - new string[] { "BS", "W" } - ); - - PdfObj obj = null; - if (dashLen == 0) - obj = GetBorderStyle(nStyle); - else - obj = new PdfObj("D"); - - Utils.pdf_dict_putl(annotObj, obj, new string[] { "BS", "S" }); - - if (nClouds > 0) - { - annotObj.pdf_dict_put_dict(new PdfObj("BE"), 2); - obj = annotObj.pdf_dict_get(new PdfObj("BE")); - obj.pdf_dict_put(new PdfObj("S"), new PdfObj("C")); - obj.pdf_dict_put_int(new PdfObj("I"), (long)nClouds); + apText = rebuilt.ToString(); + apUpdated = true; } - } - public void SetColors(Color colors = null, float[] stroke = null, float[] fill = null) - { - pdf_annot_type type = _nativeAnnotion.pdf_annot_type(); - if (type == pdf_annot_type.PDF_ANNOT_FREE_TEXT) + if (apUpdated) { - throw new Exception("cannot be used for FreeText annotations"); + byte[] updated = Encoding.UTF8.GetBytes("q\n" + apText.TrimEnd() + "\nQ\n"); + _setAP(updated, rect != null ? 1 : 0); } - Document doc = Parent.Parent; - Color colors_ = new Color(); - if (colors == null) - { - colors_.Fill = fill; - colors_.Stroke = stroke; - } - - fill = colors_.Fill; - stroke = colors_.Stroke; + //------------------------------- + // handle annotation rotations + //------------------------------- + if (annotType != AnnotationType.Caret + && annotType != AnnotationType.Circle + && annotType != AnnotationType.FileAttachment + && annotType != AnnotationType.Ink + && annotType != AnnotationType.Line + && annotType != AnnotationType.PolyLine + && annotType != AnnotationType.Polygon + && annotType != AnnotationType.Square + && annotType != AnnotationType.Stamp + && annotType != AnnotationType.Text) + return; - List fillAnnots = new List() - { - PdfAnnotType.PDF_ANNOT_CIRCLE, - PdfAnnotType.PDF_ANNOT_SQUARE, - PdfAnnotType.PDF_ANNOT_LINE, - PdfAnnotType.PDF_ANNOT_POLY_LINE, - PdfAnnotType.PDF_ANNOT_POLYGON, - PdfAnnotType.PDF_ANNOT_REDACT - }; + int rot = Rotation; // get value from annot object + if (rot == -1) // nothing to change + return; - if (stroke != null && stroke.Length == 0) + var center = (Rect.TL + Rect.BR) / 2.0; // center of annot rect + if (rot == 0) // undo rotations { - doc.SetKeyXRef(this.Xref, "C", "[]"); - } - else if (stroke != null) - { - string s = ""; - if (stroke.Length == 1) - s = string.Format(System.Globalization.CultureInfo.InvariantCulture, "[{0}]", Utils.FloatToString(stroke[0])); - if (stroke.Length == 3) - s = string.Format(System.Globalization.CultureInfo.InvariantCulture, "[{0} {1} {2}]", Utils.FloatToString(stroke[0]), Utils.FloatToString(stroke[1]), Utils.FloatToString(stroke[2])); - else - s = string.Format( - System.Globalization.CultureInfo.InvariantCulture, - "[{0} {1} {2} {3}]", - Utils.FloatToString(stroke[0]), - Utils.FloatToString(stroke[1]), - Utils.FloatToString(stroke[2]), - Utils.FloatToString(stroke[3]) - ); - doc.SetKeyXRef(this.Xref, "C", s); - } - - if (fill != null && !fillAnnots.Contains(this.Type.Item1)) - { - Console.WriteLine( - string.Format( - "Warning: fill color ignored for annot type '{0}'", - this.Type.Item1 - ) - ); + if (apnMatrix == Matrix.Identity) + return; // matrix already is a no-op + var quad = Rect.Morph(center, apnMatrix.Inverted() ?? Matrix.Identity); // derotate rect + SetRect(new Rect(quad.Rect)); + SetApnMatrix(Matrix.Identity); // appearance matrix = no-op return; } - if (fill != null && fill.Length == 0) - doc.SetKeyXRef(this.Xref, "IC", "[]"); - else if (fill != null) - { - string s = ""; - if (fill.Length == 1) - { - s = string.Format(System.Globalization.CultureInfo.InvariantCulture, "[{0}]", Utils.FloatToString(fill[0])); - } - else if (fill.Length == 3) - { - s = string.Format(System.Globalization.CultureInfo.InvariantCulture, "[{0} {1} {2}]", Utils.FloatToString(fill[0]), Utils.FloatToString(fill[1]), Utils.FloatToString(fill[2])); - } - else - { - s = string.Format(System.Globalization.CultureInfo.InvariantCulture, "[{0} {1} {2} {3}]", Utils.FloatToString(fill[0]), Utils.FloatToString(fill[1]), Utils.FloatToString(fill[2]), Utils.FloatToString(fill[3])); - } - doc.SetKeyXRef(this.Xref, "IC", s); - } + var mat = Matrix.Rotation(rot); + var rotated = Rect.Morph(center, mat); + SetRect(new Rect(rotated.Rect)); + SetApnMatrix(apnMatrix * mat); } - public void SetFlags(int flags) + private bool _update_appearance(float opacity, string blendMode, float[] fillColor, int rotate, AnnotationType annotType) { - PdfAnnot annot = _nativeAnnotion; - mupdf.mupdf.pdf_set_annot_flags(annot, flags); - } + var annotObj = AnnotObj; + int nFill = fillColor?.Length ?? 0; - /// - /// Set various properties. - /// - /// a dictionary compatible with the info property - /// a string containing the text for type Text and FreeText annotations. Commonly used for filling the text field of annotation pop-up windows. - /// a string containing the title of the annotation pop-up window. By convention, this is used for the annotation author. - /// creation timestamp. - /// last modified timestamp. - /// subject. - public void SetInfo( - AnnotInfo info = null, - string content = null, - string title = null, - string creationDate = null, - string modDate = null, - string subject = null - ) - { - if (info != null) + bool supportsInterior = annotType == AnnotationType.Square + || annotType == AnnotationType.Circle + || annotType == AnnotationType.Line + || annotType == AnnotationType.PolyLine + || annotType == AnnotationType.Polygon; + if (nFill == 0 || !supportsInterior) + mupdf.mupdf.pdf_dict_del(annotObj, mupdf.mupdf.pdf_new_name("IC")); + else if (annotType == AnnotationType.FreeText) { - content = info.Content; - title = info.Title; - creationDate = info.CreationDate; - modDate = info.ModDate; - subject = info.Subject; - info = null; + var col = mupdf.mupdf.pdf_new_array(ParentPdfDocument, nFill); + for (int i = 0; i < nFill; i++) + mupdf.mupdf.pdf_array_push_real(col, fillColor[i]); + mupdf.mupdf.pdf_dict_put(annotObj, mupdf.mupdf.pdf_new_name("C"), col); } - - PdfAnnot annot = _nativeAnnotion; - bool isMarkup = Convert.ToBoolean(annot.pdf_annot_has_author()); - - if (!string.IsNullOrEmpty(content)) - annot.pdf_set_annot_contents(content); - if (isMarkup) + else { - if (!string.IsNullOrEmpty(title)) - annot.pdf_set_annot_author(title); - if (!string.IsNullOrEmpty(creationDate)) - annot - .pdf_annot_obj() - .pdf_dict_put_text_string(new PdfObj("CreationDate"), creationDate); + var col = mupdf.mupdf.pdf_new_array(ParentPdfDocument, nFill); + for (int i = 0; i < nFill; i++) + mupdf.mupdf.pdf_array_push_real(col, fillColor[i]); + mupdf.mupdf.pdf_dict_put(annotObj, mupdf.mupdf.pdf_new_name("IC"), col); + } + + bool insertRot = rotate >= 0; + if (annotType != AnnotationType.Caret + && annotType != AnnotationType.Circle + && annotType != AnnotationType.FreeText + && annotType != AnnotationType.FileAttachment + && annotType != AnnotationType.Ink + && annotType != AnnotationType.Line + && annotType != AnnotationType.PolyLine + && annotType != AnnotationType.Polygon + && annotType != AnnotationType.Square + && annotType != AnnotationType.Stamp + && annotType != AnnotationType.Text) + insertRot = false; + if (insertRot) + mupdf.mupdf.pdf_dict_put_int(annotObj, mupdf.mupdf.pdf_new_name("Rotate"), rotate); + + mupdf.mupdf.pdf_dirty_annot(NativeAnnot); + mupdf.mupdf.pdf_update_annot(NativeAnnot); + + if ((opacity < 0 || opacity >= 1) && string.IsNullOrEmpty(blendMode)) + return true; - if (!string.IsNullOrEmpty(modDate)) - annot.pdf_annot_obj().pdf_dict_put_text_string(new PdfObj("M"), modDate); + var ap = Helpers.PdfDictGetl(annotObj, mupdf.mupdf.pdf_new_name("AP"), mupdf.mupdf.pdf_new_name("N")); + if (ap.m_internal == null) return true; + var resources = mupdf.mupdf.pdf_dict_get(ap, mupdf.mupdf.pdf_new_name("Resources")); + if (resources.m_internal == null) + resources = mupdf.mupdf.pdf_dict_put_dict(ap, mupdf.mupdf.pdf_new_name("Resources"), 2); - if (!string.IsNullOrEmpty(subject)) - annot - .pdf_annot_obj() - .pdf_dict_puts("Subj", mupdf.mupdf.pdf_new_text_string(subject)); + var alp0 = mupdf.mupdf.pdf_new_dict(ParentPdfDocument, 3); + if (opacity >= 0 && opacity < 1) + { + mupdf.mupdf.pdf_dict_put_real(alp0, mupdf.mupdf.pdf_new_name("CA"), opacity); + mupdf.mupdf.pdf_dict_put_real(alp0, mupdf.mupdf.pdf_new_name("ca"), opacity); + mupdf.mupdf.pdf_dict_put_real(annotObj, mupdf.mupdf.pdf_new_name("CA"), opacity); + } + if (!string.IsNullOrEmpty(blendMode)) + { + mupdf.mupdf.pdf_dict_put_name(alp0, mupdf.mupdf.pdf_new_name("BM"), blendMode); + mupdf.mupdf.pdf_dict_put_name(annotObj, mupdf.mupdf.pdf_new_name("BM"), blendMode); } + var extg = mupdf.mupdf.pdf_dict_get(resources, mupdf.mupdf.pdf_new_name("ExtGState")); + if (extg.m_internal == null) + extg = mupdf.mupdf.pdf_dict_put_dict(resources, mupdf.mupdf.pdf_new_name("ExtGState"), 2); + mupdf.mupdf.pdf_dict_put(extg, mupdf.mupdf.pdf_new_name("H"), alp0); + return true; } - public void SetIrtXref(int xref) + private static void UpdateTimingTest() { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfPage page = annot.pdf_annot_page(); - - PdfDocument pageDoc = page.doc(); - if (xref < 1 || xref >= pageDoc.pdf_xref_len()) - throw new Exception(Utils.ErrorMessages["MSG_BAD_XREF"]); - - PdfObj irt = pageDoc.pdf_new_indirect(xref, 0); - PdfObj subt = irt.pdf_dict_get(new PdfObj("Subtype")); - PdfAnnotType irtSubt = (PdfAnnotType) - mupdf.mupdf.pdf_annot_type_from_string(subt.pdf_to_name()); - if ((int)irtSubt < 0) - throw new Exception(Utils.ErrorMessages["MSG_IS_NO_ANNOT"]); - - annotObj.pdf_dict_put(new PdfObj("IRT"), irt); - - pageDoc.Dispose(); + int total = 0; + for (int i = 0; i < 30 * 1000; i++) total += i; } - public void SetLanguage(string language) + /// + /// Annotation Pixmap. + /// + public Pixmap GetPixmap(Matrix matrix = null, Colorspace cs = null, bool alpha = false) { - PdfAnnot annot = _nativeAnnotion; - TextLanguage lang; - if (language == null) - lang = TextLanguage.FZ_LANG_UNSET; - else - lang = (TextLanguage)mupdf.mupdf.fz_text_language_from_string(language); - - annot.pdf_set_annot_language((fz_text_language)lang); + var ctm = (matrix ?? Matrix.Identity).ToFzMatrix(); + var colorspace = (cs ?? Colorspace.CsRGB).ToFzColorspace(); + var pix = mupdf.mupdf.pdf_new_pixmap_from_annot(NativeAnnot, ctm, colorspace, new mupdf.FzSeparations(), alpha ? 1 : 0); + return new Pixmap(pix); } - public void SetLineEnds(PdfLineEnding start, PdfLineEnding end) + /// + /// Make annotation TextPage. + /// + public TextPage GetTextPage(int flags = 0) { - PdfAnnot annot = _nativeAnnotion; - if (annot.pdf_annot_has_line_ending_styles() != 0) - { - annot.pdf_set_annot_line_ending_styles( - (pdf_line_ending)start, - (pdf_line_ending)end - ); - } - else - Console.WriteLine("bad annot type for line ends"); + var opts = new mupdf.fz_stext_options(); + opts.flags = flags; + var stp = new mupdf.FzStextPage(NativeAnnot, new mupdf.FzStextOptions(opts)); + return new TextPage(stp); } - public void SetName(string name) + /// + /// Sound attachment data for sound annotations. + /// + public byte[] GetSoundData() { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - annotObj.pdf_dict_put_name(new PdfObj("Name"), name); + var sound = GetSound(); + return sound != null && sound.TryGetValue("stream", out var stream) ? (byte[])stream : null; } - public void SetOC(int oc = 0) + public Dictionary GetSound() { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - if (oc == 0) - annotObj.pdf_dict_del(new PdfObj("OC")); - else - AddOCObject(annotObj.pdf_get_bound_document(), annotObj, oc); + if (Type != AnnotationType.Sound) + throw new ArgumentException(Constants.MSG_BAD_ANNOT_TYPE); + var sound = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("Sound")); + if (sound.m_internal == null) + throw new ArgumentException(Constants.MSG_BAD_ANNOT_TYPE); + if (mupdf.mupdf.pdf_dict_get(sound, mupdf.mupdf.pdf_new_name("F")).m_internal != null) + throw new InvalidOperationException("unsupported sound stream"); + + var result = new Dictionary(); + var rate = mupdf.mupdf.pdf_dict_get(sound, mupdf.mupdf.pdf_new_name("R")); + if (rate.m_internal != null) result["rate"] = mupdf.mupdf.pdf_to_real(rate); + var channels = mupdf.mupdf.pdf_dict_get(sound, mupdf.mupdf.pdf_new_name("C")); + if (channels.m_internal != null) result["channels"] = mupdf.mupdf.pdf_to_int(channels); + var bps = mupdf.mupdf.pdf_dict_get(sound, mupdf.mupdf.pdf_new_name("B")); + if (bps.m_internal != null) result["bps"] = mupdf.mupdf.pdf_to_int(bps); + var encoding = mupdf.mupdf.pdf_dict_get(sound, mupdf.mupdf.pdf_new_name("E")); + if (encoding.m_internal != null) result["encoding"] = mupdf.mupdf.pdf_to_name(encoding); + var compression = mupdf.mupdf.pdf_dict_gets(sound, "CO"); + if (compression.m_internal != null) result["compression"] = mupdf.mupdf.pdf_to_name(compression); + result["stream"] = Helpers.BufferToBytes(mupdf.mupdf.pdf_load_stream(sound)); + return result; } - public static void AddOCObject(PdfDocument doc, PdfObj _ref, int xref) + /// + /// Set / remove annotation optional content reference. + /// + public void SetOC(int oc) { - PdfObj indObj = doc.pdf_new_indirect(xref, 0); - if (indObj.pdf_is_dict() == 0) - throw new Exception(Utils.ErrorMessages["MSG_BAD_OC_REF"]); - - PdfObj type = indObj.pdf_dict_get(new PdfObj("Type")); - if (type.pdf_objcmp(new PdfObj("OCG")) == 0 || type.pdf_objcmp(new PdfObj("OCMD")) == 0) + var obj = mupdf.mupdf.pdf_annot_obj(NativeAnnot); + if (oc <= 0) + mupdf.mupdf.pdf_dict_dels(obj, "OC"); + else { - _ref.pdf_dict_put(new PdfObj("OC"), indObj); + var pdf = Parent.Parent.NativePdfDocument; + Helpers.JM_add_oc_object(pdf, obj, oc); } - else - throw new Exception(Utils.ErrorMessages["MSG_BAD_OC_REF"]); } - public void SetOpacity(float opacity) + /// + /// Delete popup and response annotations. + /// + public void DeleteResponses() { - PdfAnnot annot = _nativeAnnotion; - if (!Utils.INRANGE(opacity, 0.0f, 1.0f)) + var page = mupdf.mupdf.pdf_annot_page(NativeAnnot); + while (true) { - annot.pdf_set_annot_opacity(1); - return; + var irtAnnot = Helpers.JM_find_annot_irt(NativeAnnot); + if (irtAnnot.m_internal == null) break; + mupdf.mupdf.pdf_delete_annot(page, irtAnnot); } + mupdf.mupdf.pdf_dict_del(AnnotObj, mupdf.mupdf.pdf_new_name("Popup")); - annot.pdf_set_annot_opacity(opacity); - if (opacity < 1.0f) + // Python: also scan /Annots and remove entries whose /Parent is this annot. + var annots = mupdf.mupdf.pdf_dict_get(page.obj(), mupdf.mupdf.pdf_new_name("Annots")); + int n = mupdf.mupdf.pdf_array_len(annots); + bool found = false; + for (int i = n - 1; i >= 0; i--) { - PdfPage page = annot.pdf_annot_page(); - page.m_internal.transparency = 1; + var o = mupdf.mupdf.pdf_array_get(annots, i); + if (o.m_internal == null) continue; + var p = mupdf.mupdf.pdf_dict_get(o, mupdf.mupdf.pdf_new_name("Parent")); + if (mupdf.mupdf.pdf_objcmp(p, AnnotObj) == 0) + { + mupdf.mupdf.pdf_array_delete(annots, i); + found = true; + } } + if (found) + mupdf.mupdf.pdf_dict_put(page.obj(), mupdf.mupdf.pdf_new_name("Annots"), annots); } - public void SetOpen(int isOpen) + /// + /// Get annotation optional content reference. + /// + public int GetOC() { - PdfAnnot annot = _nativeAnnotion; - annot.pdf_set_annot_is_open(isOpen); + var obj = mupdf.mupdf.pdf_annot_obj(NativeAnnot); + var oc = mupdf.mupdf.pdf_dict_gets(obj, "OC"); + return oc.m_internal != null ? mupdf.mupdf.pdf_to_num(oc) : 0; } - public void SetPopup(Rect rect) - { - PdfAnnot annot = _nativeAnnotion; - PdfPage page = annot.pdf_annot_page(); - Matrix rot = Utils.RotatePageMatrix(page); - - FzRect r = rect.ToFzRect().fz_transform_rect(rot.ToFzMatrix()); - annot.pdf_set_annot_popup(r); - } + /// + /// Normal appearance stream text. + /// + public string GetAPNormal() => GetAP("N"); + /// + /// Rollover appearance stream text. + /// + public string GetAPRollover() => GetAP("R"); + /// + /// Down appearance stream text. + /// + public string GetAPDown() => GetAP("D"); - public void SetRect(Rect rect) + /// + /// Get annotation appearance stream. + /// + public string GetAP(string which = "N") { - PdfAnnot annot = _nativeAnnotion; - PdfPage pdfPage = annot.pdf_annot_page(); - Matrix rot = Utils.RotatePageMatrix(pdfPage); - FzRect r = rect.ToFzRect().fz_transform_rect(rot.ToFzMatrix()); + var ap = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("AP")); + if (ap.m_internal == null) return ""; - if (r.fz_is_empty_rect() != 0 || r.fz_is_infinite_rect() != 0) + mupdf.PdfObj apStream; + switch (which.ToUpper()) { - throw new Exception(Utils.ErrorMessages["MSG_BAD_RECT"]); + case "N": apStream = mupdf.mupdf.pdf_dict_get(ap, mupdf.mupdf.pdf_new_name("N")); break; + case "R": apStream = mupdf.mupdf.pdf_dict_get(ap, mupdf.mupdf.pdf_new_name("R")); break; + case "D": apStream = mupdf.mupdf.pdf_dict_get(ap, mupdf.mupdf.pdf_new_name("D")); break; + default: apStream = mupdf.mupdf.pdf_dict_get(ap, mupdf.mupdf.pdf_new_name("N")); break; } + if (apStream.m_internal == null) return ""; + if (mupdf.mupdf.pdf_is_stream(apStream) == 0) return ""; + try { - annot.pdf_set_annot_rect(r); - } - catch (Exception e) - { - //Console.WriteLine(string.Format("cannot set rect: {0}", e)); + var buf = mupdf.mupdf.pdf_load_stream(apStream); + return Helpers.BufferToUtf8(buf); } - finally + catch { - r.Dispose(); + return ""; } } - public Page GetParent() + /// + /// Set annotation appearance stream. + /// + public void SetAP(string which, byte[] buffer) { - Page ret = null; - if (Parent != null) - ret = Parent; - else + if (buffer == null || buffer.Length == 0) return; + var pdf = ParentPdfDocument; + var ap = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("AP")); + if (ap.m_internal == null) { - PdfPage page = _nativeAnnotion.pdf_annot_page(); - PdfDocument pageDoc = page.doc(); - Document doc = (page.m_internal == null) ? null : new Document(pageDoc); - pageDoc.Dispose(); - ret = new Page(page, doc); - - Parent = ret; + ap = mupdf.mupdf.pdf_new_dict(pdf, 3); + mupdf.mupdf.pdf_dict_put(AnnotObj, mupdf.mupdf.pdf_new_name("AP"), ap); } - return ret; - } + var fzBuf = Helpers.BufferFromBytes(buffer); + var stream = mupdf.mupdf.pdf_add_stream(pdf, fzBuf, new mupdf.PdfObj(), 0); - public void SetRotation(int rotate = 0) - { - PdfAnnot annot = _nativeAnnotion; - PdfAnnotType type = (PdfAnnotType)annot.pdf_annot_type(); - if ( - !( - new List() - { - PdfAnnotType.PDF_ANNOT_CARET, - PdfAnnotType.PDF_ANNOT_CIRCLE, - PdfAnnotType.PDF_ANNOT_FREE_TEXT, - PdfAnnotType.PDF_ANNOT_FILE_ATTACHMENT, - PdfAnnotType.PDF_ANNOT_INK, - PdfAnnotType.PDF_ANNOT_LINE, - PdfAnnotType.PDF_ANNOT_POLY_LINE, - PdfAnnotType.PDF_ANNOT_POLYGON, - PdfAnnotType.PDF_ANNOT_SQUARE, - PdfAnnotType.PDF_ANNOT_STAMP, - PdfAnnotType.PDF_ANNOT_TEXT - } - ).Contains((PdfAnnotType)type) - ) + var r = mupdf.mupdf.pdf_annot_rect(NativeAnnot); + var bbox = mupdf.mupdf.pdf_new_array(pdf, 4); + mupdf.mupdf.pdf_array_push_real(bbox, r.x0); + mupdf.mupdf.pdf_array_push_real(bbox, r.y0); + mupdf.mupdf.pdf_array_push_real(bbox, r.x1); + mupdf.mupdf.pdf_array_push_real(bbox, r.y1); + mupdf.mupdf.pdf_dict_put(stream, mupdf.mupdf.pdf_new_name("BBox"), bbox); + mupdf.mupdf.pdf_dict_put(stream, mupdf.mupdf.pdf_new_name("Type"), mupdf.mupdf.pdf_new_name("XObject")); + mupdf.mupdf.pdf_dict_put(stream, mupdf.mupdf.pdf_new_name("Subtype"), mupdf.mupdf.pdf_new_name("Form")); + + switch (which.ToUpper()) { - return; + case "N": mupdf.mupdf.pdf_dict_put(ap, mupdf.mupdf.pdf_new_name("N"), stream); break; + case "R": mupdf.mupdf.pdf_dict_puts(ap, "R", stream); break; + case "D": mupdf.mupdf.pdf_dict_puts(ap, "D", stream); break; + default: mupdf.mupdf.pdf_dict_put(ap, mupdf.mupdf.pdf_new_name("N"), stream); break; } - - int rot = rotate; - while (rot < 0) - rot += 360; - while (rot >= 360) - rot -= 360; - - if (type == PdfAnnotType.PDF_ANNOT_FREE_TEXT && rot % 90 != 0) - rot = 0; - - PdfObj annotObj = annot.pdf_annot_obj(); - annotObj.pdf_dict_put_int(new PdfObj("Rotate"), rot); } /// - /// Update annot appearance. - /// Depending on the annot type, some parameters make no sense, - /// while others are only available in this method to achieve the - /// desired result.This is especially true for 'FreeText' annots. + /// File attachment information for file attachment annotations. /// - /// set the blend mode, all annotations. - /// set the opacity, all annotations. - /// set fontsize, 'FreeText' only. - /// set the font, 'FreeText' only. - /// set text color, 'FreeText' only. - /// set border color, 'FreeText' only. - /// set fill color, all annotations. - /// draw diagonal lines, 'Redact' only. - /// set rotation, 'FreeText' and some others. - /// - public void Update( - string blendMode = null, - float opacity = float.MaxValue, - float fontSize = 0.0f, - string fontName = null, - float[] textColor = null, - float[] borderColor = null, - float[] fillColor = null, - bool crossOut = true, - int rotate = -1 - ) + public Dictionary GetFileInfo() { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - - if (borderColor != null) - { - PdfObj isRichText = annotObj.pdf_dict_get(new PdfObj("RC")); - if (isRichText != null) - throw new Exception("cannot set border_color if rich_text is False"); - } - - PdfAnnotType annotType = Type.Item1; - - int[] dt = Border.Dashes; - float borderWidth = Border.Width; - float[] stroke = Colors.Stroke; - List fill = null; - if (fillColor != null) - fill = new List(fillColor); - else - fill = new List(Colors.Fill); - - Rect rect = null; - Matrix apnMat = ApnMatrix; - if (rotate != -1) - { - while (rotate < 0) - rotate += 360; - while (rotate >= 360) - rotate -= 360; - if (annotType == PdfAnnotType.PDF_ANNOT_FREE_TEXT && rotate % 90 != 0) - rotate = 0; - } + if (Type != AnnotationType.FileAttachment) + throw new ArgumentException(Constants.MSG_BAD_ANNOT_TYPE); + var fs = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("FS")); + if (fs.m_internal == null) return null; - // handle opacity and blend mode - if (string.IsNullOrEmpty(blendMode)) - blendMode = BlendMode; - if (opacity == float.MaxValue) - opacity = Opacity; + var result = new Dictionary(); + var ufnameObj = mupdf.mupdf.pdf_dict_get(fs, mupdf.mupdf.pdf_new_name("UF")); + var fnameObj = mupdf.mupdf.pdf_dict_get(fs, mupdf.mupdf.pdf_new_name("F")); + string filename = ufnameObj.m_internal != null + ? mupdf.mupdf.pdf_to_text_string(ufnameObj) + : fnameObj.m_internal != null ? mupdf.mupdf.pdf_to_text_string(fnameObj) : ""; + result["filename"] = filename; - string opaCode = ""; - if ((opacity >= 0 && opacity < 1) || !string.IsNullOrEmpty(blendMode)) - opaCode = "/H gs\n"; - else - opaCode = ""; + var descObj = mupdf.mupdf.pdf_dict_get(fs, mupdf.mupdf.pdf_new_name("Desc")); + result["desc"] = descObj.m_internal != null ? mupdf.mupdf.pdf_to_text_string(descObj) : null; + result["length"] = -1; + result["size"] = -1; - if (annotType == PdfAnnotType.PDF_ANNOT_FREE_TEXT) + var ef = mupdf.mupdf.pdf_dict_get(fs, mupdf.mupdf.pdf_new_name("EF")); + if (ef.m_internal != null) { - Utils.CheckColor(textColor); - Utils.CheckColor(fillColor); - (List tcol, string fname, float fsize) = ParseData(this); - - bool updateDefaultAppearance = false; - if (fsize <= 0) - { - fsize = 12; - updateDefaultAppearance = true; - } - - if (textColor != null) - { - tcol = new List(textColor); - updateDefaultAppearance = true; - } - - if (fontName != null) - { - fname = fontName; - updateDefaultAppearance = true; - } - - if (fontSize > 0) - { - fsize = fontSize; - updateDefaultAppearance = true; - } - - if (updateDefaultAppearance) + var fStream = mupdf.mupdf.pdf_dict_get(ef, mupdf.mupdf.pdf_new_name("F")); + if (fStream.m_internal != null) { - Utils.MakeAnnotDA(annot, tcol == null ? -1 : tcol.Count, tcol.ToArray(), fname, fsize); - blendMode = null; // not supported for free text annotations! + // Python: expose both stream /Length and /Params /Size. + var lengthObj = mupdf.mupdf.pdf_dict_get(fStream, mupdf.mupdf.pdf_new_name("Length")); + result["length"] = lengthObj.m_internal != null ? mupdf.mupdf.pdf_to_int(lengthObj) : -1; + var sizeObj = mupdf.mupdf.pdf_dict_get(mupdf.mupdf.pdf_dict_get(fStream, mupdf.mupdf.pdf_new_name("Params")), mupdf.mupdf.pdf_new_name("Size")); + result["size"] = sizeObj.m_internal != null ? mupdf.mupdf.pdf_to_int(sizeObj) : -1; } } + return result; + } - // now invoke MuPDF to update the annot appearance - bool res = UpdateAppearance( - opacity: opacity, - blendMode: blendMode, - fillColor: fill.ToArray(), - rotate: rotate - ); - if (!res) - throw new Exception("Error updating annotation"); - - if (annotType == PdfAnnotType.PDF_ANNOT_FREE_TEXT) - { - // in absence of previous opacity, we may need to modify the AP - string apStr = Encoding.UTF8.GetString(this.GetAP()); - if (opacity >= 0 && opacity < 1 && !apStr.StartsWith("/H gs")) - this.SetAP(Encoding.UTF8.GetBytes("/H gs\n" + apStr)); - return; - } - - byte[] bFill = ColorString(fill, "f"); - byte[] bStroke = ColorString(stroke, "c"); - - Matrix pCTM = Parent.TransformationMatrix; - Matrix iMat = ~pCTM; // inverse page transf. matrix + public byte[] GetFile() + { + if (Type != AnnotationType.FileAttachment) + throw new ArgumentException(Constants.MSG_BAD_ANNOT_TYPE); + var stream = GetNestedDict(AnnotObj, "FS", "EF", "F"); + if (stream.m_internal == null) + throw new InvalidOperationException("bad PDF: file entry not found"); + return Helpers.BufferToBytes(mupdf.mupdf.pdf_load_stream(stream)); + } - string dashStr = ""; - List bDash = new List(); - UTF8Encoding utf8 = new UTF8Encoding(); - if (dt != null && dt.Length > 0) - { - string[] dashes = new string[dt.Length]; - for (int i = 0; i < dt.Length; i++) - { - dashes[i] = Convert.ToString(dt[i]); - } - dashStr = "[" + string.Join(" ", dashes) + "] 0 d \n"; - bDash.AddRange(utf8.GetBytes(dashStr)); - } - else - bDash = null; - - PdfLineEnding line_end_le = LineEnds.Item1; - PdfLineEnding line_end_ri = LineEnds.Item2; - - // read contents as created by MuPDF - byte[] ap = GetAP(); - List apTab = new List(Encoding.UTF8.GetString(ap).Split('\n')); - bool apUpdated = false; + public void UpdateFile(byte[] buffer = null, string filename = null, string ufilename = null, string desc = null) + { + if (Type != AnnotationType.FileAttachment) + throw new ArgumentException(Constants.MSG_BAD_ANNOT_TYPE); + var stream = GetNestedDict(AnnotObj, "FS", "EF", "F"); + if (stream.m_internal == null) + throw new InvalidOperationException("bad PDF: no /EF object"); - if (annotType == PdfAnnotType.PDF_ANNOT_REDACT) + var fs = mupdf.mupdf.pdf_dict_get(AnnotObj, mupdf.mupdf.pdf_new_name("FS")); + var res = Helpers.BufferFromBytes(buffer); + if (buffer != null && res.m_internal == null) + throw new ArgumentException(Constants.MSG_BAD_BUFFER); + if (res.m_internal != null) { - if (crossOut) - { - apUpdated = true; - apTab.RemoveAt(apTab.Count - 1); - string t = apTab[0]; - string ll = apTab[1]; - string lr = apTab[2]; - string ur = apTab[3]; - string ul = apTab[4]; - apTab.Add(lr); - apTab.Add(ll); - apTab.Add(ur); - apTab.Add(ll); - apTab.Add(ul); - apTab.Add("S"); - } - - List nTab = new List(); - if (borderWidth > 0 || bStroke != null) + mupdf.mupdf.pdf_update_stream(ParentPdfDocument, stream, res, 1); + // Python: adjust /DL and /Params /Size after stream replacement. + int len = buffer.Length; + var l = mupdf.mupdf.pdf_new_int(len); + mupdf.mupdf.pdf_dict_put(stream, mupdf.mupdf.pdf_new_name("DL"), l); + var parms = mupdf.mupdf.pdf_dict_get(stream, mupdf.mupdf.pdf_new_name("Params")); + if (parms.m_internal == null) { - apUpdated = true; - nTab = - borderWidth > 0 - ? new List() { string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} w", borderWidth) } - : new List(); - for (int i = 0; i < apTab.Count; i++) - { - string line = apTab[i]; - if (line.EndsWith("w")) - continue; - if (line.EndsWith("RG") && bStroke != null) - line = bStroke.Take(bStroke.Length - 1).ToString(); - nTab.Add(line); - } - apTab = nTab; + parms = mupdf.mupdf.pdf_new_dict(ParentPdfDocument, 1); + mupdf.mupdf.pdf_dict_put(stream, mupdf.mupdf.pdf_new_name("Params"), parms); } - ap = utf8.GetBytes(string.Join("\n", apTab.ToArray())); + mupdf.mupdf.pdf_dict_put(parms, mupdf.mupdf.pdf_new_name("Size"), l); } - if ( - annotType == PdfAnnotType.PDF_ANNOT_POLYGON - || annotType == PdfAnnotType.PDF_ANNOT_POLY_LINE - ) + if (!string.IsNullOrEmpty(filename)) { - List newApTab = apTab; - newApTab.RemoveAt(newApTab.Count - 1); - ap = MergeByte( - Utils.ToByte(string.Join("\n", newApTab.ToArray())), - Utils.ToByte("\n") - ); - apUpdated = true; - if (bFill != null && bFill.Length > 0) - { - if (annotType == PdfAnnotType.PDF_ANNOT_POLYGON) - ap = MergeByte(MergeByte(ap, bFill), Utils.ToByte("b")); - else if (annotType == PdfAnnotType.PDF_ANNOT_POLY_LINE) - ap = MergeByte(ap, Utils.ToByte("S")); - } - else - { - if (annotType == PdfAnnotType.PDF_ANNOT_POLYGON) - ap = MergeByte(ap, Utils.ToByte("s")); - else if (annotType == PdfAnnotType.PDF_ANNOT_POLY_LINE) - ap = MergeByte(ap, Utils.ToByte("S")); - } + mupdf.mupdf.pdf_dict_put_text_string(stream, mupdf.mupdf.pdf_new_name("F"), filename); + mupdf.mupdf.pdf_dict_put_text_string(fs, mupdf.mupdf.pdf_new_name("F"), filename); + mupdf.mupdf.pdf_dict_put_text_string(stream, mupdf.mupdf.pdf_new_name("UF"), filename); + mupdf.mupdf.pdf_dict_put_text_string(fs, mupdf.mupdf.pdf_new_name("UF"), filename); + mupdf.mupdf.pdf_dict_put_text_string(AnnotObj, mupdf.mupdf.pdf_new_name("Contents"), filename); } - - if (bDash != null) + if (!string.IsNullOrEmpty(ufilename)) { - ap = MergeByte(bDash.ToArray(), ap); - // reset dashing -only applies for LINE annots with line ends given - ap = Utils.ReplaceBytes(ap, utf8.GetBytes("\nS\n"), utf8.GetBytes("\nS\n[] 0 d\n"), 1); - apUpdated = true; + mupdf.mupdf.pdf_dict_put_text_string(stream, mupdf.mupdf.pdf_new_name("UF"), ufilename); + mupdf.mupdf.pdf_dict_put_text_string(fs, mupdf.mupdf.pdf_new_name("UF"), ufilename); } - - if (!string.IsNullOrEmpty(opaCode)) + if (!string.IsNullOrEmpty(desc)) { - ap = MergeByte(utf8.GetBytes(opaCode), ap); - apUpdated = true; + mupdf.mupdf.pdf_dict_put_text_string(stream, mupdf.mupdf.pdf_new_name("Desc"), desc); + mupdf.mupdf.pdf_dict_put_text_string(fs, mupdf.mupdf.pdf_new_name("Desc"), desc); } + } - ap = MergeByte(MergeByte(Utils.ToByte("q\n"), ap), Utils.ToByte("\nQ\n")); - - // the following handles line end symbols for 'Polygon' and 'Polyline' - if ( - ((int)line_end_le + (int)line_end_ri) > 0 - && ( - annotType == PdfAnnotType.PDF_ANNOT_POLYGON - || annotType == PdfAnnotType.PDF_ANNOT_POLY_LINE - ) - ) - { - List leFuncs = new List() - { - null, - le_square, - le_circle, - le_diamond, - le_openarrow, - le_closedarrow, - le_butt, - le_ropenarrow, - le_rclosedarrow, - le_slash - }; - - float d = 2 * Math.Max(1, Border.Width); - rect = Rect + new Rect(-d, -d, d, d); - apUpdated = true; - List points = Vertices; - Point p1 = null; - Point p2 = null; - - if ((int)line_end_le > 0 && (int)line_end_le < leFuncs.Count) - { - p1 = points[0] * iMat; - p2 = points[1] * iMat; - string left = leFuncs[(int)line_end_le](this, p1, p2, false, fillColor); - ap = MergeByte(ap, Utils.ToByte(left)); - } + public void CleanContents(int sanitize = 1) + { + var filter = new mupdf.PdfFilterOptions(); + filter.recurse = 1; + filter.instance_forms = 0; + filter.ascii = 0; + mupdf.mupdf.pdf_filter_annot_contents(ParentPdfDocument, NativeAnnot, filter); + } - if ((int)line_end_ri > 0 && (int)line_end_ri < leFuncs.Count) - { - p1 = points[points.Count-2] * iMat; - p2 = points[points.Count-1] * iMat; - string left = leFuncs[(int)line_end_ri](this, p1, p2, true, fillColor); - ap = MergeByte(ap, Utils.ToByte(left)); - } - } + public void SetPopup(Rect rect) + { + var transformed = Helpers.TransformRect(new Rect(rect), RotatePageMatrix); + mupdf.mupdf.pdf_set_annot_popup(NativeAnnot, transformed.ToFzRect()); + } - if (apUpdated) - { - if (rect != null) - { - SetRect(rect); - SetAP(ap, 1); - } - else - SetAP(ap, 0); - } + public void SetApnBBox(Rect bbox) + { + var transformed = Helpers.TransformRect(new Rect(bbox), RotatePageMatrix); + var ap = GetAppearanceStreamObject("N"); + if (ap.m_internal == null) + throw new InvalidOperationException(Constants.MSG_BAD_APN); + mupdf.mupdf.pdf_dict_put_rect(ap, mupdf.mupdf.pdf_new_name("BBox"), transformed.ToFzRect()); + } - // handle annotation rotations - if ( // only these types are supported - !( - new List() - { - PdfAnnotType.PDF_ANNOT_CARET, - PdfAnnotType.PDF_ANNOT_CIRCLE, - PdfAnnotType.PDF_ANNOT_FILE_ATTACHMENT, - PdfAnnotType.PDF_ANNOT_INK, - PdfAnnotType.PDF_ANNOT_LINE, - PdfAnnotType.PDF_ANNOT_POLY_LINE, - PdfAnnotType.PDF_ANNOT_POLYGON, - PdfAnnotType.PDF_ANNOT_SQUARE, - PdfAnnotType.PDF_ANNOT_STAMP, - PdfAnnotType.PDF_ANNOT_TEXT - } - ).Contains((PdfAnnotType)annotType) - ) + public static PdfObj pdf_dict_getl(PdfObj obj, string[] keys) + { + foreach (string key in keys) { - return; + if (obj.m_internal == null) + break; + obj = obj.pdf_dict_get(new PdfObj(key)); } + return obj; + } - int rot = Rotation; // get value from annot object - if (rot == -1) // nothing to change - return; - Point M = (this.Rect.TopLeft + this.Rect.BottomRight) / 2.0f; - Quad quad = null; - if (rot == 0) - { - if ((apnMat - new Matrix(1, 1)).Abs() < 1e-5) - return; - - quad = this.Rect.Morph(M, ~apnMat); - SetRect(quad.Rect); - SetApnMatrix(new Matrix(1.0f, 1.0f)); - return; - } + public void SetApnMatrix(Matrix matrix) + { + /* + var annot = NativeAnnot; + var annot_obj = AnnotObj; + var ap = Helpers.PdfDictGetl(annot_obj, mupdf.mupdf.pdf_new_name("AP"), mupdf.mupdf.pdf_new_name("N")); + if (ap.m_internal == null) + throw new InvalidOperationException(Constants.MSG_BAD_APN); + var mat = matrix.ToFzMatrix(); + mupdf.mupdf.pdf_dict_put_matrix(ap, mupdf.mupdf.pdf_new_name("Matrix"), mat); + */ + var annot = NativeAnnot; + var annotObj = AnnotObj;// annot.pdf_annot_obj(); + //var ap = Helpers.PdfDictGetl(annotObj, mupdf.mupdf.pdf_new_name("AP"), mupdf.mupdf.pdf_new_name("N")); + PdfObj ap = pdf_dict_getl(annotObj, new string[] { "AP", "N" }); - Matrix mat = new Matrix(rot); - quad = this.Rect.Morph(M, mat); - SetRect(quad.Rect); - SetApnMatrix(apnMat * mat); - } - - internal static string le_square( - Annot annot, - Point p1, - Point p2, - bool lr, - float[] fillColor - ) - { - ( - Matrix m, - Matrix im, - Point L, - Point R, - float w, - string scol, - string fcol, - string opacity - ) = le_annot_parms(annot, p1, p2, fillColor); - float shift = 2.5f; // 2*shift*width = length of square edge - float d = shift * Math.Max(1, w); - Point M = lr ? R - new Point(d / 2.0f, 0) : L + new Point(d / 2.0f, 0); - - Rect r = new Rect(M, M) + new Rect(-d, -d, d, d); // the square - // the square makes line longer by (2*shift - 1)*width - Point p = r.TopLeft * im; - string ap = string.Format(System.Globalization.CultureInfo.InvariantCulture, "q\n{0}{1} {2} m\n", opacity, Utils.FloatToString(p.X), Utils.FloatToString(p.Y)); - p = r.TopRight * im; - ap += string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} l\n", Utils.FloatToString(p.X), Utils.FloatToString(p.Y)); - p = r.BottomRight * im; - ap += string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} l\n", Utils.FloatToString(p.X), Utils.FloatToString(p.Y)); - p = r.BottomLeft * im; - ap += string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} l\n", Utils.FloatToString(p.X), Utils.FloatToString(p.Y)); - ap += string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} w\n", Utils.FloatToString(w)); - ap += scol + fcol + "b\nQ\n"; - return ap; - } - - internal static string le_diamond( - Annot annot, - Point p1, - Point p2, - bool lr, - float[] fillColor - ) - { - ( - Matrix m, - Matrix im, - Point L, - Point R, - float w, - string scol, - string fcol, - string opacity - ) = le_annot_parms(annot, p1, p2, fillColor); - float shift = 2.5f; // 2*shift*width = length of square edge - float d = shift * Math.Max(1, w); - Point M = lr ? R - new Point(d / 2.0f, 0) : L + new Point(d / 2.0f, 0); - - Rect r = new Rect(M, M) + new Rect(-d, -d, d, d); // the square - // the square makes line longer by(2 * shift - 1)*width - Point p = (r.TopLeft + (r.BottomLeft - r.TopLeft) * 0.5f) * im; - string ap = string.Format(System.Globalization.CultureInfo.InvariantCulture, "q\n{0}{1} {2} m\n", opacity, Utils.FloatToString(p.X), Utils.FloatToString(p.Y)); - p = (r.TopLeft + (r.TopRight - r.TopLeft) * 0.5f) * im; - ap += string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} l\n", Utils.FloatToString(p.X), Utils.FloatToString(p.Y)); - p = (r.TopRight + (r.BottomLeft - r.BottomRight) * 0.5f) * im; - ap += string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} l\n", Utils.FloatToString(p.X), Utils.FloatToString(p.Y)); - p = (r.BottomRight + (r.BottomLeft - r.BottomRight) * 0.5f) * im; - ap += string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} l\n", Utils.FloatToString(p.X), Utils.FloatToString(p.Y)); - ap += string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} w\n", Utils.FloatToString(w)); - ap += scol + fcol + "b\nQ\n"; - return ap; - } - - internal static string le_openarrow( - Annot annot, - Point p1, - Point p2, - bool lr, - float[] fillColor - ) - { - ( - Matrix m, - Matrix im, - Point L, - Point R, - float w, - string scol, - string fcol, - string opacity - ) = le_annot_parms(annot, p1, p2, fillColor); - float shift = 2.5f; - float d = shift * Math.Max(1, w); - p2 = lr ? R + new Point(d / 2.0f, 0) : L - new Point(d / 2.0f, 0); - - p1 = lr ? p2 + new Point(-2 * d, -d) : p2 + new Point(2 * d, -d); - - Point p3 = lr ? p2 + new Point(-2 * d, d) : p2 + new Point(2 * d, d); - - p1 *= im; - p2 *= im; - p3 *= im; - string ap = "\nq\n" + opacity + Utils.FloatToString(p1.X) + " " + Utils.FloatToString(p1.Y) + " m\n"; - ap += Utils.FloatToString(p2.X) + " " + Utils.FloatToString(p2.Y) + " l\n"; - ap += Utils.FloatToString(p3.X) + " " + Utils.FloatToString(p3.Y) + " l\n"; - ap += Utils.FloatToString(w) + " w\n"; - ap += scol + "S\nQ\n"; - return ap; - } - - internal static string le_closedarrow( - Annot annot, - Point p1, - Point p2, - bool lr, - float[] fillColor - ) - { - ( - Matrix m, - Matrix im, - Point L, - Point R, - float w, - string scol, - string fcol, - string opacity - ) = le_annot_parms(annot, p1, p2, fillColor); - float shift = 2.5f; - float d = shift * Math.Max(1, w); - p2 = lr ? R + new Point(d / 2.0f, 0) : L - new Point(d / 2.0f, 0); - p1 = lr ? p2 + new Point(-2 * d, -d) : p2 + new Point(2 * d, -d); - Point p3 = lr ? p2 + new Point(-2 * d, d) : p2 + new Point(2 * d, d); - p1 *= im; - p2 *= im; - p3 *= im; - string ap = $"\nq\n{opacity}{Utils.FloatToString(p1.X)} {Utils.FloatToString(p1.Y)} m\n"; - ap += $"{Utils.FloatToString(p2.X)} {Utils.FloatToString(p2.Y)} l\n"; - ap += $"{Utils.FloatToString(p3.X)} {Utils.FloatToString(p3.Y)} l\n"; - ap += $"{Utils.FloatToString(w)} w\n"; - ap += $"{scol}{fcol}b\nQ\n"; - return ap; - } - - internal static string le_butt( - Annot annot, - Point p1, - Point p2, - bool lr, - float[] fillColor - ) - { - ( - Matrix m, - Matrix im, - Point L, - Point R, - float w, - string scol, - string fcol, - string opacity - ) = le_annot_parms(annot, p1, p2, fillColor); - float shift = 3; - float d = shift * Math.Max(1, w); - var M = lr ? R : L; - var top = new Point(M.X, M.Y - d / 2.0f) * im; - var bot = new Point(M.X, M.Y + d / 2.0f) * im; - var ap = $"\nq\n{opacity}{Utils.FloatToString(top.X)} {Utils.FloatToString(top.Y)} m\n"; - ap += $"{Utils.FloatToString(bot.X)} {Utils.FloatToString(bot.Y)} l\n"; - ap += $"{Utils.FloatToString(w)} w\n"; - ap += $"{scol}s\nQ\n"; - return ap; - } - - internal static string le_ropenarrow( - Annot annot, - Point p1, - Point p2, - bool lr, - float[] fillColor - ) - { - ( - Matrix m, - Matrix im, - Point L, - Point R, - float w, - string scol, - string fcol, - string opacity - ) = le_annot_parms(annot, p1, p2, fillColor); - float shift = 2.5f; - float d = shift * Math.Max(1, w); - p2 = lr ? new Point(R.X - (d / 3.0f), R.Y) : new Point(L.X + (d / 3.0f), L.Y); - p1 = lr ? new Point(p2.X + (2 * d), p2.Y - d) : new Point(p2.X - (2 * d), p2.Y - d); - Point p3 = lr - ? new Point(p2.X + (2 * d), p2.Y + d) - : new Point(p2.X - (2 * d), p2.Y + d); - p1 *= im; - p2 *= im; - p3 *= im; - string ap = "\nq\n" + opacity + Utils.FloatToString(p1.X) + " " + Utils.FloatToString(p1.Y) + " m\n"; - ap += Utils.FloatToString(p2.X) + " " + Utils.FloatToString(p2.Y) + " l\n"; - ap += Utils.FloatToString(p3.X) + " " + Utils.FloatToString(p3.Y) + " l\n"; - ap += Utils.FloatToString(w) + " w\n"; - ap += scol + fcol + "S\nQ\n"; - - return ap; - } - - internal static string le_rclosedarrow( - Annot annot, - Point p1, - Point p2, - bool lr, - float[] fillColor - ) - { - ( - Matrix m, - Matrix im, - Point L, - Point R, - float w, - string scol, - string fcol, - string opacity - ) = le_annot_parms(annot, p1, p2, fillColor); - float shift = 2.5f; - float d = shift * Math.Max(1, w); - p2 = lr ? new Point(R.X - (2 * d), R.Y) : new Point(L.X + (2 * d), L.Y); - p1 = lr ? p2 + new Point(2 * d, -d) : p2 + new Point(-2 * d, -d); - Point p3 = lr ? p2 + new Point(2 * d, d) : p2 + new Point(-2 * d, d); - p1 *= im; - p2 *= im; - p3 *= im; - string ap = "\nq\n" + opacity + Utils.FloatToString(p1.X) + " " + Utils.FloatToString(p1.Y) + " m\n"; - ap += Utils.FloatToString(p2.X) + " " + Utils.FloatToString(p2.Y) + " l\n"; - ap += Utils.FloatToString(p3.X) + " " + Utils.FloatToString(p3.Y) + " l\n"; - ap += Utils.FloatToString(w) + " w\n"; - ap += scol + fcol + "b\nQ\n"; - - return ap; - } - - internal static string le_slash( - Annot annot, - Point p1, - Point p2, - bool lr, - float[] fillColor - ) - { - ( - Matrix m, - Matrix im, - Point L, - Point R, - float w, - string scol, - string fcol, - string opacity - ) = le_annot_parms(annot, p1, p2, fillColor); - float rw = 1.1547f * Math.Max(1, w) * 1.0f; - Point M = lr ? R : L; - Rect r = new Rect(M.X - rw, M.Y - 2 * w, M.X + rw, M.Y + 2 * w); - Point top = r.TopLeft * im; - Point bot = r.BottomRight * im; - string ap = "\nq\n" + opacity + Utils.FloatToString(top.X) + " " + Utils.FloatToString(top.Y) + " m\n"; - ap += Utils.FloatToString(bot.X) + " " + Utils.FloatToString(bot.Y) + " l\n"; - ap += Utils.FloatToString(w) + " w\n"; - ap += scol + "b\nQ\n"; - - return ap; - } - - internal static string le_circle( - Annot annot, - Point p1, - Point p2, - bool lr, - float[] fillColor - ) - { - ( - Matrix m, - Matrix im, - Point L, - Point R, - float w, - string scol, - string fcol, - string opacity - ) = le_annot_parms(annot, p1, p2, fillColor); - float shift = 2.5f; - float d = shift * Math.Max(1, w); - Point M = lr ? R - new Point(d / 2.0f, 0) : L + new Point(d / 2.0f, 0); - - Rect r = (new Rect(M, M)) + new Rect(-d, -d, d, d); - string ap = - "q\n" - + opacity - + oval_string( - r.TopLeft * im, - r.TopRight * im, - r.BottomRight * im, - r.BottomLeft * im - ); - ap += string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} w\n", Utils.FloatToString(w)); - ap += scol + fcol + "b\nQ\n"; - - return ap; - } - - internal static string bezier(Point p, Point q, Point r) - { - string f = "{0} {1} {2} {3} {4} {5} c\n"; - - return string.Format(System.Globalization.CultureInfo.InvariantCulture, f, - Utils.FloatToString(p.X), Utils.FloatToString(p.Y), - Utils.FloatToString(q.X), Utils.FloatToString(q.Y), - Utils.FloatToString(r.X), Utils.FloatToString(r.Y)); - } - - internal static string oval_string(Point p1, Point p2, Point p3, Point p4) - { - float kappa = 0.55228474983f; - Point ml = p1 + (p4 - p1) * 0.5f; - Point mo = p1 + (p2 - p1) * 0.5f; - Point mr = p2 + (p3 - p2) * 0.5f; - Point mu = p4 + (p3 - p4) * 0.5f; - Point ol1 = ml + (p1 - ml) * kappa; - Point ol2 = mo + (p1 - mo) * kappa; - Point or1 = mo + (p2 - mo) * kappa; - Point or2 = mr + (p2 - mr) * kappa; - Point ur1 = mr + (p3 - mr) * kappa; - Point ur2 = mu + (p3 - mu) * kappa; - Point ul1 = mu + (p4 - mu) * kappa; - Point ul2 = ml + (p4 - ml) * kappa; - - string ap = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} m\n", Utils.FloatToString(ml.X), Utils.FloatToString(ml.Y)); - ap += Annot.bezier(ol1, ol2, mo); - ap += Annot.bezier(or1, or2, mr); - ap += Annot.bezier(ur1, ur2, mu); - ap += Annot.bezier(ul1, ul2, ml); - - return ap; - } - - internal static ( - Matrix, - Matrix, - Point, - Point, - float, - string, - string, - string - ) le_annot_parms(Annot annot, Point p1, Point p2, float[] fillColor) - { - float w = annot.Border.Width; - float[] sc = annot.Colors.Stroke; - if (sc == null) - sc = new float[3] { 0, 0, 0 }; - - string[] scStr = new string[sc.Length]; - for (int i = 0; i < sc.Length; i++) - { - scStr[i] = Utils.FloatToString(sc[i]); - } - - string scol = string.Join(" ", scStr) + " RG\n"; - scStr = null; - float[] fc = null; - if (fillColor != null) - fc = fillColor; - else - fc = annot.Colors.Fill; - if (fc == null) - fc = new float[3] { 1, 1, 1 }; - - string[] fcolStr = new string[fc.Length]; - for (int i = 0; i < fc.Length; i++) - { - fcolStr[i] = Utils.FloatToString(fc[i]); - } - string fcol = string.Join(" ", fcolStr) + " rg\n"; - - Point np1 = p1; - Point np2 = p2; - Matrix m = new Matrix(Utils.HorMatrix(np1, np2)); - Matrix im = ~m; - Point L = np1 * m; - Point R = np2 * m; - string opacity = ""; - if (0 <= annot.Opacity && annot.Opacity < 1) - opacity = "/H gs\n"; - else - opacity = ""; + if (ap.m_internal == null) + throw new InvalidOperationException(Constants.MSG_BAD_APN); - return (m, im, L, R, w, scol, fcol, opacity); + ap.pdf_dict_put_matrix(new PdfObj("Matrix"), matrix.ToFzMatrix()); } - public static byte[] MergeByte(byte[] a, byte[] b) + public void SetLanguage(string language = null) { - return Enumerable.Concat(a, b).ToArray(); + var lang = string.IsNullOrEmpty(language) + ? mupdf.fz_text_language.FZ_LANG_UNSET + : mupdf.mupdf.fz_text_language_from_string(language); + mupdf.mupdf.pdf_set_annot_language(NativeAnnot, lang); } - public static byte[] ColorString(dynamic cs, string code) + /// + /// Set line end codes. + /// + public void SetLineEnds(int start, int end) { - UTF8Encoding utf8 = new UTF8Encoding(); - if (cs is List) - cs = cs.ToArray(); - string cc = ColorCode(cs, code); - if (cc == "") - return utf8.GetBytes(""); - - return utf8.GetBytes(cc + "\n"); + if (mupdf.mupdf.pdf_annot_has_line_ending_styles(NativeAnnot) != 0) + mupdf.mupdf.pdf_set_annot_line_ending_styles(NativeAnnot, (mupdf.pdf_line_ending)start, (mupdf.pdf_line_ending)end); + else + Trace.TraceWarning("bad annot type for line ends"); } - public static string ColorCode(float cs, string code) + public void SetRotation(int rotate = 0) { - string s = ""; - - s = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} ", cs); - if (code == "c") - return s + "G "; + switch (Type) + { + case AnnotationType.Caret: + case AnnotationType.Circle: + case AnnotationType.FreeText: + case AnnotationType.FileAttachment: + case AnnotationType.Ink: + case AnnotationType.Line: + case AnnotationType.PolyLine: + case AnnotationType.Polygon: + case AnnotationType.Square: + case AnnotationType.Stamp: + case AnnotationType.Text: + break; + default: + return; + } - return s + "g "; + while (rotate < 0) rotate += 360; + while (rotate >= 360) rotate -= 360; + if (Type == AnnotationType.FreeText && rotate % 90 != 0) + rotate = 0; + mupdf.mupdf.pdf_dict_put_int(AnnotObj, mupdf.mupdf.pdf_new_name("Rotate"), rotate); } - public static string ColorCode(float[] cs, string code) + public void SetOpen(bool isOpen) { - string s = ""; - if (cs == null || cs.Length == 0) - return ""; + mupdf.mupdf.pdf_set_annot_is_open(NativeAnnot, isOpen ? 1 : 0); + } - Utils.CheckColor(cs); - if (cs.Length == 1) - { - s = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} ", Utils.FloatToString(cs[0])); - if (code == "c") - return s + "G "; - return s + "g "; - } + // ─── IDisposable ──────────────────────────────────────────────── - if (cs.Length == 3) + /// + /// Releases resources used by this annotation wrapper. + /// + public void Dispose() + { + if (!_disposed) { - s = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} {2} ", Utils.FloatToString(cs[0]), Utils.FloatToString(cs[1]), Utils.FloatToString(cs[2])); - if (code == "c") - return s + "RG "; - return s + "rg "; + Parent.ForgetAnnotRef(this); + _disposed = true; } - - s = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} {2} {3} ", Utils.FloatToString(cs[0]), Utils.FloatToString(cs[1]), Utils.FloatToString(cs[2]), Utils.FloatToString(cs[3])); - if (code == "c") - return s + "K "; - - return s + "k "; + GC.SuppressFinalize(this); } - public dynamic GetText( - Page page, - string option = "text", - Rect clip = null, - int flags = 0, - TextPage stPage = null, - bool sort = false, - char[] delimiters = null - ) - { - return Utils.GetText(page, option, clip, flags, stPage, sort, delimiters); - } + ~Annot() { Dispose(); } /// - /// Get text in the box area + /// Returns a string representation of this annotation. /// - /// - /// - /// - /// - public string GetTextbox(Page page, Rect rect, TextPage textPage) + public override string ToString() => $"Annot('{TypeString}' on {Parent})"; + + private byte[] _getAP() { - return Utils.GetTextbox(page, rect, textPage); + var ap = GetAppearanceStreamObject("N"); + if (mupdf.mupdf.pdf_is_stream(ap) == 0) + return null; + // Python _getAP: returns AP/N stream bytes when present. + return Helpers.BufferToBytes(mupdf.mupdf.pdf_load_stream(ap)); } - public void UpdateFile( - byte[] buffer = null, - string fileName = null, - string uFilename = null, - string desc = null - ) + private void _setAP(byte[] buffer, int rect = 0) { - PdfAnnot annot = _nativeAnnotion; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfDocument pdf = annotObj.pdf_get_bound_document(); - pdf_annot_type type = annot.pdf_annot_type(); - - if (type != pdf_annot_type.PDF_ANNOT_FILE_ATTACHMENT) + var apobj = GetAppearanceStreamObject("N"); + if (apobj.m_internal == null || mupdf.mupdf.pdf_is_stream(apobj) == 0) + throw new InvalidOperationException(Constants.MSG_BAD_APN); + var res = Helpers.BufferFromBytes(buffer); + if (res.m_internal == null) + throw new ArgumentException(Constants.MSG_BAD_BUFFER); + mupdf.mupdf.pdf_update_stream(ParentPdfDocument, apobj, res, 1); + if (rect != 0) { - throw new Exception(Utils.ErrorMessages["MSG_BAD_ANNOT_TYPE"]); + // Python _setAP(rect=1): sync AP /BBox to annot /Rect. + var bbox = mupdf.mupdf.pdf_dict_get_rect(AnnotObj, mupdf.mupdf.pdf_new_name("Rect")); + mupdf.mupdf.pdf_dict_put_rect(apobj, mupdf.mupdf.pdf_new_name("BBox"), bbox); } - PdfObj stream = Utils.pdf_dict_getl(annotObj, new string[] { "FS", "EF", "F" }); + } - if (stream.m_internal == null) - Console.WriteLine("bad PDF: no /EF object"); - - PdfObj fs = annotObj.pdf_dict_get(new PdfObj("FS")); - FzBuffer res = Utils.BufferFromBytes(buffer); + private mupdf.PdfObj GetAppearanceStreamObject(string which) + { + var ap = mupdf.mupdf.pdf_dict_gets(AnnotObj, "AP"); + if (ap.m_internal == null) return ap; + return mupdf.mupdf.pdf_dict_gets(ap, which); + } - if (buffer != null && res.m_internal == null) - throw new Exception(Utils.ErrorMessages["MSG_BAD_BUFFER"]); - if (res.m_internal != null) + private static mupdf.PdfObj GetNestedDict(mupdf.PdfObj root, params string[] keys) + { + var current = root; + foreach (var key in keys) { - Utils.UpdateStream(pdf, stream, res, 1); - uint len = res.fz_buffer_storage( - new SWIGTYPE_p_p_unsigned_char(IntPtr.Zero, false) - ); - PdfObj l = mupdf.mupdf.pdf_new_int((long)len); - stream.pdf_dict_put(new PdfObj("DL"), l); - Utils.pdf_dict_putl(stream, l, new string[] { "Params", "Size" }); + if (current.m_internal == null) return current; + current = mupdf.mupdf.pdf_dict_gets(current, key); } - - if (fileName != null) + return current; + } + + private static float[] NormalizeColorSequence(object value) + { + if (value == null) return null; + if (value is float[] fa) return fa; + if (value is double[] da) return Array.ConvertAll(da, x => (float)x); + if (value is int[] ia) return Array.ConvertAll(ia, x => (float)x); + if (value is float f) return new[] { f }; + if (value is double d) return new[] { (float)d }; + if (value is int i) return new[] { (float)i }; + if (value is System.Collections.IEnumerable seq && value is not string) { - stream.pdf_dict_put_text_string(new PdfObj("F"), fileName); - fs.pdf_dict_put_text_string(new PdfObj("F"), fileName); - stream.pdf_dict_put_text_string(new PdfObj("UF"), fileName); - fs.pdf_dict_put_text_string(new PdfObj("UF"), fileName); - annotObj.pdf_dict_put_text_string(new PdfObj("Contents"), fileName); + var list = new List(); + foreach (var item in seq) + { + if (item == null) return null; + list.Add(Convert.ToSingle(item)); + } + return list.ToArray(); } - - if (uFilename != null) + return null; + } + + // Python/legacy compatibility aliases (mirrors _alias(Annot, ...)). + public byte[] get_file() => GetFile(); + public byte[] fileGet() => get_file(); + public Pixmap get_pixmap(Matrix matrix = null, Colorspace cs = null, bool alpha = false) => GetPixmap(matrix, cs, alpha); + public Dictionary get_sound() => GetSound(); + public byte[] soundGet() => GetSoundData(); + public TextPage get_textpage(int flags = 0) => GetTextPage(flags); + public TextPage getTextPage(int flags = 0) => get_textpage(flags); + public string get_text(string option = "text", int flags = 0) + { + using (var tp = GetTextPage(flags)) { - stream.pdf_dict_put_text_string(new PdfObj("UF"), uFilename); - fs.pdf_dict_put_text_string(new PdfObj("UF"), uFilename); + return tp.ExtractText(); } - - if (desc != null) + } + public string get_textbox(Rect rect, int flags = 0) + { + using (var tp = GetTextPage(flags)) { - stream.pdf_dict_put_text_string(new PdfObj("Desc"), desc); - fs.pdf_dict_put_text_string(new PdfObj("Desc"), desc); + return tp.ExtractTextbox(rect); } } - } - - internal class Factory : mupdf.PdfFilterFactory2 - { - private PdfSanitizeFilterOptions sopts; - - public Factory(PdfSanitizeFilterOptions sopts) - : base() - { - this.sopts = sopts; - use_virtual_filter(); - } - - public override pdf_processor filter( - fz_context arg_0, - pdf_document doc, - pdf_processor chain, - int stParents, - fz_matrix transform, - pdf_filter_options options - ) - { - return mupdf.mupdf.ll_pdf_new_sanitize_filter( - doc, - chain, - stParents, - transform, - options, - new SWIGTYPE_p_void( - pdf_sanitize_filter_options.getCPtr(sopts.internal_()).Handle, - true - ) - ); - } + public (int start, int end)? line_ends() => LineEnds; + public void set_blendmode(string mode) => SetBlendMode(mode); + public void setBlendMode(string mode) => set_blendmode(mode); + public void set_border(float width = -1, float[] dashes = null, string style = null) => SetBorder(width, dashes, style); + public void set_colors(float[] stroke = null, float[] fill = null) => SetColors(stroke, fill); + public void set_flags(int flags) => Flags = flags; + public void set_info(string content = null, string title = null, string creationDate = null, string modDate = null, string subject = null) + => SetInfo(content, title, creationDate, modDate, subject); + public void set_line_ends(int start, int end) => SetLineEnds(start, end); + public void set_name(string name) => SetName(name); + public void set_oc(int oc) => SetOC(oc); + public void setOC(int oc) => set_oc(oc); + public void set_opacity(float opacity) => SetOpacity(opacity); + public bool? set_rect(Rect rect) => SetRect(rect); + public void update_file(byte[] buffer = null, string filename = null, string ufilename = null, string desc = null) + => UpdateFile(buffer, filename, ufilename, desc); + public void fileUpd(byte[] buffer = null, string filename = null, string ufilename = null, string desc = null) + => update_file(buffer, filename, ufilename, desc); } } diff --git a/MuPDF.NET/App.config b/MuPDF.NET/App.config deleted file mode 100644 index 5b023ab1..00000000 --- a/MuPDF.NET/App.config +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/MuPDF.NET/Archive.cs b/MuPDF.NET/Archive.cs index 54b37039..29c69b45 100644 --- a/MuPDF.NET/Archive.cs +++ b/MuPDF.NET/Archive.cs @@ -1,249 +1,465 @@ -using mupdf; -using ICSharpCode.SharpZipLib.Zip; -using ICSharpCode.SharpZipLib.Tar; +using System; using System.Collections.Generic; using System.IO; -using System; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; namespace MuPDF.NET { - public class Archive + /// + /// PyMuPDF Archive port: multi-archive with mounted sub-archives (dirs, tree buffers, zip/tar, nested archives). + /// + public class Archive : IDisposable { - static Archive() - { - Utils.InitApp(); - } + private mupdf.FzArchive? _nativeArchive; + private bool _disposed; + private readonly List> _subarchives = new List>(); + /// Holds buffers/streams that must outlive mounts (in-memory zip/tar). + private readonly List _heldDisposables = new List(); - private FzArchive _nativeArchive; - - private List _subArchives; - - public List EntryList + internal mupdf.FzArchive NativeArchive { get { - return _subArchives; + if (_disposed) throw new ObjectDisposedException(nameof(Archive)); + return _nativeArchive!; } } - public Archive(string dirName, string path = "") + /// Empty multi-archive (PyMuPDF Archive()). + public Archive() { - _subArchives = new List(); _nativeArchive = mupdf.mupdf.fz_new_multi_archive(); - Add(content: dirName, path: path); } - public Archive() + /// + /// PyMuPDF Archive(*args) with one or two arguments: forwarded to . + /// + public Archive(object first, string? second = null) + : this() { - _nativeArchive = mupdf.mupdf.fz_new_multi_archive(); - _subArchives = new List(); + if (second != null) + Add(first, second); + else + Add(first); } - public Archive(byte[] content, string path = "") + /// + /// Archive(dirname [, mountPath]) / Archive(file, mountPath) — same as after construction. + /// + public Archive(string path, string? mountPath = null) + : this() { - _subArchives = new List(); - _nativeArchive = mupdf.mupdf.fz_new_multi_archive(); - Add(content: content, path: path); + Add(path, mountPath); } - public FzArchive ToFzArchive() + /// Archive(data, name) — buffer as tree entry (PyMuPDF add(data, name)). + public Archive(byte[] data, string name) + : this() { - return _nativeArchive; + Add(data, name, null); } - public override string ToString() + /// Archive(archive [, mountPath]) — mount nested archive. + public Archive(Archive other, string? mountPath = null) + : this() { - return $"Archive, sub-archive: {_subArchives.Count}"; + if (other == null) throw new ArgumentNullException(nameof(other)); + Add(other, mountPath); } - private void _AddArch(FzArchive subArch, string path = null) + internal Archive(mupdf.FzArchive archive) { - _nativeArchive.fz_mount_multi_archive(subArch, path); + _nativeArchive = archive; } - private void _AddDir(string folder, string path = null) + /// Number of recorded sub-archive metadata entries (PyMuPDF len(entry_list)). + public int EntryCount => _subarchives.Count; + + /// PyMuPDF entry_list: dicts with fmt, entries, path. + public List> EntryList => _subarchives; + + private static string? NormalizeMountPath(string? path) { - FzArchive sub = mupdf.mupdf.fz_open_directory(folder); - _nativeArchive.fz_mount_multi_archive(sub, path); + if (string.IsNullOrEmpty(path)) return null; + return path; } - private void _AddTreeItem(byte[] memory, string name, string path = null) + /// + /// MuPDF takes ownership of the native fz_tree in ; + /// detach the managed wrapper so it does not fz_drop_tree on dispose/GC. + /// + private static void ReleaseFzTreeOwnedByArchive(mupdf.FzTree tree) { - FzBuffer buf = Utils.BufferFromBytes(memory); - FzArchive sub = new FzTree().fz_new_tree_archive(); - sub.fz_tree_archive_add_buffer(name, buf); - _nativeArchive.fz_mount_multi_archive(sub, path); + if (tree == null) return; + const BindingFlags bf = BindingFlags.Instance | BindingFlags.NonPublic; + var type = typeof(mupdf.FzTree); + var ownField = type.GetField("swigCMemOwn", bf); + var ptrField = type.GetField("swigCPtr", bf); + ownField?.SetValue(tree, false); + ptrField?.SetValue(tree, new HandleRef(null, IntPtr.Zero)); } - private void _AddZiptarFile(string filePath, int type, string path = null) + private void MountSubArchive(mupdf.FzArchive sub, string? mountPath) { - FzArchive sub = null; - if (type == 1) - sub = mupdf.mupdf.fz_open_zip_archive(path); - else - sub = mupdf.mupdf.fz_open_tar_archive(path); - _nativeArchive.fz_mount_multi_archive(sub, path); + NativeArchive.fz_mount_multi_archive(sub, NormalizeMountPath(mountPath)); } - private void _AddZiptarMemory(byte[] memory, int type, string path = null) + private void AddDirInternal(string folder, string? mountPath) { - FzBuffer buf = Utils.BufferFromBytes(memory); - FzStream stream = buf.fz_open_buffer(); - FzArchive sub = null; - if (type == 1) - sub = stream.fz_open_zip_archive_with_stream(); - else - sub = stream.fz_open_tar_archive_with_stream(); - _nativeArchive.fz_mount_multi_archive(sub, path); + var sub = mupdf.mupdf.fz_open_directory(folder); + MountSubArchive(sub, mountPath); } - private void MakeSubArch(string fmt, List entries, string mount) + private void AddTreeItemInternal(byte[] memory, string name, string? mountPath) { - SubArchive subarch = new SubArchive() - { - Fmt = fmt, - Entries = entries, - Path = mount - }; + var buf = Helpers.BufferFromBytes(memory); + var tree = new mupdf.FzTree(); + var sub = tree.fz_new_tree_archive(); + ReleaseFzTreeOwnedByArchive(tree); + sub.fz_tree_archive_add_buffer(name, buf); + buf.Dispose(); + MountSubArchive(sub, mountPath); + } + + private static bool ArchivePathsEqual(string? a, string? b) => + (a == null && b == null) || (a != null && b != null && string.Equals(a, b, StringComparison.Ordinal)); - if (fmt != "tree" || _subArchives.Count == 0) + private void RecordSubarch(string fmt, List entries, string? path) + { + var subarch = new Dictionary { - _subArchives.Add(subarch); - } - else + ["fmt"] = fmt, + ["entries"] = entries, + ["path"] = path, + }; + if (fmt == "tree" && _subarchives.Count > 0) { - SubArchive ltree = _subArchives[_subArchives.Count - 1]; - if (ltree.Fmt != "tree" || ltree.Path != subarch.Path) + var last = _subarchives[_subarchives.Count - 1]; + last.TryGetValue("path", out var lpObj); + var lp = lpObj as string; + if (last.TryGetValue("fmt", out var lf) && lf is string lfStr && lfStr == "tree" + && ArchivePathsEqual(lp, path)) { - _subArchives.Add(subarch); - } - else - { - ltree.Entries.AddRange(subarch.Entries); - _subArchives[_subArchives.Count - 1] = ltree; + if (last["entries"] is List el) + el.AddRange(entries); + return; } } + _subarchives.Add(subarch); } /// - /// Append a sub-archive. The meaning of the parameters are exactly the same as explained above. Of course, parameter content is not optional here. + /// Add a directory (mount path optional), a file as tree data (requires non-empty), + /// or forward to other overloads via . /// - /// The fully qualified name of the entry. - /// - public void Add(ZipFile content, string path = null) - { - string fmt = "zip"; - if (!File.Exists(path)) - throw new Exception("Need name for zip file"); - string filename = Path.GetFileName(path); - List entries = new List(); - foreach (ZipEntry e in content) - { - entries.Add(e.Name); - } - - if (string.IsNullOrEmpty(filename)) + public void Add(string path, string? mountPath = null) + { + if (string.IsNullOrEmpty(path)) + throw new ArgumentException("path required", nameof(path)); + var full = Path.GetFullPath(path); + if (Directory.Exists(full)) { - + AddDirInternal(full, mountPath); + var dirNames = new List(); + foreach (var e in Directory.GetFileSystemEntries(full)) + { + var n = Path.GetFileName(e); + if (!string.IsNullOrEmpty(n)) dirNames.Add(n); + } + RecordSubarch("dir", dirNames, mountPath); + return; } - else + if (File.Exists(full)) { - _AddZiptarFile(filename, 1, path); + if (string.IsNullOrEmpty(mountPath)) + throw new ArgumentException("Need mountPath (virtual name) for file content.", nameof(mountPath)); + var bytes = File.ReadAllBytes(full); + AddTreeItemInternal(bytes, mountPath, null); + RecordSubarch("tree", new List { mountPath }, null); + return; } - MakeSubArch(fmt, entries, path); + throw new ArgumentException($"Not a file or directory: '{path}'", nameof(path)); + } + + /// Add in-memory item as tree buffer (PyMuPDF bytes path; is entry name). + public void Add(byte[] data, string name, string? mountPath = null) + { + if (data == null) throw new ArgumentNullException(nameof(data)); + if (string.IsNullOrEmpty(name)) + throw new ArgumentException("Need name for binary content.", nameof(name)); + AddTreeItemInternal(data, name, mountPath); + RecordSubarch("tree", new List { name }, mountPath); + } + + /// Mount another multi-archive (PyMuPDF nested Archive). + public void Add(Archive other, string? mountPath = null) + { + if (other == null) throw new ArgumentNullException(nameof(other)); + MountSubArchive(other.NativeArchive, mountPath); + RecordSubarch("multi", new List(), mountPath); + } + + /// Mount a zip file from disk (type_ == 1 in PyMuPDF). + public void AddZipFile(string zipPath, string? mountPath = null) + { + if (string.IsNullOrEmpty(zipPath) || !File.Exists(zipPath)) + throw new ArgumentException("zip path must exist", nameof(zipPath)); + var sub = mupdf.mupdf.fz_open_zip_archive(Path.GetFullPath(zipPath)); + MountSubArchive(sub, mountPath); + var names = ListZipEntryNames(File.ReadAllBytes(Path.GetFullPath(zipPath))); + RecordSubarch("zip", names, mountPath); + } + + /// Mount a tar file from disk. + public void AddTarFile(string tarPath, string? mountPath = null) + { + if (string.IsNullOrEmpty(tarPath) || !File.Exists(tarPath)) + throw new ArgumentException("tar path must exist", nameof(tarPath)); + var sub = mupdf.mupdf.fz_open_tar_archive(Path.GetFullPath(tarPath)); + MountSubArchive(sub, mountPath); + var names = ListTarEntryNames(File.ReadAllBytes(tarPath)); + RecordSubarch("tar", names, mountPath); + } + + /// Mount zip from raw bytes (in-memory). + public void AddZipBytes(byte[] data, string? mountPath = null) + { + if (data == null || data.Length == 0) throw new ArgumentException("data required", nameof(data)); + var buf = Helpers.BufferFromBytes(data); + var stm = buf.fz_open_buffer(); + _heldDisposables.Add(buf); + _heldDisposables.Add(stm); + var sub = stm.fz_open_zip_archive_with_stream(); + MountSubArchive(sub, mountPath); + var names = ListZipEntryNames(data); + RecordSubarch("zip", names, mountPath); } - public void Add(TarInputStream content, string path = null) + /// Mount tar from raw bytes. + public void AddTarBytes(byte[] data, string? mountPath = null) { - string fmt = "tar"; - if (!File.Exists(path)) - throw new Exception("Need name for zip file"); + if (data == null || data.Length == 0) throw new ArgumentException("data required", nameof(data)); + var buf = Helpers.BufferFromBytes(data); + var stm = buf.fz_open_buffer(); + _heldDisposables.Add(buf); + _heldDisposables.Add(stm); + var sub = stm.fz_open_tar_archive_with_stream(); + MountSubArchive(sub, mountPath); + RecordSubarch("tar", ListTarEntryNames(data), mountPath); + } - string filename = Path.GetFileName(path); - List entries = new List(); - TarEntry entry; - while ((entry = content.GetNextEntry()) != null) + /// + /// PyMuPDF-style heterogeneous add: string path, raw bytes, Tuple<byte[], string>, file path tuple, + /// nested , , or sequence of items. + /// + public void Add(object content, string? mountPath = null) + { + switch (content) { - entries.Add(entry.Name); + case null: + throw new ArgumentNullException(nameof(content)); + case string s: + Add(s, mountPath); + return; + case byte[] bytes: + if (string.IsNullOrEmpty(mountPath)) + throw new ArgumentException("Need mountPath (virtual name) for raw bytes.", nameof(mountPath)); + Add(bytes, mountPath, null); + return; + case MemoryStream ms: + if (string.IsNullOrEmpty(mountPath)) + throw new ArgumentException("Need mountPath (virtual name) for stream content.", nameof(mountPath)); + Add(ms.ToArray(), mountPath, null); + return; + case Archive arch: + Add(arch, mountPath); + return; + case Tuple t: + Add(t.Item1, t.Item2, mountPath); + return; + case Tuple pathAndName: + { + var fp = Path.GetFullPath(pathAndName.Item1); + if (!File.Exists(fp)) + throw new ArgumentException($"Not a file: '{pathAndName.Item1}'", nameof(content)); + AddTreeItemInternal(File.ReadAllBytes(fp), pathAndName.Item2, mountPath); + RecordSubarch("tree", new List { pathAndName.Item2 }, mountPath); + return; + } + case System.Collections.IEnumerable seq when content is not string: + foreach (var item in seq) + Add(item, mountPath); + return; + default: + throw new ArgumentException($"Unrecognised type for Archive.Add: {content.GetType().Name}", nameof(content)); } + } - if (string.IsNullOrEmpty(filename)) + /// PyMuPDF read_entry. + public byte[] Read(string name) => ReadEntry(name); + + /// PyMuPDF read_entry. + public byte[] ReadEntry(string name) + { + var buf = NativeArchive.fz_read_archive_entry(name); + try { - + return Helpers.BufferToBytes(buf); } - else + finally { - _AddZiptarFile(filename, 0, path); + buf?.Dispose(); } - MakeSubArch(fmt, entries, path); } - public void Add(FzArchive content, string path) + /// PyMuPDF has_entry. + public bool Has(string name) => HasEntry(name); + + /// PyMuPDF has_entry. + public bool HasEntry(string name) => NativeArchive.fz_has_archive_entry(name) != 0; + + public void Dispose() { - _AddArch(content, path); - MakeSubArch("multi", new List(), path); + if (!_disposed) + { + try + { + _nativeArchive?.Dispose(); + } + finally + { + for (int i = _heldDisposables.Count - 1; i >= 0; i--) + { + try { _heldDisposables[i]?.Dispose(); } catch { /* ignore */ } + } + _heldDisposables.Clear(); + _nativeArchive = null; + _disposed = true; + } + } + GC.SuppressFinalize(this); } - public void Add(byte[] content, string path) - { - List entries = new List(); + ~Archive() { Dispose(); } - if (string.IsNullOrEmpty(path)) - throw new Exception("Need name for binary content"); - entries.Add(path); - _AddTreeItem(content as byte[], path); - MakeSubArch("tree", entries, null); - } + public override string ToString() => $"Archive, sub-archives: {EntryCount}"; - public void Add(string content, string path = "") + /// + /// Central-directory scan (no ), so net472/net48 avoid + /// ZipArchiveMode / extra reference assembly issues; matches typical namelist() output. + /// + private static List ListZipEntryNames(byte[] zip) { - string fmt = null; - string mount = null; - List entries = new List(); - - if (Directory.Exists(content)) + var names = new List(); + if (zip == null || zip.Length < 22) + return names; + const uint sigEocd = 0x06054b50u; + const uint sigCd = 0x02014b50u; + int eocd = -1; + int scanMin = Math.Max(0, zip.Length - 65557); + for (int i = zip.Length - 22; i >= scanMin; i--) { - fmt = "dir"; - mount = path; - entries = new List(Directory.GetFiles(content)); - _AddDir(content, path); - MakeSubArch(fmt, entries, mount); + if (BitConverter.ToUInt32(zip, i) == sigEocd) + { + eocd = i; + break; + } } - else if (File.Exists(content)) + if (eocd < 0) + return names; + uint cdOffset = BitConverter.ToUInt32(zip, eocd + 16); + if (cdOffset == 0xffffffffu || cdOffset >= zip.Length) + return names; + int p = (int)cdOffset; + Encoding cp437; + try { - if (string.IsNullOrEmpty(path)) - throw new Exception("need name for binary content"); - byte[] ff = File.ReadAllBytes(content); - fmt = "tree"; - mount = null; - entries.Add(path); - _AddTreeItem(ff, path); - MakeSubArch(fmt, entries, mount); + cp437 = Encoding.GetEncoding(437); } - else - throw new Exception("Not a file or directory"); + catch (ArgumentException) + { + cp437 = Encoding.UTF8; + } + while (p + 46 <= zip.Length && BitConverter.ToUInt32(zip, p) == sigCd) + { + int n = BitConverter.ToUInt16(zip, p + 28); + int m = BitConverter.ToUInt16(zip, p + 30); + int k = BitConverter.ToUInt16(zip, p + 32); + if (p + 46 + n > zip.Length || n < 0 || m < 0 || k < 0) + break; + if (n > 0) + { + int gp = BitConverter.ToUInt16(zip, p + 8); + var enc = (gp & 0x800) != 0 ? Encoding.UTF8 : cp437; + var name = enc.GetString(zip, p + 46, n); + names.Add(name.Replace('\\', '/')); + } + p += 46 + n + m + k; + } + return names; } - /// - /// Checks whether an entry exists in any of the sub-archives. - /// - /// The fully qualified name of the entry. - /// - public bool HasEntry(string name) + /// Best-effort USTAR/GNU tar name list (PyMuPDF getnames()). + private static List ListTarEntryNames(byte[] data) { - return _nativeArchive.fz_has_archive_entry(name) != 0; + var names = new List(); + if (data == null || data.Length < 512) + return names; + var enc = Encoding.ASCII; + int pos = 0; + while (pos + 512 <= data.Length) + { + if (IsTarZeroBlock(data, pos)) + { + pos += 512; + if (pos + 512 <= data.Length && IsTarZeroBlock(data, pos)) + break; + continue; + } + + string name = enc.GetString(data, pos, 100).TrimEnd('\0', ' '); + string prefix = enc.GetString(data, pos + 345, 155).TrimEnd('\0', ' '); + long size = ParseTarOctal(data, pos + 124, 12); + if (size < 0) size = 0; + + if (!string.IsNullOrEmpty(name) && name != "././@LongLink") + { + string full = string.IsNullOrEmpty(prefix) + ? name + : prefix.TrimEnd('/') + "/" + name.TrimStart('/'); + names.Add(full.Replace('\\', '/')); + } + + int padded = (int)(((size + 511) / 512) * 512); + pos += 512 + padded; + if (padded < 0 || pos > data.Length) + break; + } + return names; } - /// - /// Retrieve the data of an entry. - /// - /// The fully qualified name of the entry. - /// - public byte[] ReadEntry(string name) + private static bool IsTarZeroBlock(byte[] d, int o) + { + for (int i = 0; i < 512; i++) + if (d[o + i] != 0) return false; + return true; + } + + private static long ParseTarOctal(byte[] b, int o, int len) { - FzBuffer buf = _nativeArchive.fz_read_archive_entry(name); - - return Utils.BinFromBuffer(buf); + long v = 0; + int end = Math.Min(o + len, b.Length); + for (int i = o; i < end; i++) + { + byte c = b[i]; + if (c == 0 || c == (byte)' ') continue; + if (c < (byte)'0' || c > (byte)'7') break; + v = (v << 3) + (c - (byte)'0'); + } + return v; } } } diff --git a/MuPDF.NET/BarCodes.cs b/MuPDF.NET/BarCodes.cs deleted file mode 100644 index 654513b6..00000000 --- a/MuPDF.NET/BarCodes.cs +++ /dev/null @@ -1,618 +0,0 @@ -using BarcodeReader.Core; -using BarcodeReader.Core.AustraliaPostCode; -using BarcodeReader.Core.Aztec; -using BarcodeReader.Core.CodaBlockF; -using BarcodeReader.Core.Code128; -using BarcodeReader.Core.Code16K; -using BarcodeReader.Core.Code39; -using BarcodeReader.Core.Code93; -using BarcodeReader.Core.Common; -using BarcodeReader.Core.Datamatrix; -using BarcodeReader.Core.EAN; -using BarcodeReader.Core.FormLines; -using BarcodeReader.Core.FormOMR; -using BarcodeReader.Core.FormTables; -using BarcodeReader.Core.GS1DataBar; -using BarcodeReader.Core.IntelligentMail; -using BarcodeReader.Core.LegacyDecoders; -using BarcodeReader.Core.MaxiCode; -using BarcodeReader.Core.MICR; -using BarcodeReader.Core.MicroPDF; -using BarcodeReader.Core.MSI; -using BarcodeReader.Core.PatchCode; -using BarcodeReader.Core.PDF417; -using BarcodeReader.Core.Pharmacode; -using BarcodeReader.Core.PostNet; -using BarcodeReader.Core.QR; -using BarcodeReader.Core.RoyalMail; -using BarcodeReader.Core.RoyalMailKIX; -using BarcodeReader.Core.TriOptic; -using BarcodeWriter.Core; -using SkiaSharp; -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Reflection; -using System.Text; -using SymbologyType = BarcodeWriter.Core.SymbologyType; - -namespace MuPDF.NET -{ - internal sealed class Config - { - public bool TryHarder { get; set; } - public bool TryInverted { get; set; } - public bool PureBarcode { get; set; } - public bool Multi { get; set; } = true; - public int[] Crop { get; set; } - public int Threads { get; set; } - public bool AutoRotate { get; set; } - - public Config() - { - Multi = true; - Threads = 1; - } - } - - /// - /// Barcode Reader Class - /// - public class BarcodeReader - { - private readonly string _configFile = null; - private readonly object _reader = null; - - static BarcodeReader() - { - Utils.InitApp(); - } - - public BarcodeReader(string barcodeType, string configFile) - { - _configFile = configFile; - - barcodeType = barcodeType.ToUpper(); - - if (barcodeType == "AZTEC") _reader = new AztecReader(); - else if (barcodeType == "BOXES") _reader = new FieldsToFillDetector(); - else if (barcodeType == "CODABAR") _reader = new CodabarReader(); - else if (barcodeType == "CODABLOCKF") _reader = new CodaBlockFReader(); - else if (barcodeType == "CODE128") _reader = new Code128Reader(); - else if (barcodeType == "CODE16K") _reader = new Code16KReader(); - else if (barcodeType == "CODE39") _reader = new Code39Reader(); - else if (barcodeType == "CODE39_LINEARREADER") _reader = new Code39LinearReader() { }; - else if (barcodeType == "CODE39_EX") _reader = new Code39Reader() { UsePatternFinderNoiseRowEx = true }; - else if (barcodeType == "CODE39_NOISE1") _reader = new Code39Reader() { NoiseLevel = 1 }; - else if (barcodeType == "CODE93") _reader = new Code93Reader(); - else if (barcodeType == "DM") _reader = new DMReader(); - else if (barcodeType == "DM_DPM") _reader = new DM_DPM_Reader(); - else if (barcodeType == "EAN13") _reader = new EAN13LinearReader() { }; - else if (barcodeType == "EAN2") _reader = new EAN2LinearReader() { }; - else if (barcodeType == "EAN5") _reader = new EAN5LinearReader() { }; - else if (barcodeType == "EAN8") _reader = new EAN8LinearReader() { }; - else if (barcodeType == "EAN_UPC_OLD") _reader = new EANReader_old() { FindUpce = true, FindEan13 = true, FindEan2 = true, FindEan5 = true, FindEan8 = true, FindUpca = true }; - else if (barcodeType == "GS1DATABAREXP") _reader = new GS1DataBarExpanded(); - else if (barcodeType == "GS1DATABAREXPSTACKED") _reader = new GS1DataBarExpandedStacked(); - else if (barcodeType == "GS1DATABAROMNI") _reader = new GS1DataBarOmnidirectional(); - else if (barcodeType == "GS1DATABARSTACKED") _reader = new GS1DataBarStacked(); - else if (barcodeType == "GS1DATABARSTACKEDOMNI") _reader = new GS1DataBarStacked(); - else if (barcodeType == "GS1DATABARLIMITED") _reader = new GS1DataBarLimited(); - else if (barcodeType == "HORIZONTALLINES") _reader = new HorizontalLinesDecoder(); - else if (barcodeType == "I2OF5") _reader = new I2of5Reader(); - else if (barcodeType == "IM") _reader = new IMReader(); - else if (barcodeType == "KIX") _reader = new KIXReader(); - else if (barcodeType == "LINETABLES") _reader = new LineTablesDetector(); - else if (barcodeType == "MAXICODE") _reader = new MaxiCodeReader(); - else if (barcodeType == "MICR") - { - _reader = new MICRReader(); //throw new Exception("MICR is not enabled! Pleae fix!"); //_reader = new MICR(); - //(reader as MICRReader).RestrictHorizontal = true; - //(reader as MICRReader).MinDigitCount = 5; // change from 15 to 10 min digit count (will search for blocks with 7 numbers or more) - //(reader as MICRReader).MinRad = 5; // set min radius to 5 so it will read images like CanadianChequeSamplePAR.png - - } - else if (barcodeType == "MICROPDF") _reader = new MicroPDFReader(); - else if (barcodeType == "MSI") _reader = new MSIReader(); - else if (barcodeType == "OMRCIRCLE") _reader = new FormOMRCircle(); - else if (barcodeType == "OMRCIRCLE_EXT") _reader = new FormOMRCircle() { ExtendedMode = true }; - else if (barcodeType == "OMROVAL") _reader = new FormOMROval(); - else if (barcodeType == "OMROVAL_EXT") _reader = new FormOMROval() { ExtendedMode = true }; - else if (barcodeType == "OMRSQUARE") _reader = new FormOMRSquare(); - else if (barcodeType == "OMRSQUARE_EXT") _reader = new FormOMRSquare() { ExtendedMode = true }; - else if (barcodeType == "OMRSQUARELPATTERN") _reader = new FormOMRSquareLPattern(); - else if (barcodeType == "OMRRECTANGLE") _reader = new FormOMRRectangle(); - else if (barcodeType == "OMRRECTANGLE_EXT") _reader = new FormOMRRectangle() { ExtendedMode = true }; - else if (barcodeType == "OMRRECTANGLELPATTERNVERT") _reader = new FormOMRRectangleLPatternVert(); - else if (barcodeType == "OMRRECTANGLELPATTERNHORIZ") _reader = new FormOMRRectangleLPatternHoriz(); - else if (barcodeType == "PATCH") _reader = new PatchCodeReader(); - else if (barcodeType == "PHARMA") _reader = new PharmaReader(); - else if (barcodeType == "PDF417") _reader = new PDF417Reader(); - else if (barcodeType == "POSTCODE") _reader = new PostCodeReader(); - else if (barcodeType == "POSTNET") _reader = new PostNetReader(); - else if (barcodeType == "QR") _reader = new QRReader(); - else if (barcodeType == "RAWOMR") _reader = new RawSlicer(); - else if (barcodeType == "RM") _reader = new RMReader(); - else if (barcodeType == "VERTICALLINES") _reader = new VerticalLinesDecoder(); - else if (barcodeType == "UPC_A") _reader = new UPCALinearReader() { }; - else if (barcodeType == "UPC_E") _reader = new UPCELinearReader() { }; - else if (barcodeType == "TRIOPTIC") _reader = new TriOpticLinearReader(); - else throw new Exception("unknown decoder type"); - } - - - /// - /// Output results in string[] form - /// - private List listFoundBarcodesAsStrings = new List(); - private List listFoundBarcodesAsRectangles = new List(); - - /// - /// returns found barcodes as an array of strings - /// - /// - public string[] GetFoundBarcodesAsStrings() - { - return listFoundBarcodesAsStrings.ToArray(); - } - - public SKRect[] GetFoundBarcodesAsRectangles() - { - return listFoundBarcodesAsRectangles.ToArray(); - } - - - /// - /// Contains exception message - /// - public string ExceptionMessage = null; - - /// - /// Indicates if got exception last time decoding bitmap - /// - public bool GotException = false; - - /// - /// last decoding time - /// - public long LastDecodingTime = 0; - - /// - /// Runs the decoding - /// - /// input image - /// true if decoded successfully (see FoundBarcodes property) or false (see ExceptionMessage for more info) - public bool Decode(SKBitmap bitmap) - { - try - { - if (_configFile != null) - ApplyConfig(_reader, _configFile); - - if (_reader is QRReader) - { - (_reader as QRReader).MergePartialBarcodes = true; - } - - // reset - LastDecodingTime = 0; - - FoundBarcode[] foundBarcodes = null; - BlackAndWhiteImage bwImage; - - if (_reader is SymbologyReader2D) - { - bwImage = new BlackAndWhiteImage(bitmap, 1, (_reader as SymbologyReader2D).ThresholdFilterMethodToUse, 24); - Stopwatch sw = new Stopwatch(); - sw.Start(); - foundBarcodes = (_reader as SymbologyReader2D).Decode(bwImage); - LastDecodingTime = sw.ElapsedMilliseconds; - } - else // if (reader is SymbologyReader) - { - bwImage = new BlackAndWhiteImage(bitmap, 1, ThresholdFilterMethod.Block, 24); - Stopwatch sw = new Stopwatch(); - sw.Start(); - foundBarcodes = (_reader as SymbologyReader).Decode(bwImage); - LastDecodingTime = sw.ElapsedMilliseconds; - } - - listFoundBarcodesAsStrings.Clear(); - listFoundBarcodesAsRectangles.Clear(); - - // Sort results by Y, then X - Array.Sort(foundBarcodes, (x, y) => - { - float result = x.Rect.Top - y.Rect.Top; - - if (result == 0f) - result = x.Rect.Left - y.Rect.Left; - - return (int)result; - }); - - if (foundBarcodes != null) - { - foreach (FoundBarcode barcode in foundBarcodes) - { - StringBuilder tag = new StringBuilder(); - - // Add cells for detected tables - if (_reader is LineTablesDetector) - { - foreach (ArrayList row in (ArrayList)barcode.Tag) - tag.AppendFormat(" {0}", row.Count); - } - - listFoundBarcodesAsStrings.Add(barcode.Value + tag); - - if (barcode.Rect.IsEmpty && barcode.Polygon != null) - { - // Get bounding rectangle from barcode polygon - /* - byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - GraphicsPath path = new GraphicsPath(barcode.Polygon, pointTypes); - barcode.Rect = Rectangle.Round(path.GetBounds()); - */ - // Assuming barcode.Polygon is a PointF[] - SKPointI[] polygon = barcode.Polygon; - - var path = new SKPath(); - - // Move to first point - path.MoveTo(polygon[0].X, polygon[0].Y); - - // Draw lines to remaining points - for (int i = 1; i < polygon.Length; i++) - { - path.LineTo(polygon[i].X, polygon[i].Y); - } - - // Optional: close the shape - path.Close(); - - // Get bounds - SKRect bounds = path.Bounds; - - // Set barcode.Rect as a System.Drawing.Rectangle - barcode.Rect = new SKRect(bounds.Left, bounds.Top, bounds.Left+bounds.Width, bounds.Top+bounds.Height); - } - - listFoundBarcodesAsRectangles.Add(barcode.Rect); - } - } - - GotException = false; - - return listFoundBarcodesAsStrings.Count > 0; - - } - catch (Exception e) - { - GotException = true; - listFoundBarcodesAsStrings.Clear(); - ExceptionMessage = $"{e.GetType().ToString()}, {e.Message}\r\nException:\r\n{e.InnerException.ToString()}"; - return false; - } - } - - private void ApplyConfig(object reader, string configFile) - { - StreamReader textReader = File.OpenText(configFile); - - while (true) - { - var line = textReader.ReadLine(); - if (line == null) - break; - - line = line.Trim(); - if (line.Length == 0) - continue; - - string[] argument = line.Split(new[] { '=' }, StringSplitOptions.RemoveEmptyEntries); - if (argument.Length >= 1 && argument.Length <= 2) - { - // Split by dot assuming we can have a chain, - // e.g. `MidProperty1.MidProperty2.Property` or `MidProperty1.MidProperty2.Method()` - string[] propertyChain = argument[0].Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); - object @object = reader; - Type type = reader.GetType(); - - for (int i = 0; i < propertyChain.Length; i++) - { - string memberName = propertyChain[i]; - - // recurse mid properties - if (i < propertyChain.Length - 1) - { - PropertyInfo propertyInfo = type.GetProperty(memberName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy); - if (propertyInfo == null) - throw new Exception($"Property \"{memberName}\" does not exist in \"{type}\""); - - @object = propertyInfo.GetValue(@object, null); - type = @object.GetType(); - } - else // process the last part of the chain: a property or method - { - if (argument.Length == 2) // .Property=Value - { - PropertyInfo propertyInfo = type.GetProperty(memberName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy); - if (propertyInfo == null) - throw new Exception($"Property \"{memberName}\" does not exist in \"{type}\""); - - try - { - TypeConverter typeConverter = TypeDescriptor.GetConverter(propertyInfo.PropertyType); - object value = typeConverter.ConvertFromInvariantString(argument[1]); - propertyInfo.SetValue(@object, value, BindingFlags.FlattenHierarchy, null, null, CultureInfo.InvariantCulture); - } - catch (Exception exception) - { - throw new Exception($"Could not set value \"{argument[1]}\" to property \"{argument[0]}\"", exception); - } - } - else if (memberName.EndsWith("()", StringComparison.Ordinal)) // .Method() - { - string methodName = memberName.Substring(0, memberName.Length - 2); - MethodInfo methodInfo = type.GetMethod(methodName, new Type[0]); - if (methodInfo == null) - throw new Exception($"Method \"{methodName}\" does not exist in \"{type}\""); - - try - { - methodInfo.Invoke(@object, null); - } - catch (Exception exception) - { - throw new Exception($"Could not invoke method \"{argument[0]}\"", exception); - } - } - else throw new Exception($"Invalid config argument \"{line}\""); - } - } - - Console.WriteLine($"Configuration: {line}"); - } - else throw new Exception($"Invalid config argument \"{line}\""); - } - } - } - - /// - /// Barcode Writer Class - /// - public class BarcodeWriter - { - private readonly SymbologyType _symbologyType = SymbologyType.Unknown; - private readonly object _encoder = null; - - static BarcodeWriter() - { - Utils.InitApp(); - } - public BarcodeWriter(string barcodeType) - { - switch (barcodeType) - { - case "CODE128": _symbologyType = SymbologyType.Code128; break; - case "CODE39": _symbologyType = SymbologyType.Code39; break; - case "POSTNET": _symbologyType = SymbologyType.Postnet; break; - case "UPC_A": _symbologyType = SymbologyType.UPCA; break; // UPC-A - case "EAN8": _symbologyType = SymbologyType.EAN8; break; - case "CODABAR": _symbologyType = SymbologyType.Codabar; break; - case "I2OF5": _symbologyType = SymbologyType.I2of5; break; - case "CODE93": _symbologyType = SymbologyType.Code93; break; - case "EAN13": _symbologyType = SymbologyType.EAN13; break; - case "JAN13": _symbologyType = SymbologyType.JAN13; break; - case "BOOKLAND": _symbologyType = SymbologyType.Bookland; break; - case "UPC_E": _symbologyType = SymbologyType.UPCE; break; // UPC-E - case "PDF417": _symbologyType = SymbologyType.PDF417; break; - case "PDF417_TRUNCATED": _symbologyType = SymbologyType.PDF417Truncated; break; - case "DM": _symbologyType = SymbologyType.DataMatrix; break; - case "QR": _symbologyType = SymbologyType.QRCode; break; - case "AZTEC": _symbologyType = SymbologyType.Aztec; break; - case "PLANET": _symbologyType = SymbologyType.Planet; break; - case "EAN128": _symbologyType = SymbologyType.EAN128; break; - case "GS1_128": _symbologyType = SymbologyType.GS1_128; break; - case "USPSSACKLABEL": _symbologyType = SymbologyType.USPSSackLabel; break; - case "USPSTRAYLABEL": _symbologyType = SymbologyType.USPSTrayLabel; break; - case "DEUTSCHEPOSTIDENTCODE": _symbologyType = SymbologyType.DeutschePostIdentcode; break; - case "DEUTSCHEPOSTLEITCODE": _symbologyType = SymbologyType.DeutschePostLeitcode; break; - case "NUMLY": _symbologyType = SymbologyType.Numly; break; - case "PZN": _symbologyType = SymbologyType.PZN; break; - case "OPTICALPRODUCT": _symbologyType = SymbologyType.OpticalProduct; break; - case "SWISSPOSTPARCEL": _symbologyType = SymbologyType.SwissPostParcel; break; - case "RM": _symbologyType = SymbologyType.RoyalMail; break; // Royal Mail - case "DUTCHKIX": _symbologyType = SymbologyType.DutchKix; break; - case "SINGAPORE": _symbologyType = SymbologyType.SingaporePostalCode; break; - case "EAN2": _symbologyType = SymbologyType.EAN2; break; - case "EAN5": _symbologyType = SymbologyType.EAN5; break; - case "EAN14": _symbologyType = SymbologyType.EAN14; break; - case "MACROPDF417": _symbologyType = SymbologyType.MacroPDF417; break; - case "MICROPDF417": _symbologyType = SymbologyType.MicroPDF417; break; - case "GS1DATAMATRIX": _symbologyType = SymbologyType.GS1_DataMatrix; break; - case "TELEPEN": _symbologyType = SymbologyType.Telepen; break; - case "IM": _symbologyType = SymbologyType.IntelligentMail; break; - case "GS1DATABAROMNI": _symbologyType = SymbologyType.GS1_DataBar_Omnidirectional; break; - case "GS1DATABARTRUNCATED": _symbologyType = SymbologyType.GS1_DataBar_Truncated; break; - case "GS1DATABARSTACKED": _symbologyType = SymbologyType.GS1_DataBar_Stacked; break; - case "GS1DATABARSTACKEDOMNI": _symbologyType = SymbologyType.GS1_DataBar_Stacked_Omnidirectional; break; - case "GS1DATABARLIMITED": _symbologyType = SymbologyType.GS1_DataBar_Limited; break; - case "GS1DATABAREXP": _symbologyType = SymbologyType.GS1_DataBar_Expanded; break; - case "MAXICODE": _symbologyType = SymbologyType.MaxiCode; break; - case "PLESSEY": _symbologyType = SymbologyType.Plessey; break; - case "MSI": _symbologyType = SymbologyType.MSI; break; - case "ITF14": _symbologyType = SymbologyType.ITF14; break; - case "GTIN12": _symbologyType = SymbologyType.GTIN12; break; - case "GTIN8": _symbologyType = SymbologyType.GTIN8; break; - case "GTIN13": _symbologyType = SymbologyType.GTIN13; break; - case "GTIN14": _symbologyType = SymbologyType.GTIN14; break; - case "GS1_QRCODE": _symbologyType = SymbologyType.GS1_QRCode; break; // Tri-Optic - case "PHARMA": _symbologyType = SymbologyType.PharmaCode; break; - default: _symbologyType = SymbologyType.Unknown; break; - } - } - - public SKBitmap Encode( - string contents, - SKEncodedImageFormat imageFormat = SKEncodedImageFormat.Png, - int width = 0, - int height = 0, - string characterSet = null, - bool disableEci = false, - bool forceFitToRect = false, - bool pureBarcode = false, - int marginLeft = 0, - int marginTop = 0, - int marginRight = 0, - int marginBottom = 0, - int barHeight = 0, - int narrowBarWidth = 0 - ) - { - if (contents == null) - { - return null; - } - if (_symbologyType == SymbologyType.Unknown) - { - throw new Exception("Unknown symbology type"); - } - - using (BarcodeEncoder encoder = new BarcodeEncoder()) - { - encoder.Value = contents; - - encoder.Symbology = _symbologyType; - encoder.DrawCaption = !pureBarcode; - encoder.Margins = new Margins(marginLeft, marginTop, marginRight, marginBottom); - encoder.DrawQuietZones = false; - - if (barHeight > 0) - encoder.BarHeight = barHeight; - else if (height > 0) - encoder.BarHeight = height; - - if (forceFitToRect == true && width > 0 && height > 0) - { - encoder.PreserveMinReadableSize = false; - encoder.FitInto(width, height); - } - - if (narrowBarWidth > 0) - { - encoder.NarrowBarWidth = narrowBarWidth; - } - else - { - switch (_symbologyType) - { - case SymbologyType.PDF417: - case SymbologyType.PDF417Truncated: - case SymbologyType.DataMatrix: - case SymbologyType.QRCode: - case SymbologyType.Aztec: - case SymbologyType.MicroPDF417: - case SymbologyType.MacroPDF417: - case SymbologyType.GS1_DataMatrix: - case SymbologyType.GS1_QRCode: - case SymbologyType.MaxiCode: - encoder.NarrowBarWidth = 3; - break; - default: - encoder.NarrowBarWidth = 1; - break; - } - } - - SKBitmap image = encoder.GetImage(); - - return image; - } - } - - public void Encode( - string imageFile, - string contents, - SKEncodedImageFormat imageFormat = SKEncodedImageFormat.Png, - int width = 0, - int height = 0, - string characterSet = null, - bool disableEci = false, - bool forceFitToRect = false, - bool pureBarcode = false, - int marginLeft = 0, - int marginTop = 0, - int marginRight = 0, - int marginBottom = 0, - int barHeight = 0, - int narrowBarWidth = 0 - ) - { - if (contents == null) - { - return; - } - if (_symbologyType == SymbologyType.Unknown) - { - throw new Exception("Unknown symbology type"); - } - - using (BarcodeEncoder encoder = new BarcodeEncoder()) - { - encoder.Value = contents; - encoder.Symbology = _symbologyType; - encoder.DrawCaption = !pureBarcode; - encoder.Margins = new Margins(marginLeft, marginTop, marginRight, marginBottom); - encoder.DrawQuietZones = false; - - if (barHeight > 0) - encoder.BarHeight = barHeight; - else if (height > 0) - encoder.BarHeight = height; - - if (forceFitToRect == true && width > 0 && height > 0) - { - encoder.PreserveMinReadableSize = false; - encoder.FitInto(width, height); - } - - if (narrowBarWidth > 0) - { - encoder.NarrowBarWidth = narrowBarWidth; - } - else - { - switch (_symbologyType) - { - case SymbologyType.PDF417: - case SymbologyType.PDF417Truncated: - case SymbologyType.DataMatrix: - case SymbologyType.QRCode: - case SymbologyType.Aztec: - case SymbologyType.MicroPDF417: - case SymbologyType.MacroPDF417: - case SymbologyType.GS1_DataMatrix: - case SymbologyType.GS1_QRCode: - case SymbologyType.MaxiCode: - encoder.NarrowBarWidth = 3; - break; - default: - encoder.NarrowBarWidth = 1; - break; - } - } - - if (forceFitToRect == false) - encoder.SaveImage(imageFile, imageFormat); - else - encoder.SaveImage(imageFile, imageFormat, new SKSize(width, height), 0, 0); - } - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockFilter.cs b/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockFilter.cs deleted file mode 100644 index 1b80e0dd..00000000 --- a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockFilter.cs +++ /dev/null @@ -1,377 +0,0 @@ -using System; -using System.Diagnostics; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core -{ - internal class BlackAndWhiteBlockFilterOld: BlackAndWhiteBlockFilter - { - public BlackAndWhiteBlockFilterOld(IPreparedImage image, int thresholdLevelAdjustment) - : base(image, thresholdLevelAdjustment) - { - // setting the super block size to 1 so it will not be used actually - SUPERBLOCK_SIZE = 1; - } - } - -#if !OLD_GRAYSCALEIMAGE - /// - /// Converts given grayscale image into a black and white (1 bit) image. - /// This filter operates on blocks of 8x8 pixels and calculates - /// threshold on 5x5 groups of blocks. This technique allows proper - /// binarization of images with severe luminance differences. - /// - internal class BlackAndWhiteBlockFilter : IBlackAndWhiteFilter, IParallelSupporting - { - - // size of the block where average luminance is calculated - private const int BLOCK_SIZE = 8; - // superblock size to normalize block luminance - // set to 1 or 0 to disable - protected int SUPERBLOCK_SIZE = 6; - //difference between the darkest and brighter pixel in a block to consider - //it as an homogeneous block (all black or all white). - protected int ThresholdLevelAdjustment; - - protected IPreparedImage _image; - protected XBitArray[] _rows; - private XBitArray[] _columns; - - public BlackAndWhiteBlockFilter(IPreparedImage image, int thresholdLevelAdjustment) - { - _image = image; - ThresholdLevelAdjustment = thresholdLevelAdjustment; - - _rows = new XBitArray[_image.Height]; - _columns = new XBitArray[_image.Width]; - - for (int i = 0; i < _rows.Length; i++) - _rows[i] = new XBitArray(_image.Width); - - binarizeEntireImage(); - } - - public XBitArray GetRow(int y) - { - return _rows[y]; - } - - public void ResetColumns() - { - lock (_columns) - { - for (int x = 0; x < _image.Width; x++) - _columns[x] = null; - } - } - - public XBitArray GetColumn(int x) - { - lock (_columns) - { - if (_columns[x] == null) - { - XBitArray column = new XBitArray(_image.Height); - - //now fill the column - for (int j = 0; j < column.Size; j++) - { - column[j] = GetRow(j)[x]; - } - - _columns[x] = column; - } - - return _columns[x]; - } - } - - protected virtual void binarizeEntireImage() - { - if (IsSourceImageAlreadyBinarized()) - { - return; - } - - int width = _image.Width; - int height = _image.Height; - int blockCountX = width / BLOCK_SIZE; - if ((width % BLOCK_SIZE) != 0) - blockCountX++; - - int blockCountY = height / BLOCK_SIZE; - if ((height % BLOCK_SIZE) != 0) - blockCountY++; - - int[][] blackPoints = calculateBlackPoints(blockCountX, blockCountY); - - calculateThresholdForBlock(blockCountX, blockCountY, blackPoints); - } - - private bool IsSourceImageAlreadyBinarized() - { - var isBinarized = true; - - //try to read pixels and fill rows - Parallel.For(0, _image.Height, -1, 100, (pair) => - { - for (var y = pair.From; y < pair.To; y++) - { - var row = _image.GetRow(y); - var outRow = _rows[y]; - - for (var x = 0; x < row.Length; x++) - { - var pixel = row[x]; - if (pixel > 0 && pixel < 255) - { - isBinarized = false; - break; - } - else - { - outRow[x] = pixel == 0; - } - } - } - }); - - return isBinarized; - } - - private int[][] calculateBlackPoints(int blockCountX, int blockCountY) - { - int width = _image.Width; - int height = _image.Height; - - - int[][] blackPoints = new int[blockCountY][]; - for (int i = 0; i < blackPoints.Length; i++) - blackPoints[i] = new int[blockCountX]; - - Parallel.For(0, blockCountY, (blockY) => - { - int startRow = blockY * BLOCK_SIZE; - if ((startRow + BLOCK_SIZE) >= height) - startRow = height - BLOCK_SIZE; - - int startY = startRow < 0 ? -startRow : 0; - - int min = 255; - int max = 0; - - for (int blockX = 0; blockX < blockCountX; blockX++) - { - int startColumn = blockX * BLOCK_SIZE; - if ((startColumn + BLOCK_SIZE) >= width) - startColumn = width - BLOCK_SIZE; - - int startX = startColumn < 0 ? -startColumn : 0; - - int sum = 0; - min = 255; - max = 0; - for (int y = startY; y < BLOCK_SIZE; y++) - { - byte[] row = _image.GetRow(startRow + y); - for (int x = startX; x < BLOCK_SIZE; x++) - { - int pixel = row[startColumn + x] & 0xff; - sum += pixel; - if (pixel < min) - min = pixel; - - if (pixel > max) - max = pixel; - } - } - - int average; - - var count = (BLOCK_SIZE - startX) * (BLOCK_SIZE - startY); - - if (max - min > ThresholdLevelAdjustment) - { - average = sum / count; // calculating average from blocks - } - else - { - // When min == max == 0, let average be 1 so all is black - //average = max == 0 ? 1 : min / 2; - if (max < 50) average=max*2+1; - else if (min > 200) average = min/2-1; - else average = min / 2; - //average = 100; - } - - blackPoints[blockY][blockX] = average; - } - } - ); - - return blackPoints; - } - - // For each 8x8 block in the image, calculate the average black point using a 5x5 grid - // of the blocks around it. Also handles the corner cases. - private void calculateThresholdForBlock(int blockCountX, int blockCountY, int[][] blackPoints) - { - int width = _image.Width; - int height = _image.Height; - - double[,] blocksAverages = new double[blockCountY, blockCountX]; - double[,] superBlocksAverages = null; - //int[,] blocksAverages = new int[blockCountY, blockCountX]; - //int[,] superBlocksAverages = null; - - - // if superblock size defined > 1 - if (SUPERBLOCK_SIZE>1) - superBlocksAverages = new double[blockCountY / SUPERBLOCK_SIZE +1, blockCountX / SUPERBLOCK_SIZE + 1]; - //superBlocksAverages = new int[blockCountY / SUPERBLOCK_SIZE + 1, blockCountX / SUPERBLOCK_SIZE + 1]; - - // calculating averages in each block - Parallel.For(0, blockCountY, (y) => - { - int startRow = y * BLOCK_SIZE; - if ((startRow + BLOCK_SIZE) >= height) - startRow = height - BLOCK_SIZE; - - for (int x = 0; x < blockCountX; x++) - { - int startColumn = x * BLOCK_SIZE; - if ((startColumn + BLOCK_SIZE) >= width) - startColumn = width - BLOCK_SIZE; - - int left = (x > 1) ? x : 2; - left = (left < blockCountX - 2) ? left : blockCountX - 3; - - int top = (y > 1) ? y : 2; - top = (top < blockCountY - 2) ? top : blockCountY - 3; - - int sum = 0; - int sumCount = 0; - for (int z = -2; z <= 2; z++) - { - if ((top + z) < 1 || left-2 <0) - continue; - - int[] blackRow = blackPoints[top + z]; - sum += blackRow[left - 2]; - sum += blackRow[left - 1]; - sum += blackRow[left]; - sum += blackRow[left + 1]; - sum += blackRow[left + 2]; - // count how much members we have for the sum - sumCount += 5; - } - - double divider = 25d; // emperic value! with default settings we should divide by 20 not 25 but it is not working for some noisty images so we use 25 - - // if using BlockOld type then finding the exact average value - if (SUPERBLOCK_SIZE == 1) - divider = sumCount * 1d; - - double average = sum / divider; - - if(sumCount == 0) - blocksAverages[y, x] = blackPoints[y][x]; - else - //double average = sum / sumCount * 1d; // finding the average value from all components in the sum - blocksAverages[y, x] = average; - } - } - ); - - int actualSuperBlockCountY = blockCountY / SUPERBLOCK_SIZE; - int actualSuperBlockCountX = blockCountX / SUPERBLOCK_SIZE; - - bool useSuperBlocks = SUPERBLOCK_SIZE > 1 && actualSuperBlockCountY > 2 && actualSuperBlockCountX > 2; - - if (useSuperBlocks) - { - // calculating averages in superblocks (1 superblock per 4 blocks) - for (int y = 0; y < blockCountY; y++) - { - for (int x = 0; x < blockCountX; x++) - { - double aver = blocksAverages[y,x]; - //int aver = blocksAverages[y, x]; - int superX = x / SUPERBLOCK_SIZE; - int superY = y / SUPERBLOCK_SIZE; - superBlocksAverages[superY, superX] += aver; - } - } - - // set averages in superblocks to average value (average per 4 blocks) - for (int y = 0; y < blockCountY / SUPERBLOCK_SIZE + 1; y++) - { - for (int x = 0; x < blockCountX / SUPERBLOCK_SIZE + 1; x++) - { - superBlocksAverages[y, x] = superBlocksAverages[y, x] / SUPERBLOCK_SIZE / SUPERBLOCK_SIZE; - } - } - - } - - // now binarizing image - Parallel.For(0, blockCountY, (y) => - { - int startRow = y * BLOCK_SIZE; - if ((startRow + BLOCK_SIZE) >= height) - startRow = height - BLOCK_SIZE; - - for (int x = 0; x < blockCountX; x++) - { - int startColumn = x * BLOCK_SIZE; - if ((startColumn + BLOCK_SIZE) >= width) - startColumn = width - BLOCK_SIZE; - - double average = blocksAverages[y, x]; - //int average = blocksAverages[y, x]; - - // if superblock size > 1 - if (useSuperBlocks) - { - // read superblock average - int superX = x / SUPERBLOCK_SIZE; - int superY = y / SUPERBLOCK_SIZE; - double delta = superBlocksAverages[superY, superX]; - - // calculating average between block and superblock - // and rounding to greatest value - average = (int)Math.Round((average + delta) / 2d, 0, MidpointRounding.AwayFromZero); - } - - binarizeBlock(startRow, startColumn, average); - } - } - ); - } - - private void binarizeBlock(int startRow, int startColumn, double threshold) - //private void binarizeBlock(int startRow, int startColumn, int threshold) - { - int startY = startRow < 0 ? -startRow : 0; - int startX = startColumn < 0 ? -startColumn : 0; - - for (int y = startY; y < BLOCK_SIZE; y++) - { - byte[] row = _image.GetRow(startRow + y); - XBitArray bits = _rows[startRow + y]; - for (int x = startX; x < BLOCK_SIZE; x++) - { - int pixel = row[startColumn + x] & 0xff; - bits[startColumn + x] = (threshold - pixel * 1d) > double.Epsilon; - //bits[startColumn + x] = (pixel < threshold); - } - } - } - - public bool IsParallelSupported - { - get { return true; } - } - } -#endif -} diff --git a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockFilterLegacy.cs b/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockFilterLegacy.cs deleted file mode 100644 index eeaafdb3..00000000 --- a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockFilterLegacy.cs +++ /dev/null @@ -1,285 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeReader.Core -{ - /// - /// Converts given grayscale image into a black and white (1 bit) image. - /// This filter operates on blocks of 8x8 pixels and calculates - /// threshold on 5x5 groups of blocks. This technique allows proper - /// binarization of images with severe luminance differences. - /// - internal class BlackAndWhiteBlockFilterLegacy : IBlackAndWhiteFilter - { - // size of the block where average luminance is calculated - private const int BLOCK_SIZE = 8; - - // superblock size to normalize block luminance - // set to 1 or 0 to disable - private const int SUPERBLOCK_SIZE = 6; - - private IPreparedImage _image; - private XBitArray[] _rows; - private XBitArray[] _columns; - - public BlackAndWhiteBlockFilterLegacy(IPreparedImage image, int thresholdLevelAdjustment) - { - _image = image; - } - - public XBitArray GetRow(int y) - { - if (_rows == null) - { - _rows = new XBitArray[_image.Height]; - for (int i = 0; i < _rows.Length; i++) - _rows[i] = new XBitArray(_image.Width); - - binarizeEntireImage(); - } - - return _rows[y]; - } - - public XBitArray GetColumn(int x) - { - if (_columns == null) - { - _columns = new XBitArray[_image.Width]; - } - - if (_columns[x] == null) - _columns[x] = new XBitArray(_image.Height); - else - return _columns[x]; - - XBitArray column = _columns[x]; - - // now fill the column - for (int j = 0; j < column.Size; j++) - { - column[j] = GetRow(j)[x]; - } - - return column; - } - - public void ResetColumns() - { - lock (_columns) - { - for (int x = 0; x < _image.Width; x++) - _columns[x] = null; - } - } - - private void binarizeEntireImage() - { - int width = _image.Width; - int height = _image.Height; - int blockCountX = width / BLOCK_SIZE; - if ((width % BLOCK_SIZE) != 0) - blockCountX++; - - int blockCountY = height / BLOCK_SIZE; - if ((height % BLOCK_SIZE) != 0) - blockCountY++; - - int[][] blackPoints = calculateBlackPoints(blockCountX, blockCountY); - - calculateThresholdForBlock(blockCountX, blockCountY, blackPoints); - } - - private int[][] calculateBlackPoints(int blockCountX, int blockCountY) - { - int width = _image.Width; - int height = _image.Height; - - - int[][] blackPoints = new int[blockCountY][]; - for (int i = 0; i < blackPoints.Length; i++) - blackPoints[i] = new int[blockCountX]; - - for (int blockY = 0; blockY < blockCountY; blockY++) - { - int startRow = blockY * BLOCK_SIZE; - if ((startRow + BLOCK_SIZE) >= height) - startRow = height - BLOCK_SIZE; - - int min = 255; - int max = 0; - - for (int blockX = 0; blockX < blockCountX; blockX++) - { - int startColumn = blockX * BLOCK_SIZE; - if ((startColumn + BLOCK_SIZE) >= width) - startColumn = width - BLOCK_SIZE; - - int sum = 0; - min = 255; - max = 0; - for (int y = 0; y < BLOCK_SIZE; y++) - { - byte[] row = _image.GetRow(startRow + y); - for (int x = 0; x < BLOCK_SIZE; x++) - { - int pixel = row[startColumn + x] & 0xff; - sum += pixel; - if (pixel < min) - min = pixel; - - if (pixel > max) - max = pixel; - } - } - - int average; - - if (max - min > 24) - { - average = sum >> 6; - } - else - { - // When min == max == 0, let average be 1 so all is black - //average = max == 0 ? 1 : min >> 1; - if (max < 50) average = max * 2 + 1; - else if (min > 200) average = min / 2 - 1; - else average = min >> 1; - //average = 100; - } - - blackPoints[blockY][blockX] = average; - } - } - - return blackPoints; - } - - // For each 8x8 block in the image, calculate the average black point using a 5x5 grid - // of the blocks around it. Also handles the corner cases. - private void calculateThresholdForBlock(int blockCountX, int blockCountY, int[][] blackPoints) - { - int width = _image.Width; - int height = _image.Height; - - int[,] blocksAverages = new int[blockCountY, blockCountX]; - int[,] superBlocksAverages = null; - - // if superblock size defined > 1 - if (SUPERBLOCK_SIZE > 1) - superBlocksAverages = new int[blockCountY / SUPERBLOCK_SIZE + 1, blockCountX / SUPERBLOCK_SIZE + 1]; - - // calculating averages in each block - for (int y = 0; y < blockCountY; y++) - { - int startRow = y * BLOCK_SIZE; - if ((startRow + BLOCK_SIZE) >= height) - startRow = height - BLOCK_SIZE; - - for (int x = 0; x < blockCountX; x++) - { - int startColumn = x * BLOCK_SIZE; - if ((startColumn + BLOCK_SIZE) >= width) - startColumn = width - BLOCK_SIZE; - - int left = (x > 1) ? x : 2; - left = (left < blockCountX - 2) ? left : blockCountX - 3; - - int top = (y > 1) ? y : 2; - top = (top < blockCountY - 2) ? top : blockCountY - 3; - - int sum = 0; - for (int z = -2; z <= 2; z++) - { - if ((top + z) < 1 || left - 2 < 0) - continue; - - int[] blackRow = blackPoints[top + z]; - sum += blackRow[left - 2]; - sum += blackRow[left - 1]; - sum += blackRow[left]; - sum += blackRow[left + 1]; - sum += blackRow[left + 2]; - } - - int average = sum / 25; - blocksAverages[y, x] = average; - } - } - - int actualSuperBlockCountY = blockCountY / SUPERBLOCK_SIZE; - int actualSuperBlockCountX = blockCountX / SUPERBLOCK_SIZE; - - bool useSuperBlocks = SUPERBLOCK_SIZE > 1 && actualSuperBlockCountY > 2 && actualSuperBlockCountX > 2; - - if (useSuperBlocks) - { - // calculating averages in superblocks (1 superblock per 4 blocks) - for (int y = 0; y < blockCountY; y++) - { - for (int x = 0; x < blockCountX; x++) - { - int aver = blocksAverages[y, x]; - int superX = x / SUPERBLOCK_SIZE; - int superY = y / SUPERBLOCK_SIZE; - superBlocksAverages[superY, superX] += aver; - } - } - - // set averages in superblocks to average value (average per 4 blocks) - for (int y = 0; y < blockCountY / SUPERBLOCK_SIZE + 1; y++) - { - for (int x = 0; x < blockCountX / SUPERBLOCK_SIZE + 1; x++) - { - superBlocksAverages[y, x] = superBlocksAverages[y, x] / SUPERBLOCK_SIZE / SUPERBLOCK_SIZE; - } - } - } - - // now binarizing image - for (int y = 0; y < blockCountY; y++) - { - int startRow = y * BLOCK_SIZE; - if ((startRow + BLOCK_SIZE) >= height) - startRow = height - BLOCK_SIZE; - - for (int x = 0; x < blockCountX; x++) - { - int startColumn = x * BLOCK_SIZE; - if ((startColumn + BLOCK_SIZE) >= width) - startColumn = width - BLOCK_SIZE; - - int average = blocksAverages[y, x]; - // if superblock size > 1 - if (useSuperBlocks) - { - // read superblock average - int superX = x / SUPERBLOCK_SIZE; - int superY = y / SUPERBLOCK_SIZE; - float delta = superBlocksAverages[superY, superX]; - - // calculating average between block and superblock - average = (int) Math.Round((average + delta) / 2f, 0, MidpointRounding.AwayFromZero); - } - - binarizeBlock(startRow, startColumn, average); - } - } - } - - private void binarizeBlock(int startRow, int startColumn, int threshold) - { - for (int y = 0; y < BLOCK_SIZE; y++) - { - byte[] row = _image.GetRow(startRow + y); - XBitArray bits = _rows[startRow + y]; - for (int x = 0; x < BLOCK_SIZE; x++) - { - int pixel = row[startColumn + x] & 0xff; - bits[startColumn + x] = (pixel < threshold); - } - } - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockFilter_old.cs b/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockFilter_old.cs deleted file mode 100644 index 82e43e55..00000000 --- a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockFilter_old.cs +++ /dev/null @@ -1,310 +0,0 @@ -using System; -using System.Diagnostics; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core -{ - -#if OLD_GRAYSCALEIMAGE - - /// - /// Converts given grayscale image into a black and white (1 bit) image. - /// This filter operates on blocks of 8x8 pixels and calculates - /// threshold on 5x5 groups of blocks. This technique allows proper - /// binarization of images with severe luminance differences. - /// - internal class BlackAndWhiteBlockFilter : IBlackAndWhiteFilter - { - - // size of the block where average luminance is calculated - private const int BLOCK_SIZE = 8; - // superblock size to normalize block luminance - // set to 1 or 0 to disable - protected int SUPERBLOCK_SIZE = 6; - //difference between the darkest and brighter pixel in a block to consider - //it as an homogeneous block (all black or all white). - protected int ThresholdLevelAdjustment; - - protected IPreparedImage _image; - protected XBitArray[] _rows; - private XBitArray[] _columns; - - public BlackAndWhiteBlockFilter(IPreparedImage image, int thresholdLevelAdjustment) - { - var sw = Stopwatch.StartNew(); - - _image = image; - ThresholdLevelAdjustment = thresholdLevelAdjustment; - - _rows = new XBitArray[_image.Height]; - _columns = new XBitArray[_image.Width]; - - for (int i = 0; i < _rows.Length; i++) - _rows[i] = new XBitArray(_image.Width); - - binarizeEntireImage(); - - sw.Stop(); - Console.WriteLine(sw.Elapsed); - } - - public XBitArray GetRow(int y) - { - return _rows[y]; - } - - public XBitArray GetColumn(int x) - { - lock (_columns) - { - if (_columns[x] == null) - { - XBitArray column = new XBitArray(_image.Height); - - //now fill the column - for (int j = 0; j < column.Size; j++) - { - column[j] = GetRow(j)[x]; - } - - _columns[x] = column; - } - - return _columns[x]; - } - } - - protected virtual void binarizeEntireImage() - { - int width = _image.Width; - int height = _image.Height; - int blockCountX = width / BLOCK_SIZE; - if ((width % BLOCK_SIZE) != 0) - blockCountX++; - - int blockCountY = height / BLOCK_SIZE; - if ((height % BLOCK_SIZE) != 0) - blockCountY++; - - int[][] blackPoints = calculateBlackPoints(blockCountX, blockCountY); - - calculateThresholdForBlock(blockCountX, blockCountY, blackPoints); - } - - private int[][] calculateBlackPoints(int blockCountX, int blockCountY) - { - int width = _image.Width; - int height = _image.Height; - - - int[][] blackPoints = new int[blockCountY][]; - for (int i = 0; i < blackPoints.Length; i++) - blackPoints[i] = new int[blockCountX]; - - for (int blockY = 0; blockY < blockCountY; blockY++) - { - int startRow = blockY * BLOCK_SIZE; - if ((startRow + BLOCK_SIZE) >= height) - startRow = height - BLOCK_SIZE; - - int min = 255; - int max = 0; - - for (int blockX = 0; blockX < blockCountX; blockX++) - { - int startColumn = blockX * BLOCK_SIZE; - if ((startColumn + BLOCK_SIZE) >= width) - startColumn = width - BLOCK_SIZE; - - int sum = 0; - min = 255; - max = 0; - for (int y = 0; y < BLOCK_SIZE; y++) - { - byte[] row = _image.GetRow(startRow + y); - for (int x = 0; x < BLOCK_SIZE; x++) - { - int pixel = row[startColumn + x] & 0xff; - sum += pixel; - if (pixel < min) - min = pixel; - - if (pixel > max) - max = pixel; - } - } - - int average; - - if (max - min > ThresholdLevelAdjustment) - { - average = sum / (BLOCK_SIZE * BLOCK_SIZE); // calculating average from blocks - } - else - { - // When min == max == 0, let average be 1 so all is black - //average = max == 0 ? 1 : min / 2; - if (max < 50) average=max*2+1; - else if (min > 200) average = min/2-1; - else average = min / 2; - //average = 100; - } - - blackPoints[blockY][blockX] = average; - } - } - - return blackPoints; - } - - // For each 8x8 block in the image, calculate the average black point using a 5x5 grid - // of the blocks around it. Also handles the corner cases. - private void calculateThresholdForBlock(int blockCountX, int blockCountY, int[][] blackPoints) - { - int width = _image.Width; - int height = _image.Height; - - double[,] blocksAverages = new double[blockCountY, blockCountX]; - double[,] superBlocksAverages = null; - //int[,] blocksAverages = new int[blockCountY, blockCountX]; - //int[,] superBlocksAverages = null; - - - // if superblock size defined > 1 - if (SUPERBLOCK_SIZE>1) - superBlocksAverages = new double[blockCountY / SUPERBLOCK_SIZE +1, blockCountX / SUPERBLOCK_SIZE + 1]; - //superBlocksAverages = new int[blockCountY / SUPERBLOCK_SIZE + 1, blockCountX / SUPERBLOCK_SIZE + 1]; - - // calculating averages in each block - for (int y = 0; y < blockCountY; y++) - { - int startRow = y * BLOCK_SIZE; - if ((startRow + BLOCK_SIZE) >= height) - startRow = height - BLOCK_SIZE; - - for (int x = 0; x < blockCountX; x++) - { - int startColumn = x * BLOCK_SIZE; - if ((startColumn + BLOCK_SIZE) >= width) - startColumn = width - BLOCK_SIZE; - - int left = (x > 1) ? x : 2; - left = (left < blockCountX - 2) ? left : blockCountX - 3; - - int top = (y > 1) ? y : 2; - top = (top < blockCountY - 2) ? top : blockCountY - 3; - - int sum = 0; - int sumCount = 0; - for (int z = -2; z <= 2; z++) - { - if ((top + z) < 1 || left-2 <0) - continue; - - int[] blackRow = blackPoints[top + z]; - sum += blackRow[left - 2]; - sum += blackRow[left - 1]; - sum += blackRow[left]; - sum += blackRow[left + 1]; - sum += blackRow[left + 2]; - // count how much members we have for the sum - sumCount += 5; - } - - double divider = 25d; // emperic value! with default settings we should divide by 20 not 25 but it is not working for some noisty images so we use 25 - - // if using BlockOld type then finding the exact average value - if (SUPERBLOCK_SIZE == 1) - divider = sumCount * 1d; - - double average = sum / divider; - - //double average = sum / sumCount * 1d; // finding the average value from all components in the sum - blocksAverages[y, x] = average; - } - } - - int actualSuperBlockCountY = blockCountY / SUPERBLOCK_SIZE; - int actualSuperBlockCountX = blockCountX / SUPERBLOCK_SIZE; - - bool useSuperBlocks = SUPERBLOCK_SIZE > 1 && actualSuperBlockCountY > 2 && actualSuperBlockCountX > 2; - - if (useSuperBlocks) - { - // calculating averages in superblocks (1 superblock per 4 blocks) - for (int y = 0; y < blockCountY; y++) - { - for (int x = 0; x < blockCountX; x++) - { - double aver = blocksAverages[y,x]; - //int aver = blocksAverages[y, x]; - int superX = x / SUPERBLOCK_SIZE; - int superY = y / SUPERBLOCK_SIZE; - superBlocksAverages[superY, superX] += aver; - } - } - - // set averages in superblocks to average value (average per 4 blocks) - for (int y = 0; y < blockCountY / SUPERBLOCK_SIZE + 1; y++) - { - for (int x = 0; x < blockCountX / SUPERBLOCK_SIZE + 1; x++) - { - superBlocksAverages[y, x] = superBlocksAverages[y, x] / SUPERBLOCK_SIZE / SUPERBLOCK_SIZE; - } - } - - } - - // now binarizing image - for (int y = 0; y < blockCountY; y++) - { - int startRow = y * BLOCK_SIZE; - if ((startRow + BLOCK_SIZE) >= height) - startRow = height - BLOCK_SIZE; - - for (int x = 0; x < blockCountX; x++) - { - int startColumn = x * BLOCK_SIZE; - if ((startColumn + BLOCK_SIZE) >= width) - startColumn = width - BLOCK_SIZE; - - double average = blocksAverages[y, x]; - //int average = blocksAverages[y, x]; - - // if superblock size > 1 - if (useSuperBlocks) - { - // read superblock average - int superX = x / SUPERBLOCK_SIZE; - int superY = y / SUPERBLOCK_SIZE; - double delta = superBlocksAverages[superY, superX]; - - // calculating average between block and superblock - // and rounding to greatest value - average = (int)Math.Round((average + delta) / 2d, 0, MidpointRounding.AwayFromZero); - } - - binarizeBlock(startRow, startColumn, average); - } - } - } - - private void binarizeBlock(int startRow, int startColumn, double threshold) - //private void binarizeBlock(int startRow, int startColumn, int threshold) - { - for (int y = 0; y < BLOCK_SIZE; y++) - { - byte[] row = _image.GetRow(startRow + y); - XBitArray bits = _rows[startRow + y]; - for (int x = 0; x < BLOCK_SIZE; x++) - { - int pixel = row[startColumn + x] & 0xff; - bits[startColumn + x] = (threshold - pixel * 1d) > double.Epsilon; - //bits[startColumn + x] = (pixel < threshold); - } - } - } - } -#endif - -} diff --git a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockGridFilter.cs b/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockGridFilter.cs deleted file mode 100644 index 0f666620..00000000 --- a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockGridFilter.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core -{ - /// - /// Applies special "grid" filter to binarized image. - /// Use it to remove thin single lines and single pixels from image. - /// - class BlackAndWhiteBlockGridFilter : BlackAndWhiteBlockFilter - { - public int SmoothRadius { get; set; } = 6; //5 - public float Sensitivity { get; set; } = 0.3f; //0.4 - - public BlackAndWhiteBlockGridFilter(IPreparedImage image, int thresholdLevelAdjustment) : base(image, thresholdLevelAdjustment) - { - } - - protected override void binarizeEntireImage() - { - base.binarizeEntireImage(); - Smooth(); - } - - private void Smooth() - { - var w = _image.Width; - var h = _image.Height; - - //build integral image - var integral = IntegralBuilder.Build(this, w, h); - - //parameters - var r = SmoothRadius; - var size = 2 * r + 1; - var lim1 = Sensitivity * size * size; - var step = 1; - - //scan pixels - Parallel.For(r, h - r, (y) => - { - var row = GetRow(y); - for (int x = r; x < w - r; x += step) - { - if (!row[x]) continue;//white pixel - - //get sum of pixels around - var sum = IntegralBuilder.GetSum(integral, x - r, y - r, size, size); - if (sum <= lim1) - { - //thin line or single pixel -> erase pixel - row[x] = false; - }; - } - } - ); - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockMedianFilter.cs b/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockMedianFilter.cs deleted file mode 100644 index 4a7e58a5..00000000 --- a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockMedianFilter.cs +++ /dev/null @@ -1,313 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace BarcodeReader.Core -{ - /// - /// Applies median filter to grayscale or binarized image. - /// Use it to remove thin lines and single pixels from image. - /// - class BlackAndWhiteBlockMedianFilter : BlackAndWhiteBlockFilter - { - public bool Prefiltering = false; - public bool Use8Connectivity = false; - - public BlackAndWhiteBlockMedianFilter(IPreparedImage image, int thresholdLevelAdjustment) : base(image, thresholdLevelAdjustment) - { - } - - protected override void binarizeEntireImage() - { - //prefiltering - if (Prefiltering) - { - if(Use8Connectivity) - SmoothGrayscale8(); - else - SmoothGrayscale4(); - } - - //binarization - base.binarizeEntireImage(); - - //postfiltering - if (!Prefiltering) - { - if (Use8Connectivity) - SmoothBinarized8(); - else - SmoothBinarized3(); - //SmoothBinarized5(); - } - - } - - struct Pixel - { - public int X; - public int Y; - public byte Val; - } - - private void SmoothGrayscale4() - { - var w = _image.Width; - var h = _image.Height; - - var toChange = new LinkedList(); - - var pixels = new byte[5]; - - for (int y = 1; y < h - 1; y++) - { - var row0 = _image.GetRow(y - 1); - var row1 = _image.GetRow(y); - var row2 = _image.GetRow(y + 1); - - for (int x = 1; x < w - 1; x++) - { - pixels[0] = row0[x]; - pixels[1] = row1[x - 1]; - pixels[2] = row1[x + 0]; - pixels[3] = row1[x + 1]; - pixels[4] = row2[x]; - - Array.Sort(pixels); - - var need = pixels[2]; - - if (need != row1[x]) - toChange.AddLast(new Pixel() {X = x, Y = y, Val = need}); - } - } - - foreach (var p in toChange) - _image.GetRow(p.Y)[p.X] = p.Val; - } - - private void SmoothGrayscale8() - { - var w = _image.Width; - var h = _image.Height; - - var toChange = new LinkedList(); - - var pixels = new byte[9]; - - for (int y = 1; y < h - 1; y++) - { - var row0 = _image.GetRow(y - 1); - var row1 = _image.GetRow(y); - var row2 = _image.GetRow(y + 1); - - for (int x = 1; x < w - 1; x++) - { - pixels[0] = row0[x - 1]; - pixels[1] = row0[x + 0]; - pixels[2] = row0[x + 1]; - pixels[3] = row1[x - 1]; - pixels[4] = row1[x + 0]; - pixels[5] = row1[x + 1]; - pixels[6] = row2[x - 1]; - pixels[7] = row2[x + 0]; - pixels[8] = row2[x + 1]; - - Array.Sort(pixels); - - var need = pixels[4]; - - if (need != row1[x]) - toChange.AddLast(new Pixel() { X = x, Y = y, Val = need }); - } - } - - foreach (var p in toChange) - _image.GetRow(p.Y)[p.X] = p.Val; - } - - private void SmoothBinarized3() - { - var w = _image.Width; - var h = _image.Height; - - var toFalse = new LinkedList(); - var toTrue = new LinkedList(); - - for (int y = 1; y < h - 2; y++) - { - var row0 = _rows[y - 1]; - var row1 = _rows[y + 0]; - var row2 = _rows[y + 1]; - var row3 = _rows[y + 2]; - - for (int x = 1; x < w - 1; x++) - { - var sum = 0; - if (row0[x]) sum++; - if (row1[x]) sum++; - if (row2[x]) sum++; - if (row3[x]) sum++; - - var need = sum > 1; - - if (row1[x + 0]) - { - if (!need) - toFalse.AddLast(new MyPoint(x, y)); - } - else - { - if (need) - toTrue.AddLast(new MyPoint(x, y)); - } - } - } - - foreach (var p in toTrue) - _rows[p.Y][p.X] = true; - - foreach (var p in toFalse) - _rows[p.Y][p.X] = false; - } - - private void SmoothBinarized5() - { - var w = _image.Width; - var h = _image.Height; - - var toFalse = new LinkedList(); - var toTrue = new LinkedList(); - - for (int y = 2; y < h - 2; y++) - { - var row0 = _rows[y - 2]; - var row1 = _rows[y - 1]; - var row2 = _rows[y + 0]; - var row3 = _rows[y + 1]; - var row4 = _rows[y + 2]; - - for (int x = 1; x < w - 1; x++) - { - var sum = 0; - if (row0[x]) sum++; - if (row1[x]) sum++; - if (row2[x]) sum++; - if (row3[x]) sum++; - if (row4[x]) sum++; - - var need = sum > 2; - - if (row2[x + 0]) - { - if (!need) - toFalse.AddLast(new MyPoint(x, y)); - } - else - { - if (need) - toTrue.AddLast(new MyPoint(x, y)); - } - } - } - - foreach (var p in toTrue) - _rows[p.Y][p.X] = true; - - foreach (var p in toFalse) - _rows[p.Y][p.X] = false; - } - - private void SmoothBinarized4() - { - var w = _image.Width; - var h = _image.Height; - - var toFalse = new LinkedList(); - var toTrue = new LinkedList(); - - for (int y = 1; y < h - 1; y++) - { - var row0 = _rows[y-1]; - var row1 = _rows[y]; - var row2 = _rows[y + 1]; - - for (int x = 1; x < w - 1; x++) - { - var sum = 0; - if (row0[x]) sum++; - if (row1[x - 1]) sum++; - if (row1[x + 0]) sum++; - if (row1[x + 1]) sum++; - if (row2[x]) sum++; - - var need = sum > 2; - - if (row1[x + 0]) - { - if (!need) - toFalse.AddLast(new MyPoint(x, y)); - }else - { - if (need) - toTrue.AddLast(new MyPoint(x, y)); - } - } - } - - foreach (var p in toTrue) - _rows[p.Y][p.X] = true; - - foreach (var p in toFalse) - _rows[p.Y][p.X] = false; - } - - private void SmoothBinarized8() - { - var w = _image.Width; - var h = _image.Height; - - var toFalse = new LinkedList(); - var toTrue = new LinkedList(); - - for (int y = 1; y < h - 1; y++) - { - var row0 = _rows[y - 1]; - var row1 = _rows[y]; - var row2 = _rows[y + 1]; - - for (int x = 1; x < w - 1; x++) - { - var sum = 0; - if (row0[x - 1]) sum++; - if (row0[x + 0]) sum++; - if (row0[x + 1]) sum++; - if (row1[x - 1]) sum++; - if (row1[x + 0]) sum++; - if (row1[x + 1]) sum++; - if (row2[x - 1]) sum++; - if (row2[x + 0]) sum++; - if (row2[x + 1]) sum++; - - var need = sum > 4; - - if (row1[x + 0]) - { - if (!need) - toFalse.AddLast(new MyPoint(x, y)); - } - else - { - if (need) - toTrue.AddLast(new MyPoint(x, y)); - } - } - } - - foreach (var p in toTrue) - _rows[p.Y][p.X] = true; - - foreach (var p in toFalse) - _rows[p.Y][p.X] = false; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockSmoothedFilter.cs b/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockSmoothedFilter.cs deleted file mode 100644 index 8dcb9fc6..00000000 --- a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBlockSmoothedFilter.cs +++ /dev/null @@ -1,82 +0,0 @@ -namespace BarcodeReader.Core -{ - /// - /// Removes thin lines and single pixels. - /// Use it for dotted DPM datamatrix. - /// - class BlackAndWhiteBlockSmoothedFilter : BlackAndWhiteBlockFilter - { - private int w; - private int h; - - public int SmoothRadius { get; set; } = 6; //6 - public float Sensitivity { get; set; } = 1.1f; //1.1 - - public BlackAndWhiteBlockSmoothedFilter(IPreparedImage image, int thresholdLevelAdjustment) : base(image, thresholdLevelAdjustment) - { - } - - protected override void binarizeEntireImage() - { - base.binarizeEntireImage(); - Smooth(); - } - - private void Smooth() - { - w = _image.Width; - h = _image.Height; - - var integral = IntegralBuilder.Build(this, w, h); - var size = SmoothRadius;//5/6 - var lim0 = size * size / 120f; - var lim1 = Sensitivity * size * size;//1.1//1.1 - var step = 1; - - //find corners - for (int y = size; y < h - size; y += step) - for (int x = size; x < w - size; x += step) - { - var pixel = IntegralBuilder.GetSum(integral, x, y, 1, 1); - if (pixel > 0) - continue; - - // s0 s1 - // s2 s3 - var s0 = IntegralBuilder.GetSum(integral, x - size, y - size, size, size); - var s1 = IntegralBuilder.GetSum(integral, x, y - size, size, size); - var s2 = IntegralBuilder.GetSum(integral, x - size, y, size, size); - var s3 = IntegralBuilder.GetSum(integral, x, y, size, size); - - if (s0 + s1 + s2 + s3 < lim1) continue; - - //if (s0 > lim0 && s1 > lim0 && s2 > lim0 && s3 > lim0) continue; - //if (s0 < lim0 || s1 < lim0 || s2 < lim0 || s3 < lim0) continue; - - FillCircle(x, y, size / 4); - - //DebugHelper.FillInNoScaled(Color.Red, 2/* + size / 2*/, new MyPoint(x, y)); - - //DebugHelper.DrawSquare(x, y, Color.Red); - } - } - - void FillCircle(int x, int y, int radius) - { - var r2 = radius * radius; - - for(int i = -radius; i <= radius; i++) - for (int j = -radius; j <= radius; j++) - { - if (i * i + j * j > r2) continue; - FillSafe(x + i, y + j); - } - } - - void FillSafe(int x, int y) - { - if (x < 0 || y < 0 || x >= w || y >= h) return; - _rows[y][x] = true; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBypassFilter.cs b/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBypassFilter.cs deleted file mode 100644 index bae4c1aa..00000000 --- a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteBypassFilter.cs +++ /dev/null @@ -1,66 +0,0 @@ -namespace BarcodeReader.Core -{ - internal class BlackAndWhiteBypassFilter : IBlackAndWhiteFilter - { - private readonly IPreparedImage _image; - private XBitArray[] _rows; - private XBitArray[] _columns; - - public BlackAndWhiteBypassFilter(IPreparedImage image) - { - _image = image; - - _rows = new XBitArray[_image.Height]; - _columns = new XBitArray[_image.Width]; - } - - public XBitArray GetRow(int y) - { - lock (_rows) - { - if (_rows[y] != null) - return _rows[y]; - - _rows[y] = new XBitArray(_image.Width); - XBitArray row = _rows[y]; - - byte[] imageRow = _image.GetRow(y); - - for (int x = 0; x < _image.Width; x++) - row[x] = imageRow[x] == 0; - - return row; - } - } - - public void ResetColumns() - { - lock (_columns) - { - for (int x = 0; x < _image.Width; x++) - _columns[x] = null; - } - } - - public XBitArray GetColumn(int x) - { - lock (_columns) - { - if (_columns[x] == null) - _columns[x] = new XBitArray(_image.Height); - else - return _columns[x]; - - XBitArray column = _columns[x]; - - // now fill the column - for (int y = 0; y < column.Size; y++) - { - column[y] = GetRow(y)[x]; - } - - return column; - } - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteFilter.cs b/MuPDF.NET/Barcode/Decoders/BlackAndWhiteFilter.cs deleted file mode 100644 index 60f50a94..00000000 --- a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteFilter.cs +++ /dev/null @@ -1,200 +0,0 @@ -using System; - -namespace BarcodeReader.Core -{ -#if CORE_DEV - public -#else - internal -#endif - interface IBlackAndWhiteFilter - { - XBitArray GetRow(int y); - XBitArray GetColumn(int y); - void ResetColumns(); - } - - /// - /// Converts given grayscale image into a black and white (1 bit) image. - /// -#if CORE_DEV - public -#else - internal -#endif - class BlackAndWhiteFilter : IBlackAndWhiteFilter - { - private const int LuminanceBits = 5; - private const int LuminanceShift = 8 - LuminanceBits; - private const int LuminanceBucketCount = 1 << LuminanceBits; - - private IPreparedImage _image; - private int[] _buckets = null; - private XBitArray[] _rows; - private XBitArray[] _columns; - - public BlackAndWhiteFilter(IPreparedImage image) - { - _image = image; - - _rows = new XBitArray[_image.Height]; - _columns = new XBitArray[_image.Width]; - } - - /// - /// Gets specified row of grayscale image converted to black and white. - /// - /// The row index. - /// The array that contains black and white row. - public XBitArray GetRow(int y) - { - lock (_rows) - { - int width = _image.Width; - - if (_rows[y] != null) - return _rows[y]; - - _rows[y] = new XBitArray(width); - XBitArray row = _rows[y]; - - if (_buckets == null) - _buckets = new int[LuminanceBucketCount]; - else - Array.Clear(_buckets, 0, _buckets.Length); - - byte[] grayScaleRow = _image.GetRow(y); - for (int x = 0; x < width; x++) - { - int pixel = grayScaleRow[x]; - _buckets[pixel >> LuminanceShift]++; - } - - bool isUnusable; - int blackPoint = estimateBlackPoint(_buckets, out isUnusable); - if (isUnusable) - return row; - - int left = grayScaleRow[0]; - int center = grayScaleRow[1]; - for (int x = 1; x < width - 1; x++) - { - int right = grayScaleRow[x + 1]; - - // -1 4 -1 box filter with a weight of 2. - int luminance = ((center << 2) - left - right) >> 1; - if (luminance < blackPoint) - row[x] = true; - - left = center; - center = right; - } - - return row; - } - } - - public void ResetColumns() - { - lock (_columns) - { - for (int x = 0; x < _image.Width; x++) - _columns[x] = null; - } - } - - /// - /// Gets specified column of grayscale image converted to black and white. - /// - /// The column index. - /// The array that contains black and white row. - public XBitArray GetColumn(int x) - { - lock (_columns) - { - if (_columns[x] == null) - _columns[x] = new XBitArray(_image.Height); - else - return _columns[x]; - - XBitArray column = _columns[x]; - - // now fill the column - for (int j = 0; j < column.Size; j++) - { - column[j] = GetRow(j)[x]; - } - - return column; - } - } - - private static int estimateBlackPoint(int[] buckets, out bool isUnusable) - { - // Find the tallest peak in the histogram. - int firstPeakIndex = 0; - int firstPeakValue = 0; - for (int x = 0; x < buckets.Length; x++) - { - if (buckets[x] > firstPeakValue) - { - firstPeakIndex = x; - firstPeakValue = buckets[x]; - } - } - - int maxBucketValue = firstPeakValue; - - // Find an another tallest peak (shorter than first) which is - // somewhat far from the tallest peak. - int secondPeakIndex = 0; - int secondPeakScore = 0; - for (int x = 0; x < buckets.Length; x++) - { - int distanceToFirst = x - firstPeakIndex; - // Encourage more distant second peaks by multiplying by square of distance. - int score = buckets[x] * distanceToFirst * distanceToFirst; - if (score > secondPeakScore) - { - secondPeakIndex = x; - secondPeakScore = score; - } - } - - // Make sure firstPeakIndex corresponds to the black peak. - if (firstPeakIndex > secondPeakIndex) - { - int temp = firstPeakIndex; - firstPeakIndex = secondPeakIndex; - secondPeakIndex = temp; - } - - // If there is too little contrast in the image to pick a - // meaningful black point, mark result as unusable. - if (secondPeakIndex - firstPeakIndex <= buckets.Length >> 4) - { - isUnusable = true; - return 0; - } - - // Find a valley between them that is low and closer to the white peak. - int bestValleyIndex = secondPeakIndex - 1; - int bestValleyScore = -1; - for (int x = secondPeakIndex - 1; x > firstPeakIndex; x--) - { - int distanceToFirst = x - firstPeakIndex; - int distanceToSecond = secondPeakIndex - x; - - int score = distanceToFirst * distanceToFirst * distanceToSecond * (maxBucketValue - buckets[x]); - if (score > bestValleyScore) - { - bestValleyIndex = x; - bestValleyScore = score; - } - } - - isUnusable = false; - return bestValleyIndex << LuminanceShift; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteImage.cs b/MuPDF.NET/Barcode/Decoders/BlackAndWhiteImage.cs deleted file mode 100644 index 11882ec7..00000000 --- a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteImage.cs +++ /dev/null @@ -1,597 +0,0 @@ -using System; -using SkiaSharp; -using System.Runtime.InteropServices; - -namespace BarcodeReader.Core -{ -#if CORE_DEV - public -#else - internal -#endif - class BlackAndWhiteImage : IDisposable - { - private int _width, _height; - private int _scanStep; - - private IPreparedImage _image; - private BlackAndWhiteImage _unrotated; - private XBitArray[] _rows, _columns; //cache of scanned rows and cols - - private bool _alreadyDisposed; - - private ThresholdFilterMethod _method; - private IBlackAndWhiteFilter _filter; - private int _thresholdLevelAdjustment; - - const float PI=(float)Math.PI; - private MyPointF p0; - private MyVectorF vdX, vdY; - private bool _rotated90; - - private BlackAndWhiteImage() - { - } - - public BlackAndWhiteImage(SKBitmap bmp, int scanStep, ThresholdFilterMethod method, int thresholdLevelAdjustment) : this(bmp, scanStep, method, thresholdLevelAdjustment, false) - { - - } - - - public BlackAndWhiteImage(SKBitmap bmp, int scanStep, ThresholdFilterMethod method, int thresholdLevelAdjustment, bool sourceImageIsBinarized) - { - _scanStep = scanStep; - _method = method; - _thresholdLevelAdjustment = thresholdLevelAdjustment; - - switch (_method) - { - case ThresholdFilterMethod.Block: - _image = new GrayscaleImage(bmp, scanStep, sourceImageIsBinarized); - _filter = new BlackAndWhiteBlockFilter(_image, thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.Threshold: - _image = new GrayscaleImage(bmp, scanStep, sourceImageIsBinarized); - _filter = new BlackAndWhiteThresholdFilter(_image, 127); - break; - case ThresholdFilterMethod.ThresholdEx: - _image = new GrayscaleImage(bmp, scanStep, sourceImageIsBinarized); - _filter = new BlackAndWhiteThresholdFilter(_image, thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.BlockSmoothed: - _image = new GrayscaleImage(bmp, scanStep); - _filter = new BlackAndWhiteBlockSmoothedFilter(_image, thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.BlockMedian: - _image = new GrayscaleImage(bmp, scanStep); - _filter = new BlackAndWhiteBlockMedianFilter(_image, thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.BlockGrid: - _image = new GrayscaleImage(bmp, scanStep); - _filter = new BlackAndWhiteBlockGridFilter(_image, thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.BlockOld: - _image = new GrayscaleImage(bmp, scanStep); - _filter = new BlackAndWhiteBlockFilterLegacy(_image, thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.Enhancing: - _image = new EnhancedBWImage(bmp /*, scanStep*/); - _filter = new BlackAndWhiteBypassFilter(_image); - break; - default: - _image = new GrayscaleImage(bmp, scanStep); - _filter = new BlackAndWhiteFilter(_image); - break; - } - - _width = _image.Width; - _height = _image.Height; - - vdX = new MyVectorF(1f, 0f); - vdY = new MyVectorF(0f, 1f); - } - - - //angle [0..360] but actually only 0..89 are enough (combined with 90) - //90 rotations are created just swaping rows by columns, thus much faster. - /* - public BlackAndWhiteImage(BlackAndWhiteImage image, float degAngle) - { - //normalize [0..360] - while (degAngle >= 360F) degAngle -= 360F; - while (degAngle < 0F) degAngle += 360F; - - _thresholdLevelAdjustment = image._thresholdLevelAdjustment; - _scanStep = image._scanStep; //keep same scanStep as the original image - if (_scanStep == 1) - { - _unrotated = image; - if (degAngle == 90F) - { - _rotated90 = true; - } - else - { - _rotated90 = false; - CalculateRotatedBorders(image.Width, image.Height, degAngle, out _width, out _height, out p0, out vdX, out vdY); - } - _method = ThresholdFilterMethod.Rotated; - _rows = new XBitArray[_height]; - _columns = new XBitArray[_width]; - } - else - { - _image = new GrayscaleImage(image._image.SKBitmap, image._scanStep, degAngle); - _width = _image.Width; - _height = _image.Height; - - _method = image._method; - - // since the rotation made the prepared BW pixels gray again, set Block filter for it. - if (_method == ThresholdFilterMethod.Enhancing) - _method = ThresholdFilterMethod.Block; - - switch (_method) - { - case ThresholdFilterMethod.Block: - _filter = new BlackAndWhiteBlockFilter(_image, _thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.Threshold: - _filter = new BlackAndWhiteThresholdFilter(_image, 127); - break; - case ThresholdFilterMethod.BlockSmoothed: - _filter = new BlackAndWhiteBlockSmoothedFilter(_image, _thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.BlockMedian: - _filter = new BlackAndWhiteBlockMedianFilter(_image, _thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.BlockGrid: - _filter = new BlackAndWhiteBlockGridFilter(_image, _thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.BlockOld: - _filter = new BlackAndWhiteBlockFilterLegacy(_image, _thresholdLevelAdjustment); - break; - default: - _filter = new BlackAndWhiteFilter(_image); - break; - } - - vdX = new MyVectorF(1f, 0f); - vdY = new MyVectorF(0f, 1f); - } - } - */ - public BlackAndWhiteImage(BlackAndWhiteImage image, float degAngle) - { - // Normalize angle to [0..360) - while (degAngle >= 360f) degAngle -= 360f; - while (degAngle < 0f) degAngle += 360f; - - _thresholdLevelAdjustment = image._thresholdLevelAdjustment; - _scanStep = image._scanStep; // Keep same scanStep - - if (_scanStep == 1) - { - _unrotated = image; - - if (degAngle == 90f) - { - _rotated90 = true; - } - else - { - _rotated90 = false; - - CalculateRotatedBorders(image.Width, image.Height, degAngle, - out _width, out _height, out p0, out vdX, out vdY); - } - - _method = ThresholdFilterMethod.Rotated; - _rows = new XBitArray[_height]; - _columns = new XBitArray[_width]; - } - else - { - // ?? Replace System.Drawing.Bitmap with SKBitmap directly - _image = new GrayscaleImage(image._image.SKBitmap, image._scanStep, degAngle); - _width = _image.Width; - _height = _image.Height; - - _method = image._method; - - // Since rotated grayscale image is not thresholded, reset method - if (_method == ThresholdFilterMethod.Enhancing) - _method = ThresholdFilterMethod.Block; - - switch (_method) - { - case ThresholdFilterMethod.Block: - _filter = new BlackAndWhiteBlockFilter(_image, _thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.Threshold: - _filter = new BlackAndWhiteThresholdFilter(_image, 127); - break; - case ThresholdFilterMethod.BlockSmoothed: - _filter = new BlackAndWhiteBlockSmoothedFilter(_image, _thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.BlockMedian: - _filter = new BlackAndWhiteBlockMedianFilter(_image, _thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.BlockGrid: - _filter = new BlackAndWhiteBlockGridFilter(_image, _thresholdLevelAdjustment); - break; - case ThresholdFilterMethod.BlockOld: - _filter = new BlackAndWhiteBlockFilterLegacy(_image, _thresholdLevelAdjustment); - break; - default: - _filter = new BlackAndWhiteFilter(_image); - break; - } - - // Identity transform vectors - vdX = new MyVectorF(1f, 0f); - vdY = new MyVectorF(0f, 1f); - } - } - - public BlackAndWhiteImage Clone() - { - BlackAndWhiteImage copy = new BlackAndWhiteImage(); - copy._width = _width; - copy._height = _height; - copy._scanStep = _scanStep; - copy._image = _image == null ? null : _image.Clone(); - copy._unrotated = _unrotated; - copy._alreadyDisposed = _alreadyDisposed; - copy._method = _method; - copy._filter = _filter; - copy._thresholdLevelAdjustment = _thresholdLevelAdjustment; - copy.p0 = new MyPointF(p0.X, p0.Y); - copy.vdX = new MyVectorF(vdY.X, vdY.Y); - copy._rotated90 = _rotated90; - - if (_rows == null) - copy._rows = null; - else - { - copy._rows = new XBitArray[_rows.Length]; - for (var i = 0; i < _rows.Length; i++) - { - if (_rows[i] != null) - copy._rows[i] = _rows[i].Clone(); - } - } - - if (_columns == null) - copy._columns = null; - else - { - copy._columns = new XBitArray[_columns.Length]; - for (var i = 0; i < _columns.Length; i++) - { - if (_columns[i] != null) - copy._columns[i] = _columns[i].Clone(); - } - } - - return copy; - } - - public static void CalculateRotatedBorders(int width, int height, float degAngle, out int rotatedWidth, out int rotatedHeight, out MyPointF p0, out MyVectorF vdX, out MyVectorF vdY) - { - p0 = new MyPointF(); - float angle = PI * degAngle / 180F; //convert degree to radians - - float w = (float)width; - float h = (float)height; - float cosA = (float)Math.Cos(angle); - float sinA = (float)Math.Sin(angle); - - //origin p0 and main directions vdX, vdY - vdX.X = cosA; - vdX.Y = sinA; - vdY.X = -vdX.Y; //rotated 90 - vdY.Y = vdX.X; - float l; - if (angle <= PI / 2F) - { - l = w * sinA; - p0.X = l * sinA; - p0.Y = -l * cosA; - rotatedWidth = (int)(w * cosA + h * sinA); - rotatedHeight = (int)(h * cosA + w * sinA); - } - else if (angle < PI) - { - l = -h * cosA; - p0.X = w + l * sinA; - p0.Y = -l * cosA; - rotatedWidth = (int)(h * sinA - w * cosA); - rotatedHeight = (int)(w * sinA - h * cosA); - } - else if (angle < 3F * PI / 2F) - { - l = -w * sinA; - p0.X = w + l * sinA; - p0.Y = h - l * cosA; - rotatedWidth = -(int)(w * cosA + h * sinA); - rotatedHeight = -(int)(h * cosA + w * sinA); - } - else - { - l = h * cosA; - p0.X = l * sinA; - p0.Y = h - l * cosA; - rotatedWidth = -(int)(h * sinA - w * cosA); - rotatedHeight = -(int)(w * sinA - h * cosA); - } - } - - public void PostprocessResults(FoundBarcode[] barcodes) - { - if (_scanStep > 1) - { - foreach (FoundBarcode b in barcodes) - { - SKPointI[] poly = b.Polygon; - for (int i = 0; i < poly.Length; i++) poly[i].Y *= _scanStep; - } - } - } - - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (!_alreadyDisposed) - { - if (disposing) - { - if (_image != null) - _image.Dispose(); - } - - _image = null; - _alreadyDisposed = true; - } - } - - public void Save(string fileName) - { -#if DEBUG_IMAGE - SKBitmap output = GetAsBitmap(); - output.Save(fileName); - output.Dispose(); -#endif - } - - public SKBitmap GetAsBitmap() - { - // Use 8-bit grayscale format - var imageInfo = new SKImageInfo(Width, Height, SKColorType.Gray8, SKAlphaType.Opaque); - SKBitmap output = new SKBitmap(imageInfo); - - for (int y = 0; y < Height; y++) - { - XBitArray row = GetRow(y); - - for (int x = 0; x < Width; x++) - { - // White = 255, Black = 0 - byte value = row[x] ? (byte)255 : (byte)0; - output.SetPixel(x, y, new SKColor(value, value, value)); - } - } - - return output; - } - /* - public SKBitmap GetAsBitmap() { - - SKBitmap output = new SKBitmap(Width, Height, PixelFormat.Format1bppIndexed); - byte[] scan1bpp = null; - - for (int y = 0; y < Height; y++) - { - SKRect imgRect = new SKRect(0, y, 0+Width, y+1); - BitmapData imgData = output.LockBits(imgRect, ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed); - - if (scan1bpp == null) - scan1bpp = new byte[imgData.Stride]; - - Array.Clear(scan1bpp, 0, scan1bpp.Length); - XBitArray row = GetRow(y); - - for (int x = 0; x < Width; x++) - { - if (!row[x]) - scan1bpp[x / 8] |= (byte)(0x80 >> (x % 8)); - } - - Marshal.Copy(scan1bpp, 0, imgData.Scan0, scan1bpp.Length); - output.UnlockBits(imgData); - } - - return output; - } - */ - - /// - /// Gets the width of the image. - /// - /// The width of the image. - public int Width - { - get - { - return _rotated90?_unrotated.Height:_width; - } - } - - /// - /// Gets the height of the image. - /// - /// The height of the image. - public int Height - { - get - { - return _rotated90?_unrotated.Width: _height; - } - } - - public bool In(MyPoint p, int tolerance) - { - return p.X >= -tolerance && p.X < Width + tolerance && - p.Y >= -tolerance && p.Y < Height + tolerance; - } - public bool In(MyPoint p) { return In(p.X, p.Y); } - public bool In(int x, int y) { return InX(x) && InY(y); } - public bool InX(int x) { return x >= 0 && x < Width; } - public bool InY(int y) { return y >= 0 && y < Height; } - - /// - /// Gets specified row of the image. - /// - /// The row index. - /// The array that contains black and white row. - public XBitArray GetRow(int y) - { - switch (_method) - { - case ThresholdFilterMethod.Rotated: - { - if (_rotated90) - return _unrotated.GetColumn(Height - 1 - y); - if (_rows[y] == null) - return _rows[y] = ScanRotatedRow(y); - return _rows[y]; - } - case ThresholdFilterMethod.None: - return _rows[y]; - default: - return _filter.GetRow(y); - } - - return null; - } - - /// - /// Interpolated value - /// - public float GetPixelInterpolated(float x, float y) - { - var i = (int)x; - var j = (int)y; - var kx = x - i; - var ky = y - j; - - if (i + 1 >= Width || j + 1 >= Height) return 127; - - var row0 = GetRow(j); - var row1 = GetRow(j + 1); - - var v00 = row0[i] ? -1 : 1; - var v10 = (row0[i + 1] ? -1 : 1) - v00;//right - var v01 = (row1[i] ? -1 : 1) - v00;//bottom - var v11 = (row1[i + 1] ? -1 : 1) - v00;//bottom-right - - return v00 + v10 * kx * (1 - ky) + v01 * (1 - kx) * ky + v11 * kx * ky; - } - - XBitArray ScanRotatedRow(int y) - { - XBitArray row = new XBitArray(_width); - MyPointF a = p0 + vdY * (float)y; - for (int i = 0; i < _width; i++) - { - row[i] = _unrotated.IsBlack(a); - a += vdX; - } - return row; - } - - XBitArray ScanRotatedColumn(int x) - { - XBitArray column = new XBitArray(_height); - for (int i = 0 ; i < _height; i++) - { - if (_rows[i] == null) _rows[i] = ScanRotatedRow(i); - column[i] = _rows[i][x]; - } - return column; - } - - public SKPointI Unrotate(MyPointF p) - { - SKPointI Q = new SKPointI(); - if (_rotated90) - Q = _unrotated.Unrotate(new MyPointF(Height -1 -p.Y, p.X)); - else - Q=p0 + vdX * p.X + vdY * p.Y; - return Q; - } - - public SKPointI[] Unrotate(SKPointI[] ps) - { - SKPointI[] r = new SKPointI[ps.Length]; - for (int i = 0; i < ps.Length; i++) - r[i] = Unrotate(ps[i]); - return r; - } - - public SKPointI[] Unrotate(SKRect rect) - { - SKPointI[] polygon = new SKPointI[5]; - polygon[0] = Unrotate(new MyPointF(rect.Left, rect.Top)); - polygon[1] = Unrotate(new MyPointF(rect.Right, rect.Top)); - polygon[2] = Unrotate(new MyPointF(rect.Right, rect.Bottom)); - polygon[3] = Unrotate(new MyPointF(rect.Left, rect.Bottom)); - polygon[4] = polygon[0]; - return polygon; - } - - public bool IsBlack(MyPoint p) - { - if (p.Y >= 0 && p.Y < Height && p.X >= 0 && p.X < Width) return GetRow(p.Y)[p.X]; - return false; //by default white - } - - public void ResetColumns() - { - _filter.ResetColumns(); - } - - public XBitArray GetColumn(int x) - { - switch (_method) - { - default: - return _filter.GetColumn(x); - case ThresholdFilterMethod.Rotated: - if (_rotated90) - return _unrotated.GetRow(x).Reverse(); - if (_columns[x] == null) - return _columns[x] = ScanRotatedColumn(x); - return _columns[x]; - } - return null; - } - - public int ThresholdLevelAdjustment - { - get { return _thresholdLevelAdjustment; } - set { _thresholdLevelAdjustment = value; } - } - - public bool IsParallelSupported - { - get { return (_filter is IParallelSupporting) ? (_filter as IParallelSupporting).IsParallelSupported : false; } - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteThresholdFilter.cs b/MuPDF.NET/Barcode/Decoders/BlackAndWhiteThresholdFilter.cs deleted file mode 100644 index 337c7df3..00000000 --- a/MuPDF.NET/Barcode/Decoders/BlackAndWhiteThresholdFilter.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Diagnostics; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core -{ - /// - /// Converts given grayscale image into a black and white (1 bit) image. - /// This filter make simple cutoff with predefined threshlod level. - /// - internal class BlackAndWhiteThresholdFilter : IBlackAndWhiteFilter, IParallelSupporting - { - //cutoff threshold - protected readonly int ThresholdLevel = 127; - - protected IPreparedImage _image; - protected XBitArray[] _rows; - private XBitArray[] _columns; - - public BlackAndWhiteThresholdFilter(IPreparedImage image, int thresholdLevel = 127) - { - _image = image; - ThresholdLevel = thresholdLevel; - - _rows = new XBitArray[_image.Height]; - _columns = new XBitArray[_image.Width]; - - for (int i = 0; i < _rows.Length; i++) - _rows[i] = new XBitArray(_image.Width); - - binarizeEntireImage(); - } - - public XBitArray GetRow(int y) - { - return _rows[y]; - } - - public void ResetColumns() - { - lock (_columns) - { - for (int x = 0; x < _image.Width; x++) - _columns[x] = null; - } - } - - public XBitArray GetColumn(int x) - { - lock (_columns) - { - if (_columns[x] == null) - { - XBitArray column = new XBitArray(_image.Height); - - //now fill the column - for (int j = 0; j < column.Size; j++) - { - column[j] = GetRow(j)[x]; - } - - _columns[x] = column; - } - - return _columns[x]; - } - } - - protected virtual void binarizeEntireImage() - { - //try to read pixels and fill rows - Parallel.For(0, _image.Height, -1, 100, (pair) => - { - for (var y = pair.From; y < pair.To; y++) - { - var row = _image.GetRow(y); - var outRow = _rows[y]; - - for (var x = 0; x < row.Length; x++) - { - if(row[x] < ThresholdLevel) - outRow[x] = true; - } - } - }); - } - - public bool IsParallelSupported - { - get { return true; } - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/Decoders.csproj b/MuPDF.NET/Barcode/Decoders/Decoders.csproj deleted file mode 100644 index 5e9cedb9..00000000 --- a/MuPDF.NET/Barcode/Decoders/Decoders.csproj +++ /dev/null @@ -1,248 +0,0 @@ - - - - true - bin\DEBUG_IMAGE\ - TRACE;DEBUG;CORE_DEV;NET40;DEBUG_IMAGE - full - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - - Debug - AnyCPU - {EBD90B99-9CA8-4B33-97B0-52172542A0E8} - Library - Properties - BarcodeReader.Core - Decoders - v2.0 - 512 - - - - true - full - false - bin\Debug\ - TRACE;DEBUG;CORE_DEV;NET40 - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE;CORE_DEV; - prompt - 4 - false - - - - - C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Core.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/EnhancedBWImage.cs b/MuPDF.NET/Barcode/Decoders/EnhancedBWImage.cs deleted file mode 100644 index aae2fdca..00000000 --- a/MuPDF.NET/Barcode/Decoders/EnhancedBWImage.cs +++ /dev/null @@ -1,274 +0,0 @@ -using System; -using SkiaSharp; - -namespace BarcodeReader.Core -{ - internal class EnhancedBWImage : IPreparedImage - { - private byte[][] _samples; - - public int nBilateralFilter = 1;// This can be increased to 2 for better edge, but will add to computation - - public int[][] sharpFilter = { new int[] { -1, -1, -1 }, new int[] { -1, 12, -1 }, new int[] { -1, -1, -1 } }; - public int filterWeight = 4; - public int EDGE_FILTER_THRESOLD = 32;// default 32. higher value results in smoothing of image - - public SKBitmap SKBitmap { get; private set; } - public int Width { get; private set; } - public int Height { get; private set; } - - private EnhancedBWImage() - { - } - - public EnhancedBWImage(SKBitmap sourceBitmap) - { - _samples = Process(sourceBitmap); - } - - public IPreparedImage Clone() - { - EnhancedBWImage copy = new EnhancedBWImage(); - - if (_samples == null) - copy._samples = null; - else - { - copy._samples = new byte[_samples.Length][]; - for (var i = 0; i < _samples.Length; i++) - { - if (_samples[i] != null) - { - copy._samples[i] = new byte[_samples[i].Length]; - Array.Copy(_samples[i], copy._samples[i], _samples[i].Length); - } - } - } - - copy.nBilateralFilter = nBilateralFilter; - copy.filterWeight = filterWeight; - copy.EDGE_FILTER_THRESOLD = EDGE_FILTER_THRESOLD; - copy.SKBitmap = SKBitmap == null ? null : (SKBitmap) SKBitmap.Copy(); - copy.Width = Width; - copy.Height = Height; - - return copy; - } - - public void Dispose() - { - } - - public byte[] GetRow(int y) - { - if (y < 0 || y >= Height) - throw new ArgumentException(); - - return _samples[y]; - } - - public byte[][] Process(SKBitmap bitmap) - { - Width = bitmap.Width; - Height = bitmap.Height; - - // Clone input - SKBitmap tmpBitmap = bitmap.Copy(); - - // Convert SKBitmap to byte[] in RGB format - int bytesPerPixel = 3; - int nChannels = bytesPerPixel; - int stride = Width * bytesPerPixel; - byte[] imageBytes = new byte[Height * stride]; - - for (int y = 0; y < Height; y++) - { - for (int x = 0; x < Width; x++) - { - SKColor color = tmpBitmap.GetPixel(x, y); - int index = y * stride + x * bytesPerPixel; - imageBytes[index] = color.Red; - imageBytes[index + 1] = color.Green; - imageBytes[index + 2] = color.Blue; - } - } - - // Apply image processing steps - imageBytes = Sharpen(imageBytes, Height, Width, nChannels, bytesPerPixel, stride); - - for (int f = 0; f < nBilateralFilter; f++) - imageBytes = EdgePreservedSmoothing(imageBytes, Height, Width, nChannels, bytesPerPixel, stride); - - int thresholdValue = OtsuThreshold.GetOtsuThreshold(imageBytes, Height, Width, stride); - imageBytes = threshold(imageBytes, Height, Width, nChannels, bytesPerPixel, stride, thresholdValue); - - // Write back to SKBitmap - for (int y = 0; y < Height; y++) - { - for (int x = 0; x < Width; x++) - { - int index = y * stride + x * bytesPerPixel; - byte r = imageBytes[index]; - byte g = imageBytes[index + 1]; - byte b = imageBytes[index + 2]; - - tmpBitmap.SetPixel(x, y, new SKColor(r, g, b)); - } - } - -#if DEBUG_IMAGE - using var image = SKImage.FromBitmap(tmpBitmap); - using var data = image.Encode(SKEncodedImageFormat.Png, 100); - using var stream = File.OpenWrite("EnhancedBWImage.png"); - data.SaveTo(stream); -#endif - - return ConstructSamples(imageBytes, stride); - } - - private byte[][] ConstructSamples(byte[] bytes, int stride) - { - byte[][] samples = new byte[Height][]; - - for (int y = 0; y < Height; y++) - { - samples[y] = new byte[Width]; - - for (int x = 0, srcX = y * stride; x < Width; x++) - { - samples[y][x] = bytes[srcX]; - srcX += 3; - } - } - - return samples; - } - - // Sharpens the input image using sharp filter - public byte[] Sharpen(byte[] inData, int height, int width, int nChannels, int bytesPerPixel, int stride) - { - int heightUpper = height - 1; - int widthUpper = width - 1; - - for (int i = 1; i < heightUpper; i++) - { - for (int j = 1; j < widthUpper; j++) - { - int sum = 0; - for (int k = -1; k < 2; k++) - { - for (int l = -1; l < 2; l++) - { - //compute weighted sum - sum += inData[(i + k) * stride + (j + l) * bytesPerPixel] * sharpFilter[k + 1][l + 1]; - } - } - if (sum < 0) sum = 0; - - for (int b = 0; b < nChannels; b++) - { - inData[i * stride + j * bytesPerPixel + b] = (byte) (sum / filterWeight); - } - } - } - - return inData; - } - - // This smoothes / blurs image while preserving edges - // a faster alternative to expensive Bilateral filter of OpenCV - public byte[] EdgePreservedSmoothing(byte[] inData, int height, int width, int nChannels, int bytesPerPixel_InputImage, int stride) - { - int[] diff = new int[4]; - - for (int i = 1; i < height - 1; i++) - { - for (int j = 1; j < width - 1; j++) - { - int x = inData[i * stride + j * bytesPerPixel_InputImage]; - - int l0 = inData[i * stride + (j - 1) * bytesPerPixel_InputImage]; - byte l1 = inData[i * stride + (j + 1) * bytesPerPixel_InputImage]; - diff[0] = Math.Abs(l1 - x) + Math.Abs(l0 - x); - - byte t0 = inData[(i - 1) * stride + j * bytesPerPixel_InputImage]; - byte t1 = inData[(i + 1) * stride + j * bytesPerPixel_InputImage]; - diff[1] = Math.Abs(t1 - x) + Math.Abs(t0 - x); - - byte d0 = inData[(i - 1) * stride + (j - 1) * bytesPerPixel_InputImage]; - byte d1 = inData[(i + 1) * stride + (j + 1) * bytesPerPixel_InputImage]; - diff[2] = Math.Abs(t1 - x) + Math.Abs(t0 - x); - - byte e0 = inData[(i - 1) * stride + (j + 1) * bytesPerPixel_InputImage]; - byte e1 = inData[(i + 1) * stride + (j - 1) * bytesPerPixel_InputImage]; - diff[3] = Math.Abs(t1 - x) + Math.Abs(t0 - x); - - int min = diff[0]; - int minIndex = 0; - - for (int k = 1; k < 4; ++k) - { - if (diff[k] < min) - { - min = diff[k]; - minIndex = k; - } - } - - if (diff[minIndex] > EDGE_FILTER_THRESOLD) - continue; - - switch (minIndex) - { - case 0: - x = (l1 + l0 + x) / 3; - break; - - case 1: - x = (t1 + t0 + x) / 3; - break; - - case 2: - x = (d1 + d0 + x) / 3; - break; - - case 3: - x = (e1 + e0 + x) / 3; - break; - } - - for (int b = 0; b < nChannels; b++) - { - inData[i * stride + j * bytesPerPixel_InputImage + b] = (byte) (x); - } - } - } - - return inData; - } - - // Threshold the image to binary using given threshold value - public byte[] threshold(byte[] inData, int height, int width, int nChannels, int bytesPerPixel, int stride, int thr) - { - for (int i = 0; i < height; i++) - { - for (int j = 0; j < width; j++) - { - int val = 0; - - if (inData[i * stride + j * bytesPerPixel] > thr) - { - val = 255; - } - - for (int b = 0; b < nChannels; b++) - { - inData[i * stride + j * bytesPerPixel + b] = (byte) (val); - } - } - } - - return inData; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/FoundBarcode.cs b/MuPDF.NET/Barcode/Decoders/FoundBarcode.cs deleted file mode 100644 index cdd92644..00000000 --- a/MuPDF.NET/Barcode/Decoders/FoundBarcode.cs +++ /dev/null @@ -1,70 +0,0 @@ -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core -{ -#if CORE_DEV - public -#else - internal -#endif - class FoundBarcode - { - public FoundBarcode() - { - } - - public FoundBarcode(FoundBarcode source) - { - Value = source.Value; - RawData = source.RawData; - Rect = source.Rect; - Polygon = source.Polygon; - BarcodeFormat = source.BarcodeFormat; - Color = source.Color; - Confidence = source.Confidence; - ParentRegion = source.ParentRegion; - } - - public FoundBarcode(SymbologyType barcodeType, BarCodeRegion region) - { - BarcodeFormat = barcodeType; - Polygon = new SKPointI[] { region.A, region.B, region.C, region.D, region.A }; - Color = Color.Blue; - //byte[] pointTypes = new byte[5] { (byte)PathPointType.Start, (byte)PathPointType.Line, (byte)PathPointType.Line, (byte)PathPointType.Line, (byte)PathPointType.Line }; - //using (GraphicsPath path = new GraphicsPath(Polygon, pointTypes)) - // Rect = Rectangle.Round(path.GetBounds()); - Rect = Utils.DrawPath(Polygon); - Value = (region.Data != null ? region.Data[0].ToString() : "?"); - Confidence = region.Confidence; - ParentRegion = region; - } - - public string Value { get; set; } - - public int[] RawData { get; set; } - - public SKRect Rect { get; set; } - - public SKPointI[] Polygon { get; set; } - - public SymbologyType BarcodeFormat { get; set; } - - public SKColor Color { get; set; } - - public float Confidence { get; set; } = 1f; - - public object Tag { get; set; } - - public int StructureAppendIndex { get; set; } = -1; - - public int StructureAppendCount { get; set; } = -1; - - public BarCodeRegion ParentRegion { get; set; } - - public override string ToString() - { - return Value; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/GrayscaleImage.cs b/MuPDF.NET/Barcode/Decoders/GrayscaleImage.cs deleted file mode 100644 index 1bc827d0..00000000 --- a/MuPDF.NET/Barcode/Decoders/GrayscaleImage.cs +++ /dev/null @@ -1,448 +0,0 @@ -using System; -using SkiaSharp; -using System.Runtime.InteropServices; - -namespace BarcodeReader.Core -{ -#if !OLD_GRAYSCALEIMAGE - - /// - /// Allows requesting grayscale (luminance) values for any given - /// source image row. - /// -#if CORE_DEV - public -#else - internal -#endif - class GrayscaleImage : IPreparedImage - { - public readonly bool UseMarshalCopy = true; // use Marshal.Copy or GetPixel instead (safe in ASP.NET Medium Trust) - - private bool _alreadyDisposed; - - private SKBitmap _bitmap; - private int _scanStep; - private bool _shouldDisposeBitmap; - - private int _originalWidth; //original size of bitmap - private int _originalHeight; - - const float PI = (float) Math.PI; - private MyPointF p0; - private MyVectorF vdX, vdY; - private bool _simpleMode; - - /// - /// already computed grayscale samples of an image. - /// this array gets allocated and filled when calls to GetRow are made - /// - private byte[][] _samples; - - - private GrayscaleImage() - { - } - - // Slow resampling of the bitmap with the given angle (slow because marshal copy can not be used). - /* - public GrayscaleImage(SKBitmap bitmap, int scanStep, float degAngle) - { - //normalize [0..360] - while (degAngle >= 360F) degAngle -= 360F; - while (degAngle < 0F) degAngle += 360F; - - _shouldDisposeBitmap = true; - UseMarshalCopy = IsFullyTrusted; //in ASP Medium trust mode => false - _scanStep = scanStep; - BlackAndWhiteImage.CalculateRotatedBorders(bitmap.Width, bitmap.Height, degAngle, out _originalWidth, out _originalHeight, out p0, out vdX, out vdY); - - SKBitmap newBitmap = new SKBitmap(_originalWidth, _originalHeight, Graphics.FromImage(bitmap)); - using (Graphics graphics = Graphics.FromImage(newBitmap)) - { - graphics.TranslateTransform((float)_originalWidth / 2, (float)_originalHeight / 2); - graphics.RotateTransform(-degAngle); - graphics.TranslateTransform(-(float)bitmap.Width / 2, -(float)bitmap.Height / 2); - graphics.DrawImage(bitmap, new SKPointI(0, 0)); - } - - _bitmap = newBitmap; - - Width = _originalWidth; - Height = _originalHeight / _scanStep; - - // - if (UseMarshalCopy) - GetPixelsViaMarshalCopy(); - else - GetPixelsViaGetPixels(); - } - */ - - public GrayscaleImage(SKBitmap bitmap, int scanStep, float degAngle) - { - // Normalize angle to [0, 360) - while (degAngle >= 360f) degAngle -= 360f; - while (degAngle < 0f) degAngle += 360f; - - _shouldDisposeBitmap = true; - UseMarshalCopy = IsFullyTrusted; // your custom trust check - _scanStep = scanStep; - - // Calculate rotated borders - BlackAndWhiteImage.CalculateRotatedBorders(bitmap.Width, bitmap.Height, degAngle, out _originalWidth, out _originalHeight, out p0, out vdX, out vdY); - - // Create target bitmap for the rotated image - SKBitmap newBitmap = new SKBitmap(_originalWidth, _originalHeight); - using (var canvas = new SKCanvas(newBitmap)) - { - // Clear canvas (optional) - canvas.Clear(SKColors.White); - - // Center transform - canvas.Translate(_originalWidth / 2f, _originalHeight / 2f); - canvas.RotateDegrees(-degAngle); // negative because Skia rotates clockwise - canvas.Translate(-bitmap.Width / 2f, -bitmap.Height / 2f); - - // Draw the original bitmap into the rotated context - canvas.DrawBitmap(bitmap, 0, 0); - } - - _bitmap = newBitmap; - Width = _originalWidth; - Height = _originalHeight / _scanStep; - - // Get grayscale pixels - if (UseMarshalCopy) - GetPixelsViaMarshalCopy(); // youll need to update this to use `SKPixmap` - else - GetPixelsViaGetPixels(); // or modify it to use `SKBitmap.GetPixel` - } - - /// Take to attention only Blue channel - public GrayscaleImage(SKBitmap bitmap, int scanStep, bool simpleMode) - { - UseMarshalCopy = IsFullyTrusted; //in ASP Medium trust mode => false - _simpleMode = simpleMode; - _bitmap = bitmap; - _shouldDisposeBitmap = false; - _scanStep = scanStep; - _originalWidth = _bitmap.Width; - _originalHeight = _bitmap.Height; - Width = _originalWidth; - Height = _originalHeight / _scanStep; - - if (UseMarshalCopy) - GetPixelsViaMarshalCopy(); - else - GetPixelsViaGetPixels(); - } - - - public GrayscaleImage(SKBitmap bitmap, int scanStep) : this(bitmap, scanStep, false) - { - } - - - bool IsFullyTrusted - { - get - { - try - { - //new System.Security.Permissions.SecurityPermission(System.Security.Permissions.PermissionState.Unrestricted).Demand(); - return true; - } - catch (Exception) - { - return false; - } - } - } - - public IPreparedImage Clone() - { - GrayscaleImage copy = new GrayscaleImage(); - copy._alreadyDisposed = _alreadyDisposed; - copy._bitmap = _bitmap == null ? null : (SKBitmap) _bitmap.Copy(); - copy._scanStep = _scanStep; - copy._shouldDisposeBitmap = _shouldDisposeBitmap; - copy._originalWidth = _originalWidth; - copy._originalHeight = _originalHeight; - copy.Width = Width; - copy.Height = Height; - copy.p0 = new MyPointF(p0.X, p0.Y); - copy.vdX = new MyVectorF(vdX.X, vdX.Y); - copy.vdY = new MyVectorF(vdY.X, vdY.Y); - - if (_samples == null) - copy._samples = null; - else - { - copy._samples = new byte[_samples.Length][]; - for (var i = 0; i < _samples.Length; i++) - { - if (_samples[i] != null) - { - copy._samples[i] = new byte[_samples[i].Length]; - Array.Copy(_samples[i], copy._samples[i], _samples[i].Length); - } - } - } - - return copy; - } - - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (!_alreadyDisposed) - { - if (disposing) - { - if (_shouldDisposeBitmap && _bitmap != null) - _bitmap.Dispose(); - } - - _bitmap = null; - _alreadyDisposed = true; - } - } - - public SKBitmap SKBitmap - { - get { return _bitmap; } - } - - public bool ShouldDisposeBitmap - { - get { return _shouldDisposeBitmap; } - set { _shouldDisposeBitmap = value; } - } - - public void Save(string fileName) - { -#if DEBUG_IMAGE - SKBitmap output = new SKBitmap(Width, Height, PixelFormat.Format24bppRgb); - byte[] scanBytes = null; - SKRect imgRect = new SKRect(0, 0, Width, 1); - - for (int y = 0; y < Height; y++) - { - imgRect.Y = y; - BitmapData imgData = output.LockBits(imgRect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); - - if (scanBytes == null) - scanBytes = new byte[imgData.Stride]; - - Array.Clear(scanBytes, 0, scanBytes.Length); - byte[] row = GetRow(y); - - for (int x = 0, scanPos = 0; x < Width; x++) - { - byte luminance = row[x]; - scanBytes[scanPos++] = luminance; - scanBytes[scanPos++] = luminance; - scanBytes[scanPos++] = luminance; - } - - Marshal.Copy(scanBytes, 0, imgData.Scan0, scanBytes.Length); - output.UnlockBits(imgData); - } - - output.Save(fileName); - output.Dispose(); -#endif - } - - /// - /// Gets the width of the image. - /// - /// The width of the image. - public int Width { get; private set; } - - /// - /// Gets the height of the image. - /// - /// The height of the image. - public int Height { get; private set; } - - /// - /// Gets specified row of image converted to grayscale. - /// - /// The row index. - /// The array that contains grayscale row. - public byte[] GetRow(int y) - { - return _samples[y]; - } - - /// - /// Index of red component. - /// - public const short R = 2; - - /// - /// Index of green component. - /// - public const short G = 1; - - /// - /// Index of blue component. - /// - public const short B = 0; - - /// - /// Index of alpha component. - /// - public const short A = 3; - - private void GetPixelsViaMarshalCopy() - { - normalizeBitmapFormat(); - - // Get pixel info and pointer - using (SKPixmap pixmap = _bitmap.PeekPixels()) - { - int stride = pixmap.RowBytes; - int totalBytes = stride * _originalHeight; - IntPtr pixelsPtr = pixmap.GetPixels(); - - // Allocate managed array - byte[] pixels = new byte[totalBytes]; - - // Copy unmanaged pixels into managed array - Marshal.Copy(pixelsPtr, pixels, 0, totalBytes); - - // Allocate _samples rows - _samples = new byte[Height][]; - - int start = 0; - int step = stride * _scanStep; - - if (_simpleMode) - ToGrayScaleSimple(start, step, pixels); - else - ToGrayScale(start, step, pixels); - } - } - /* - private void GetPixelsViaMarshalCopy() - { - normalizeBitmapFormat(); - - //get pixels - BitmapData imgData = _bitmap.LockBits(new Rectangle(0, 0, _originalWidth, _originalHeight), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); - var pixels = new byte[imgData.Stride * _originalHeight]; - Marshal.Copy(imgData.Scan0, pixels, 0, imgData.Stride * _originalHeight); - - //copy pixels to byte array - _samples = new byte[Height][]; - - var start = 0; - var step = imgData.Stride * _scanStep; - - if (_simpleMode) - ToGrayScaleSimple(start, step, pixels); - else - ToGrayScale(start, step, pixels); - - _bitmap.UnlockBits(imgData); - } - */ - - private void ToGrayScale(int start, int step, byte[] pixels) - { - for (int y = 0; y < Height; y++, start += step) - { - var row = _samples[y] = new byte[Width]; - var srcX = start; - for (int x = 0; x < row.Length; x++, srcX += 4) - { - row[x] = (byte) ((306 * pixels[srcX + R] + 601 * pixels[srcX + G] + 117 * pixels[srcX + B]) >> 10); - } - } - } - - private void ToGrayScaleSimple(int start, int step, byte[] pixels) - { - for (int y = 0; y < Height; y++, start += step) - { - var row = _samples[y] = new byte[Width]; - var srcX = start; - for (int x = 0; x < row.Length; x++, srcX += 4) - { - row[x] = pixels[srcX]; - } - } - } - - private void GetPixelsViaGetPixels() - { - //copy pixels to byte array - _samples = new byte[Height][]; - - for (int y = 0; y < Height; y++) - { - var row = _samples[y] = new byte[Width]; - for (int x = 0; x < Width; x++) - { - var pixel = _bitmap.GetPixel(x, y * _scanStep); - if(_simpleMode) - row[x] = pixel.Blue; - else - row[x] = (byte) ((306 * pixel.Red + 601 * pixel.Green + 117 * pixel.Blue) >> 10); - } - } - } - - private void normalizeBitmapFormat() - { - // Ensure bitmap is in 24bpp equivalent format (RGB) - if (_bitmap.ColorType != SKColorType.Rgb888x || _bitmap.AlphaType != SKAlphaType.Opaque) - { - // Create a new bitmap with desired format - var temp = new SKBitmap(_bitmap.Width, _bitmap.Height, SKColorType.Rgb888x, SKAlphaType.Opaque); - - using (var canvas = new SKCanvas(temp)) - { - canvas.Clear(SKColors.White); - canvas.DrawBitmap(_bitmap, new SKPoint(0, 0)); - } - - if (_shouldDisposeBitmap) - _bitmap.Dispose(); - - _bitmap = temp; - _shouldDisposeBitmap = true; - } - } - /* - private void normalizeBitmapFormat() - { - if (_bitmap.PixelFormat != PixelFormat.Format24bppRgb) - { - SKBitmap temp = new SKBitmap(_bitmap.Width, _bitmap.Height, PixelFormat.Format24bppRgb); - using (Graphics g = Graphics.FromImage(temp)) - { - g.Clear(Color.White); - g.DrawImage(_bitmap, new Rectangle(0, 0, _bitmap.Width, _bitmap.Height), 0, 0, _bitmap.Width, _bitmap.Height, GraphicsUnit.Pixel); - } - - if (_shouldDisposeBitmap) - _bitmap.Dispose(); - - _bitmap = temp; - _shouldDisposeBitmap = true; - } - } - */ - } - -#endif // !OLD_GRAYSCALEIMAGE -} diff --git a/MuPDF.NET/Barcode/Decoders/GrayscaleImage_old.cs b/MuPDF.NET/Barcode/Decoders/GrayscaleImage_old.cs deleted file mode 100644 index b097a646..00000000 --- a/MuPDF.NET/Barcode/Decoders/GrayscaleImage_old.cs +++ /dev/null @@ -1,366 +0,0 @@ -using System; -using SkiaSharp; -using System.Runtime.InteropServices; - -namespace BarcodeReader.Core -{ -#if OLD_GRAYSCALEIMAGE - - /// - /// Allows requesting grayscale (luminance) values for any given - /// source image row. - /// -#if CORE_DEV - public -#else - internal -#endif - class GrayscaleImage : IPreparedImage - { - public bool UseMarshalCopy = true; // use Marshal.Copy or GetPixel instead (safe in ASP.NET Medium Trust) - - private bool _alreadyDisposed; - - /// - /// Index of red component. - /// - public const short R = 2; - - /// - /// Index of green component. - /// - public const short G = 1; - - /// - /// Index of blue component. - /// - public const short B = 0; - - /// - /// Index of alpha component. - /// - public const short A = 3; - - private SKBitmap _bitmap; - private int _scanStep; - private bool _shouldDisposeBitmap; - - private int _originalWidth; //original size of bitmap - private int _originalHeight; - - const float PI = (float) Math.PI; - private MyPointF p0; - private MyVectorF vdX, vdY; - - - /// - /// already computed grayscale samples of an image. - /// this array gets allocated and filled when calls to GetRow are made - /// - private byte[][] _samples; - - /// - /// bytes of a one source image row - /// - private byte[] _imgBytes; - - private GrayscaleImage() - { - } - - public GrayscaleImage(SKBitmap bitmap, int scanStep) - { - _shouldDisposeBitmap = false; - UseMarshalCopy = true; - _bitmap = bitmap; - _scanStep = scanStep; - _originalWidth = _bitmap.Width; - _originalHeight = _bitmap.Height; - Width = _originalWidth; - Height = _originalHeight / _scanStep; - normalizeBitmapFormat(); - vdX = new MyVectorF(1f, 0f); - vdY = new MyVectorF(0f, 1f); - } - - // Slow resampling of the bitmap with the given angle (slow because marshal copy can not be used). - public GrayscaleImage(SKBitmap bitmap, int scanStep, float degAngle) - { - //normalize [0..360] - while (degAngle >= 360F) degAngle -= 360F; - while (degAngle < 0F) degAngle += 360F; - - _shouldDisposeBitmap = true; - UseMarshalCopy = true; - _scanStep = scanStep; - BlackAndWhiteImage.CalculateRotatedBorders(bitmap.Width, bitmap.Height, degAngle, out _originalWidth, out _originalHeight, out p0, out vdX, out vdY); - - SKBitmap newBitmap = new SKBitmap(_originalWidth, _originalHeight, Graphics.FromImage(bitmap)); - using (Graphics graphics = Graphics.FromImage(newBitmap)) - { - graphics.TranslateTransform((float)_originalWidth / 2, (float)_originalHeight / 2); - graphics.RotateTransform(-degAngle); - graphics.TranslateTransform(-(float)bitmap.Width / 2, -(float)bitmap.Height / 2); - graphics.DrawImage(bitmap, new SKPointI(0, 0)); - } - - _bitmap = newBitmap; - - Width = _originalWidth; - Height = _originalHeight / _scanStep; - } - - public IPreparedImage Clone() - { - GrayscaleImage copy = new GrayscaleImage(); - copy.UseMarshalCopy = UseMarshalCopy; - copy._alreadyDisposed = _alreadyDisposed; - copy._bitmap = _bitmap == null ? null : (SKBitmap) _bitmap.Clone(); - copy._scanStep = _scanStep; - copy._shouldDisposeBitmap = _shouldDisposeBitmap; - copy._originalWidth = _originalWidth; - copy._originalHeight = _originalHeight; - copy.Width = Width; - copy.Height = Height; - copy.p0 = new MyPointF(p0.X, p0.Y); - copy.vdX = new MyVectorF(vdX.X, vdX.Y); - copy.vdY = new MyVectorF(vdY.X, vdY.Y); - - if (_samples == null) - copy._samples = null; - else - { - copy._samples = new byte[_samples.Length][]; - for (var i = 0; i < _samples.Length; i++) - { - if (_samples[i] != null) - { - copy._samples[i] = new byte[_samples[i].Length]; - Array.Copy(_samples[i], copy._samples[i], _samples[i].Length); - } - } - } - - if (_imgBytes == null) - copy._imgBytes = null; - else - { - copy._imgBytes = new byte[_imgBytes.Length]; - Array.Copy(_imgBytes, copy._imgBytes, _imgBytes.Length); - } - - return copy; - } - - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (!_alreadyDisposed) - { - if (disposing) - { - if (_shouldDisposeBitmap && _bitmap != null) - _bitmap.Dispose(); - } - - _bitmap = null; - _alreadyDisposed = true; - } - } - - public SKBitmap SKBitmap - { - get { return _bitmap; } - } - - public bool ShouldDisposeBitmap - { - get { return _shouldDisposeBitmap; } - set { _shouldDisposeBitmap = value; } - } - - public void Save(string fileName) - { -#if DEBUG_IMAGE - SKBitmap output = new SKBitmap(Width, Height, PixelFormat.Format24bppRgb); - byte[] scanBytes = null; - Rectangle imgRect = new Rectangle(0, 0, Width, 1); - - for (int y = 0; y < Height; y++) - { - imgRect.Y = y; - BitmapData imgData = output.LockBits(imgRect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); - - if (scanBytes == null) - scanBytes = new byte[imgData.Stride]; - - Array.Clear(scanBytes, 0, scanBytes.Length); - byte[] row = GetRow(y); - - for (int x = 0, scanPos = 0; x < Width; x++) - { - byte luminance = row[x]; - scanBytes[scanPos++] = luminance; - scanBytes[scanPos++] = luminance; - scanBytes[scanPos++] = luminance; - } - - Marshal.Copy(scanBytes, 0, imgData.Scan0, scanBytes.Length); - output.UnlockBits(imgData); - } - - output.Save(fileName); - output.Dispose(); -#endif - } - - /// - /// Gets the width of the image. - /// - /// The width of the image. - public int Width { get; private set; } - - /// - /// Gets the height of the image. - /// - /// The height of the image. - public int Height { get; private set; } - - /// - /// Gets specified row of image converted to grayscale. - /// - /// The row index. - /// The array that contains grayscale row. - public byte[] GetRow(int y) - { - if (y < 0 || y >= Height) - throw new ArgumentException(); - - if (_samples == null) - { - _samples = new byte[Height][]; - - //constructAllSampleRows(); - } - - if (_samples[y] == null) - { - _samples[y] = new byte[Width]; - - if (UseMarshalCopy) - constructSampleRow_ViaMarshalCopy(y); // use Marshal.Copy (fast but not compatible with ASP.NET medium trust - else - constructSampleRow_Safe(y); // safe in ASP.NET Medium Trust (uses GetPixel) but very slow (x10 times slower) - } - - return _samples[y]; - } - - /// - /// Constructs the sample row using default grayscale transformation. - /// Y = 0.299R + 0.587G + 0.114B - /// - /// The row index. - private void constructSampleRow_Safe(int y) - { - // retrieve data using GetPixel (to avoid problems in ASP.NET Medium Trust) - // for faster variant (x10 speed faster) with Marshal.Copy see constructSampleRow_viaMarshalCopy() - MyPointF a = p0 + vdY * (float)(y * _scanStep); - int luminance; - for (int x = 0; x < Width; x++) - { - int xx = (int)a.X, yy = (int)a.Y; - if (xx >= 0 && xx < _bitmap.Width && yy >= 0 && yy < _bitmap.Height) - { - Color tmp = _bitmap.GetPixel((int)a.X, (int)a.Y); - luminance = (306 * tmp.R + 601 * tmp.G + 117 * tmp.B) >> 10; - } - else luminance = 255; - _samples[y][x] = (byte)luminance; - a += vdX; - } - } - - /// - /// Constructs the sample row using default grayscale transformation. - /// Y = 0.299R + 0.587G + 0.114B - /// This variant uses Marshal.Copy which can not be used in the ASP.NET Medium Trust mode - /// - /// The row index. - private void constructSampleRow_ViaMarshalCopy(int y) - { - lock (_bitmap) - { - Rectangle imgRect = new Rectangle(0, y * _scanStep, Width, 1); - BitmapData imgData = _bitmap.LockBits(imgRect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); - - if (_imgBytes == null || _imgBytes.Length < imgData.Stride) - _imgBytes = new byte[imgData.Stride]; - - Marshal.Copy(imgData.Scan0, _imgBytes, 0, imgData.Stride); - _bitmap.UnlockBits(imgData); - - for (int x = 0, srcX = 0; x < Width; x++, srcX += 3) - { - int luminance = (306 * _imgBytes[srcX + R] + - 601 * _imgBytes[srcX + G] + 117 * _imgBytes[srcX + B]) >> 10; - - _samples[y][x] = (byte) luminance; - } - } - } - - /*private void constructAllSampleRows() - { - _samples = new byte[Height][]; - - Rectangle imgRect = new Rectangle(0, 0, Width, Height); - BitmapData imgData = _bitmap.LockBits(imgRect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); - - byte[] allBytes = new byte[imgData.Stride * Height]; - int stride = imgData.Stride; - Marshal.Copy(imgData.Scan0, allBytes, 0, stride * Height); - _bitmap.UnlockBits(imgData); - - for (int row = 0; row < Height; row++) - { - _samples[row] = new byte[Width]; - - for (int x = 0, srcX = row * stride; x < Width; x++) - { - int luminance = 117 * allBytes[srcX++]; - luminance += 601 * allBytes[srcX++]; - luminance += 306 * allBytes[srcX++]; - - _samples[row][x] = (byte) (luminance >> 10); - } - } - }*/ - - private void normalizeBitmapFormat() - { - if (_bitmap.PixelFormat != PixelFormat.Format24bppRgb) - { - SKBitmap temp = new SKBitmap(_bitmap.Width, _bitmap.Height, PixelFormat.Format24bppRgb); - using (Graphics g = Graphics.FromImage(temp)) - { - g.Clear(Color.White); - g.DrawImage(_bitmap, new Rectangle(0, 0, _bitmap.Width, _bitmap.Height), 0, 0, _bitmap.Width, _bitmap.Height, GraphicsUnit.Pixel); - } - - if (_shouldDisposeBitmap) - _bitmap.Dispose(); - - _bitmap = temp; - _shouldDisposeBitmap = true; - } - } - } - -#endif // OLD_GRAYSCALEIMAGE -} diff --git a/MuPDF.NET/Barcode/Decoders/IBarcodeConsumer.cs b/MuPDF.NET/Barcode/Decoders/IBarcodeConsumer.cs deleted file mode 100644 index a85dd47e..00000000 --- a/MuPDF.NET/Barcode/Decoders/IBarcodeConsumer.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace BarcodeReader.Core -{ -#if CORE_DEV - public -#else - internal -#endif - interface IBarcodeConsumer - { - bool consumeBarcode(object foundBarcode); - } -} diff --git a/MuPDF.NET/Barcode/Decoders/IBarcodeDecoder.cs b/MuPDF.NET/Barcode/Decoders/IBarcodeDecoder.cs deleted file mode 100644 index 7b49b1a5..00000000 --- a/MuPDF.NET/Barcode/Decoders/IBarcodeDecoder.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace BarcodeReader.Core -{ - internal interface IBarcodeDecoder : IDisposable - { - SymbologyType GetBarCodeType(); - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/IParallelSupporting.cs b/MuPDF.NET/Barcode/Decoders/IParallelSupporting.cs deleted file mode 100644 index 7529bb3d..00000000 --- a/MuPDF.NET/Barcode/Decoders/IParallelSupporting.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace BarcodeReader.Core -{ - /// - /// Supports multithreading - /// -#if CORE_DEV - public -#else - internal -#endif - interface IParallelSupporting - { - bool IsParallelSupported { get; } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/IPreparedImage.cs b/MuPDF.NET/Barcode/Decoders/IPreparedImage.cs deleted file mode 100644 index f6c665d3..00000000 --- a/MuPDF.NET/Barcode/Decoders/IPreparedImage.cs +++ /dev/null @@ -1,20 +0,0 @@ -using SkiaSharp; -using System; - -namespace BarcodeReader.Core -{ -#if CORE_DEV - public -#else - internal -#endif - interface IPreparedImage : IDisposable - { - int Width { get; } - int Height { get; } - SKBitmap SKBitmap { get; } - - byte[] GetRow(int y); - IPreparedImage Clone(); - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/IntegralBuilder.cs b/MuPDF.NET/Barcode/Decoders/IntegralBuilder.cs deleted file mode 100644 index e9e70a4b..00000000 --- a/MuPDF.NET/Barcode/Decoders/IntegralBuilder.cs +++ /dev/null @@ -1,70 +0,0 @@ -using BarcodeReader.Core; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core -{ -#if CORE_DEV - public -#else - internal -#endif - static class IntegralBuilder - { - public static int[][] Build(IBlackAndWhiteFilter img, int w, int h) - { - var res = new int[w + 1][]; - - for (int x = 0; x < w + 1; x++) - res[x] = new int[h + 1]; - - XBitArray[] rows = new XBitArray[h]; - - for (int y = 0; y < h; y++) - rows[y] = img.GetRow(y); - - //sum by rows - Parallel.For(0, h, (y) => - { - int sumRow = 0; - for (int x = 0; x < w; x++) - { - sumRow += rows[y][x] ? 1 : 0; - res[x + 1][y + 1] = sumRow; - } - } - ); - - //sum by cols - Parallel.For(0, w, (x) => - { - var col = res[x + 1]; - for (int y = 1; y < h; y++) - { - col[y + 1] += col[y]; - } - } - ); - - return res; - } - - public static int GetSum(int[][] integral, int x, int y, int width, int height) - { - return integral[x + width][y + height] + integral[x][y] - integral[x+ width][y] - integral[x][y + height]; - } - - public static int GetSumSafe(int[][] integral, int x, int y, int width, int height) - { - if (x + width >= integral.Length) - { - width = integral.Length - 1 - x; - } - - if (y + height >= integral[0].Length) - { - height = integral[0].Length - 1 - y; - } - return integral[x + width][y + height] + integral[x][y] - integral[x + width][y] - integral[x][y + height]; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/LegacyDecoders/CodabarReader.cs b/MuPDF.NET/Barcode/Decoders/LegacyDecoders/CodabarReader.cs deleted file mode 100644 index 97b62871..00000000 --- a/MuPDF.NET/Barcode/Decoders/LegacyDecoders/CodabarReader.cs +++ /dev/null @@ -1,315 +0,0 @@ -using System; -using System.Text; -using System.Collections; -using SkiaSharp; - -namespace BarcodeReader.Core.LegacyDecoders -{ -#if CORE_DEV - public -#else - internal -#endif - class CodabarReader : SymbologyReader - { - private static string _alphabet = "0123456789-$:/.+ABCD"; - private static int _startA = _alphabet.IndexOf('A'); - - // Codabar symbology uses wide-narrow patterns for it's symbols - // In order to simplify and speed up comparison, we pack individual - // patterns into ints using following rule: - // wnwnnnw -> 1010001 -> 0x51 - - private static int[] _patterns = - { - 0x03, 0x06, 0x09, 0x60, 0x12, 0x42, - 0x21, 0x24, 0x30, 0x48, 0x0c, 0x18, - 0x45, 0x51, 0x54, 0x15, 0x1a, 0x29, - 0x0b, 0x0e, - }; - - // temporary array used by buildWideNarrowPattern - private float[] _wideModulesWidths = new float[7]; - - public CodabarReader() : base(null) - { - } - - public CodabarReader(IBarcodeConsumer consumer) : base(consumer) - { - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Codabar; - } - - public override FoundBarcode[] DecodeRow(int rowNumber, XBitArray row) - { - int offset = 0; - while (offset < row.Size) - { - if (row[offset]) - break; - - offset++; - } - - // offset now points to a first black pixel - int symbolStart = offset; - bool processingWhite = false; - - // each Codabar symbol consists of 7 modules - int currentModule = 0; - int[] moduleWidths = new int[7]; - - for (int x = offset; x < row.Size; x++) - { - // if pixel is white and processing white - // or - // pixel is black and processing black - if (row[x] ^ processingWhite) - { - moduleWidths[currentModule]++; - } - else - { - // color changed - if (currentModule == moduleWidths.Length - 1) - { - // we've completed symbol - int pattern = buildWideNarrowPattern(moduleWidths); - int symbol = -1; - for (int i = _startA; i < _startA + 4; i++) - { - if (pattern == _patterns[i]) - { - symbol = i; - break; - } - } - - if (symbol >= 0) - { - // found start symbol - if (!RequireQuietZones || HaveWhiteSpaceBefore(row, x, symbolStart)) - { - // we have white space that is half of symbol long - // try to decode consecutive symbols - FoundBarcode found = decodeFromStartSymbol(rowNumber, row, x, symbolStart, symbol); - - if (found == null) - return null; - - return new FoundBarcode[] { found }; - } - } - - SkipTwoModules(ref symbolStart, moduleWidths); - currentModule--; - } - else - { - currentModule++; - } - - moduleWidths[currentModule] = 1; - processingWhite = !processingWhite; - } - } - - return null; - } - - private int buildWideNarrowPattern(int[] moduleWidths) - { - int currentNarrowModuleWidth = 0; - int wideModuleCount; - float mostWide; - - for (; ; ) - { - Array.Clear(_wideModulesWidths, 0, 7); - mostWide = 0; - - // find thinnest module bigger then previously found one - int minWidth = int.MaxValue; - for (int i = 0; i < moduleWidths.Length; i++) - { - int width = moduleWidths[i]; - if (width < minWidth && width > currentNarrowModuleWidth) - minWidth = width; - } - - currentNarrowModuleWidth = minWidth; - wideModuleCount = 0; - int pattern = 0; - for (int i = 0; i < moduleWidths.Length; i++) - { - if (moduleWidths[i] > currentNarrowModuleWidth) - { - pattern |= 1 << (moduleWidths.Length - 1 - i); - - _wideModulesWidths[wideModuleCount] = moduleWidths[i]; - if (mostWide < moduleWidths[i]) - mostWide = moduleWidths[i]; - - wideModuleCount++; - } - } - - if (wideModuleCount < 2) - { - // each Codabar symbol should contain at least 2 wide modules - // some special and start/stop characters contain 3 wide modules - break; - } - - if (wideModuleCount == 2 || wideModuleCount == 3) - { - bool shouldContinue = false; - for (int i = 0; i < wideModuleCount; i++) - { - if ((mostWide / _wideModulesWidths[i]) > 1.6f) - { - if (wideModuleCount == 2) - { - // we have only two wide modules and they are - // to distant from each other - return -1; - } - - // we have three wide modules that are too distant - // from each other. try to continue, maybe we'll - // find two good wide modules. - shouldContinue = true; - } - } - - if (shouldContinue) - continue; - - return pattern; - } - } - - return -1; - } - - private FoundBarcode decodeFromStartSymbol(int rowNumber, XBitArray row, int offset, int startOffset, int startSymbol) - { - int[] moduleWidths = new int[7]; - - ArrayList symbols = new ArrayList(); - symbols.Add(startSymbol); - - bool done = false; - int offsetBeforeSymbol = offset; - while (!done) - { - // skip inter-character gap (white space between symbols) - int gap = 0; - while (offset < row.Size) - { - if (!row[offset]) - { - offset++; - gap++; - } - else - break; - } - - if (offset == row.Size) - { - // while skipping gap we've ran off image boundary - // revert offset changes and break - offset -= gap; - break; - } - - offsetBeforeSymbol = offset; - int symbolIndex = decodeSymbol(row, offset, moduleWidths); - if (symbolIndex != -1) - { - symbols.Add(symbolIndex); - - for (int i = 0; i < moduleWidths.Length; i++) - offset += moduleWidths[i]; - - if (symbolIndex >= _startA) - done = true; - } - else - done = true; - } - - if (symbols.Count < 2) - { - // should contain at least 2 symbols - return null; - } - - if (RequireQuietZones && !HaveWhiteSpaceAfter(row, offsetBeforeSymbol, offset)) - return null; - - FoundBarcode result = new FoundBarcode(); - result.BarcodeFormat = SymbologyType.Codabar; - result.Rect = new SKRect(startOffset, rowNumber, offset, rowNumber+1); - result.RawData = AsIntArray(symbols); - - StringBuilder sb = new StringBuilder(); - bool symbolComplete = false; - for (int i = 1; i < result.RawData.Length && !symbolComplete; i++) - { - int symbol = result.RawData[i]; - if (symbol == _startA || symbol == (_startA + 1) || - symbol == (_startA + 2) || symbol == (_startA + 3)) - { - // found stop symbol - symbolComplete = true; - break; - } - - sb.Append(_alphabet[symbol]); - } - - if (!symbolComplete) - return null; - - result.Value = sb.ToString(); - if (result.Value.Length == 0) - return null; - - return result; - } - - private int decodeSymbol(XBitArray row, int offset, int[] moduleWidths) - { - bool read = readModules(row, offset, moduleWidths); - if (!read) - return -1; - - int pattern = buildWideNarrowPattern(moduleWidths); - if (pattern < 0) - return -1; - - char decodedChar = patternToChar(pattern); - if (decodedChar == (char)0) - return -1; - - return _alphabet.IndexOf(decodedChar); - } - - private static char patternToChar(int pattern) - { - for (int i = 0; i < _patterns.Length; i++) - { - if (_patterns[i] == pattern) - return _alphabet[i]; - } - - return (char)0; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/LegacyDecoders/EANReader_old.cs b/MuPDF.NET/Barcode/Decoders/LegacyDecoders/EANReader_old.cs deleted file mode 100644 index 86df0fc6..00000000 --- a/MuPDF.NET/Barcode/Decoders/LegacyDecoders/EANReader_old.cs +++ /dev/null @@ -1,981 +0,0 @@ -using System; -using System.Text; -using System.Collections; -using BarcodeReader.Core.Common; -using SkiaSharp; - -namespace BarcodeReader.Core.LegacyDecoders -{ - /// - /// Reads "product barcodes" (EAN-13, EAN-8, UPC-A, UPC-E) - /// -#if CORE_DEV - public -#else - internal -#endif - class EANReader_old : SymbologyReader - { - private static int[] _startStopPattern = { 1, 1, 1 }; - private static int[] _middlePattern = { 1, 1, 1, 1, 1 }; - - // Please take a look at: - // http://www.barcodeisland.com/ean13.phtml - // for the additional information about encoding table - - // Odd parity patterns for left-hand encoding - private static int[][] _leftOddPatterns = - { - new int[] {3, 2, 1, 1}, // 0 - new int[] {2, 2, 2, 1}, // 1 - new int[] {2, 1, 2, 2}, // 2 - new int[] {1, 4, 1, 1}, // 3 - new int[] {1, 1, 3, 2}, // 4 - new int[] {1, 2, 3, 1}, // 5 - new int[] {1, 1, 1, 4}, // 6 - new int[] {1, 3, 1, 2}, // 7 - new int[] {1, 2, 1, 3}, // 8 - new int[] {3, 1, 1, 2} // 9 - }; - - // All left-hand encoding patterns (odd parity and even parity) - private static int[][] _leftPatterns = - { - // odd parity - _leftOddPatterns[0], _leftOddPatterns[1], - _leftOddPatterns[2], _leftOddPatterns[3], - _leftOddPatterns[4], _leftOddPatterns[5], - _leftOddPatterns[6], _leftOddPatterns[7], - _leftOddPatterns[8], _leftOddPatterns[9], - - // even parity - new int[] {1, 1, 2, 3}, // 0 - new int[] {1, 2, 2, 2}, // 1 - new int[] {2, 2, 1, 2}, // 2 - new int[] {1, 1, 4, 1}, // 3 - new int[] {2, 3, 1, 1}, // 4 - new int[] {1, 3, 2, 1}, // 5 - new int[] {4, 1, 1, 1}, // 6 - new int[] {2, 1, 3, 1}, // 7 - new int[] {3, 1, 2, 1}, // 8 - new int[] {2, 1, 1, 3} // 9 - }; - - // In order to simplify and speed up comparison, we pack individual - // patterns of first digits into ints using following rule: - // 5 (Even, Even, Odd, Odd, Even) -> 11001 -> 0x19 - - private static int[] _ean13FirstDigitPatterns = - { - 0x00, 0x0b, 0x0d, 0x0e, 0x13, - 0x19, 0x1c, 0x15, 0x16, 0x1a - }; - - private static int[] _upceStopPattern = { 1, 1, 1, 1, 1, 1 }; - - // In order to simplify and speed up comparison, we pack individual - // patterns of first digits into ints using following rule: - // 5 (Even, Odd, Odd, Even, Even, Odd) -> 100110 -> 0x26 for number system 0 - // 5 (Odd, Even, Even, Odd, Odd, Even) -> 011001 -> 0x19 for number system 1 - - private static int[][] _upcePatterns = - { - // for number system 0 - new int[] {0x38, 0x34, 0x32, 0x31, 0x2c, 0x26, 0x23, 0x2a, 0x29, 0x25}, - - // for number system 1 - new int[] {0x07, 0x0b, 0x0d, 0x0e, 0x13, 0x19, 0x1c, 0x15, 0x16, 0x1a} - }; - - private static int[] _supplementStartPattern = { 1, 1, 2 }; - private static int[] _supplementSeparatorPattern = { 1, 1 }; - - // In order to simplify and speed up comparison, we pack individual - // patterns of EAN-5 structures into ints using following rule: - // 5 (Odd, Odd, Even, Even, Odd) -> 00110 -> 0x06 - private static int[] _ean5Structures = - { - 0x18, 0x14, 0x12, 0x11, 0x0c, 0x06, 0x03, 0x0a, 0x09, 0x05 - }; - - private const double MaxAverageDifference = 0.42f; - private const double MaxSymbolDifference = 0.6f; - - private const float MaxAverageDifferenceEAN2 = 0.42f; - private const float MaxSymbolDifferenceEAN2 = 0.6f; - - private const float MaxAverageDifferenceEAN5 = 0.42f; - private const float MaxSymbolDifferenceEAN5 = 0.6f; - - private SymbologyType _currentSymbology = SymbologyType.Unknown; - - private bool _findEan13; - private bool _findEan8; - private bool _findUpca; - private bool _findUpce; - private bool _findEan2; - private bool _findEan5; - private bool _findOrphanedSupplementals; - - /// - /// Indicates if we have found EAN13 already so we should scan for EAN2 or EAN5 (if _findOrphanedSupplementals == true) - /// - private bool _ean13found = false; - private SKRect _lastEan13Rectangle = new SKRect(); - - private int[] _moduleWidths = new int[4]; - - private int _patternOffset; - private int _afterPatternOffset; - - private ArrayList _digits = new ArrayList(); - - public EANReader_old() : this(null) - { - } - - public EANReader_old(IBarcodeConsumer consumer) : base(consumer) - { - BeforeDecoding(); - } - - public bool FindEan13 - { - get { return _findEan13; } - set { _findEan13 = value; } - } - - public bool FindEan8 - { - get { return _findEan8; } - set { _findEan8 = value; } - } - - public bool FindUpca - { - get { return _findUpca; } - set { _findUpca = value; } - } - - public bool FindUpce - { - get { return _findUpce; } - set { _findUpce = value; } - } - - public bool FindEan2 - { - get { return _findEan2; } - set { _findEan2 = value; } - } - public bool FindEan5 - { - get { return _findEan5; } - set { _findEan5 = value; } - } - - public bool FindOrphanedSupplementals - { - get { return _findOrphanedSupplementals; } - set { _findOrphanedSupplementals = value; } - } - - public override SymbologyType GetBarCodeType() - { - return _currentSymbology; - } - - public override void BeforeDecoding() - { - //reset decoder - _ean13found = false; // set that we have not found ean13 yet - } - - public override FoundBarcode[] DecodeRow(int rowNumber, XBitArray row) - { - _patternOffset = 0; - _afterPatternOffset = 0; - - for (; ; ) - { - if (!findPattern(row, _afterPatternOffset, false, _startStopPattern)) - return null; - - int patternWidth = _afterPatternOffset - _patternOffset; - if (!RequireQuietZones || HaveWhiteSpaceBefore(row, _patternOffset + 2 * patternWidth, _patternOffset)) - { - // we have white space that have the same length as the - // start pattern width - // try to decode consecutive symbols - FoundBarcode found = decodeFromStartSymbol(rowNumber, row); - - if (found != null) - { - - if (found.BarcodeFormat == SymbologyType.EAN13) - { - _ean13found = true; // indicate that we found ean13 - // save last ean13 rect - _lastEan13Rectangle = found.Rect; - - // if we have found ean13 already but we should NOT search for supplemental ean2 or ean5 - // so we should exit as well - if (!_findOrphanedSupplementals) - return new FoundBarcode[] { found }; - } - else // non ean 13 barcode so we simply return found barcode - return new FoundBarcode[] { found }; - - } - - // if we do not have previous ean13 so we should not continue anyway - if (!_ean13found && !FindOrphanedSupplementals) - return null; - - // we go here if we have ean13 and we need to search for supplemental barcode - - // if we have NOT found ean13 previously then we should exit anyway - //if (_findOrphanedSupplementals && found.Type != SymbologyType.EAN13)) - // _afterPatternOffset = 0; - - FoundBarcode foundSupplement = null; - if (_findEan2 || _findEan5) - { - if (findPattern(row, _afterPatternOffset, false, _supplementStartPattern)) - foundSupplement = decodeSupplementFromStartSymbol(rowNumber, row); - } - - if (foundSupplement != null) - { - // filter supplemented barcodes from main barcode - // first checking if rectangles intersects with last ean13 - - SKRect rectSupplemental = foundSupplement.Rect; - bool allowThisSupplemental = !rectSupplemental.IntersectsWith(_lastEan13Rectangle); - - // NOTE: supplemental EAN2/5 should be placed at the right (if aligned horizontally form left to right) - // OR at the bottom (if aligned vertically from top to bottom) - - // additional check if supplemental do not crossing largest - // side of ean 13 barcode - if (allowThisSupplemental){ - // checking by width (both ean13 and ean2/5 in the same row) - if (_lastEan13Rectangle.Width > _lastEan13Rectangle.Height){ - allowThisSupplemental = - // placed at the right and do not intersect - (rectSupplemental.Left > _lastEan13Rectangle.Right && rectSupplemental.Right > _lastEan13Rectangle.Right) && - // and located at max 50% of the ean13 width - (rectSupplemental.Left < (_lastEan13Rectangle.Right + _lastEan13Rectangle.Width / 2)) && - // and not far away down as well - (rectSupplemental.Top < (_lastEan13Rectangle.Bottom + _lastEan13Rectangle.Width / 2)); - - } - else { - // else checking by the height (both ean13 and ean2/5 in the same column) - allowThisSupplemental = - // placed at the bottom and do not intersect - (rectSupplemental.Top > _lastEan13Rectangle.Bottom && rectSupplemental.Top > _lastEan13Rectangle.Bottom) && - // located at least less than 50% of the original barcode width - (rectSupplemental.Top < (_lastEan13Rectangle.Bottom + _lastEan13Rectangle.Height / 2)) && - // and not far away right as well - (rectSupplemental.Left < (_lastEan13Rectangle.Right + _lastEan13Rectangle.Height/ 2)); - - } - } - - if (allowThisSupplemental) - { - // so we have supplemental - // so we should reset found ean13 flag and its rectangle as - // we could have one supplemental per single ean13 only - _ean13found = false; - - if (found != null) - // return the pair of current ean 13 barcode and found supplement - return new FoundBarcode[] { found, foundSupplement }; - else - // return the supplemental barcode only - return new FoundBarcode[] { foundSupplement }; - } - else - { - if (found != null) - return new FoundBarcode[] { found }; - else - return null; - } - - } - else if (found != null) - return new FoundBarcode[] { found }; - - return null; - } - } - } - - protected bool findPattern(XBitArray row, int offset, bool patternStartsFromWhite, int[] pattern) - { - if (patternStartsFromWhite) - { - while (offset < row.Size) - { - if (!row[offset]) - break; - - offset++; - } - } - else - { - while (offset < row.Size) - { - if (row[offset]) - break; - - offset++; - } - } - - bool processingWhite = patternStartsFromWhite; - - int[] moduleWidths = new int[pattern.Length]; - int currentModule = 0; - - for (int x = offset; x < row.Size; x++) - { - if (row[x] ^ processingWhite) - { - moduleWidths[currentModule]++; - } - else - { - if (currentModule == pattern.Length - 1) - { - if (calcDifference(moduleWidths, pattern, MaxSymbolDifference) < MaxAverageDifference) - { - _patternOffset = offset; - _afterPatternOffset = x; - return true; - } - - SkipTwoModules(ref offset, moduleWidths); - currentModule--; - } - else - { - currentModule++; - } - - moduleWidths[currentModule] = 1; - processingWhite = !processingWhite; - } - } - - return false; - } - - private FoundBarcode decodeFromStartSymbol(int rowNumber, XBitArray row) - { - int startSymbolOffset = _patternOffset; - int firstDigitOffset = _afterPatternOffset; - - if (!decodeFromStartImpl(row, startSymbolOffset, firstDigitOffset)) - return null; - - if (_digits.Count < 6) - return null; - - return packResult(row, rowNumber, startSymbolOffset); - } - - private FoundBarcode packResult(XBitArray row, int rowNumber, int startSymbolOffset) - { - int patternWidth = 3 * (_afterPatternOffset - _patternOffset); - if (RequireQuietZones && !HaveWhiteSpaceAfter(row, _afterPatternOffset - patternWidth, _afterPatternOffset)) - { - // sorry, but we should have white space that have the same - // length as the stop pattern width - // try to decode consecutive symbols - return null; - } - - int[] rawData = AsIntArray(_digits); - if (!verifyCheckSum(rawData)) - return null; - - FoundBarcode result = new FoundBarcode(); - result.BarcodeFormat = _currentSymbology; - result.Value = decodeRawData(rawData); - result.Rect = new SKRect(startSymbolOffset, rowNumber, _afterPatternOffset, rowNumber+1); - result.RawData = rawData; - - if (_currentSymbology == SymbologyType.EAN13) - { - // found a value encoded with EAN-13 - // we need to check if this value can and should be converted - // to UPC-A or should be ignored. - - if (!_findEan13 && result.Value[0] == '0' && _findUpca) - { - // converting to UPC-A - result.Value = result.Value.Substring(1); - result.BarcodeFormat = SymbologyType.UPCA; - } - else - { - // this value can't be converted to UPC-A - // so, left it unchanged or ignore, if EAN-13 was - // not requested - - if (!_findEan13) - return null; - } - } - - return result; - } - - private bool decodeFromStartImpl(XBitArray row, int startSymbolOffset, int firstDigitOffset) - { - if (_findEan13 || _findUpca) - { - if (decodeEan13(row, firstDigitOffset)) - return true; - } - - if (_findEan8) - { - if (decodeEan8(row, firstDigitOffset)) - return true; - } - - if (_findUpce) - { - if (decodeUpce(row, firstDigitOffset)) - return true; - } - - return false; - } - - private FoundBarcode decodeSupplementFromStartSymbol(int rowNumber, XBitArray row) - { - int startSymbolOffset = _patternOffset; - - if (_findEan5) - { - if (decodeEan5(row, _afterPatternOffset)) - return packResult(row, rowNumber, startSymbolOffset); - } - - if (_findEan2) - { - if (decodeEan2(row, _afterPatternOffset)) - return packResult(row, rowNumber, startSymbolOffset); - } - - - - return null; - } - - protected virtual bool verifyCheckSum(int[] data) - { - if (_currentSymbology == SymbologyType.EAN5 || - _currentSymbology == SymbologyType.EAN2) - { - // already checked. - return true; - } - - int[] valueToCheck = data; - if (_currentSymbology == SymbologyType.UPCE) - { - // in case of UPCE it should always start with ZERO - if (data[0] != 0) - return false; // return false if not starting with zero - - // converting value from UPCE to UPCA for the further verification - valueToCheck = convertUpceToUpca(data); - } - - if (valueToCheck == null || valueToCheck.Length == 0) - return false; - - int sum = 0; - for (int i = valueToCheck.Length - 2; i >= 0; i -= 2) - { - int digit = valueToCheck[i]; - if (digit < 0 || digit > 9) - return false; - - sum += digit; - } - - sum *= 3; - - for (int i = valueToCheck.Length - 1; i >= 0; i -= 2) - { - int digit = valueToCheck[i]; - if (digit < 0 || digit > 9) - return false; - - sum += digit; - } - - return sum % 10 == 0; - } - - public static int[] convertUpceToUpca(int[] upce) - { - int[] upceCore = null; - - if (upce.Length == 6) - { - upceCore = upce; - } - else if (upce.Length == 7) - { - // truncate last digit, assume it is just check digit - upceCore = new int[6]; - Array.Copy(upce, upceCore, 6); - } - else if (upce.Length == 8) - { - // truncate first and last digit, - // assume first digit is number system digit - // last digit is check digit - upceCore = new int[6]; - Array.Copy(upce, 1, upceCore, 0, 6); - } - else - { - return null; - } - - int[] upca = new int[12]; - int lastDigit = upceCore[5]; - switch (lastDigit) - { - case 0: - case 1: - case 2: - upca[1] = upceCore[0]; - upca[2] = upceCore[1]; - upca[3] = lastDigit; - upca[8] = upceCore[2]; - upca[9] = upceCore[3]; - upca[10] = upceCore[4]; - break; - case 3: - upca[1] = upceCore[0]; - upca[2] = upceCore[1]; - upca[3] = upceCore[2]; - upca[9] = upceCore[3]; - upca[10] = upceCore[4]; - break; - case 4: - upca[1] = upceCore[0]; - upca[2] = upceCore[1]; - upca[3] = upceCore[2]; - upca[4] = upceCore[3]; - upca[10] = upceCore[4]; - break; - default: - upca[1] = upceCore[0]; - upca[2] = upceCore[1]; - upca[3] = upceCore[2]; - upca[4] = upceCore[3]; - upca[5] = upceCore[4]; - upca[10] = lastDigit; - break; - } - - int check = calculateUpcaChecksum(upca); - if (check > -1) - { - upca[11] = check; - } - - return upca; - } - - protected static int calculateUpcaChecksum(int[] data) - { - - if (data == null || data.Length == 0) - return -1; - - int sum = 0; - for (int i = data.Length - 2; i >= 0; i -= 2) - { - int digit = data[i]; - if (digit < 0 || digit > 9) - return -1; - - sum += digit; - } - - sum *= 3; - - for (int i = data.Length - 1; i >= 0; i -= 2) - { - int digit = data[i]; - if (digit < 0 || digit > 9) - return -1; - - sum += digit; - } - - return sum % 10; - } - - protected int decodeSymbol(XBitArray row, int offset, int[][] patterns){ - return decodeSymbol(row, offset, patterns, MaxSymbolDifference, MaxAverageDifference); - } - - protected int decodeSymbol(XBitArray row, int offset, int[][] patterns,double maxSymbolDifference, double maxAverageDifference) - { - bool read = readModules(row, offset, _moduleWidths); - if (!read) - return -1; - - double smallestDifference = maxAverageDifference; - int symbol = -1; - - for (int i = 0; i < patterns.Length; i++) - { - double difference = calcDifference(_moduleWidths, patterns[i], maxSymbolDifference); - if (difference < smallestDifference) - { - symbol = i; - smallestDifference = difference; - } - } - - if (symbol >= 0) - return symbol; - - return -1; - } - - protected bool decodeEan13(XBitArray row, int firstDigitOffset) - { - _currentSymbology = SymbologyType.EAN13; - - _digits.Clear(); - Array.Clear(_moduleWidths, 0, 4); - - int pattern = 0; - int rowOffset = firstDigitOffset; - for (int x = 0; x < 6 && rowOffset < row.Size; x++) - { - int symbol = decodeSymbol(row, rowOffset, _leftPatterns); - if (symbol < 0 || symbol > 19) - return false; - - _digits.Add(symbol % 10); - for (int i = 0; i < _moduleWidths.Length; i++) - rowOffset += _moduleWidths[i]; - - if (symbol >= 10) - pattern |= 1 << (5 - x); - } - - int firstDigit = patternToFirstEanDigit(pattern); - if (firstDigit == -1) - return false; - - _digits.Insert(0, firstDigit); - - if (!findPattern(row, rowOffset, true, _middlePattern)) - return false; - - rowOffset = _afterPatternOffset; - for (int x = 0; x < 6 && rowOffset < row.Size; x++) - { - int symbol = decodeSymbol(row, rowOffset, _leftOddPatterns); - if (symbol < 0 || symbol > 9) - return false; - - _digits.Add(symbol); - for (int i = 0; i < _moduleWidths.Length; i++) - rowOffset += _moduleWidths[i]; - } - - if (_digits.Count != 12 && _digits.Count != 13) - return false; - - return findPattern(row, rowOffset, false, _startStopPattern); - } - - protected bool decodeEan8(XBitArray row, int firstDigitOffset) - { - _currentSymbology = SymbologyType.EAN8; - - _digits.Clear(); - Array.Clear(_moduleWidths, 0, 4); - - int rowOffset = firstDigitOffset; - for (int x = 0; x < 4 && rowOffset < row.Size; x++) - { - int symbol = decodeSymbol(row, rowOffset, _leftOddPatterns); - if (symbol < 0 || symbol > 9) - return false; - - _digits.Add(symbol); - for (int i = 0; i < _moduleWidths.Length; i++) - rowOffset += _moduleWidths[i]; - } - - if (!findPattern(row, rowOffset, true, _middlePattern)) - return false; - - rowOffset = _afterPatternOffset; - for (int x = 0; x < 4 && rowOffset < row.Size; x++) - { - int symbol = decodeSymbol(row, rowOffset, _leftOddPatterns); - if (symbol < 0 || symbol > 9) - return false; - - _digits.Add(symbol); - for (int i = 0; i < _moduleWidths.Length; i++) - rowOffset += _moduleWidths[i]; - } - - if (_digits.Count != 8) - return false; - - return findPattern(row, rowOffset, false, _startStopPattern); - } - - protected bool decodeUpce(XBitArray row, int firstDigitOffset) - { - _currentSymbology = SymbologyType.UPCE; - - _digits.Clear(); - Array.Clear(_moduleWidths, 0, 4); - - int pattern = 0; - int rowOffset = firstDigitOffset; - for (int x = 0; x < 6 && rowOffset < row.Size; x++) - { - int symbol = decodeSymbol(row, rowOffset, _leftPatterns); - if (symbol < 0 || symbol > 19) - return false; - - _digits.Add(symbol % 10); - for (int i = 0; i < _moduleWidths.Length; i++) - rowOffset += _moduleWidths[i]; - - if (symbol >= 10) - pattern |= 1 << (5 - x); - } - - if (_digits.Count != 6) - return false; - - if (!postProcessUpce(pattern)) - return false; - - return findPattern(row, rowOffset, true, _upceStopPattern); - } - - protected bool decodeEan2(XBitArray row, int firstDigitOffset) - { - _currentSymbology = SymbologyType.EAN2; - - _digits.Clear(); - Array.Clear(_moduleWidths, 0, 4); - - bool firstIsOdd = false; - bool secondIsOdd = false; - - int rowOffset = firstDigitOffset; - for (int x = 0; x < 2 && rowOffset < row.Size; x++) - { - int symbol = decodeSymbol(row, rowOffset, _leftPatterns, MaxSymbolDifferenceEAN2, MaxAverageDifferenceEAN2); - if (symbol < 0 || symbol > 19) - return false; - - _digits.Add(symbol % 10); - _patternOffset = rowOffset; - - for (int i = 0; i < _moduleWidths.Length; i++) - rowOffset += _moduleWidths[i]; - - if (x == 0) - firstIsOdd = symbol < 10; - else - secondIsOdd = symbol < 10; - - if (x != 1) - { - int[] separatorModuleWidth = new int[2]; - bool read = readModules(row, rowOffset, separatorModuleWidth); - if (!read) - return false; - - double difference = calcDifference(separatorModuleWidth, _supplementSeparatorPattern, MaxSymbolDifferenceEAN2); - if (difference > MaxAverageDifferenceEAN2) - return false; - - for (int i = 0; i < separatorModuleWidth.Length; i++) - rowOffset += separatorModuleWidth[i]; - } - } - - if (_digits.Count != 2) - return false; - - if (validateEan2(firstIsOdd, secondIsOdd)) - { - _afterPatternOffset = rowOffset; - return true; - } - - return false; - } - - protected bool decodeEan5(XBitArray row, int firstDigitOffset) - { - _currentSymbology = SymbologyType.EAN5; - - _digits.Clear(); - Array.Clear(_moduleWidths, 0, 4); - - int pattern = 0; - int rowOffset = firstDigitOffset; - for (int x = 0; x < 5 && rowOffset < row.Size; x++) - { - int symbol = decodeSymbol(row, rowOffset, _leftPatterns, MaxSymbolDifferenceEAN5, MaxAverageDifferenceEAN5); - if (symbol < 0 || symbol > 19) - return false; - - _digits.Add(symbol % 10); - _patternOffset = rowOffset; - - for (int i = 0; i < _moduleWidths.Length; i++) - rowOffset += _moduleWidths[i]; - - if (symbol >= 10) - pattern |= 1 << (4 - x); - - if (x != 4) - { - int[] separatorModuleWidth = new int[2]; - bool read = readModules(row, rowOffset, separatorModuleWidth); - if (!read) - return false; - - double difference = calcDifference(separatorModuleWidth, _supplementSeparatorPattern, MaxSymbolDifferenceEAN5); - if (difference > MaxAverageDifferenceEAN5) - return false; - - for (int i = 0; i < separatorModuleWidth.Length; i++) - rowOffset += separatorModuleWidth[i]; - } - } - - if (_digits.Count != 5) - return false; - - int checkSum = patternToEan5CheckSumDigit(pattern); - if (checkSum == -1) - return false; - - if (validateEan5CheckSum(checkSum)) - { - _afterPatternOffset = rowOffset; - return true; - } - - return false; - } - - private static int patternToFirstEanDigit(int pattern) - { - for (int i = 0; i < 10; i++) - { - if (pattern == _ean13FirstDigitPatterns[i]) - return i; - } - - return -1; - } - - private bool postProcessUpce(int pattern) - { - for (int numberSystem = 0; numberSystem <= 1; numberSystem++) - { - for (int i = 0; i < 10; i++) - { - if (pattern == _upcePatterns[numberSystem][i]) - { - _digits.Insert(0, numberSystem); - _digits.Add(i); - return true; - } - } - } - - return false; - } - - private string decodeRawData(int[] rawData) - { - StringBuilder sb = new StringBuilder(); - foreach (int i in rawData) - sb.Append((char)('0' + i)); - - return sb.ToString(); - } - - private static int patternToEan5CheckSumDigit(int pattern) - { - for (int i = 0; i < 10; i++) - { - if (pattern == _ean5Structures[i]) - return i; - } - - return -1; - } - - private bool validateEan5CheckSum(int checkSum) - { - int total = 0; - for (int i = 0; i < 5; i++) - { - if (i % 2 == 0) - total += (int)_digits[i] * 3; - else - total += (int)_digits[i] * 9; - } - - return (total % 10) == checkSum; - } - - private bool validateEan2(bool firstIsOdd, bool secondIsOdd) - { - int data = (int)_digits[0] * 10 + (int)_digits[1]; - int pattern = data % 4; - - if (pattern == 0) - return (firstIsOdd && secondIsOdd); - - if (pattern == 1) - return (firstIsOdd && !secondIsOdd); - - if (pattern == 2) - return (!firstIsOdd && secondIsOdd); - - if (pattern == 3) - return (!firstIsOdd && !secondIsOdd); - - // make compiler happy - return false; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/LegacyDecoders/I2of5Reader.cs b/MuPDF.NET/Barcode/Decoders/LegacyDecoders/I2of5Reader.cs deleted file mode 100644 index ecb940e7..00000000 --- a/MuPDF.NET/Barcode/Decoders/LegacyDecoders/I2of5Reader.cs +++ /dev/null @@ -1,783 +0,0 @@ -using System; -using System.Text; -using System.Collections; -using System.Drawing; -using System.Collections.Generic; -using SkiaSharp; - -namespace BarcodeReader.Core.LegacyDecoders -{ - - /// - /// internal enum - /// interleaved 2 of 5 submodes - /// -#if CORE_DEV - public -#else - internal -#endif - enum I2OF5SubMode - { - /// - /// interleaved 2 of 5 - /// - Interleaved2of5, - /// - /// itf-14 ( - /// - ITF14, - GTIN14, - Circular - }; - - /// - /// ITF-14 decoder (similar to Interleaved 2 of 5 but requires 14 digits only) - /// -#if CORE_DEV - public -#else - internal -#endif - class ITF14Reader : I2of5Reader - { - public ITF14Reader() : this(null) - { - } - - public ITF14Reader(IBarcodeConsumer consumer) : base(consumer) - { - SubMode = I2OF5SubMode.ITF14; // switch to itf-14 mode - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.ITF14; - } - } - -#if CORE_DEV - public -#else - internal -#endif - class GTIN14Reader : I2of5Reader - { - public GTIN14Reader() : this(null) - { - } - - public GTIN14Reader(IBarcodeConsumer consumer) : base(consumer) - { - SubMode = I2OF5SubMode.GTIN14; // switch to gtin-14 mode - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.GTIN14; - } - } - -#if CORE_DEV - public -#else - internal -#endif - class CircularI2of5Reader : I2of5Reader - { - public CircularI2of5Reader() : this(null) - { - } - - public CircularI2of5Reader(IBarcodeConsumer consumer) : base(consumer) - { - SubMode = I2OF5SubMode.Circular; // switch to gtin-14 mode - - // values found during experiments - - // "after" quote zone is quite large as it is inside the "circle" - CONST_QUIET_ZONE_AFTER_WIDTH_FACTOR = 2.2f; // 2.2f; - // "before" quite zone is quite small as it may start anywhere - CONST_QUIET_ZONE_BEFORE_WIDTH_FACTOR = 0.1f;// 2.2f; - - // values found during experiments with circle barcodes - MaxAverageDifference = 0.5f; - MaxSymbolDifference = 0.7f; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.CircularI2of5; - } - } - - /// - /// Interleaved 2 of 5 decoder - /// -#if CORE_DEV - public -#else - internal -#endif - class I2of5Reader : SymbologyReader - { - /// - /// defines the width of the "after" quiet zone (in stop symbol width) - /// - protected float CONST_QUIET_ZONE_AFTER_WIDTH_FACTOR = 2.2f; - - /// - /// defines the width of the "before" quiet zone (in stop symbol width) - /// - protected float CONST_QUIET_ZONE_BEFORE_WIDTH_FACTOR = 2.2f; - - protected float MaxAverageDifference = 0.4f; - protected float MaxSymbolDifference = 0.7f; - - private List _cachedBarcodes = new List(); - - /// - /// Max factor allowed for most wide bar width to current bar width - /// We are using this factor to filter allowed bars (i.e. we are not accepting ones which are "too" wide) - /// - private const float MOST_WIDE_MAX_FACTOR = 1.34f; // 1.34f is the factor that works OK for most images including noisy ones. Larger factor leads to more false positives - - private static string _alphabet = "0123456789"; - - private static int[] _startPattern = { 1, 1, 1, 1 }; - - // stop patter gets built when needed. it helps to - // construct valid proportion between wide and narrow widths - private int[] _stopPattern = { 2, 1, 1 }; - - // Interleaved 2 of 5 symbology uses wide-narrow patterns for - // it's symbols (each two symbols are packed in interleaved manner) - // In order to simplify and speed up comparison, we pack individual - // patterns into ints using following rule: - // wnnnw -> 10001 -> 0x11 - - private static int[] _patterns = - { - 0x06, 0x11, 0x09, 0x18, 0x05, - 0x14, 0x0c, 0x03, 0x12, 0x0a - }; - - - private ArrayList _wideModuleWidths = new ArrayList(); - private ArrayList _narrowModuleWidths = new ArrayList(); - - /// - /// internal sub mode switch to use - /// ITF14 mode (similar to i2of5 but 14 digits only) - /// GTIN14 mode (similar to i2of5 but 14 digits only) - /// Circular (circular barcode based on i2of5) - /// or - /// Normal (interleaved 2 of 5) - /// - protected I2OF5SubMode SubMode = I2OF5SubMode.Interleaved2of5; - - public I2of5Reader() : this(null) - { - } - - public I2of5Reader(IBarcodeConsumer consumer) : base(consumer) - { - // require quite zones by default! - BeforeDecoding(); - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.I2of5; - } - - public override void BeforeDecoding() - { - // enable quiet zones by default - RequireQuietZones = true; - // clear cached barcodes - _cachedBarcodes.Clear(); - } - - - - /// - /// override i2of5 to always require quiet zones - /// - new public bool RequireQuietZones - { - get - { - // always require quite zone - return true; - } - set {; } - } - - - public override FoundBarcode[] DecodeRow(int rowNumber, XBitArray row) - { - int offset = 0; - while (offset < row.Size) - { - if (row[offset]) - break; - - offset++; - } - - // offset now points to a first black pixel - int symbolStart = offset; - bool processingWhite = false; - - // start symbol for Interleaved 2 of 5 consists of 4 modules - int currentModule = 0; - int[] moduleWidths = new int[4]; - - for (int x = offset; x < row.Size; x++) - { - if (row[x] ^ processingWhite) - { - moduleWidths[currentModule]++; - } - else - { - if (currentModule == moduleWidths.Length - 1) - { - double difference = calcDifference(moduleWidths, _startPattern, MaxSymbolDifference); - if (difference < MaxAverageDifference) - { - int iWhiteSpace = 0; - foreach (int ii in moduleWidths) - iWhiteSpace += ii; - - if (!RequireQuietZones || HaveWhiteSpaceBefore(row, x + (int) Math.Round(iWhiteSpace * CONST_QUIET_ZONE_BEFORE_WIDTH_FACTOR, 0), symbolStart)) - { - // we have white space that is half of symbol long - // try to decode consecutive symbols - FoundBarcode found = decodeFromStartSymbol(rowNumber, row, x, symbolStart); - - if (found == null) - return null; - - return new FoundBarcode[] { found }; - } - } - - SkipTwoModules(ref symbolStart, moduleWidths); - currentModule--; - } - else - { - currentModule++; - } - - moduleWidths[currentModule] = 1; - processingWhite = !processingWhite; - } - } - - return null; - } - - private FoundBarcode decodeFromStartSymbol(int rowNumber, XBitArray row, int offset, int startOffset) - { - _wideModuleWidths.Clear(); - _narrowModuleWidths.Clear(); - - // each Interleaved 2 of 5 symbol consists of 5 modules, - // but symbols are encoded in pairs, so we should read 10 modules - // and the split them into two symbols - int[] moduleWidths = new int[10]; - int[] blackModules = new int[5]; - int[] whiteModules = new int[5]; - - ArrayList symbols = new ArrayList(); - - int offsetBeforeSymbol = offset; - for (; ; ) - { - offsetBeforeSymbol = offset; - bool read = readModules(row, offset, moduleWidths); - if (read) - { - for (int i = 0, k = 0; i < 10; i += 2, k++) - { - blackModules[k] = moduleWidths[i]; - whiteModules[k] = moduleWidths[i + 1]; - } - - int whitePattern = -1; - int blackPattern = buildWideNarrowPattern(blackModules); - if (blackPattern != -1) - whitePattern = buildWideNarrowPattern(whiteModules); - - if (blackPattern != -1 && whitePattern != -1) - { - int blackIndex = patternToIndex(blackPattern); - if (blackIndex == -1) - break; - - int whiteIndex = patternToIndex(whitePattern); - if (whiteIndex == -1) - break; - - symbols.Add(blackIndex); - symbols.Add(whiteIndex); - - for (int i = 0; i < moduleWidths.Length; i++) - offset += moduleWidths[i]; - } - else - { - // 10 modules were read successfully, but they do not - // describe valid symbol. but may be first 3 of them - // describe stop pattern - if (goodForStopSymbol(moduleWidths)) - { - // calculate the width of the detected stop symbol - int stopSymbolWidthSum = 0; - int moduleWidthsLength = moduleWidths.Length; - - if (SubMode == I2OF5SubMode.Circular) - { - for (int ii = 0; ii < 3; ii++) - { - stopSymbolWidthSum += moduleWidths[ii]; - } - } - else // non circular - { - for (int ii = 0; ii < 3; ii++) - { - stopSymbolWidthSum += moduleWidths[moduleWidthsLength - 1 - ii]; - } - } - - // check if we have a quiet zone - if (RequireQuietZones) - { - if (SubMode == I2OF5SubMode.Circular) - { - if (RequireQuietZones && HaveWhiteSpaceAfter(row, offsetBeforeSymbol + stopSymbolWidthSum, offset + stopSymbolWidthSum + (int) Math.Round(stopSymbolWidthSum * CONST_QUIET_ZONE_AFTER_WIDTH_FACTOR, 0), true)) - break; - } - else - { - if (RequireQuietZones && HaveWhiteSpaceAfter(row, offsetBeforeSymbol + stopSymbolWidthSum, offset + (int) Math.Round(stopSymbolWidthSum * CONST_QUIET_ZONE_AFTER_WIDTH_FACTOR, 0))) - break; - } - } - else // does not require white space after so break instantly - break; - } - return null; - } - } - else - { - // failed to read 10 modules, but maybe there were enough - // modules (3, actually) for stop pattern - if (goodForStopSymbol(moduleWidths)) - { - // calculate the width of the detected stop symbol - int stopSymbolWidthSum = 0; - int moduleWidthsLength = moduleWidths.Length; - - - if (SubMode == I2OF5SubMode.Circular) - { - for (int ii = 0; ii < 3; ii++) - { - stopSymbolWidthSum += moduleWidths[ii]; - } - } - else // non circular - { - for (int ii = 0; ii < 3; ii++) - { - stopSymbolWidthSum += moduleWidths[moduleWidthsLength - 1 - ii]; - } - } - - // check if we have a quiet zone - if (RequireQuietZones) - { - if (SubMode == I2OF5SubMode.Circular) - { - if (RequireQuietZones && HaveWhiteSpaceAfter(row, offsetBeforeSymbol + stopSymbolWidthSum, offset + (int) Math.Round(stopSymbolWidthSum * CONST_QUIET_ZONE_AFTER_WIDTH_FACTOR, 0), true)) - break; - } - else - { - if (RequireQuietZones && HaveWhiteSpaceAfter(row, offsetBeforeSymbol + stopSymbolWidthSum, offset + (int) Math.Round(stopSymbolWidthSum * CONST_QUIET_ZONE_AFTER_WIDTH_FACTOR, 0))) - break; - } - } - else // does not require white space after so break instantly - break; - } - - - return null; - } - } - - int stopSymbolWidth = 0; - for (int i = 0; i < 3; i++) - stopSymbolWidth += moduleWidths[i]; - - if (SubMode == I2OF5SubMode.Circular) - { - if (RequireQuietZones && !HaveWhiteSpaceAfter(row, offsetBeforeSymbol + stopSymbolWidth, offset + (int) Math.Round(stopSymbolWidth * CONST_QUIET_ZONE_AFTER_WIDTH_FACTOR, 0), true)) - return null; - } - else - { - if (RequireQuietZones && !HaveWhiteSpaceAfter(row, offsetBeforeSymbol + stopSymbolWidth, offset + (int) Math.Round(stopSymbolWidth * CONST_QUIET_ZONE_AFTER_WIDTH_FACTOR, 0))) - return null; - } - offset += stopSymbolWidth; - - if ( - (SubMode == I2OF5SubMode.GTIN14 || SubMode == I2OF5SubMode.ITF14) - && symbols.Count != 14 - ) - return null; - - if (symbols.Count == 0) - return null; - - float fConfidence = VerifyCheckSumAndGetConfidence(ref symbols); - - // no confidence so exit - if (fConfidence < float.Epsilon) - return null; - - // circular barcode - if (SubMode == I2OF5SubMode.Circular) - { - // if confidence is not 1.00 and we have ODD number of symbols - // then try to find the latest with the checkbox - /* - while (fConfidence < 0.98f && symbols.Count > 2) - { - // we should try to remove the very last digit and try again - symbols.RemoveAt(symbols.Count - 1); - fConfidence = VerifyCheckSumAndGetConfidence(ref symbols); - - if (fConfidence > 0.95f) - symbols.RemoveAt(symbols.Count - 1); - else - fConfidence = 0.0f; - } - */ - - if (symbols.Count > 2) - fConfidence = 0.0f; - else - fConfidence = 1.0f; - - // again: no confidence so exit - if (fConfidence < float.Epsilon) - return null; - } - - int[] rawdata = AsIntArray(symbols); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < rawdata.Length; i++) - { - int symbol = rawdata[i]; - sb.Append(_alphabet[symbol]); - } - - if (!CheckIfShouldBeProcessed(sb.ToString(), offset, startOffset, rowNumber, stopSymbolWidth)) - return null; - - // interleabed 2 of 5 - // checking minimal allowed length - if (SubMode != I2OF5SubMode.Circular) - { - int length = sb.Length; - if (length < 4 || length % 2 != 0) - { - // Interleaved 2 of 5 barcode must have length - // greater than 6 (yeah, we try to reduce parasites) - // and also length must be even. - return null; - } - } - - FoundBarcode result = new FoundBarcode(); - result.Confidence = fConfidence; - - if (SubMode == I2OF5SubMode.ITF14) //(this is ITF14Reader) - result.BarcodeFormat = SymbologyType.ITF14; - else if (SubMode == I2OF5SubMode.GTIN14) // (this is GTIN14Reader) - result.BarcodeFormat = SymbologyType.GTIN14; - else if (SubMode == I2OF5SubMode.Circular) - result.BarcodeFormat = SymbologyType.CircularI2of5; - else - result.BarcodeFormat = SymbologyType.I2of5; - - result.Rect = new SKRect(startOffset, rowNumber, offset, rowNumber+1); - result.RawData = rawdata; - - - result.Value = sb.ToString(); - - // check and save into cached if not yet - AddBarcodeToCache(result); - return result; - } - - /// - /// adds the barcode to the cache - /// - /// - protected void AddBarcodeToCache(FoundBarcode result) - { - _cachedBarcodes.Add(result); - } - - - /// - /// Checks if new found barcode should be added - /// - /// - /// - /// - /// - /// - /// Returns False if this barcode is already cached - /// True (i.e. continue wth this barcode) otherwise - protected bool CheckIfShouldBeProcessed(string valueToCheckAsExisting, int offset, int startOffset, int rowNumber, int stopSymbolWidth) - { - // checkin against cached barcodes - // we filter barcodes which are close to the existing i2of5 more than quiet zone - // and if new barcode is the sub-string of the existing one - foreach (FoundBarcode exBar in _cachedBarcodes) - { - // check if valueToCheckAsExisting is the substring of the - // existing barcode - if ( - // if new value falls into one of existing or equal! - exBar.Value.IndexOf(valueToCheckAsExisting) > -1 && - // if new value length LESSER than existing - exBar.Value.Length >= valueToCheckAsExisting.Length - ) - { - // now also check if new barcode is quite close to existing ones - // first check if has the same align - // as we scan from top to bottom so new barcode can appear only below existing ones - if ( - ( - Math.Abs(startOffset - exBar.Rect.Left) < (int) Math.Round(stopSymbolWidth * CONST_QUIET_ZONE_AFTER_WIDTH_FACTOR, 0) //&& - // bottom is quiet close to the existing barcode - //Math.Abs(rowNumber - exBar.SrcRect.Bottom) < (int)Math.Round(stopSymbolWidth * CONST_QUIET_ZONE_AFTER_WIDTH_FACTOR, 0) - ) - || - ( - // barcode cutted from the left or from the right - ( - startOffset >= exBar.Rect.Left && - Math.Abs(offset - exBar.Rect.Right) < (int) Math.Round(stopSymbolWidth * CONST_QUIET_ZONE_AFTER_WIDTH_FACTOR, 0) - ) - ) - - ) - return false; // return false as new barcode is too close to the existing one - - } - } - return true; - } - - /// - /// verifies the checksum of the given data using mod 10 checksum algorithm - /// - /// - /// - private float VerifyCheckSumAndGetConfidence(ref ArrayList data) - { - int datalength = data.Count; // length of data without the very last one - // output result flag - bool checksumMatched = false; - // 2 cases: - // 1) EVEN number of digits with checksum: - // - should contain ODD number of digits + single checksum digit - // if checksum matches then return confidence 1.00 - // 2) EVEN number of digits without checksum: - // return 0.5 as confidence - int iRetries = 0; - for (iRetries = 0; iRetries < 2; iRetries++) - { - int sum = 0; - for (int i = 0; i < datalength - 1; i++) - sum += (int) (data[i]) * (i % 2 == 0 ? 3 : 1); - sum = sum % 10; - if (sum != 0) - sum = 10 - sum; - - // getting the result - checksumMatched = sum == (int) data[datalength - 1]; - - if (checksumMatched) - break; - - // when trying for the first time then we may try to remove the very last symbol and try again - // if checksum is not matching then we should check if we have odd number of symbols - // like 13 symbols or 15 - // but in I2of5 we should have even number - // for example we may have: 16444118888780 (14 digits) and this is not correct with checksum (checksum is not zero) - // but 1644411888878 is correct (checksum = 8 and with 13 digits) - - if (iRetries == 0) - { - // try to decrease the data length the very last digit and try again - if (data.Count > 2 && (int) data[data.Count - 1] == 0) - datalength--; - else - break; - } - else - break; - } - - if (checksumMatched) - { - return 1.00f; - } - else - { - if (data.Count % 2 > 0) // if ODD number of digits - return 0.0f; - else - return 0.5f; - - } - } - - - private int buildWideNarrowPattern(int[] moduleWidths) - { - int currentNarrowModuleWidth = 0; - int wideModuleCount; - float mostWide; - - for (; ; ) - { - mostWide = 0; - - // find thinnest module bigger then previously found one - int minWidth = int.MaxValue; - for (int i = 0; i < moduleWidths.Length; i++) - { - int width = moduleWidths[i]; - if (width < minWidth && width > currentNarrowModuleWidth) - minWidth = width; - } - - currentNarrowModuleWidth = minWidth; - wideModuleCount = 0; - int wideModulesWidth = 0; - int pattern = 0; - for (int i = 0; i < moduleWidths.Length; i++) - { - if (moduleWidths[i] > currentNarrowModuleWidth) - { - pattern |= 1 << (moduleWidths.Length - 1 - i); - if (mostWide < moduleWidths[i]) - mostWide = moduleWidths[i]; - - wideModuleCount++; - wideModulesWidth += moduleWidths[i]; - } - } - - if (wideModuleCount < 2) - { - // each Interleaved 2 of 5 symbol must contain 2 wide modules - break; - } - - if (wideModuleCount == 2) - { - // check wide modules relative width - for (int i = 0; i < moduleWidths.Length; i++) - { - if (moduleWidths[i] > currentNarrowModuleWidth) - { - if ((mostWide / moduleWidths[i]) > MOST_WIDE_MAX_FACTOR) - { - // this wide module width is too different - // from the most wide module width - return -1; - } - } - } - - _wideModuleWidths.Add(wideModulesWidth >> 1); - _narrowModuleWidths.Add(currentNarrowModuleWidth); - return pattern; - } - } - - return -1; - } - - private static int patternToIndex(int pattern) - { - for (int i = 0; i < _patterns.Length; i++) - { - if (_patterns[i] == pattern) - return i; - } - - return -1; - } - - private bool goodForStopSymbol(int[] moduleWidths) - { - - if (SubMode == I2OF5SubMode.Circular) - { - - if (_wideModuleWidths.Count == 0 || _narrowModuleWidths.Count == 0) - { - return false; - } - } - else - { - if (_wideModuleWidths.Count == 0 || _narrowModuleWidths.Count == 0 || - _wideModuleWidths.Count % 2 != 0 || - _narrowModuleWidths.Count % 2 != 0) - { - return false; - } - } - - int wideModuleWidth = 0; - foreach (object o in _wideModuleWidths) - wideModuleWidth += (int) o; - wideModuleWidth /= _wideModuleWidths.Count; - - int narrowModuleWidth = 0; - foreach (object o in _narrowModuleWidths) - narrowModuleWidth += (int) o; - narrowModuleWidth /= _narrowModuleWidths.Count; - - wideModuleWidth /= narrowModuleWidth; - //_stopPattern[0] = wideModuleWidth; - //_stopPattern[1] = 1; - //_stopPattern[2] = 1; - - double difference = calcDifference(moduleWidths, _stopPattern, MaxSymbolDifference, true); - if (difference < MaxAverageDifference) - return true; - - return false; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/LegacyDecoders/TriopticCode39Reader.cs b/MuPDF.NET/Barcode/Decoders/LegacyDecoders/TriopticCode39Reader.cs deleted file mode 100644 index 803e75ad..00000000 Binary files a/MuPDF.NET/Barcode/Decoders/LegacyDecoders/TriopticCode39Reader.cs and /dev/null differ diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/AustraliaPostCode/PostCodeDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/AustraliaPostCode/PostCodeDecoder.cs deleted file mode 100644 index 81e4029a..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/AustraliaPostCode/PostCodeDecoder.cs +++ /dev/null @@ -1,85 +0,0 @@ -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.AustraliaPostCode -{ - //Each 4-state bar encodes a value 0..3. This defines a base-4 message, incluing RS correction bits. - //The code starts with 4 bars defining the FCC, the kind of barcode. There are 3 possible message lenghts: 11, 16, 21 - //Then follows a DPID code (destination postcode identifier), and finally user data. - //There are 2 tables to decode de barcode, table N and tableC. They are used in different parts of the message - //FCC and DPID are encoded using tableN. User date is encoded using tableC. - internal class PostCodeDecoder : IDecoderAscDescBars - { - static readonly string[] tableN = new string[] { "0", "1", "2", null, "3", "4", "5", null, "6", "7", "8", null, "9" }; - static readonly string[] tableC = new string[]{"A","B","C"," ","D","E","F","#","G","H","I","a","b", - "c","d","e","J","K","L","f","M","N","O","g","P","Q","R","h","i","j","k","l","S","T","U", - "m","V","W","X","n","Y","Z","0","o","p","q","r","s","1","2","3","t","4","5","6","u","7", - "8","9","v","w","x","y","z"}; - - - public PostCodeDecoder() - { - } - - //Generic method to decode some bars from pIn, to pIn+N, in groups of step, using the given char table. - protected string DecodeChars(int[] base4, int pIn, int N, int step, string[] table) - { - string code = ""; - for (int i = 0; i < N; i++) - { - int value = 0; - for (int j = 0; j < step; j++) value = value*4 + base4[pIn++]; - if (value > table.Length || table[value] == null) code += "*"; //ERROR - else code += table[value]; - } - return code; - } - - //Each 4-state bar encodes a value 0..3. This defines a base-4 message, incluing RS correction bits. - //The bit string is converted to triplets of 4-state values. So, each triplet has 6 bits length. - //There are 3 possible message lenghts: 11, 16, 21 - //Then apply Reed Solomon to this message and check if it equals to the RS bits in the message - //IMPORTANT: Reed Solomon parameters are: words of 6 bits, add 4 correction words, and uses 67 as - // basis to generate polinomials!!! - public virtual string Decode(bool[][] samples, out float confidence) - { - //To base 4 code - int N=samples.Length; // 2+ 20+[1+16+31]+12 + 2= 37, 52, 67 - int[] base4 = new int[N-4]; //without start and stop bars 33, 48, 63 - for (int i = 2; i < samples.Length - 2; i++) - base4[i - 2] = (samples[i][0] && samples[i][1] ? 0 : - samples[i][0] && !samples[i][1] ? 1 : - !samples[i][0] && samples[i][1] ? 2 : 3); - - - //Reed Solomon - int[] words = new int[N == 37 ? 11 : N == 52 ? 16 : 21]; - int j = 0; - for (int i = 0; i < words.Length; i++) - words[i] = base4[j++] * 16 + base4[j++] * 4 + base4[j++]; - - ReedSolomon rs = new ReedSolomon(words, 4, 6, 67, 1); - rs.Correct(out confidence); - if (!rs.CorrectionSucceeded) return null; - words=rs.CorrectedData; - - j = 0; - for (int i = 0; i < words.Length; i++, j+=3) - { - int n = words[i]; - for (int k = 2; k >=0; k--) { base4[j + k] = n % 4; n >>= 2; } - } - - - //Decode - string FCC = DecodeChars(base4, 0, 2, 2, tableN); //FCC: 4 bars tableN - string DPID= DecodeChars(base4, 4, 8, 2, tableN); //DPID: 16 base tableN - string CUSTOMER=""; - if (FCC=="11" && N==37) {} - else if (FCC=="59" && N==52) CUSTOMER=DecodeChars(base4,20,5,3,tableC); - else if (FCC=="62" && N==67) CUSTOMER=DecodeChars(base4,20,10,3,tableC); - else return null; - - return FCC + DPID + CUSTOMER ; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/AustraliaPostCode/PostCodeReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/AustraliaPostCode/PostCodeReader.cs deleted file mode 100644 index 6e90f3ce..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/AustraliaPostCode/PostCodeReader.cs +++ /dev/null @@ -1,35 +0,0 @@ -using BarcodeReader.Core.Common; -using BarcodeReader.Core.IntelligentMail; - -namespace BarcodeReader.Core.AustraliaPostCode -{ - //The only difference between IM and PostCode reader is the length and ratio of the barcode. - //A different decoder is also set. -#if CORE_DEV - public -#else - internal -#endif - class PostCodeReader : IMReader - { - public override SymbologyType GetBarCodeType() - { - return SymbologyType.AustralianPostCode; - } - - protected override bool CheckNBars(int nBars) - { - return nBars == 37 || nBars == 52 || nBars == 67; - } - - protected override bool CheckRatio(float ratio) - { - return ratio > 2f && ratio < 25f; - } - - protected override IDecoderAscDescBars GetDecoder() - { - return new PostCodeDecoder(); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecFinder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecFinder.cs deleted file mode 100644 index 103dd1ff..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecFinder.cs +++ /dev/null @@ -1,57 +0,0 @@ -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Aztec -{ - class SmallAztecFactory : FinderFactory - { - public float GetOffsetFactor() { return 0.75F; } - public float GetCenterRatio() { return 1F / 7F; } - public int GetNumModules() { return 7; } - public bool CanHaveHoles() { return false; } - public SquareFinder IsFinder(ImageScaner scan, MyPointF A, MyPointF B, MyPointF C, MyPointF D) - { - AztecFinder finder = new AztecFinder(A, B, C, D, 7); - - //Sample the finder to be discard wrong candidates. - //This test is not complete, but seems to be enough. - MyVectorF luRight = finder.RightNormal * finder.Width / 7F; - MyVectorF luDown = finder.DownNormal * finder.Height / 7F; - Grid grid = new Grid(finder.Center(), luRight, luDown); - - int count = 0; - for (int i = -2; i <= 2; i++) - { - if (scan.isBlackSample(grid.GetSamplePointRegular(i, -3),0f)) count++; - if (!scan.isBlackSample(grid.GetSamplePointRegular(i, -2),0f)) count++; - if (!scan.isBlackSample(grid.GetSamplePointRegular(i, 2),0f)) count++; - if (scan.isBlackSample(grid.GetSamplePointRegular(i, 3),0f)) count++; - } - if (count > 3) return null; - - return finder; - } - } - - class BigAztecFactory : FinderFactory - { - public float GetOffsetFactor() { return 0.75F; } - public float GetCenterRatio() { return 1F / 11F; } - public int GetNumModules() { return 11; } - public bool CanHaveHoles() { return false; } - public SquareFinder IsFinder(ImageScaner scan, MyPointF A, MyPointF B, MyPointF C, MyPointF D) - { - AztecFinder finder = new AztecFinder(A, B, C, D, 11); - //TODO check modules around - return finder; - } - } - - class AztecFinder: SquareFinder - { - //Pattern of Aztec finder - public static readonly int[][] finder = new int[][]{new int[]{ 1, 1, 1, 1, 1, 1, 1 }}; //WBWBWBW - - public AztecFinder(MyPointF A, MyPointF B, MyPointF C, MyPointF D, int numModules) : base(A, B, C, D, numModules) { } - public AztecFinder(AztecFinder f) : base(f) { } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecOrientation.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecOrientation.cs deleted file mode 100644 index 065f2155..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecOrientation.cs +++ /dev/null @@ -1,195 +0,0 @@ -using System; -using System.Collections; - -namespace BarcodeReader.Core.Aztec -{ - // class designed to account for the orientation patterns used to orient the aztec code - class AztecOrientation - { - public enum Pattern { TopLeft = 0, TopRight = 1, BottomLeft = 3, BottomRight = 2 } - - private static readonly Hashtable IndexMap = new Hashtable(); - - static AztecOrientation() - { - IndexMap.Add(7, 0); - IndexMap.Add(6, 1); - IndexMap.Add(1, 2); - IndexMap.Add(0, 3); - } - - private enum Mirrored { DontKnowYet, Yes, No } - - private readonly MyPoint[] points = new MyPoint[4]; - - private Mirrored mirrored = Mirrored.DontKnowYet; - - // tells if the current orientation is valid - public bool IsValid - { - get - { - if (GetValidCount() < 3) - { - return false; - } - - MyVector scanVector = GetModeScanVector(); - if (Math.Abs(scanVector.X + scanVector.Y) != 1) - { - return false; - } - - MyVector s1 = points[1] - points[0]; - s1 = (MyVector)(s1 / s1.Length); - MyVector s2 = points[2] - points[1]; - s2 = (MyVector)(s2 / s2.Length); - if (RotateClockwise(s1) != s2) - { - return false; - } - - return true; - } - } - - public MyPoint StartPoint - { - get - { - return points[0]; - } - } - - public AztecOrientation() - { - for (int i = 0; i < 4; i++) - { - points[i] = MyPoint.Empty; - } - } - - public void Rotate(AztecFinder finder) - { - for (int i = 0; i < 4; i++) - if (points[i].X == 0 && points[i].Y == 0) break; - else finder.Rotate(); - } - - // adds a pattern, by classifying it on the palette of possible patterns - public bool AddPattern(bool[] patternBits, MyPoint center) - { - int index = Classify(patternBits, ref mirrored); - if (!IsIndexValid(index)) - { - if (GetValidCount() == 3) - { - for (int i = 0; i < 4; ++i) - { - if (points[i].IsEmpty) - { - points[i] = center; - return true; - } - } - } - else - { - return false; - } - } - - points[(int)IndexMap[index]] = center; - return true; - } - - // gets the start vector for scanning the mode bits - public MyVector GetModeScanVector() - { - MyVector vector = points[1] - points[0]; - return (MyVector)(vector / vector.Length); - } - - // rotates the scan vector clockwise, relative to the actual orientation - public MyVector RotateClockwise(MyVector vector) - { - return mirrored == Mirrored.Yes - ? AztecUtils.RotateCounterClockwise(vector) - : AztecUtils.RotateClockwise(vector); - } - - private int GetValidCount() - { - int i = 0; - foreach (MyPoint point in points) - { - if (!point.IsEmpty) - { - ++i; - } - } - - return i; - } - - private static bool IsIndexValid(int index) - { - return IndexMap.ContainsKey(index); - } - - // classifies a set of bits as a locator pattern (also takes care of mirroring) - private static int Classify(bool[] patternBits, ref Mirrored mirrored) - { - if (patternBits.Length != 3) - { - throw new Exception("Invalid pattern length"); - } - - int index = 0; - switch (mirrored) - { - case Mirrored.Yes: - for (int i = 0; i < 3; ++i) - { - if (patternBits[i]) - { - index |= (1 << (2 - i)); - } - } - break; - case Mirrored.No: - for (int i = 0; i < 3; ++i) - { - if (patternBits[i]) - { - index |= (1 << i); - } - } - break; - default: - mirrored = Mirrored.No; - int nmIndex = Classify(patternBits, ref mirrored); - mirrored = Mirrored.Yes; - int mIndex = Classify(patternBits, ref mirrored); - if (nmIndex == mIndex) - { - index = nmIndex; - mirrored = Mirrored.DontKnowYet; - break; - } - - if (!IsIndexValid(mIndex)) - { - index = nmIndex; - mirrored = Mirrored.No; - break; - } - - index = mIndex; - break; - } - - return index; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecReader.cs deleted file mode 100644 index edcb16d9..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecReader.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace BarcodeReader.Core.Aztec -{ -#if CORE_DEV - public -#else - internal -#endif - partial class AztecReader : SymbologyReader2D - { - //Scan image rows main step - protected int scanRowStep = 1; - - //Max difference between the center of the finder and the center of the mid segment of the finder - protected float finderMaxCentersDifference = 0.1f; - - //Max distance in pixels between the center of the finder and the center of the mid segment of the finder - protected int finderMaxCentersDistanceInPixels = 2; - - int width, height; - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Aztec; - } - - protected override FoundBarcode[] DecodeBarcode() - { - this.width = BWImage.Width; - this.height = BWImage.Height; -#if DEBUG - //this.bmp=bwImage.GetAsBitmap(); //needed to save images during execution. -#endif - return Scan(); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecScaner.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecScaner.cs deleted file mode 100644 index 2705891e..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecScaner.cs +++ /dev/null @@ -1,483 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Aztec -{ -#if CORE_DEV - public -#else - internal -#endif - partial class AztecReader - { - //object that holds the bw image + methods to sample, follow vertices,... - ImageScaner scan; - //list of found codes found and correctly decoded. - LinkedList candidates; - //list of found and rejected finders, and codes to avoid exploring twice the same barcode - LinkedList exclusion; - - //patternFinder is used in the main scan loop (scanning horizontal lines). - //crossFinder is used for each horizontal pattern found, to check if it is also - //a pattern verically - IPatternFinderNoiseRow patternFinder, crossFinder; - PatternFinderNoise crossFinder2, crossFinder3; - - LinkedList foundPatterns; - - FoundBarcode[] Scan() - { - scan = new ImageScaner(BWImage); - patternFinder = new PatternFinderNoiseRow(AztecFinder.finder, true, false, 2); - //crossFinder uses the same hash table - crossFinder = new PatternFinderNoiseRow(AztecFinder.finder, true, false, 2); - crossFinder2 = new PatternFinderNoise(BWImage, AztecFinder.finder[0], false, 2); - crossFinder3 = new PatternFinderNoise(BWImage, AztecFinder.finder[0], false, 2); - candidates = new LinkedList(); - exclusion = new LinkedList(); - foundPatterns = new LinkedList(); - - -#if DEBUG_IMAGE - //bwSourceImage.GetAsBitmap().Save(@"out.png"); -#endif - //main loop to scan horizontal lines - for (int y = 0; y < height && (ExpectedNumberOfBarcodes <= 0 || candidates.Count < ExpectedNumberOfBarcodes); y += scanRowStep) - { - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - ScanRow(y, BWImage.GetRow(y)); - } - - ArrayList result = new ArrayList(); - foreach (BarCodeRegion c in candidates) - { - FoundBarcode foundBarcode = new FoundBarcode(); - - String data = ""; - if (c.Data != null) foreach (ABarCodeData d in c.Data) data += d.ToString(); - - foundBarcode.BarcodeFormat = SymbologyType.Aztec; - foundBarcode.Value = data; - - foundBarcode.Polygon = new SKPointI[5] { c.A, c.B, c.D, c.C, c.A }; - foundBarcode.Color = SKColors.Blue; - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(foundBarcode.Polygon, pointTypes); - //foundBarcode.Rect = Rectangle.Round(path.GetBounds()); - foundBarcode.Rect = Utils.DrawPath(foundBarcode.Polygon); - foundBarcode.Confidence = c.Confidence; - result.Add(foundBarcode); - - } - return (FoundBarcode[])result.ToArray(typeof(FoundBarcode)); - } - - //Scans a horizontal line looking for finder patterns (101010101). - //For each finder pattern found checks if it is a pattern vertically. - //Then tries to find the finder. - private void ScanRow(int y, XBitArray row) - { - //look for the finder - patternFinder.NewSearch(row); - while (patternFinder.NextPattern() != null) - { - MyPoint a = new MyPoint(patternFinder.First, y); - MyPoint b = new MyPoint(patternFinder.Last, y); - MyPoint center = new MyPoint(patternFinder.Center, a.Y); //center of the mid black segment of the finder - int xCenter=(a.X+b.X)/2; - int d = xCenter - center.X; - if (d < 0) d = -d; - //if the center of the finder falls close to the center of the mid black segment of the finder - if (Calc.Around(d, 0F, (float)(b.X-a.X)*finderMaxCentersDifference) || d prev = foundPatterns.Find(p); - if (prev != null) prev.Value = p; - else - { - foundPatterns.AddLast(p); - - //If the finder pattern is found in a place where another finder was found previously, it is skipped. - bool done = false; - foreach (BarCodeRegion c in exclusion) - if (c.In(center)) { done = true; break; } - - if (!done) - { -#if FIND_PATTERN - MyPoint Y = new MyPoint(0, 1); - SquareFinder f = new SquareFinder(a + Y,b + Y, a, b, 7); - candidates.AddLast(f); -#else - //checks if a vertical pattern cross the horizontal pattern in the middle - MyPoint pUp, pDown; - SmallAztecFactory factory = new SmallAztecFactory(); - AztecFinder finder = null; - if (SquareFinder.CheckCrossPattern(scan, a, b, center, crossFinder, factory, out pUp, out pDown)) - { - //calculates black - white threshold using the pixels in the pattern - scan.setBWThreshold(a,b); - finder = (AztecFinder)SquareFinder.IsFinder(scan, a, b, center, crossFinder2, crossFinder3, factory); - if (finder != null) - { - exclusion.AddFirst(finder); -#if FIND_FINDER - candidates.AddLast(finder); -#else - AztecSymbol symbol = CountModules(scan, ref finder); - if (symbol != null) - { - BitArray stream = null; - float confidence = 1F; - if (symbol.Type != AztecType.Rune) stream = ReadSymbol(symbol, out confidence); - ABarCodeData[] data = DecodeData(symbol, stream); - if (data != null) - { - finder.Data = data; - finder.Confidence = confidence; - candidates.AddFirst(finder); - exclusion.AddFirst(finder); - } - } -#endif - } - } -#endif - } - } - } - } - - //clean old patterns - Pattern.RemoveOldPatterns(foundPatterns, y); - } - - AztecSymbol CountModules(ImageScaner scan, ref AztecFinder finder) - { -#if DEBUG_IMAGE - scan.Reset(); -#endif - bool[][] centralPoints = Common.Utils.NewBoolArray(15, 15); - Grid centralGrid = new Grid(7, 7, finder.C, finder.A, finder.D , finder.B , true); - centralGrid.ExtractPoints(scan, centralPoints, new MyPoint(-2, -2), new MyPoint(0, 0), 11, 11); -#if DEBUG_IMAGE - scan.setPixel(finder.A, Color.Blue); - scan.setPixel(finder.B, Color.Blue); - scan.setPixel(finder.C, Color.Blue); - scan.setPixel(finder.D, Color.Blue); - scan.Save(@"outSamples.png"); -#endif - - // determining if the symbol is compact or not - int darkModuleCount = 0; - for (int i = 0; i < 10; ++i) - { - if (centralPoints[0][i]) - { - darkModuleCount++; - } - if (centralPoints[i][10]) - { - darkModuleCount++; - } - if (centralPoints[10][10 - i]) - { - darkModuleCount++; - } - if (centralPoints[10 - i][0]) - { - darkModuleCount++; - } - } - - AztecSymbol symbol = null; - if (darkModuleCount > 3) - { - symbol = AztecSymbol.GetCompactSymbol(centralPoints, finder); - } - - if (symbol==null) //try 11x11 - { - //centralGrid.ExtractPoints(scan, centralPoints, new MyPoint(-4, -4), new MyPoint(0, 0), 15, 15); - //symbol = AztecSymbol.GetFullRangeSymbol(centralPoints); - //if (symbol == null) - { - //Recalc corners - MyPoint left = centralGrid.GetSamplePoint(-2, 3); - MyPoint right = centralGrid.GetSamplePoint(8, 3); - MyPoint top = centralGrid.GetSamplePoint(3, -2); - MyPoint bottom = centralGrid.GetSamplePoint(3, 8); - - left = scan.NextTransition(left, new MyVectorF(-1F, 0F), finder.ModuleWidth, false); - right = scan.NextTransition(right, new MyVectorF(1F, 0F), finder.ModuleWidth, false); - top = scan.NextTransition(top, new MyVectorF(0F, -1F), finder.ModuleHeight, false); - bottom = scan.NextTransition(bottom, new MyVectorF(0F, 1F), finder.ModuleHeight, false); - - BigAztecFactory factory = new BigAztecFactory(); - finder = (AztecFinder)SquareFinder.IsFinder(scan, left, right, top, bottom, factory); - if (finder != null) - { - centralGrid = new Grid(11, 11, finder.C, finder.A, finder.D, finder.B, true); -#if DEBUG_IMAGE - scan.Reset(); - scan.setPixel(finder.A, Color.Blue); - scan.setPixel(finder.B, Color.Blue); - scan.setPixel(finder.C, Color.Blue); - scan.setPixel(finder.D, Color.Blue); -#endif - centralGrid.ExtractPoints(scan, centralPoints, new MyPoint(-2, -2), new MyPoint(0, 0), 15, 15); - - symbol = AztecSymbol.GetFullRangeSymbol(centralPoints, finder); - } - } - } -#if DEBUG_IMAGE - scan.Save(@"outSamples.png"); -#endif - - if (symbol == null) - { - return null; - } - - if (symbol.Type == AztecType.Rune) return symbol; - - int gridsPerSide = symbol.Grids.Length; - // if we have only one grid, use the central one we estabilished earlier - if (gridsPerSide == 1) - { - Grid grid = new Grid(finder.Center(), finder.ModuleRight, finder.ModuleDown); - int N=symbol.SideModuleCount; - grid.ExtractPointsRegular(scan, symbol.Bitarray, new MyPoint(-N/2, -N/2), new MyPoint(0,0), N, N); - finder.A = grid.GetSamplePointRegular(-(float)N / 2F , (float)N / 2F); - finder.B = grid.GetSamplePointRegular((float)N / 2F, (float)N / 2F); - finder.C = grid.GetSamplePointRegular(-(float)N / 2F, -(float)N / 2F); - finder.D = grid.GetSamplePointRegular((float)N / 2F, -(float)N / 2F); - } - else // setup subgrids otherwise, for each quadplane - { - int N = symbol.SideModuleCount; - int M = (N - 1)/2; - MyVectorF vdX = finder.ModuleRight; - MyVectorF vdY = finder.ModuleDown; - int nGridsPerQuad=M/16 + (M%16!=0?1:0); - int nGrids = nGridsPerQuad * 2 + 1; - - int[] mapCoord = new int[nGrids]; - MyPointF[][] alignment=new MyPointF[nGrids][]; - mapCoord[0] = 0; - int remainderModules = (N - 1 - (nGrids -3) * 16) / 2; - for (int i = 0; i < nGrids; i++) - { - if (i == 1) mapCoord[i] = remainderModules; - else if (i>1) mapCoord[i]= mapCoord[i - 1] + 16; - alignment[i] = new MyPointF[nGrids]; - for (int j = 0; j < nGrids; j++) alignment[i][j] = MyPointF.Empty; - } - mapCoord[nGrids - 1] = mapCoord[nGrids - 2] + remainderModules; - - MyPoint C = new MyPoint(nGridsPerQuad, nGridsPerQuad); - MyPointF p0 = alignment[nGridsPerQuad][nGridsPerQuad] = finder.Center(); - float moduleWidth=finder.ModuleWidth; - float moduleHeight=finder.ModuleHeight; - - //new grid using possibly rotated finder - Grid grid = new Grid(11, 11, finder.C, finder.A, finder.D, finder.B, true); - - FindGrids(p0, grid.GetSamplePoint(11, 5), vdX, vdY, moduleWidth, moduleHeight, M, alignment, C, new MyVector(1, 0)); - FindGrids(p0, grid.GetSamplePoint(5, 11), vdY, vdX, moduleHeight, moduleWidth, M, alignment, C, new MyVector(0, 1)); - FindGrids(p0, grid.GetSamplePoint(-1, 5), -vdX, vdY, moduleWidth, moduleHeight, M, alignment, C, new MyVector(-1, 0)); - FindGrids(p0, grid.GetSamplePoint(5, -1), -vdY, vdX, moduleHeight, moduleWidth, M, alignment, C, new MyVector(0, -1)); - - FindQuadGrids(p0, -vdX, -vdY, moduleWidth, moduleHeight, nGridsPerQuad, alignment, C, new MyVector(-1, -1)); - FindQuadGrids(p0, vdX, -vdY, moduleWidth, moduleHeight, nGridsPerQuad, alignment, C, new MyVector(-1, 1)); - FindQuadGrids(p0, -vdX, vdY, moduleWidth, moduleHeight, nGridsPerQuad, alignment, C, new MyVector(1, -1)); - FindQuadGrids(p0, vdX, vdY, moduleWidth, moduleHeight, nGridsPerQuad, alignment, C, new MyVector(1, 1)); - - finder.C = alignment[0][0] -vdX*0.5F -vdY*0.5F; - finder.D = alignment[0][nGrids - 1] + vdX * 0.5F - vdY * 0.5F; - finder.A = alignment[nGrids - 1][0] - vdX * 0.5F + vdY * 0.5F; - finder.B = alignment[nGrids - 1][nGrids - 1] + vdX * 0.5F + vdY * 0.5F; -#if DEBUG_IMAGE - scan.Save(@"outGrids.png"); -#endif - for (int y=1; y 0; --l) // walk through the bit data, and asseble the original bitstream - { - for (int s = 0; s < 4; ++s) - { - for (int i = 0; i < l * 4 + offset; ++i) - { - if (index < dataStream.Count) - { - dataStream[index++] = symbol.Bitarray[point.Y][point.X]; - MyPoint neighbor = point + neighborDir; - if (symbol.IsFinderBit(neighbor)) - { - neighbor += neighborDir; - } - dataStream[index++] = symbol.Bitarray[neighbor.Y][neighbor.X]; - point += nextDir; - if (symbol.IsFinderBit(point)) - { - point += nextDir; - } - } - } - point += nextDir; - if (symbol.IsFinderBit(point)) - { - point += nextDir; - } - neighborDir = AztecUtils.RotateClockwise(neighborDir); - nextDir = AztecUtils.RotateClockwise(nextDir); - } - for (int i = 0; i < 2; ++i) - { - do - { - point += new MyVector(1, 1); - } while (symbol.IsFinderBit(point)); - } - } - - /*for (int i = 0; i < dataStream.Count; i++) - { - if (i % symbol.RsWordWidth == 0) Console.Write("|"); - Console.Write(dataStream[i] ? "o" : "."); - }*/ - - // slice up the bitstream into RS codewords - int[] rsWords = AztecUtils.SliceBitStream(dataStream, symbol.RsWordWidth, symbol.BitCount%symbol.RsWordWidth); - ReedSolomon corrector = new ReedSolomon(rsWords, rsWords.Length - symbol.DataCodewordCount, - symbol.RsWordWidth, AztecUtils.Polynoms[symbol.RsWordWidth], 1); - corrector.Correct(out confidence); // correct any errors - - // if confidence is zero - if (!corrector.CorrectionSucceeded) - return null; - - // false-positive correction - if (corrector.CorrectedData.Length < symbol.DataCodewordCount) - return null; - - int[] dataWords = new int[symbol.DataCodewordCount]; - Array.Copy(corrector.CorrectedData, dataWords, symbol.DataCodewordCount); - - /*RS rs = new RS(new GF(2, symbol.RsWordWidth, AztecUtils.Polynoms[symbol.RsWordWidth]), rsWords, rsWords.Length - symbol.DataCodewordCount, false); - rs.correct(); - bool equals = true; - for (int i = 0; i < rs.correctedData.Length; i++) if (rs.correctedData[i] != dataWords[i]) - { equals = false; break; } - if (!equals) - { - confidence = 0.5F; - }*/ - - // create the error-corrected bitstream, without pad bits and rw words this time - dataStream = AztecUtils.AssembleBitStream(dataWords, symbol.RsWordWidth); - //dataStream = AztecUtils.AssembleBitStream(rs.correctedData, symbol.RsWordWidth); - /*Console.WriteLine("Corrected:"); - for (int i = 0; i < dataStream.Count; i++) - { - if (i % symbol.RsWordWidth == 0) Console.Write("|"); - Console.Write(dataStream[i] ? "o" : "."); - }*/ - return dataStream; - } - - ABarCodeData[] DecodeData(AztecSymbol symbol, BitArray stream) - { - if (symbol.Type == AztecType.Rune) // only one byte for runes - return new ABarCodeData[] { new Base256BarCodeData(new byte[] { (byte)symbol.ModeWord }, Encoding) }; - if (stream!=null) return AztecSymbolDecoder.DecodeText(stream, Encoding); // decode otherwise - return null; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecSymbol.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecSymbol.cs deleted file mode 100644 index ccc7275d..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecSymbol.cs +++ /dev/null @@ -1,290 +0,0 @@ -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Aztec -{ - internal enum AztecType - { - FullRange, - Compact, - Rune, - Undef - } - - // stores & manages information about the actual logical aztec symbol - class AztecSymbol - { - public readonly int SideModuleCount; - - public readonly int DataCodewordCount; - - public readonly int ModeWord; - - public readonly int BitCount; - - public readonly int RsWordWidth; - - public readonly int LayerCount; - - public readonly AztecType Type; - - public readonly Grid[][] Grids; - - // for the final module read step - public readonly MyPoint[][] StartPoints; - - public readonly bool[][] Bitarray; - - public float Confidence=0F; - - // construct the object based on the given info (load up reference data) - private AztecSymbol(AztecType type, int dataCodewordCount, int layerCount, int modeWord, float confidence) - { - DataCodewordCount = dataCodewordCount; - ModeWord = modeWord; - LayerCount = layerCount; - Type = type; - int gridCount; - - switch (Type) - { - case AztecType.FullRange: - RsWordWidth = AztecUtils.FullRangeBitCount[LayerCount]; - BitCount = AztecUtils.FullRangeCapacities[LayerCount]; - SideModuleCount = AztecUtils.FullRangeSizes[LayerCount]; - gridCount = AztecUtils.FullRangeGridCount[LayerCount]; - break; - case AztecType.Compact: - RsWordWidth = AztecUtils.CompactBitCount[LayerCount]; - BitCount = AztecUtils.CompactCapacities[LayerCount]; - SideModuleCount = AztecUtils.CompactSizes[LayerCount]; - gridCount = 1; - break; - default: - gridCount = 1; - break; - } - - Grids = new Grid[gridCount][]; - StartPoints = new MyPoint[gridCount][]; - for (int i = 0; i < gridCount; ++i) - { - Grids[i] = new Grid[gridCount]; - StartPoints[i] = new MyPoint[gridCount]; - } - - Bitarray = new bool[SideModuleCount][]; - for (int i = 0; i < SideModuleCount; ++i) - { - Bitarray[i] = new bool[SideModuleCount]; - } - } - - // point has coordinates > 0 - public bool IsFinderBit(MyPoint point) - { - if (Type != AztecType.FullRange) - { - return false; - } - return (point.X - SideModuleCount / 2) % 16 == 0 || (point.Y - SideModuleCount / 2) % 16 == 0; - } - - // gets the grid's starting point at x,y - public MyPoint GetGridStart(int x, int y) - { - MyPoint start = new MyPoint(0, 0); - int gridCountHalf = Grids.Length / 2; - - if (x < gridCountHalf) - { - ++x; - } - - if (y < gridCountHalf) - { - ++y; - } - - start.X += (x - gridCountHalf) * 16; - start.Y += (y - gridCountHalf) * 16; - - return start; - } - - // returns the grid that gives the most accurate location for the given coordinates - public Grid GetGridForCoordinate(int x, int y) - { - if (Grids.Length == 1) - { - return Grids[0][0]; - } - - int gridHalf = SideModuleCount / 2; - int gridCountHalf = Grids.Length / 2; - - int xGrid = (x - gridHalf); - xGrid = xGrid < 0 ? xGrid / 16 - 1 : xGrid / 16; - xGrid += gridCountHalf; - if (xGrid < 0) - { - xGrid = 0; - } - if (xGrid == Grids.Length) - { - --xGrid; - } - - int yGrid = (y - gridHalf); - yGrid = yGrid < 0 ? yGrid / 16 - 1 : yGrid / 16; - yGrid += gridCountHalf; - if (yGrid < 0) - { - yGrid = 0; - } - if (yGrid == Grids.Length) - { - --yGrid; - } - - return Grids[yGrid][xGrid]; - } - - // reads the image, assuming that we have a full-range symbol - public static AztecSymbol GetFullRangeSymbol(bool[][] centralPoints, AztecFinder finder) - { - AztecOrientation orientation = GetOrientation(centralPoints, 15); - if (!orientation.IsValid) - { - return null; - } - - //rotate finder - orientation.Rotate(finder); - - - // read the mode words - int[] modeWords = ReadModeWords(orientation, centralPoints, 11, true); - ReedSolomon modeCorrector = new ReedSolomon(modeWords, 6, 4, AztecUtils.Polynoms[4], 1); - float confidence; - modeCorrector.Correct(out confidence); - - if (!modeCorrector.CorrectionSucceeded) - { - return null; - } - - modeWords = modeCorrector.CorrectedData; - ushort modeValue = (ushort)(modeWords[0] * 4096 + modeWords[1] * 256 + modeWords[2] * 16 + modeWords[3]); - int dataCodewordCount = (modeValue & 0x7FF) + 1; - int layerCount = (modeValue >> 11) + 1; - - return new AztecSymbol(AztecType.FullRange, dataCodewordCount, layerCount, modeValue, confidence); - } - - // reads the image, assuming that we have a compact symbol - public static AztecSymbol GetCompactSymbol(bool[][] centralPoints, AztecFinder finder) - { - AztecOrientation orientation = GetOrientation(centralPoints, 11); - if (!orientation.IsValid) - { - return null; - } - - // read the mode words - int[] modeWords = ReadModeWords(orientation, centralPoints, 7, false); - - //rotate finder - orientation.Rotate(finder); - - ReedSolomon modeCorrector = new ReedSolomon(modeWords, 5, 4, AztecUtils.Polynoms[4], 1); - float confidence; - modeCorrector.Correct(out confidence); - - if (!modeCorrector.CorrectionSucceeded) - { - // check for runes - for (int i = 0; i < modeWords.Length; i++) - { - modeWords[i] ^= 0x0A; - } - modeCorrector = new ReedSolomon(modeWords, 5, 4, AztecUtils.Polynoms[4], 1); - modeCorrector.Correct(out confidence); - if (!modeCorrector.CorrectionSucceeded) - { - return null; - } - - // we have an Aztec rune! - byte runeMessage = (byte)(modeWords[0] * 16 + modeWords[1]); - return new AztecSymbol(AztecType.Rune, 1, 0, runeMessage, confidence ); - } - - modeWords = modeCorrector.CorrectedData; - byte modeValue = (byte)(modeWords[0] * 16 + modeWords[1]); - int dataCodewordCount = (modeValue & 0x3F) + 1; - int layerCount = (modeValue >> 6) + 1; - - return new AztecSymbol(AztecType.Compact, dataCodewordCount, layerCount, modeValue, confidence); - } - - // sets up an orientation object based on bit data - private static AztecOrientation GetOrientation(bool[][] centralPoints, int gridSize) - { - AztecOrientation orientation = new AztecOrientation(); - orientation.AddPattern(new bool[] { centralPoints[0][1], centralPoints[0][0], centralPoints[1][0] }, - new MyPoint(0, 0)); - orientation.AddPattern( - new bool[] { centralPoints[gridSize - 2][0], centralPoints[gridSize - 1][0], centralPoints[gridSize - 1][1] }, - new MyPoint(0, gridSize - 1)); - orientation.AddPattern( - new bool[] - { - centralPoints[gridSize - 1][gridSize - 2], centralPoints[gridSize - 1][gridSize - 1], - centralPoints[gridSize - 2][gridSize - 1] - }, new MyPoint(gridSize - 1, gridSize - 1)); - orientation.AddPattern( - new bool[] { centralPoints[1][gridSize - 1], centralPoints[0][gridSize - 1], centralPoints[0][gridSize - 2] }, - new MyPoint(gridSize - 1, 0)); - - return orientation; - } - - // reads the mode words from the central bit matrix - private static int[] ReadModeWords(AztecOrientation orientation, bool[][] centralPoints, int sideLength, bool hasFinder) - { - int j = 4 * (sideLength - (hasFinder ? 1 : 0)); - bool[] modeBits = new bool[j--]; - MyVector sideDir = orientation.GetModeScanVector(); - MyPoint nextBit = orientation.StartPoint + sideDir; - for (int s = 0; s < 4; ++s) - { - for (int b = 0; b < sideLength; ++b) - { - nextBit = nextBit + sideDir; - if (hasFinder && b == sideLength / 2) - { - continue; - } - modeBits[j--] = centralPoints[nextBit.Y][nextBit.X]; - } - nextBit = nextBit + 2 * sideDir; - sideDir = orientation.RotateClockwise(sideDir); - nextBit = nextBit + sideDir; - } - - int[] modeWords = new int[modeBits.Length / 4]; - for (int i = modeWords.Length - 1, b = 0; i >= 0; --i) - { - for (int m = 0; m < 4; ++m, ++b) - { - if (modeBits[b]) - { - modeWords[i] |= (1 << m); - } - } - } - - return modeWords; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecSymbolDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecSymbolDecoder.cs deleted file mode 100644 index a8242955..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecSymbolDecoder.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using System.Collections; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Aztec -{ - class AztecSymbolDecoder - { - // Text charset - public static readonly int[] TextSet = new int[] - { - (int) TextShiftLatch.PunctShift, 32, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, (int) TextShiftLatch.LowerLatch, - (int) TextShiftLatch.MixedLatch, (int) TextShiftLatch.DigitLatch, - (int) TextShiftLatch.Special, - (int) TextShiftLatch.PunctShift, 32, 97, 98, 99, 100, 101, 102, - 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, - 116, 117, 118, 119, 120, 121, 122, - (int) TextShiftLatch.UpperShift, (int) TextShiftLatch.MixedLatch, - (int) TextShiftLatch.DigitLatch, (int) TextShiftLatch.Special, - (int) TextShiftLatch.PunctShift, 32, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 27, 28, 29, 30, 31, 64, 92, 94, 95, 96, 124, 126, - 127, (int) TextShiftLatch.LowerLatch, - (int) TextShiftLatch.UpperLatch, (int) TextShiftLatch.PunctLatch, - (int) TextShiftLatch.Special, - (int) TextShiftLatch.Special, 13, (int) TextShiftLatch.Special, - (int) TextShiftLatch.Special, (int) TextShiftLatch.Special, - (int) TextShiftLatch.Special, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 47, 58, 59, 60, 61, 62, 63, 91, 93, 123, 125, - (int) TextShiftLatch.UpperLatch, - (int) TextShiftLatch.PunctShift, 32, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 57, 44, 46, (int) TextShiftLatch.UpperLatch, - (int) TextShiftLatch.UpperShift - }; - - public const int TextPageLength = 32; - - private enum TextShiftLatch - { - UpperLatch = -1, LowerLatch = -2, MixedLatch = -3, PunctLatch = -4, DigitLatch = -5, UpperShift = -6, PunctShift = -9, DigitShift = -10, Special = 0 - } - - // decodes the given symbols using Text encodation - public static ABarCodeData[] DecodeText(BitArray bitStream, System.Text.Encoding encoding) - { - ArrayList resultList = new ArrayList(); - StringBuilder chars = new StringBuilder(); - int lastLatch = -1; - int latch = 0; - int sliceSize = 5; - for (int i = 0; i <= bitStream.Length - sliceSize; ) - { - int code = AztecUtils.BitSliceValue(bitStream, ref i, sliceSize); - int textSetIndex = code + TextPageLength * latch; - if (textSetIndex < 0 || textSetIndex >= TextSet.Length) - { - break; - } - int value = TextSet[textSetIndex]; - if (value == 0) - { - if (chars.Length > 0) - { - resultList.Add(new StringBarCodeData(chars.ToString())); - chars = new StringBuilder(); - } - - ABarCodeData[] special = HandleSpecial(code, latch, bitStream, ref i, encoding); - if (special.Length > 0) - { - resultList.AddRange(special); - } - if (lastLatch >= 0) - { - latch = lastLatch; - lastLatch = -1; - } - } - else if (value < 0) - { - if (value < -5) - { - lastLatch = latch; - value += 5; - } - - latch = -1 - value; - } - else - { - chars.Append((char)value); - if (lastLatch >= 0) - { - latch = lastLatch; - lastLatch = -1; - } - } - - sliceSize = (-1 - latch) == (int)TextShiftLatch.DigitShift || - (-1 - latch) == (int)TextShiftLatch.DigitLatch - ? 4 - : 5; - } - if (chars.Length > 0) - { - resultList.Add(new StringBarCodeData(chars.ToString())); - } - - ABarCodeData[] result = new ABarCodeData[resultList.Count]; - resultList.CopyTo(result); - - return result; - } - - private static ABarCodeData[] HandleSpecial(int value, int latch, BitArray bitStream, ref int index, System.Text.Encoding encoding) - { - // Handle byte encoding - if (latch < 3 && value == 31) - { - int length = AztecUtils.BitSliceValue(bitStream, ref index, 5); - if (length == 0) - { - length = AztecUtils.BitSliceValue(bitStream, ref index, 11) + 31; - } - - // if exceed stream length, return empty (issue #1242) - if (bitStream.Count <= index) - { - return new ABarCodeData[0]; - } - - byte[] chars = new byte[length]; - for (int i = 0; i < length; ++i) - { - chars[i] = (byte)AztecUtils.BitSliceValue(bitStream, ref index, 8); - } - - return new ABarCodeData[] { new Base256BarCodeData(chars, encoding) }; - } - else if (latch == 3) - { - switch (value) - { - case 0: // FLG(n) - int flag = AztecUtils.BitSliceValue(bitStream, ref index, 3); - switch (flag) - { - case 0: // ASCII 29 - return new ABarCodeData[] { new StringBarCodeData(new byte[] { 29 }) }; - default: // ECI switch - int eci = 0; - for (int i = 0; i < flag; ++i) - { - int code = AztecUtils.BitSliceValue(bitStream, ref index, 4); - int textSetIndex = 4 * TextPageLength + code; - if (textSetIndex < 0 || textSetIndex >= TextSet.Length) - { - return new ABarCodeData[0]; - } - int digit = TextSet[textSetIndex] - 48; - if (digit < 0 || digit > 9) - { - return new ABarCodeData[0]; - } - eci = eci * 10 + digit; - } - return new ABarCodeData[] { new ECISwitchSymbol(eci) }; - } - case 2: - return new ABarCodeData[] { new StringBarCodeData("\r\n") }; - case 3: - return new ABarCodeData[] { new StringBarCodeData(". ") }; - case 4: - return new ABarCodeData[] { new StringBarCodeData(", ") }; - case 5: - return new ABarCodeData[] { new StringBarCodeData(": ") }; - default: - throw new Exception("Invalid special value"); - } - } - - throw new Exception("Invalid special value"); - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecUtils.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecUtils.cs deleted file mode 100644 index da4b3841..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Aztec/AztecUtils.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System; -using System.Collections; - -namespace BarcodeReader.Core.Aztec -{ - class AztecUtils - { - #region Reference data - - public static readonly int[] Polynoms = new int[] { 0, 0, 0, 0, 19, 0, 67, 0, 301, 0, 1033, 0, 4201 }; - - public static readonly MyVector[] RotationMap = new MyVector[] - { - new MyVector(1, 0), new MyVector(0, -1), new MyVector(-1, 0), - new MyVector(0, 1) - }; - - public static readonly int[] FullRangeSizes = new int[] - { - 0, 19, 23, 27, 31, 37, 41, 45, 49, 53, 57, 61, 67, 71, 75, 79, - 83, 87, 91, 95, 101, 105, 109, 113, 117, 121, 125, 131, 135, - 139, 143, 147, 151 - }; - - public static readonly int[] FullRangeCapacities = new int[] - { - 0, 128, 288, 480, 704, 960, 1248, 1568, 1920, 2304, 2720, - 3168, 3648, 4160, 4704, 5280, 5888, 6528, 7200, 7904, - 8640, 9408, 10208, 11040, 11904, 12800, 13728, 14688, 15680, - 16704, 17760, 18848, 19968 -/* Old table : wrong because of offset missing bits 0, 126, 288, 480, 704, 960, 1248, 1568, 1920, 2300, 2720, - 3160, 3640, 4160, 4700, 5280, 5880, 6520, 7200, 7900, - 8640, 9400, 10200, 11040, 11904, 12792, 13728, 14688, 15672, - 16704, 17760, 18840, 19968 -*/ - }; - - public static readonly int[] FullRangeBitCount = new int[] - { - 0, 6, 6, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10 - , 10, 10, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12 - }; - - public static readonly int[] FullRangeGridCount = new int[] - { - 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, - 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8 - }; - - public static readonly int[] CompactSizes = new int[] { 0, 15, 19, 23, 27 }; - - public static readonly int[] CompactCapacities = new int[] { 0, 104, 240, 408, 608 }; - - public static readonly int[] CompactBitCount = new int[] { 0, 6, 6, 8, 8 }; - #endregion - - - // rotates an axis vector clockwise - public static MyVector RotateClockwise(MyVector vector) - { - for (int i = 0; i < 4; ++i) - { - if (RotationMap[i] == vector) - { - int n = (i + 1) % 4; - return RotationMap[n]; - } - } - - throw new Exception("Invalid direction vector"); - } - - // rotates an axis vector counter-clockwise - public static MyVector RotateCounterClockwise(MyVector vector) - { - for (int i = 0; i < 4; ++i) - { - if (RotationMap[i] == vector) - { - int n = (i + 3) % 4; - return RotationMap[n]; - } - } - - throw new Exception("Invalid direction vector"); - } - - // cuts up a bitstream into fixed size chunks - public static int[] SliceBitStream(BitArray bitStream, int sliceSize, int offset) - { - int[] result = new int[(bitStream.Length-offset) / sliceSize]; - for (int i = result.Length - 1, index = bitStream.Length - 1; i >= 0; --i) - { - for (int b = 0; b < sliceSize; ++b) - { - if (bitStream[index--]) - { - result[i] |= (1 << b); - } - } - } - - return result; - } - - // put together a bitstream from slices, and take care of removing the padding 0's and 1's - public static BitArray AssembleBitStream(int[] codeWords, int sliceSize) - { - int streamLength = codeWords.Length * sliceSize; - BitArray result = new BitArray(streamLength); - int magicWord = (1 << sliceSize) - 2; - - int index = 0; - for (int i = 0; i < codeWords.Length; ++i) - { - for (int b = sliceSize - 1; b >= 0; --b) - { - result[index++] = (codeWords[i] & (1 << b)) != 0; - } - - if (codeWords[i] == 1 || codeWords[i] == magicWord) - { - index--; - } - } - result.Length = index; - - return result; - } - - // take one piece of the given size from the bitstream, and report its value - public static int BitSliceValue(BitArray dataStream, ref int index, int length) - { - int value = 0; - for (int i = length - 1; i >= 0; --i, ++index) - { - if (index < dataStream.Count) - { - - if (dataStream[index]) - { - value |= (1 << i); - } - } - - } - - return value; - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/CodablockF/CodaBlockFDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/CodablockF/CodaBlockFDecoder.cs deleted file mode 100644 index b747188e..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/CodablockF/CodaBlockFDecoder.cs +++ /dev/null @@ -1,339 +0,0 @@ -using System; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.CodaBlockF -{ - //Class to sample a region (4 corners) and decode a codablock-F barcode. - //The region is sampled in row direction, at a very small step to ensure several scans for barcode row. - //The number of rows is encoded in the first char of the first row. The row id is encoded in the first - //char of the next rows. - //Uses a BarSymbolReader, a generic barcode reader to convert bars to codebytes. Then these codebytes - //are decoded using 128 encoding schema. - class CodaBlockFDecoder - { - public BarCodeRegion Decode(ImageScaner scan, MyPointF up, MyPointF endUp, MyPointF endDown, MyPointF down, FoundPattern foundPattern) - { - MyVectorF vdY = (up - down); - float startLength = vdY.Length; - vdY = vdY.Normalized; - MyVectorF vdX = new MyVectorF(-vdY.Y, vdY.X); - - MyVectorF endVdY = (endUp - endDown).Normalized; - float d = foundPattern.moduleLength; - int[][] symbols = null; - - //scan region rows, at small step of barcode module length. Result is stored in symbols array. - //symbols array is initialized later, when the first row is read and the number of rows is known. - //Seems that results are better using bar widths instead of E (sum of 2 consecutive bar widths). - BarSymbolReader reader = new BarSymbolReader(scan, 6, 11, true, true, foundPattern.moduleLength, _patterns, ShortStop, false, null); - while (d < startLength) - { - MyPointF a = up - vdY * d; - MyPointF b = endUp - endVdY * d; - float error, maxError,confidence; - int[] row = reader.Read(a, b, out error, out maxError, out confidence); //convert scanline to bytecodes array. - int nRow=-1, nRows=-1; - if (verifyCheckSum(row)) //if checksum is right, use the data row. Otherwise skip to the next row. - { - int s = row[1]; - int l = row[2]; - //extract encodation A, B or C, and number of rows or rowId. - switch (s) - { - case Shift: - case CodeB: - if (0 <= l && l <= 10) { l = l + 34; nRows = l; nRow = 0; } - else if (64 <= l && l <= 95) { l = l - 62; nRows = l; nRow = 0; } - else if (11 <= l && l <= 15) { l = l - 10; nRow = l; } - else if (26 <= l && l <= 63) { l = l - 20; nRow = l; } - break; - case CodeC: - if (0 <= l && l <= 42) { nRows = l + 2; nRow = 0; } - else if (43 <= l && l <= 85) { nRow = l - 42; } - break; - } - } - //store data in the symbols array - if (nRow==0 && symbols==null && nRows>=2 && nRows<=44) symbols=new int[nRows][]; - if (symbols!=null && nRow >=0 && nRow candidates = new LinkedList(); //found barcodes - - int[][] startPattern = new int[][] { new int[] { 2, 1, 1, 4, 1, 2 } }; //the only finder (start-A) - int[] stopPattern = new int[] { 3, 3, 1, 1, 1, 2 }; //without last black bar - IPatternFinderNoiseRow startFinder; //object to find finders in a horizontal row - PatternFinderNoise stopFinder; //object to find finders using a bresenham line - CodaBlockFDecoder decoder = new CodaBlockFDecoder(); //object to decode data to chars - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.CodablockF; - } - - //Scans the image row by row, looking for start-A pattern. For each pattern found, try to - //add this patterns to previously found patterns. This is implemented in the StackedPattern. - //StackedPatterns are useful to track better the edge and detect the angle of the barcode. - protected override FoundBarcode[] DecodeBarcode() - { - scan = new ImageScaner(BWImage); - startFinder = new PatternFinderNoiseRow(startPattern, true, true, 2); - stopFinder = new PatternFinderNoise(BWImage, stopPattern, false, 2); - LinkedList foundPatterns = new LinkedList(); - LinkedList removedPatterns; - - for (int y = 0; y < BWImage.Height; y += ScanStep) - { - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - XBitArray row = BWImage.GetRow(y); - startFinder.NewSearch(row); - FoundPattern foundPattern; - while ((foundPattern = startFinder.NextPattern()) != null) - { - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - //foundPattern is the index of the found pattern - MyPoint a = new MyPoint(startFinder.First, y); - MyPoint b = new MyPoint(startFinder.Last, y); - //Check if the same pattern was processed in the last row. - Pattern p = new Pattern(foundPattern, a.X, b.X, y); - LinkedListNode prev = foundPatterns.Find(p); - if (prev != null) - { //pattern already processed the last row - if (prev.Value.y != y) - { - StackedPattern sp = (StackedPattern)prev.Value; - sp.NewRow(a.X, b.X, y); - } - } - else - { //new - StackedPattern sp = new StackedPattern(foundPattern, a.X, b.X, y); - foundPatterns.AddLast(sp); - } - } - - //clean old patterns and process them - removedPatterns = Pattern.RemoveOldPatterns(foundPatterns, y); - foreach (Pattern p in removedPatterns) - { - StackedPattern sp = (StackedPattern)p; - if (sp.y - sp.startY > stackedPatternMinHeight && (float)(sp.y - sp.startY) / (float)(sp.startXEnd - sp.startXIn) > stackedPatternMinRatio) - ProcessPattern(sp); - - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - } - } - - //clean stackedPatterns - foreach (Pattern p in foundPatterns) - { - StackedPattern sp = (StackedPattern)p; - //check ratio and process the stacked pattern - if (sp.y - sp.startY > stackedPatternMinHeight && (float)(sp.y - sp.startY) / (float)(sp.startXEnd - sp.startXIn) > stackedPatternMinRatio) - ProcessPattern(sp); - - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - } - - FoundBarcode[] results = new FoundBarcode[candidates.Count]; - int nn = 0; - foreach (BarCodeRegion r in candidates) - { - FoundBarcode f = new FoundBarcode(); - f.BarcodeFormat = SymbologyType.CodablockF; - f.Polygon = new SKPointI[] { r.A, r.B, r.C, r.D, r.A }; - f.Color = SKColors.Blue; - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(f.Polygon, pointTypes); - //f.Rect = Rectangle.Round(path.GetBounds()); - f.Rect = Utils.DrawPath(f.Polygon); - f.Value = (r.Data != null ? r.Data[0].ToString() : "?"); - f.Confidence = r.Confidence; - results[nn++] = f; - } - return results; - } - - //Find the top and bottom corners of the finder, starting at the center point of the pattern, - //and tracking the edge. Then traces 3 perpendicular lines (at 50%, 25% and 75%) looking for - //the stop pattern. Once is found, check ratio, sample the region and decode data. - void ProcessPattern(StackedPattern p) - { - MyPoint center, rCenter; p.Center(out center, out rCenter); - MyPoint midCenter = center; - midCenter.X += (int)p.MeanWidth(); - float moduleLength = p.MeanWidth() / 11f; //Start char has 11 modules - - foreach (BarCodeRegion c in candidates) - if (c.In(midCenter)) return; - - //Track edge - EdgeTrack et = new EdgeTrack(scan); - et.Track(center, new MyVector(-1, 0), moduleLength, true); - - //find the top and bottom points of the edge - MyPointF up = et.Up(); - MyPointF down = et.Down(); - - //Calculate main directions - MyVectorF vdY = (up - down); - vdY = vdY.Normalized; - MyVectorF vdX = new MyVectorF(-vdY.Y, vdY.X); - - //Calculate rotated module length - float rotatedModuleLength = moduleLength * (float)Math.Cos(vdX.Angle) * (float)Math.Cos(vdX.Angle); - p.foundPattern.moduleLength = rotatedModuleLength; - - //try 3 perpendicular scan lines to find the stop pattern - foreach (float cc in crossPoints) - { - MyPointF c = up * cc + down * (1f - cc); - Bresenham br = new Bresenham(c, vdX); - stopFinder.NewSearch(br, false, -1); - while (stopFinder.NextPattern()!=-1) - { - MyPoint end = stopFinder.Last; - MyPointF endUp, endDown; - try - { - et.Track(end, new MyVector(-1, 0), moduleLength, false); - endUp = et.Up(); - endDown = et.Down(); - } - catch (Exception) { break; } - float dstart = (up - down).Length; - float dstop = (endUp - endDown).Length; - //check ratio - if (Calc.Around(dstart / dstop, 1F, maxRatioStartStopLength)) - { - BarCodeRegion r = decoder.Decode(scan, up, endUp, endDown, down, p.foundPattern); - if (r != null) { candidates.AddLast(r); return; } - } - } - } - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code128/Code128Reader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Code128/Code128Reader.cs deleted file mode 100644 index b2180863..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code128/Code128Reader.cs +++ /dev/null @@ -1,430 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Code128 -{ - //Main class to scan an image looking for code128 barcodes. It inherits from Reader2DNoise, - //responsible to find start and stop patterns in the image, and calling FindBarcode methods - //to read the barcode in between. -#if CORE_DEV - public -#else - internal -#endif - class Code128Reader : Reader2DNoise - { - public Code128Reader() - { - //Define start and stop patterns. Code128 has 3 start patterns startA, startB, startC - //that defines the initial encoding table, and one unique stop pattern. - startPatterns = _startPatterns; - stopPatterns = _stopPatterns; - useE = true; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Code128; - } - - //Method to scan a single row - protected override BarCodeRegion FindBarcode(ImageScaner scan, int startPattern, MyPoint start, MyPoint end, FoundPattern foundPattern) - { - BarSymbolReader reader = new BarSymbolReader(scan, 6, 11, true, true, foundPattern.moduleLength, _patterns, -1, false, null); - float error, maxError, confidence; - int[] row = reader.Read(start, end, out error, out maxError, out confidence); - row[0] = startPattern % 3 + StartA; - if (error < 1f) - { - BarCodeRegion r = new BarCodeRegion(start, reader.Current, reader.Current + new MyPoint(0, 5), start + new MyPoint(0, 5)); - r.Confidence = confidence; - if (Decode(r, row)) return r; - } - return null; - } - - //Method to scan a region. It starts with a scanning the line in the middle of the region - //to be able to read quickly good quality barcodes. If it fails, then uses the slower - //loose projection reader. - int[] nBars = new int[] {6, 6}; - int[] nModules = new int[] { 11, 11 }; - int[] tIndexs = new int[] { 0, 1 }; - int[][][] tables = new int[][][] { _startPatterns, _patterns }; - override protected BarCodeRegion FindBarcode(ImageScaner scan, int startPattern, BarCodeRegion r, FoundPattern foundPattern) - { - BarSymbolReader reader = new BarSymbolReader(scan, nBars, nModules, tIndexs, true, true, foundPattern.moduleLength, tables, useE, null); - - MyPoint a = r.A * 0.5f + r.D * 0.5f; - MyPoint b = r.B * 0.5f + r.C * 0.5f; - - float error, maxError, confidence; - int[] row = reader.Read(a, b, out error, out maxError, out confidence); - r.Confidence = confidence; - if (row != null && row.Length > 0) - { -#if DEBUG - var line = scan.GetPixels(a, b); - DebugHelper.AddDebugItem("old " + a.ToString(), line, a, b); -#endif - - //add region to list of "good" regions - if (confidence > 0.5f) - foundRegions.AddLast(r); - - //try to decode - row[0] = startPattern % 3 + StartA; - if (Decode(r, row)) return r; //if sampling is good enough, then use simple row scanning - } - - // - BarSymbolReaderLooseProjection reader2 = new BarSymbolReaderLooseProjection(scan, 6, 11, true, _patterns, 106, true, useE); - float[] widths = new float[] { 0.5f, 0.9f }; - foreach (float w in widths) - { - row = reader2.Read(r, foundPattern.moduleLength, w); - if (row != null && row.Length > 0) - { - row[0] = startPattern % 3 + StartA; - if (Decode(r, row)) return r; - } - } - - return null; - } - - - //method to read barcodes without stop pattern - protected override BarCodeRegion FindBarcode(ImageScaner scan, int startPattern, MyPoint start, MyVectorF vd, FoundPattern foundPattern) - { - BarSymbolReader reader = new BarSymbolReader(scan, nBars, nModules, tIndexs, true, true, foundPattern.moduleLength, tables, useE, null, true); - MyPoint end; - float error, maxError, confidence; - int[] row = reader.Read(start, vd, out error, out maxError, out confidence, out end); ; - if (row != null && row.Length > 0) - { - row[0] = startPattern % 3 + StartA; - if (error < 1f) - { - BarCodeRegion r = new BarCodeRegion(start, reader.Current, reader.Current + new MyPoint(0, 5), start + new MyPoint(0, 5)); - r.Confidence = confidence; - if (Decode(r, row)) return r; - } - } - return null; - } - - //Method to decode bytecodes into the final string. - protected bool Decode(BarCodeRegion r, int[] row) - { - int offset=0; - int end = 0; while (end < row.Length && row[end++] != Stop); - if (row[end - 1] != Stop) { end++; offset = 1; } - int[] rawData = new int[end]; - Array.Copy(row, rawData, end-offset); - if (offset==1) rawData[end - 1] = Stop; - - if (rawData == null || rawData.Length < 3) return false; - if (!verifyCheckSum(rawData)) return false; - int currentCodeSet; - switch (rawData[0]) - { - case StartA: - currentCodeSet = CodeA; - break; - case StartB: - currentCodeSet = CodeB; - break; - case StartC: - currentCodeSet = CodeC; - break; - default: - // shouldn't happen :-) - return false; - } - - StringBuilder sb = new StringBuilder(); - bool gotShift = false; - - for (int i = 1; i < rawData.Length - 2; i++) - { - bool shiftInEffect = gotShift; - gotShift = false; - - int symbol = rawData[i]; - - switch (currentCodeSet) - { - case CodeA: - if (symbol < 64) - { - sb.Append((char)(symbol + ' ')); - } - else if (symbol < 96) - { - sb.Append((char)(symbol - 64)); - } - else - { - switch (symbol) - { - case FNC1: sb.Append(""); break; - case FNC2: sb.Append(""); break; - case FNC3: sb.Append(""); break; - case FNC4A: sb.Append(""); break; - - case Shift: - gotShift = true; - currentCodeSet = CodeB; - break; - - case CodeB: - currentCodeSet = CodeB; - break; - - case CodeC: - currentCodeSet = CodeC; - break; - } - } - break; - - case CodeB: - if (symbol < 96) - { - sb.Append((char)(symbol + ' ')); - } - else - { - switch (symbol) - { - case FNC1: sb.Append(""); break; - case FNC2: sb.Append(""); break; - case FNC3: sb.Append(""); break; - case FNC4B: sb.Append(""); break; - - case Shift: - gotShift = true; - currentCodeSet = CodeC; - break; - - case CodeA: - currentCodeSet = CodeA; - break; - - case CodeC: - currentCodeSet = CodeC; - break; - } - } - break; - - case CodeC: - if (symbol < 100) - { - if (symbol < 10) - sb.Append('0'); - - sb.Append(symbol); - } - else - { - switch (symbol) - { - case FNC1: sb.Append(""); break; - - case CodeA: - currentCodeSet = CodeA; - break; - - case CodeB: - currentCodeSet = CodeB; - break; - } - } - break; - } - - if (shiftInEffect) - { - switch (currentCodeSet) - { - case CodeA: - currentCodeSet = CodeC; - break; - case CodeB: - currentCodeSet = CodeA; - break; - case CodeC: - currentCodeSet = CodeB; - break; - } - } - } - - if (sb.Length == 0) - return false; - - r.Data = new ABarCodeData[] { new StringBarCodeData(sb.ToString()) }; - return true; - } - - private bool verifyCheckSum(int[] symbols) - { - if (symbols.Length < 3) - { - // at least StartX, Stop and checksum should be in every Code128 code - return false; - } - - int total = symbols[0]; - for (int i = 1; i < symbols.Length - 2; i++) - { - if (symbols[i] == -1) - return false; - - total += symbols[i] * i; - } - - return (total % 103) == symbols[symbols.Length - 2]; - } - - private const int Shift = 98; - - private const int CodeC = 99; - private const int CodeB = 100; - private const int CodeA = 101; - - private const int FNC1 = 102; - private const int FNC2 = 97; - private const int FNC3 = 96; - private const int FNC4A = 101; - private const int FNC4B = 100; - - private const int StartA = 103; - private const int StartB = 104; - private const int StartC = 105; - private const int Stop = 106; - - static readonly int[][] _startPatterns = new int[][] { - new int[] { 2, 1, 1, 4, 1, 2 }, - new int[] { 2, 1, 1, 2, 1, 4 }, - new int[] { 2, 1, 1, 2, 3, 2 } - }; - static readonly int[][] _stopPatterns = new int[][] { - new int[] { 2, 3, 3, 1, 1, 1, 2 } - }; - - static readonly int[][] _patterns = new int[][] { - new int[] { 2, 1, 2, 2, 2, 2 },//0 - new int[] { 2, 2, 2, 1, 2, 2 }, - new int[] { 2, 2, 2, 2, 2, 1 }, - new int[] { 1, 2, 1, 2, 2, 3 }, - new int[] { 1, 2, 1, 3, 2, 2 }, - new int[] { 1, 3, 1, 2, 2, 2 }, - new int[] { 1, 2, 2, 2, 1, 3 }, - new int[] { 1, 2, 2, 3, 1, 2 }, - new int[] { 1, 3, 2, 2, 1, 2 }, - new int[] { 2, 2, 1, 2, 1, 3 }, - new int[] { 2, 2, 1, 3, 1, 2 },//10 - new int[] { 2, 3, 1, 2, 1, 2 }, - new int[] { 1, 1, 2, 2, 3, 2 }, - new int[] { 1, 2, 2, 1, 3, 2 }, - new int[] { 1, 2, 2, 2, 3, 1 }, - new int[] { 1, 1, 3, 2, 2, 2 }, - new int[] { 1, 2, 3, 1, 2, 2 }, - new int[] { 1, 2, 3, 2, 2, 1 }, - new int[] { 2, 2, 3, 2, 1, 1 }, - new int[] { 2, 2, 1, 1, 3, 2 }, - new int[] { 2, 2, 1, 2, 3, 1 },//20 - new int[] { 2, 1, 3, 2, 1, 2 }, - new int[] { 2, 2, 3, 1, 1, 2 }, - new int[] { 3, 1, 2, 1, 3, 1 }, - new int[] { 3, 1, 1, 2, 2, 2 }, - new int[] { 3, 2, 1, 1, 2, 2 }, - new int[] { 3, 2, 1, 2, 2, 1 }, - new int[] { 3, 1, 2, 2, 1, 2 }, - new int[] { 3, 2, 2, 1, 1, 2 }, - new int[] { 3, 2, 2, 2, 1, 1 }, - new int[] { 2, 1, 2, 1, 2, 3 },//30 - new int[] { 2, 1, 2, 3, 2, 1 }, - new int[] { 2, 3, 2, 1, 2, 1 }, - new int[] { 1, 1, 1, 3, 2, 3 }, - new int[] { 1, 3, 1, 1, 2, 3 }, - new int[] { 1, 3, 1, 3, 2, 1 }, - new int[] { 1, 1, 2, 3, 1, 3 }, - new int[] { 1, 3, 2, 1, 1, 3 }, - new int[] { 1, 3, 2, 3, 1, 1 }, - new int[] { 2, 1, 1, 3, 1, 3 }, - new int[] { 2, 3, 1, 1, 1, 3 },//40 - new int[] { 2, 3, 1, 3, 1, 1 }, - new int[] { 1, 1, 2, 1, 3, 3 }, - new int[] { 1, 1, 2, 3, 3, 1 }, - new int[] { 1, 3, 2, 1, 3, 1 }, - new int[] { 1, 1, 3, 1, 2, 3 }, - new int[] { 1, 1, 3, 3, 2, 1 }, - new int[] { 1, 3, 3, 1, 2, 1 }, - new int[] { 3, 1, 3, 1, 2, 1 }, - new int[] { 2, 1, 1, 3, 3, 1 }, - new int[] { 2, 3, 1, 1, 3, 1 },//50 - new int[] { 2, 1, 3, 1, 1, 3 }, - new int[] { 2, 1, 3, 3, 1, 1 }, - new int[] { 2, 1, 3, 1, 3, 1 }, - new int[] { 3, 1, 1, 1, 2, 3 }, - new int[] { 3, 1, 1, 3, 2, 1 }, - new int[] { 3, 3, 1, 1, 2, 1 }, - new int[] { 3, 1, 2, 1, 1, 3 }, - new int[] { 3, 1, 2, 3, 1, 1 }, - new int[] { 3, 3, 2, 1, 1, 1 }, - new int[] { 3, 1, 4, 1, 1, 1 },//60 - new int[] { 2, 2, 1, 4, 1, 1 }, - new int[] { 4, 3, 1, 1, 1, 1 }, - new int[] { 1, 1, 1, 2, 2, 4 }, - new int[] { 1, 1, 1, 4, 2, 2 }, - new int[] { 1, 2, 1, 1, 2, 4 }, - new int[] { 1, 2, 1, 4, 2, 1 }, - new int[] { 1, 4, 1, 1, 2, 2 }, - new int[] { 1, 4, 1, 2, 2, 1 }, - new int[] { 1, 1, 2, 2, 1, 4 }, - new int[] { 1, 1, 2, 4, 1, 2 },//70 - new int[] { 1, 2, 2, 1, 1, 4 }, - new int[] { 1, 2, 2, 4, 1, 1 }, - new int[] { 1, 4, 2, 1, 1, 2 }, - new int[] { 1, 4, 2, 2, 1, 1 }, - new int[] { 2, 4, 1, 2, 1, 1 }, - new int[] { 2, 2, 1, 1, 1, 4 }, - new int[] { 4, 1, 3, 1, 1, 1 }, - new int[] { 2, 4, 1, 1, 1, 2 }, - new int[] { 1, 3, 4, 1, 1, 1 }, - new int[] { 1, 1, 1, 2, 4, 2 },//80 - new int[] { 1, 2, 1, 1, 4, 2 }, - new int[] { 1, 2, 1, 2, 4, 1 }, - new int[] { 1, 1, 4, 2, 1, 2 }, - new int[] { 1, 2, 4, 1, 1, 2 }, - new int[] { 1, 2, 4, 2, 1, 1 }, - new int[] { 4, 1, 1, 2, 1, 2 }, - new int[] { 4, 2, 1, 1, 1, 2 }, - new int[] { 4, 2, 1, 2, 1, 1 }, - new int[] { 2, 1, 2, 1, 4, 1 }, - new int[] { 2, 1, 4, 1, 2, 1 },//90 - new int[] { 4, 1, 2, 1, 2, 1 }, - new int[] { 1, 1, 1, 1, 4, 3 }, - new int[] { 1, 1, 1, 3, 4, 1 }, - new int[] { 1, 3, 1, 1, 4, 1 }, - new int[] { 1, 1, 4, 1, 1, 3 }, - new int[] { 1, 1, 4, 3, 1, 1 }, - new int[] { 4, 1, 1, 1, 1, 3 }, - new int[] { 4, 1, 1, 3, 1, 1 }, - new int[] { 1, 1, 3, 1, 4, 1 }, - new int[] { 1, 1, 4, 1, 3, 1 },//100 - new int[] { 3, 1, 1, 1, 4, 1 }, - new int[] { 4, 1, 1, 1, 3, 1 }, - new int[] { 2, 1, 1, 4, 1, 2 }, //startA 102 - new int[] { 2, 1, 1, 2, 1, 4 }, //startB 103 - new int[] { 2, 1, 1, 2, 3, 2 }, //startC 104 - new int[] { 2, 3, 3, 1, 1, 1 } //stop without last bar 105 - }; - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code128_LinearReader/Code128LinearReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Code128_LinearReader/Code128LinearReader.cs deleted file mode 100644 index c063b471..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code128_LinearReader/Code128LinearReader.cs +++ /dev/null @@ -1,467 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Code128 -{ - /// - /// Code128 reader. - /// - [Obsolete("This class does not pass all tests.")] -#if CORE_DEV - public -#else - internal -#endif - class Code128LinearReader : LinearReader - { - private bool HighPrecision = false; - - #region Patterns - - private const int Shift = 98; - - private const int CodeC = 99; - private const int CodeB = 100; - private const int CodeA = 101; - - private const int FNC1 = 102; - private const int FNC2 = 97; - private const int FNC3 = 96; - private const int FNC4A = 101; - private const int FNC4B = 100; - - private const int StartA = 103; - private const int StartB = 104; - private const int StartC = 105; - private const int Stop = 106; - - static readonly int[][] _startPatterns = new int[][] { - new int[] { 2, 1, 1, 4, 1, 2 }, - new int[] { 2, 1, 1, 2, 1, 4 }, - new int[] { 2, 1, 1, 2, 3, 2 } - }; - static readonly int[][] _stopPatterns = new int[][] { - new int[] { 2, 3, 3, 1, 1, 1, 2 } - }; - - static readonly int[][] _patterns = new int[][] { - new int[] { 2, 1, 2, 2, 2, 2 },//0 - new int[] { 2, 2, 2, 1, 2, 2 }, - new int[] { 2, 2, 2, 2, 2, 1 }, - new int[] { 1, 2, 1, 2, 2, 3 }, - new int[] { 1, 2, 1, 3, 2, 2 }, - new int[] { 1, 3, 1, 2, 2, 2 }, - new int[] { 1, 2, 2, 2, 1, 3 }, - new int[] { 1, 2, 2, 3, 1, 2 }, - new int[] { 1, 3, 2, 2, 1, 2 }, - new int[] { 2, 2, 1, 2, 1, 3 }, - new int[] { 2, 2, 1, 3, 1, 2 },//10 - new int[] { 2, 3, 1, 2, 1, 2 }, - new int[] { 1, 1, 2, 2, 3, 2 }, - new int[] { 1, 2, 2, 1, 3, 2 }, - new int[] { 1, 2, 2, 2, 3, 1 }, - new int[] { 1, 1, 3, 2, 2, 2 }, - new int[] { 1, 2, 3, 1, 2, 2 }, - new int[] { 1, 2, 3, 2, 2, 1 }, - new int[] { 2, 2, 3, 2, 1, 1 }, - new int[] { 2, 2, 1, 1, 3, 2 }, - new int[] { 2, 2, 1, 2, 3, 1 },//20 - new int[] { 2, 1, 3, 2, 1, 2 }, - new int[] { 2, 2, 3, 1, 1, 2 }, - new int[] { 3, 1, 2, 1, 3, 1 }, - new int[] { 3, 1, 1, 2, 2, 2 }, - new int[] { 3, 2, 1, 1, 2, 2 }, - new int[] { 3, 2, 1, 2, 2, 1 }, - new int[] { 3, 1, 2, 2, 1, 2 }, - new int[] { 3, 2, 2, 1, 1, 2 }, - new int[] { 3, 2, 2, 2, 1, 1 }, - new int[] { 2, 1, 2, 1, 2, 3 },//30 - new int[] { 2, 1, 2, 3, 2, 1 }, - new int[] { 2, 3, 2, 1, 2, 1 }, - new int[] { 1, 1, 1, 3, 2, 3 }, - new int[] { 1, 3, 1, 1, 2, 3 }, - new int[] { 1, 3, 1, 3, 2, 1 }, - new int[] { 1, 1, 2, 3, 1, 3 }, - new int[] { 1, 3, 2, 1, 1, 3 }, - new int[] { 1, 3, 2, 3, 1, 1 }, - new int[] { 2, 1, 1, 3, 1, 3 }, - new int[] { 2, 3, 1, 1, 1, 3 },//40 - new int[] { 2, 3, 1, 3, 1, 1 }, - new int[] { 1, 1, 2, 1, 3, 3 }, - new int[] { 1, 1, 2, 3, 3, 1 }, - new int[] { 1, 3, 2, 1, 3, 1 }, - new int[] { 1, 1, 3, 1, 2, 3 }, - new int[] { 1, 1, 3, 3, 2, 1 }, - new int[] { 1, 3, 3, 1, 2, 1 }, - new int[] { 3, 1, 3, 1, 2, 1 }, - new int[] { 2, 1, 1, 3, 3, 1 }, - new int[] { 2, 3, 1, 1, 3, 1 },//50 - new int[] { 2, 1, 3, 1, 1, 3 }, - new int[] { 2, 1, 3, 3, 1, 1 }, - new int[] { 2, 1, 3, 1, 3, 1 }, - new int[] { 3, 1, 1, 1, 2, 3 }, - new int[] { 3, 1, 1, 3, 2, 1 }, - new int[] { 3, 3, 1, 1, 2, 1 }, - new int[] { 3, 1, 2, 1, 1, 3 }, - new int[] { 3, 1, 2, 3, 1, 1 }, - new int[] { 3, 3, 2, 1, 1, 1 }, - new int[] { 3, 1, 4, 1, 1, 1 },//60 - new int[] { 2, 2, 1, 4, 1, 1 }, - new int[] { 4, 3, 1, 1, 1, 1 }, - new int[] { 1, 1, 1, 2, 2, 4 }, - new int[] { 1, 1, 1, 4, 2, 2 }, - new int[] { 1, 2, 1, 1, 2, 4 }, - new int[] { 1, 2, 1, 4, 2, 1 }, - new int[] { 1, 4, 1, 1, 2, 2 }, - new int[] { 1, 4, 1, 2, 2, 1 }, - new int[] { 1, 1, 2, 2, 1, 4 }, - new int[] { 1, 1, 2, 4, 1, 2 },//70 - new int[] { 1, 2, 2, 1, 1, 4 }, - new int[] { 1, 2, 2, 4, 1, 1 }, - new int[] { 1, 4, 2, 1, 1, 2 }, - new int[] { 1, 4, 2, 2, 1, 1 }, - new int[] { 2, 4, 1, 2, 1, 1 }, - new int[] { 2, 2, 1, 1, 1, 4 }, - new int[] { 4, 1, 3, 1, 1, 1 }, - new int[] { 2, 4, 1, 1, 1, 2 }, - new int[] { 1, 3, 4, 1, 1, 1 }, - new int[] { 1, 1, 1, 2, 4, 2 },//80 - new int[] { 1, 2, 1, 1, 4, 2 }, - new int[] { 1, 2, 1, 2, 4, 1 }, - new int[] { 1, 1, 4, 2, 1, 2 }, - new int[] { 1, 2, 4, 1, 1, 2 }, - new int[] { 1, 2, 4, 2, 1, 1 }, - new int[] { 4, 1, 1, 2, 1, 2 }, - new int[] { 4, 2, 1, 1, 1, 2 }, - new int[] { 4, 2, 1, 2, 1, 1 }, - new int[] { 2, 1, 2, 1, 4, 1 }, - new int[] { 2, 1, 4, 1, 2, 1 },//90 - new int[] { 4, 1, 2, 1, 2, 1 }, - new int[] { 1, 1, 1, 1, 4, 3 }, - new int[] { 1, 1, 1, 3, 4, 1 }, - new int[] { 1, 3, 1, 1, 4, 1 }, - new int[] { 1, 1, 4, 1, 1, 3 }, - new int[] { 1, 1, 4, 3, 1, 1 }, - new int[] { 4, 1, 1, 1, 1, 3 }, - new int[] { 4, 1, 1, 3, 1, 1 }, - new int[] { 1, 1, 3, 1, 4, 1 }, - new int[] { 1, 1, 4, 1, 3, 1 },//100 - new int[] { 3, 1, 1, 1, 4, 1 }, - new int[] { 4, 1, 1, 1, 3, 1 }, - new int[] { 2, 1, 1, 4, 1, 2 }, //startA 102 - new int[] { 2, 1, 1, 2, 1, 4 }, //startB 103 - new int[] { 2, 1, 1, 2, 3, 2 }, //startC 104 - new int[] { 2, 3, 3, 1, 1, 1 } //stop without last bar 105 - }; - - int[] nBars = new int[] { 6, 6 }; - int[] nModules = new int[] { 11, 11 }; - int[] tIndexs = new int[] { 0, 1 }; - int[][][] tables = new int[][][] { _startPatterns, _patterns }; - - #endregion - - public Code128LinearReader() - { - UseE = true; - MinConfidence = 0.4f; - MaxReadError = 70; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Code128; - } - - private BarSymbolReaderLooseProjection reader2; - - internal override void GetParams(out int[] startPattern, out int[] stopPattern, out int minModulesPerBarcode, out int maxModulesPerBarcode) - { - startPattern = new int[] { 2, 1, 1 }; - stopPattern = new int[] { 2, 3, 3, 1, 1, 1, 2 }; - - minModulesPerBarcode = 24; - maxModulesPerBarcode = int.MaxValue; - - if (Reader == null) - { - //Reader = new BarSymbolReader(Scan, 6, 11, false, true, 1, _patterns, -1, UseE, null); - Reader = new BarSymbolReader(Scan, nBars, nModules, tIndexs, false, true, 1, tables, UseE, null); - - reader2 = new BarSymbolReaderLooseProjection(Scan, 6, 11, true, _patterns, 106, true, UseE); - } - } - - internal override bool ReadSymbols(BarCodeRegion region, Pattern startPattern, Pattern stopPattern, MyPoint from, MyPoint to, float module, bool firstPass) - { - float error, maxError, confidence; - - //go 2 bars back from end of line (because stop pattern contains +1 bar) - //var toCorrected = GotoBar(to, from, 1);//!!!!! - var toCorrected = to; - -#if DEBUG - var line = Scan.GetPixels(from, toCorrected);//!!!!! - DebugHelper.AddDebugItem(region.A.ToString(), line, from, toCorrected); -#endif - - int[] row = ReadSymbols(startPattern, stopPattern, from, toCorrected, module, out error, out maxError, out confidence); - - if (error < MaxReadError) - { - if (confidence >= MinConfidence) - if (confidence > region.Confidence) - { - if(row[0] < 3) - row[0] = row[0] + StartA; - if (Decode(region, row)) //try to decode - { - region.Confidence = confidence; - return true; - } - } - } - - //try BarSymbolReaderLooseProjection - if (HighPrecision) - if (confidence > 0.6f) - { - float[] widths = new float[] {0.5f, 0.9f}; - foreach (float w in widths) - { - row = reader2.Read(region, module, w); - if (row != null && row.Length > 0) - { - if (row[0] < 3) - row[0] = row[0] + StartA; - if (Decode(region, row)) - { - region.Confidence = confidence; - return true; - } - } - } - } - - //try naive reader - //toCorrected = GotoBar(to, from, 2); - //var ReaderNaive = new BarSymbolReaderNaive(6, 11, _patterns); - //row = ReaderNaive.Read(Scan.GetPixels(from, toCorrected), out error, out maxError, out confidence); - ////var row = ReaderNaive.Read(Scan.GetPixels(from, to, 1.4f), out error, out maxError, out confidence); - ////row = ReaderNaive.Read(Scan.GetPixels3Lines(from, to, 1.4f, 3.5f), out error, out maxError, out confidence); - - //if (confidence >= MinConfidence && maxError < 0.5f) - // if (confidence > region.Confidence) - // if (Decode(region, row)) //try to decode - // { - // region.Confidence = confidence; - // return true; - // } - - return false; - } - - private MyPoint GotoBar(MyPoint from, MyPoint to, int barsCount) - { - var br = new Bresenham(from, to); - //go to first black bar - while (!br.End()) - { - if (Scan.isBlack(br.Current)) - break; - br.Next(); - } - - //skip barsCount - var isBlack = true; - while (!br.End() && barsCount > 0) - { - if (Scan.isBlack(br.Current) ^ isBlack) - { - barsCount--; - isBlack = !isBlack; - } - br.Next(); - } - - return br.Current; - } - - #region Decoders - - //Method to decode bytecodes into the final string. - internal override bool Decode(BarCodeRegion r, int[] row) - { - int offset = 0; - int end = 0; while (end < row.Length && row[end++] != Stop) ; - if (row[end - 1] != Stop) { end++; offset = 1; } - int[] rawData = new int[end]; - Array.Copy(row, rawData, end - offset); - if (offset == 1) rawData[end - 1] = Stop; - - if (rawData == null || rawData.Length < 3) return false; - if (!verifyCheckSum(rawData)) return false; - int currentCodeSet; - switch (rawData[0]) - { - case StartA: - currentCodeSet = CodeA; - break; - case StartB: - currentCodeSet = CodeB; - break; - case StartC: - currentCodeSet = CodeC; - break; - default: - // shouldn't happen :-) - return false; - } - - StringBuilder sb = new StringBuilder(); - bool gotShift = false; - - for (int i = 1; i < rawData.Length - 2; i++) - { - bool shiftInEffect = gotShift; - gotShift = false; - - int symbol = rawData[i]; - - switch (currentCodeSet) - { - case CodeA: - if (symbol < 64) - { - sb.Append((char)(symbol + ' ')); - } - else if (symbol < 96) - { - sb.Append((char)(symbol - 64)); - } - else - { - switch (symbol) - { - case FNC1: sb.Append(""); break; - case FNC2: sb.Append(""); break; - case FNC3: sb.Append(""); break; - case FNC4A: sb.Append(""); break; - - case Shift: - gotShift = true; - currentCodeSet = CodeB; - break; - - case CodeB: - currentCodeSet = CodeB; - break; - - case CodeC: - currentCodeSet = CodeC; - break; - } - } - break; - - case CodeB: - if (symbol < 96) - { - sb.Append((char)(symbol + ' ')); - } - else - { - switch (symbol) - { - case FNC1: sb.Append(""); break; - case FNC2: sb.Append(""); break; - case FNC3: sb.Append(""); break; - case FNC4B: sb.Append(""); break; - - case Shift: - gotShift = true; - currentCodeSet = CodeC; - break; - - case CodeA: - currentCodeSet = CodeA; - break; - - case CodeC: - currentCodeSet = CodeC; - break; - } - } - break; - - case CodeC: - if (symbol < 100) - { - if (symbol < 10) - sb.Append('0'); - - sb.Append(symbol); - } - else - { - switch (symbol) - { - case FNC1: sb.Append(""); break; - - case CodeA: - currentCodeSet = CodeA; - break; - - case CodeB: - currentCodeSet = CodeB; - break; - } - } - break; - } - - if (shiftInEffect) - { - switch (currentCodeSet) - { - case CodeA: - currentCodeSet = CodeC; - break; - case CodeB: - currentCodeSet = CodeA; - break; - case CodeC: - currentCodeSet = CodeB; - break; - } - } - } - - if (sb.Length == 0) - return false; - - r.Data = new ABarCodeData[] { new StringBarCodeData(sb.ToString()) }; - return true; - } - - private bool verifyCheckSum(int[] symbols) - { - if (symbols.Length < 3) - { - // at least StartX, Stop and checksum should be in every Code128 code - return false; - } - - int total = symbols[0]; - for (int i = 1; i < symbols.Length - 2; i++) - total += symbols[i] * i; - - return (total % 103) == symbols[symbols.Length - 2]; - } - - #endregion - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code16K/Code16KDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Code16K/Code16KDecoder.cs deleted file mode 100644 index c237d332..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code16K/Code16KDecoder.cs +++ /dev/null @@ -1,306 +0,0 @@ -using System; -using System.Collections.Generic; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Code16K -{ - //Class to decode a Code16K barcode from the top left and bottom left corners. - //Traces N perpendicular lines (perpendicular to top-bottom corners in a moduleLength step) to scan - //all barcode rows. This scan is redundant, and all samples are stored in a data structure. At the end, - //the most common values are used to decode the barcode. - class Code16KDecoder - { - public BarCodeRegion Decode(ImageScaner scan, MyPointF up, MyPointF down, FoundPattern foundPattern) - { - //main directions - MyVectorF vdY = (up - down); - float startLength = vdY.Length; - vdY = vdY.Normalized; - MyVectorF vdX = new MyVectorF(-vdY.Y, vdY.X); - - float d = foundPattern.moduleLength; - //data structure to store all redundant samples of each row. - Dictionary symbols = new Dictionary(); - - //scan region rows, at small step of barcode module length. Result is stored in symbols array. - //symbols array is initialized later, when the first row is read and the number of rows is known. - //Seems that results are better using bar widths instead of E (sum of 2 consecutive bar widths). - int[] nBars=new int[]{4,1,6,6,6,6,6,4}; - int[] nModules=new int[]{7,1,11,11,11,11,11,7}; - int[] tableIndexs=new int[]{0,-1,1,1,1,1,1,0}; - int[][][] ttSymbols = new int[][][] { _startStop, _patterns }; - - - BarSymbolReader reader = new BarSymbolReader(scan, nBars, nModules, tableIndexs, true, true, foundPattern.moduleLength, ttSymbols, false, null); - while (d < startLength) - { - MyPointF a = up - vdY * d; MyPoint last; - float error, maxError, confidence; - int[] row = reader.Read(a, vdX, out error, out maxError, out confidence, out last); //convert scanline to bytecodes array. - - if (row != null && row.Length == 8) - { - //find nRow (0..15) - int nRow = -1; - if (row[0] == row[7]) nRow = row[0]; - else if (row[0] == (row[7] + 4) % 8) nRow = row[0] + 8; - - if (nRow >= 0 && nRow <= 15) - { - if (!symbols.ContainsKey(nRow)) - { - SymbolSamples[] s = symbols[nRow] = new SymbolSamples[5]; - for (int i = 0; i < 5; i++) s[i] = new SymbolSamples(); - } - SymbolSamples[] samples = symbols[nRow]; - for (int i = 2; i < 7; i++) samples[i - 2].add(row[i]); - } - } - - d += foundPattern.moduleLength; - } - - //extract nRows and starting mode - if (!symbols.ContainsKey(0)) return null; - symbols[0][0].sort(); - int S = ((SymbolSample)symbols[0][0].samples[0]).symbol; - int startMode = S % 7; - int nRows = S / 7 + 2; - - //extract codewords - int[] codewords = new int[nRows*5]; - for (int r = 0; r < nRows; r++) - { - if (!symbols.ContainsKey(r)) return null; - for (int i = 0; i < 5; i++) - { - symbols[r][i].sort(); - codewords[r*5+i] = ((SymbolSample)symbols[r][i].samples[0]).symbol; - } - } - - //check CRC - int chk1 = 0, chk2=0; - for (int i = 0; i < codewords.Length - 2; i++) chk1 = (chk1 + codewords[i] * (i + 2)) % 107; - for (int i = 0; i < codewords.Length - 1; i++) chk2 = (chk2 + codewords[i] * (i + 1)) % 107; - - string msg = decodeRow(codewords); - - float l = foundPattern.moduleLength * (7 + 11 * 5 + 7); - BarCodeRegion reg = new BarCodeRegion(up, up + vdX * l, down + vdX * l, down); - reg.Data = new ABarCodeData[] { new StringBarCodeData(msg) }; - reg.Confidence = (chk1==codewords[codewords.Length-2] && chk2==codewords[codewords.Length-1]?1f:0f); - return reg; - } - - //method to decode all codewords from a code16K barcode. Starts with the starting mode (rawData[0]%7) used to set - //the initial decoding table. - private string decodeRow(int[] rawData) - { - //Initial encoding schema A, B or C. - string[] currentCodeSet = null; - string char0 = null; - switch (rawData[0]%7) - { - case 0: currentCodeSet = tableA; break; - case 1: currentCodeSet = tableB; break; - case 2: currentCodeSet = tableC; break; - case 3: currentCodeSet = tableB; char0 = "FNC1"; break; - case 4: currentCodeSet = tableC; char0 = "FNC1"; break; - case 5: currentCodeSet = tableC; char0 = "1SA"; break; - case 6: currentCodeSet = tableC; char0 = "2SA"; break; - default: return null; - } - - string msg = ""; - int nShift = 0; - string[] currentShift = null; - for (int i = 0; i < rawData.Length - 2; i++) if (i > 0 || char0 != null) - { - string[] t = (nShift > 0 ? currentShift : currentCodeSet); - string ch = (i > 0 ? (rawData[i]!=-1?t[rawData[i]]:"?") : char0); - if (ch == "pad") ch = ""; - if (ch!=null) switch (ch) { - case "1SA": currentShift = tableA; nShift = 1; break; - case "2SA": currentShift = tableA; nShift = 2; break; - case "3SA": currentShift = tableA; nShift = 3; break; - case "1SB": currentShift = tableB; nShift = 1; break; - case "2SB": currentShift = tableB; nShift = 2; break; - case "3SB": currentShift = tableB; nShift = 3; break; - case "1SC": currentShift = tableC; nShift = 1; break; - case "2SC": currentShift = tableC; nShift = 2; break; - case "3SC": currentShift = tableC; nShift = 3; break; - case "CodeA": currentCodeSet = tableA; break; - case "CodeB": currentCodeSet = tableB; break; - case "CodeC": currentCodeSet = tableC; break; - default: - if (t == tableC && rawData[i] < 100) msg += Convert.ToString(rawData[i]).PadLeft(2, '0'); - else if (ch.Length > 1) msg += "[" + ch + "]"; else msg += ch; - if (nShift>0) nShift--; - break; - } - } - return msg; - } - - - private static readonly int[][] _startStop = - { - new int[]{3,2,1,1}, new int[]{2,2,2,1}, new int[]{2,1,2,2}, new int[]{1,4,1,1}, - new int[]{1,1,3,2}, new int[]{1,2,3,1}, new int[]{1,1,1,4}, new int[]{3,1,1,2} - }; - - private static readonly int[][] _patterns = - { - new int[] { 2, 1, 2, 2, 2, 2 }, - new int[] { 2, 2, 2, 1, 2, 2 }, - new int[] { 2, 2, 2, 2, 2, 1 }, - new int[] { 1, 2, 1, 2, 2, 3 }, - new int[] { 1, 2, 1, 3, 2, 2 }, - new int[] { 1, 3, 1, 2, 2, 2 }, - new int[] { 1, 2, 2, 2, 1, 3 }, - new int[] { 1, 2, 2, 3, 1, 2 }, - new int[] { 1, 3, 2, 2, 1, 2 }, - new int[] { 2, 2, 1, 2, 1, 3 }, - new int[] { 2, 2, 1, 3, 1, 2 }, //10 - new int[] { 2, 3, 1, 2, 1, 2 }, - new int[] { 1, 1, 2, 2, 3, 2 }, - new int[] { 1, 2, 2, 1, 3, 2 }, - new int[] { 1, 2, 2, 2, 3, 1 }, - new int[] { 1, 1, 3, 2, 2, 2 }, - new int[] { 1, 2, 3, 1, 2, 2 }, - new int[] { 1, 2, 3, 2, 2, 1 }, - new int[] { 2, 2, 3, 2, 1, 1 }, - new int[] { 2, 2, 1, 1, 3, 2 }, - new int[] { 2, 2, 1, 2, 3, 1 },//20 - new int[] { 2, 1, 3, 2, 1, 2 }, - new int[] { 2, 2, 3, 1, 1, 2 }, - new int[] { 3, 1, 2, 1, 3, 1 }, - new int[] { 3, 1, 1, 2, 2, 2 }, - new int[] { 3, 2, 1, 1, 2, 2 }, - new int[] { 3, 2, 1, 2, 2, 1 }, - new int[] { 3, 1, 2, 2, 1, 2 }, - new int[] { 3, 2, 2, 1, 1, 2 }, - new int[] { 3, 2, 2, 2, 1, 1 }, - new int[] { 2, 1, 2, 1, 2, 3 },//30 - new int[] { 2, 1, 2, 3, 2, 1 }, - new int[] { 2, 3, 2, 1, 2, 1 }, - new int[] { 1, 1, 1, 3, 2, 3 }, - new int[] { 1, 3, 1, 1, 2, 3 }, - new int[] { 1, 3, 1, 3, 2, 1 }, - new int[] { 1, 1, 2, 3, 1, 3 }, - new int[] { 1, 3, 2, 1, 1, 3 }, - new int[] { 1, 3, 2, 3, 1, 1 }, - new int[] { 2, 1, 1, 3, 1, 3 }, - new int[] { 2, 3, 1, 1, 1, 3 },//40 - new int[] { 2, 3, 1, 3, 1, 1 }, - new int[] { 1, 1, 2, 1, 3, 3 }, - new int[] { 1, 1, 2, 3, 3, 1 }, - new int[] { 1, 3, 2, 1, 3, 1 }, - new int[] { 1, 1, 3, 1, 2, 3 }, - new int[] { 1, 1, 3, 3, 2, 1 }, - new int[] { 1, 3, 3, 1, 2, 1 }, - new int[] { 3, 1, 3, 1, 2, 1 }, - new int[] { 2, 1, 1, 3, 3, 1 }, - new int[] { 2, 3, 1, 1, 3, 1 },//50 - new int[] { 2, 1, 3, 1, 1, 3 }, - new int[] { 2, 1, 3, 3, 1, 1 }, - new int[] { 2, 1, 3, 1, 3, 1 }, - new int[] { 3, 1, 1, 1, 2, 3 }, - new int[] { 3, 1, 1, 3, 2, 1 }, - new int[] { 3, 3, 1, 1, 2, 1 }, - new int[] { 3, 1, 2, 1, 1, 3 }, - new int[] { 3, 1, 2, 3, 1, 1 }, - new int[] { 3, 3, 2, 1, 1, 1 }, - new int[] { 3, 1, 4, 1, 1, 1 },//60 - new int[] { 2, 2, 1, 4, 1, 1 }, - new int[] { 4, 3, 1, 1, 1, 1 }, - new int[] { 1, 1, 1, 2, 2, 4 }, - new int[] { 1, 1, 1, 4, 2, 2 }, - new int[] { 1, 2, 1, 1, 2, 4 }, - new int[] { 1, 2, 1, 4, 2, 1 }, - new int[] { 1, 4, 1, 1, 2, 2 }, - new int[] { 1, 4, 1, 2, 2, 1 }, - new int[] { 1, 1, 2, 2, 1, 4 }, - new int[] { 1, 1, 2, 4, 1, 2 },//70 - new int[] { 1, 2, 2, 1, 1, 4 }, - new int[] { 1, 2, 2, 4, 1, 1 }, - new int[] { 1, 4, 2, 1, 1, 2 }, - new int[] { 1, 4, 2, 2, 1, 1 }, - new int[] { 2, 4, 1, 2, 1, 1 }, - new int[] { 2, 2, 1, 1, 1, 4 }, - new int[] { 4, 1, 3, 1, 1, 1 }, - new int[] { 2, 4, 1, 1, 1, 2 }, - new int[] { 1, 3, 4, 1, 1, 1 }, - new int[] { 1, 1, 1, 2, 4, 2 },//80 - new int[] { 1, 2, 1, 1, 4, 2 }, - new int[] { 1, 2, 1, 2, 4, 1 }, - new int[] { 1, 1, 4, 2, 1, 2 }, - new int[] { 1, 2, 4, 1, 1, 2 }, - new int[] { 1, 2, 4, 2, 1, 1 }, - new int[] { 4, 1, 1, 2, 1, 2 }, - new int[] { 4, 2, 1, 1, 1, 2 }, - new int[] { 4, 2, 1, 2, 1, 1 }, - new int[] { 2, 1, 2, 1, 4, 1 }, - new int[] { 2, 1, 4, 1, 2, 1 },//90 - new int[] { 4, 1, 2, 1, 2, 1 }, - new int[] { 1, 1, 1, 1, 4, 3 }, - new int[] { 1, 1, 1, 3, 4, 1 }, - new int[] { 1, 3, 1, 1, 4, 1 }, - new int[] { 1, 1, 4, 1, 1, 3 }, - new int[] { 1, 1, 4, 3, 1, 1 },//96 FNC3 FNC3 96 - new int[] { 4, 1, 1, 1, 1, 3 },//97 FNC2 FNC2 97 - new int[] { 4, 1, 1, 3, 1, 1 },//98 1SB 1SA 98 - new int[] { 1, 1, 3, 1, 4, 1 },//99 CodeC CodeC 99 - new int[] { 1, 1, 4, 1, 3, 1 },//100 CodeB FNC4 CodeB - new int[] { 3, 1, 1, 1, 4, 1 },//102 FNC4 CodeA CodeA - new int[] { 4, 1, 1, 1, 3, 1 },//102 FNC1 FNC1 FNC1 - new int[] { 2, 1, 1, 4, 1, 2 },//103 PAD - new int[] { 2, 1, 1, 2, 1, 4 },//104 2SB 2SA 2SB - new int[] { 2, 1, 1, 2, 3, 2 },//105 2SC 2SC 2SB - new int[] { 2, 1, 1, 1, 3, 3 } //106 3SC 3SC 3SB - }; - - string[] tableA = { - " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", - "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", - "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", - ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", - "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", - "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", - "\\", "]", "^", "_", "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", - "ACK", "BEL", "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", - "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM", - "SUB", "ESC", "FS", "GS", "RS", "US", "FNC3", "FNC2", "1SB", "CodeC", - "CodeB", "FNC4", "FNC1", "pad", "2SB", "2SC","3SC" - }; - - string[] tableB = { - " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", - "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", - "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", - ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", - "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", - "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", - "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", - "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", - "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", - "z", "{", "|", "}", "tilde", "DEL", "FNC3", "FNC2", "1SA", "CodeC", - "FNC4", "CodeA", "FNC1", "pad", "2SA", "2SC", "3SC" - }; - - string[] tableC = { - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", - "CodeB", "CodeA", "FNC1", "pad", "1SB", "2SB", "3SB" - }; - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code16K/Code16KReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Code16K/Code16KReader.cs deleted file mode 100644 index 1354eddd..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code16K/Code16KReader.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using System.Collections.Generic; -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Code16K -{ - //Class to read Code16K barcodes. Uses an approach mixture of PDF417 and DM - // 1.-scan all rows looking for some start patterns. There are 8 start patterns. There are a lot of - // matches because start patterns have only 4 bars (BWBW). - // 2.-find the top and bottom corners of the edge where the start pattern is found, tracking the edge - // 3.-traces a perpendicular in the middle and check that there is a quiet zone. This step reject most - // of the false candidates. - // 4.-tracing a perpendicular (in the right direction) to find stop patterns doesn't work, since there - // are too many false stop finders. So, we skip this step and start decoding rows. - // 5.-traces N perpendicular lines (perpendicular to top-bottom corners) to scan all barcode rows. - // This scan is redundant, at bar module step. No CRC check per row is done. All samples of each row - // are stored and, for each symbol, the MOST COMMON decoded value, is used to decode de barcode. - // 6.- Data is decoded and global CRC is checked. -#if CORE_DEV - public -#else - internal -#endif - class Code16KReader : SymbologyReader2D - { - //Process start patterns of mínim 2 pixels height - protected int stackedPatternMinHeight = 2; - - //Reject start patterns with and edge out of -45º..45º --> cos(45)=0.707 - protected float barcodeMaxCosAngle = 0.7f; - - //When check for start quiet zone, check 4 * modules with white pixels - protected float startPatternQuietZone = 10f; - - protected ImageScaner scan; //object to sample BW image - protected LinkedList candidates = new LinkedList(); //found barcodes - - int[][] startStopPatterns = new int[][] { new int[] { 3, 2, 1, 1 }, new int[] {2,2,2,1 }, - new int[] {2,1,2,2 }, new int[] { 1,4,1,1 }, new int[] { 1,1,3,2 }, - new int[] { 1,2,3,1 }, new int[] { 1,1,1,4 }, new int[]{3,1,1,2} }; - IPatternFinderNoiseRow startFinder; //object to find finders in a horizontal row - Code16KDecoder decoder = new Code16KDecoder(); //object to decode data to chars - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Code16K; - } - - //Scans the image row by row, looking for start patterns. For each pattern found, try to - //add this patterns to previously found patterns. This is implemented in the StackedPattern. - //StackedPatterns are useful to track better the edge and detect the angle of the barcode. - //Since a code16K barcode have from 2..16 rows, each one starting with a different start pattern - //we will be able to join only start patterns of the same row. I.e, a single code16K will lead to - //N stackedPatterns, where N is the number of rows of barcode. - protected override FoundBarcode[] DecodeBarcode() - { - scan = new ImageScaner(BWImage); - startFinder = new PatternFinderNoiseRow(startStopPatterns, true, true, 2); - LinkedList foundPatterns = new LinkedList(); - LinkedList removedPatterns; - - for (int y = 0; y < BWImage.Height; y += ScanStep) - { - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - XBitArray row = BWImage.GetRow(y); - startFinder.NewSearch(row); - FoundPattern foundPattern; - while ((foundPattern = startFinder.NextPattern()) != null) - { - //foundPattern is the index of the found pattern - MyPoint a = new MyPoint(startFinder.First, y); - MyPoint b = new MyPoint(startFinder.Last, y); - //Check if another pattern was found in the last row. - Pattern p = new Pattern(foundPattern, a.X, b.X, y); - LinkedListNode prev = foundPatterns.Find(p); - if (prev != null) - { //pattern already processed the last row - if (prev.Value.y != y) - { - StackedPattern sp = (StackedPattern)prev.Value; - sp.NewRow(a.X, b.X, y); - } - } - else - { //new - StackedPattern sp = new StackedPattern(foundPattern, a.X, b.X, y); - foundPatterns.AddLast(sp); - } - } - - //clean old patterns and process them - removedPatterns = Pattern.RemoveOldPatterns(foundPatterns, y); - foreach (Pattern p in removedPatterns) - { - ProcessPattern((StackedPattern)p); - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - } - } - - //clean stackedPatterns - foreach (Pattern p in foundPatterns) - { - ProcessPattern((StackedPattern)p); - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - } - - FoundBarcode[] results = new FoundBarcode[candidates.Count]; - int nn = 0; - foreach (BarCodeRegion r in candidates) - { - FoundBarcode f = new FoundBarcode(); - f.BarcodeFormat = SymbologyType.Code16K; - f.Polygon = new SKPointI[] { r.A, r.B, r.C, r.D, r.A }; - f.Color = SKColors.Blue; - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(f.Polygon, pointTypes); - //f.Rect = Rectangle.Round(path.GetBounds()); - f.Rect = Utils.DrawPath(f.Polygon); - f.Value = (r.Data != null ? r.Data[0].ToString() : "?"); - f.Confidence = r.Confidence; - results[nn++] = f; - } - return results; - } - - //Find the top and bottom corners of the finder, starting at the center point of the pattern, - //and tracking the edge. Then traces a perpendicular line to the left direction, and check the - //quiet zone of 10 modules length (this step rejects most of the false candidates). - void ProcessPattern(StackedPattern p) - { - //check minimum aspect ratio - if (p.y - p.startY > stackedPatternMinHeight) - { - MyPoint center, rCenter; p.Center(out center, out rCenter); - MyPoint midCenter = center; - midCenter.X += (int)p.MeanWidth(); - float moduleLength = p.MeanWidth() / 7f; - - foreach (BarCodeRegion c in candidates) - if (c.In(midCenter)) return; - - //Track edge - EdgeTrack et = new EdgeTrack(scan); - et.Track(center, new MyVector(-1, 0), moduleLength, true); - - //find the top and bottom points of the edge - MyPointF up = et.Up(); - MyPointF down = et.Down(); - - //Calculate main directions - MyVectorF vdY = (up - down); - vdY = vdY.Normalized; - MyVectorF vdX = new MyVectorF(-vdY.Y, vdY.X); - - //Calculate rotated module length - float cosAngle = (float)Math.Cos(vdX.Angle); - if (cosAngle > barcodeMaxCosAngle) - { - float rotatedModuleLength = moduleLength * cosAngle; - - //check left quiet zone - Bresenham br = new Bresenham(center, -vdX); - while (scan.In(br.Current) && scan.isBlack(br.Current)) br.Next(); - - //check 10 modules length quiet zone - bool quietZone = true; - while (quietZone && scan.In(br.Current) && (br.Current - center).Length < startPatternQuietZone * rotatedModuleLength) - if (scan.isBlack(br.Current)) quietZone = false; - else br.Next(); - - if (quietZone) - { //proceed to decode - p.foundPattern.moduleLength = rotatedModuleLength; - BarCodeRegion r = decoder.Decode(scan, up, down, p.foundPattern); - if (r!=null) candidates.AddLast(r); - return; - } - } - } - } - - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/Code39ExtendedReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/Code39ExtendedReader.cs deleted file mode 100644 index c7a5f8cf..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/Code39ExtendedReader.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace BarcodeReader.Core.Code39 -{ - internal class Code39ExtendedReader : Code39Reader - { - public Code39ExtendedReader() - { - DoDecodeExtended = true; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Code39Ext; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/Code39Mod43ExtendedReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/Code39Mod43ExtendedReader.cs deleted file mode 100644 index 85919d5e..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/Code39Mod43ExtendedReader.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace BarcodeReader.Core.Code39 -{ - internal class Code39Mod43ExtendedReader : Code39Reader - { - public Code39Mod43ExtendedReader() - { - CheckSumMode = Mode.CheckMod43Checksum; - DoDecodeExtended = true; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Code39Mod43Ext; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/Code39Mod43Reader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/Code39Mod43Reader.cs deleted file mode 100644 index 88e3397c..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/Code39Mod43Reader.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace BarcodeReader.Core.Code39 -{ - internal class Code39Mod43Reader : Code39Reader - { - public Code39Mod43Reader() - { - CheckSumMode = Mode.CheckMod43Checksum; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Code39Mod43; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/Code39Reader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/Code39Reader.cs deleted file mode 100644 index 53688768..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/Code39Reader.cs +++ /dev/null @@ -1,503 +0,0 @@ -using System; -using System.Text; -using BarcodeReader.Core.Common; -using SkiaSharp; - -namespace BarcodeReader.Core.Code39 -{ - //Main class to scan an image looking for code39 barcodes. It inherits from Reader2DNoise, - //responsible to find start and stop patterns in the image, and calling FindBarcode methods - //to read the barcode in between. -#if CORE_DEV - public -#else - internal -#endif - class Code39Reader : Reader2DNoise - { - /// - /// do decode extended Code 39 symbols or not (disabled by default) - /// - protected bool DoDecodeExtended = false; - - public enum Mode { None, CheckMod43Checksum, CheckMod11ChecksumPZN8, CheckMod11ChecksumISBN10, CheckMod11ChecksumUPU }; - public Mode CheckSumMode = Mode.None; - - /// - /// Enable recognition of barcodes with space between codewords - /// It is useful to read barcodes drawn with font - /// - public bool AllowToRecognizeFontBarcode = true; - - public Code39Reader() - { - //Define start and stop patterns. Code39 has an unique start/stop pattern, but - //we define 3 different ratios for narrow&wide bars 1:2 (the normal), 1:3, or 1:4 - //This allows to detect more start patterns, than using only 1:2 ratio. - startPatterns = stopPatterns = new int[][] { new int[] { 1, 2, 1, 1, 2, 1, 2, 1, 1 } , new int[] { 1, 3, 1, 1, 3, 1, 3, 1, 1 }};//, new int[] { 1, 4, 1, 1, 4, 1, 4, 1, 1 } }; - useE = true; - singlePattern = true; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Code39; - } - - protected override void TrackEdge(MyPoint left, MyPoint right, float moduleLength, out MyPointF up, out MyPointF down) - { - EdgeTrack et = new EdgeTrack(scan); - - //first try using the first bar (thin bar) - et.Track(left, new MyVector(-1, 0), moduleLength, true); - - //find the top and bottom points of the edge - MyPointF fup = et.Up(); - MyPointF fdown = et.Down(); - - - MyPoint mid = (left + right) / 2; - et.Track(mid, new MyVector(-1, 0), moduleLength, true); - - //find the top and bottom points of the edge - up = et.Up(); - down = et.Down(); - - if ((up - down).Length > (fup - fdown).Length) - { - //Calculate main directions - MyVectorF vdY = (up - down); - vdY = vdY.Normalized; - MyVectorF vdX = new MyVectorF(-vdY.Y, vdY.X); - - //Calculate rotated module length - float cosAngle = (float)Math.Cos(vdX.Angle); - if (cosAngle < barcodeMaxCosAngle) cosAngle = 1f; //invalid angle - - float rotatedModuleLength = moduleLength * cosAngle * cosAngle; //projected module length X axis - - up -= vdX * rotatedModuleLength * 5f; - down -= vdX * rotatedModuleLength * 5f; - } - else - { - up = fup; - down = fdown; - } - } - - //Method to scan a single row - protected override BarCodeRegion FindBarcode(ImageScaner scan, int startPattern, MyPoint start, MyPoint end, FoundPattern foundPattern) - { - BarCodeRegion result = null; - BarSymbolReader reader = new BarSymbolReader(scan, 10, 13, false, true, foundPattern.moduleLength, _patterns, -1, false, null); - float error, maxError, confidence; - - int[] row = reader.Read(start, end, out error, out maxError, out confidence); - if (row!=null && row.Length>2 && row[0]==43 && row[row.Length-1]==43 && error < 1f) - { - BarCodeRegion r = new BarCodeRegion(start, reader.Current, reader.Current + new MyPoint(0, 5), start + new MyPoint(0, 5)); - r.Confidence = confidence; - if (Decode(r, row)) result= r; - } - - return result; - } - - bool IsCodewordCountAround(FoundPattern pattern, int actual, int expected) - { - var isFontBarcode = pattern.lastModule > pattern.moduleLength * 3; - - if (isFontBarcode && AllowToRecognizeFontBarcode) - { - return actual < expected && expected - actual < 5 || actual >= expected && actual - expected < 2; - } - else - { - return actual < expected && expected - actual < 2 || actual >= expected && actual - expected < 2; - } - } - - //Method to scan a region. It starts with a scanning the line in the middle of the region - //to be able to read quickly good quality barcodes. If it fails, then uses the slower - //loose projection reader. - override protected BarCodeRegion FindBarcode(ImageScaner scan, int startPattern, BarCodeRegion r, FoundPattern foundPattern) - { -#if DEBUG - DebugHelper.DrawRegion(SKColors.Red, r); -#endif - BarCodeRegion result = null; - BarSymbolReader reader = new BarSymbolReader(scan, 10, 13, false, true, foundPattern.moduleLength, _patterns, -1, false, null); - float error, maxError,confidence; - - // setting min match difference - reader.MinMatchDifference = 1E-7f; - - //expected number of codewords - int nCodewords = (int)Math.Round((r.B - r.A).Length / foundPattern.moduleLength / 13f, MidpointRounding.AwayFromZero); - - - //scan different lines of region - int[] row = null; - float[] midPoints = new float[] {0.5f, 0.3f, 0.7f, 0.4f, 0.6f, 0.1f, 0.9f, 0.2f, 0.8f}; - bool anyFound = false; - - float bestConfidence = 0; - - foreach(var mid in midPoints) - { - MyPoint a = MyPointF.Lerp(r.A, r.D, mid); - MyPoint b = MyPointF.Lerp(r.B, r.C, mid); - - row = reader.Read(a, b, out error, out maxError, out confidence); - r.Confidence = confidence; - - if (confidence > 0.7f && row != null && maxError < 3f * error && row[0] == 43 && row[row.Length - 1] == 43 && IsCodewordCountAround(foundPattern, row.Length, nCodewords)) - if (Decode(r, row)) - { - if (r.Confidence > bestConfidence)//find best confidence - { - result = r; //if sampling is good enough, then use simple row scanning - bestConfidence = r.Confidence; - } - } - - if (row != null && row.Length >= 3) anyFound = true; - } - - //if found - return - if (result != null) - { - return result; - } - - // - //scan of region is failed :( - //will use BarSymbolReaderLooseProjection... - // - - if (!anyFound) return null; - - BarSymbolReaderLooseProjection reader2 = new BarSymbolReaderLooseProjection(scan, 10, 13, true, _patterns, 43, false, useE); - float[] widths = new float[]{0.5f, 0.9f}; - - // setting min match difference - reader2.MinMatchDifference = 1E-7f; - - foreach (float w in widths) - { - row = reader2.Read(r, foundPattern.moduleLength, w); - if (row != null && row.Length > 2 && row[0] == 43 && row[row.Length - 1] == 43 && IsCodewordCountAround(foundPattern, row.Length, nCodewords)) - if (Decode(r, row)) { result = r; break; } -#if DEBUG - /*string s = ""; - for (int i = 0; i < row.Length; i++) s += row[i] + ","; - Debug.WriteLine(":" + r + "-->" + w + "-->" + s);*/ -#endif - } - - return result; - } - -#if OLD_FindBarcode - //Method to scan a region. It starts with a scanning the line in the middle of the region - //to be able to read quickly good quality barcodes. If it fails, then uses the slower - //loose projection reader. - override protected BarCodeRegion FindBarcode(ImageScaner scan, int startPattern, BarCodeRegion r, FoundPattern foundPattern) - { - BarCodeRegion result = null; - BarSymbolReader reader = new BarSymbolReader(scan, 10, 13, false, true, foundPattern, _patterns, -1, false, null); - MyPoint a = r.A * 0.5f + r.D * 0.5f; - MyPoint b = r.B * 0.5f + r.C * 0.5f; - float error, maxError, confidence; - - // setting min match difference - reader.MinMatchDifference = 1E-7f; - - //expected number of codewords - int nCodewords = (int)Math.Round((r.B - r.A).Length / foundPattern.moduleLength / 13f, MidpointRounding.AwayFromZero); - - int[] row = reader.Read(a, b, out error, out maxError, out confidence); r.Confidence = confidence; - if (confidence > 0.7f && row != null && maxError < 3f * error && row[0] == 43 && row[row.Length - 1] == 43 && Around(row.Length, nCodewords)) - if (Decode(r, row)) result = r; //if sampling is good enough, then use simple row scanning - - if (result != null) return result; - - if (row == null || row.Length < 3) return null; - //int c = 0; for (int i = 0; i < row.Length; i++) if (row[i] != -1) c++; - //if (c < row.Length / 4) return null; - - BarSymbolReaderLooseProjection reader2 = new BarSymbolReaderLooseProjection(scan, 10, 13, true, _patterns, 43, false, useE); - float[] widths = new float[] { 0.5f, 0.9f }; - - // setting min match difference - reader2.MinMatchDifference = 1E-7f; - - foreach (float w in widths) - { - row = reader2.Read(r, foundPattern.moduleLength, w); - if (row != null && row.Length > 2 && row[0] == 43 && row[row.Length - 1] == 43 && Around(row.Length, nCodewords)) - if (Decode(r, row)) { result = r; break; } -#if DEBUG - /*string s = ""; - for (int i = 0; i < row.Length; i++) s += row[i] + ","; - Debug.WriteLine(":" + r + "-->" + w + "-->" + s);*/ -#endif - } - - return result; - } - -#endif - - //Method to decode bytecodes into the final string. - protected virtual bool Decode(BarCodeRegion r, int[] row) { return Decode(r, row, 1, row!=null?row.Length - 1:0); } - protected virtual bool Decode(BarCodeRegion r, int[] row, int iStart, int iEnd) - { - string pre = ""; - if (row != null && row.Length > 2) - { - if (CheckSumMode==Mode.CheckMod43Checksum) - { - iEnd--; - int sum = 0; - for (int i = iStart; i < iEnd; i++) sum += row[i]; - sum = sum % 43; - if (sum != row[iEnd]) return false; - } - else if (CheckSumMode == Mode.CheckMod11ChecksumPZN8) - { - iEnd--; - int sum = 0; - int[] weights = new int[] { 1,2,3,4,5,6,7 }; - bool isPzn7 = false; - // check if it is PZN8 - if (iEnd - iStart != 8) - { - // check if it is PZN7 - isPzn7 = iEnd - iStart == 7; - - // otherwise exit - if (!isPzn7) - return false; - } - - if (row[iStart] != 36) return false; //PZN starts with - - iStart++; - - if (isPzn7) - // count weights as 2,3,4,5,6.. - for (int i = iStart; i < iEnd; i++) sum += row[i] * weights[i - iStart+1]; - else - // PZN8, count weights as 1,2,3,4,5,6 - for (int i = iStart; i < iEnd; i++) sum += row[i] * weights[i - iStart]; - int checkdigit = sum % 11; - if (checkdigit == 10) checkdigit = 0; - if (checkdigit != row[iEnd]) return false; - pre = "";// "PZN-"; - } - else if (CheckSumMode == Mode.CheckMod11ChecksumISBN10) - { - iEnd--; - int sum = 0; - int[] weights = new int[] { 10, 9, 8, 7, 6, 5, 4, 3, 2 }; - if (iEnd - iStart != 9) return false; - for (int i = iStart; i < iEnd; i++) sum += row[i] * weights[i - iStart]; - int checkdigit = 11 - sum % 11; - if (checkdigit == 10) checkdigit = 0; - else if (checkdigit == 11) checkdigit = 5; - if (checkdigit != row[iEnd]) return false; - } - else if (CheckSumMode == Mode.CheckMod11ChecksumUPU) - { - iEnd--; - int sum = 0; - int[] weights = new int[] { 8, 6, 4, 2, 3, 5, 9, 7 }; - if (iEnd - iStart != 8) return false; //UPU has just 8 digits + Checksum - for (int i = iStart; i < iEnd; i++) sum += row[i] * weights[i - iStart]; - int checkdigit = 11 - sum % 11; - if (checkdigit == 10) checkdigit = 0; - else if (checkdigit == 11) checkdigit = 5; - if (checkdigit != row[iEnd]) return false; - } - - string s = pre; - for (int i = iStart; i < iEnd; i++) - if (row[i] >= 0 && row[i] < _patterns.Length) - s += _alphabet.Substring(row[i], 1); - else return false; - - if (DoDecodeExtended) - s = DecodeExtended(s); - r.Data = new ABarCodeData[] { new StringBarCodeData(s) }; - return true; - } - return false; - } - - string _alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*"; - - int[][] _patterns = new int[][] { - new int[]{1,1,1,2,2,1,2,1,1,1},//0 - new int[]{2,1,1,2,1,1,1,1,2,1}, - new int[]{1,1,2,2,1,1,1,1,2,1}, - new int[]{2,1,2,2,1,1,1,1,1,1}, - new int[]{1,1,1,2,2,1,1,1,2,1}, - new int[]{2,1,1,2,2,1,1,1,1,1},//5 - new int[]{1,1,2,2,2,1,1,1,1,1}, - new int[]{1,1,1,2,1,1,2,1,2,1}, - new int[]{2,1,1,2,1,1,2,1,1,1}, - new int[]{1,1,2,2,1,1,2,1,1,1}, - new int[]{2,1,1,1,1,2,1,1,2,1},//10 - new int[]{1,1,2,1,1,2,1,1,2,1}, - new int[]{2,1,2,1,1,2,1,1,1,1}, - new int[]{1,1,1,1,2,2,1,1,2,1}, - new int[]{2,1,1,1,2,2,1,1,1,1}, - new int[]{1,1,2,1,2,2,1,1,1,1},//15 - new int[]{1,1,1,1,1,2,2,1,2,1}, - new int[]{2,1,1,1,1,2,2,1,1,1}, - new int[]{1,1,2,1,1,2,2,1,1,1}, - new int[]{1,1,1,1,2,2,2,1,1,1}, - new int[]{2,1,1,1,1,1,1,2,2,1},//20 - new int[]{1,1,2,1,1,1,1,2,2,1}, - new int[]{2,1,2,1,1,1,1,2,1,1}, - new int[]{1,1,1,1,2,1,1,2,2,1}, - new int[]{2,1,1,1,2,1,1,2,1,1}, - new int[]{1,1,2,1,2,1,1,2,1,1},//25 - new int[]{1,1,1,1,1,1,2,2,2,1}, - new int[]{2,1,1,1,1,1,2,2,1,1}, - new int[]{1,1,2,1,1,1,2,2,1,1}, - new int[]{1,1,1,1,2,1,2,2,1,1}, - new int[]{2,2,1,1,1,1,1,1,2,1},//30 - new int[]{1,2,2,1,1,1,1,1,2,1}, - new int[]{2,2,2,1,1,1,1,1,1,1}, - new int[]{1,2,1,1,2,1,1,1,2,1}, - new int[]{2,2,1,1,2,1,1,1,1,1}, - new int[]{1,2,2,1,2,1,1,1,1,1},//35 - new int[]{1,2,1,1,1,1,2,1,2,1}, - new int[]{2,2,1,1,1,1,2,1,1,1}, - new int[]{1,2,2,1,1,1,2,1,1,1}, - new int[]{1,2,1,2,1,2,1,1,1,1}, - new int[]{1,2,1,2,1,1,1,2,1,1},//40 - new int[]{1,2,1,1,1,2,1,2,1,1}, - new int[]{1,1,1,2,1,2,1,2,1,1}, - new int[]{1,2,1,1,2,1,2,1,1,1} - }; - - /// - /// Checks if value using extended alphabet and changes value accordingly. - /// - string DecodeExtended(string value) - { - StringBuilder result = new StringBuilder(); - char newChar; - - for (int i = 0; i < value.Length; i++) - { - char c = value[i]; - if (i < value.Length - 1 && "$/+%".IndexOf(c) != -1) - { - char next = value[i + 1]; - switch (c) - { - case '$': - if (next >= 'A' && next <= 'Z') - { - newChar = (char)(next - 64); - break; - } - - newChar = next; - result.Append(c); - break; - - case '/': - if (next >= 'A' && next <= 'O') - { - newChar = (char)(next - 32); - break; - } - else if (next == 'Z') - { - newChar = ':'; - break; - } - - newChar = next; - result.Append(c); - break; - - case '+': - if (next >= 'A' && next <= 'Z') - { - newChar = (char)(next + 32); - break; - } - - newChar = next; - result.Append(c); - break; - - case '%': - if (next >= 'A' && next <= 'E') - { - newChar = (char) (next - 38); - break; - } - else if (next >= 'F' && next <= 'J') - { - newChar = (char) (next - 11); - break; - } - else if (next >= 'K' && next <= 'O') - { - newChar = (char) (next + 16); - break; - } - else if (next >= 'P' && next <= 'T') - { - newChar = (char) (next + 43); - break; - } - else if (next == 'U') - { - newChar = (char) 0; - break; - } - else if (next == 'V') - { - newChar = '@'; - break; - } - else if (next == 'W') - { - newChar = '`'; - break; - } - else if (next >= 'X' && next <= 'Z') - { - newChar = (char) 127; // DEL - break; - } - - newChar = next; - result.Append(c); - break; - - default: - return null; - } - - result.Append(newChar); - i++; - } - else - { - result.Append(c); - } - } - - return result.ToString(); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/PZNReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/PZNReader.cs deleted file mode 100644 index e02e95d6..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/PZNReader.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace BarcodeReader.Core.Code39 -{ - internal class PZNReader : Code39Reader - { - public PZNReader() - { - CheckSumMode = Mode.CheckMod11ChecksumPZN8; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.PZN; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/UPUReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/UPUReader.cs deleted file mode 100644 index 6ee1e677..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39/UPUReader.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace BarcodeReader.Core.Code39 -{ - internal class UPUReader : Code39Reader - { - public UPUReader() - { - CheckSumMode = Mode.CheckMod11ChecksumUPU; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.UPU; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39_LinearReader/Code39LinearReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39_LinearReader/Code39LinearReader.cs deleted file mode 100644 index 72d0dead..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39_LinearReader/Code39LinearReader.cs +++ /dev/null @@ -1,403 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using SkiaSharp; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Code39 -{ - /// - /// Code39 reader. - /// - [Obsolete("This class does not pass all tests.")] -#if CORE_DEV - public -#else - internal -#endif - class Code39LinearReader : LinearReader - { - /// - /// do decode extended Code 39 symbols or not (disabled by default) - /// - protected bool DoDecodeExtended = false; - /// - /// Check sum mode - /// - public Mode CheckSumMode = Mode.None; - - private bool HighPrecision = false;//!!!!! - - public enum Mode { None, CheckMod43Checksum, CheckMod11ChecksumPZN8, CheckMod11ChecksumISBN10, CheckMod11ChecksumUPU }; - - - private static int[][] _startStopPatterns = { new int[] { 1, 2, 1, 1, 2, 1, 2, 1, 1 } }; - - string _alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*"; - - int[][] _patterns = new int[][] { - new int[]{1,1,1,2,2,1,2,1,1,1},//0 - new int[]{2,1,1,2,1,1,1,1,2,1}, - new int[]{1,1,2,2,1,1,1,1,2,1}, - new int[]{2,1,2,2,1,1,1,1,1,1}, - new int[]{1,1,1,2,2,1,1,1,2,1}, - new int[]{2,1,1,2,2,1,1,1,1,1},//5 - new int[]{1,1,2,2,2,1,1,1,1,1}, - new int[]{1,1,1,2,1,1,2,1,2,1}, - new int[]{2,1,1,2,1,1,2,1,1,1}, - new int[]{1,1,2,2,1,1,2,1,1,1}, - new int[]{2,1,1,1,1,2,1,1,2,1},//10 - new int[]{1,1,2,1,1,2,1,1,2,1}, - new int[]{2,1,2,1,1,2,1,1,1,1}, - new int[]{1,1,1,1,2,2,1,1,2,1}, - new int[]{2,1,1,1,2,2,1,1,1,1}, - new int[]{1,1,2,1,2,2,1,1,1,1},//15 - new int[]{1,1,1,1,1,2,2,1,2,1}, - new int[]{2,1,1,1,1,2,2,1,1,1}, - new int[]{1,1,2,1,1,2,2,1,1,1}, - new int[]{1,1,1,1,2,2,2,1,1,1}, - new int[]{2,1,1,1,1,1,1,2,2,1},//20 - new int[]{1,1,2,1,1,1,1,2,2,1}, - new int[]{2,1,2,1,1,1,1,2,1,1}, - new int[]{1,1,1,1,2,1,1,2,2,1}, - new int[]{2,1,1,1,2,1,1,2,1,1}, - new int[]{1,1,2,1,2,1,1,2,1,1},//25 - new int[]{1,1,1,1,1,1,2,2,2,1}, - new int[]{2,1,1,1,1,1,2,2,1,1}, - new int[]{1,1,2,1,1,1,2,2,1,1}, - new int[]{1,1,1,1,2,1,2,2,1,1}, - new int[]{2,2,1,1,1,1,1,1,2,1},//30 - new int[]{1,2,2,1,1,1,1,1,2,1}, - new int[]{2,2,2,1,1,1,1,1,1,1}, - new int[]{1,2,1,1,2,1,1,1,2,1}, - new int[]{2,2,1,1,2,1,1,1,1,1}, - new int[]{1,2,2,1,2,1,1,1,1,1},//35 - new int[]{1,2,1,1,1,1,2,1,2,1}, - new int[]{2,2,1,1,1,1,2,1,1,1}, - new int[]{1,2,2,1,1,1,2,1,1,1}, - new int[]{1,2,1,2,1,2,1,1,1,1}, - new int[]{1,2,1,2,1,1,1,2,1,1},//40 - new int[]{1,2,1,1,1,2,1,2,1,1}, - new int[]{1,1,1,2,1,2,1,2,1,1}, - new int[]{1,2,1,1,2,1,2,1,1,1} - }; - - private BarSymbolReaderLooseProjection LooseProjectionReader; - - public Code39LinearReader() - { - //MaxPatternSymbolDifference = 0.9f;//1.2f;// 0.83f; - //MaxPatternAverageSymbolDifference = 0.6f;//0.8//2 /0.55f; - //MaxReadError = 1f;//1.3f; - - MaxLeftAndRightModulesDifference = 1.9f; - UseE = true; - } - - internal override void GetParams(out int[] startPattern, out int[] stopPattern, out int minModulesPerBarcode, out int maxModulesPerBarcode) - { - //startPattern = stopPattern = _startStopPatterns[0]; - - //!!!!!!! - startPattern = new int[] {1, 2, 1, 1}; - stopPattern = new int[] {2, 1, 1}; - - minModulesPerBarcode = 26; - maxModulesPerBarcode = int.MaxValue; - - // - //MidPoints = new float[] {0.5f, 0.3f, 0.7f}; - MinConfidence = 0.7f; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Code39; - } - - private BarSymbolReaderNaive ReaderNaive; - - internal override bool ReadSymbols(BarCodeRegion region, Pattern startPattern, Pattern stopPattern, MyPoint from, MyPoint to, float module, bool firstPass) - { - if (Reader == null) - { - Reader = new BarSymbolReader(Scan, 10, 13, false, true, 1, _patterns, -1, UseE, null); - LooseProjectionReader = new BarSymbolReaderLooseProjection(Scan, 10, 13, true, _patterns, 43, false, UseE); - // setting min match difference - LooseProjectionReader.MinMatchDifference = 1E-7f; - - ReaderNaive = new BarSymbolReaderNaive(10, 13, _patterns); - } - - float error, maxError, confidence; - - int[] row = ReadSymbols(startPattern, stopPattern, from, to, module, out error, out maxError, out confidence); - - if (error < MaxReadError) - { - if (confidence >= MinConfidence) - if (confidence > region.Confidence) - if (Decode(region, row)) //try to decode - { - region.Confidence = confidence; - return true; - } - } - - //try naive reader - row = ReaderNaive.Read(Scan.GetPixels(from, to), out error, out maxError, out confidence); - //var row = ReaderNaive.Read(Scan.GetPixels(from, to, 1.4f), out error, out maxError, out confidence); - //row = ReaderNaive.Read(Scan.GetPixels3Lines(from, to, 1.4f, 3.5f), out error, out maxError, out confidence); - - if (confidence >= MinConfidence && maxError < 0.5f) - if (confidence > region.Confidence) - if (Decode(region, row)) //try to decode - { - region.Confidence = confidence; - return true; - } - - //try LooseProjection reader - if (firstPass && HighPrecision) - if (row != null && row.Length > 3) - { - //expected number of codewords - int nCodewords = (int)Math.Round((region.B - region.A).Length / module / 13f, MidpointRounding.AwayFromZero); - - float[] widths = new float[] { 0.5f, 0.9f }; - LooseProjectionReader.scan = Scan; - - foreach (float w in widths) - { - row = LooseProjectionReader.Read(region, module, w); - if (row != null && row.Length > 2 && row[0] == 43 && row[row.Length - 1] == 43 && Around(row.Length, nCodewords)) - if (Decode(region, row)) - { - return true; - } - } - } - - return false; - } - - bool Around(int a, int b) - { - return a < b && b - a < 2 || a >= b && a - b < 2; - } - - internal override bool Decode(BarCodeRegion r, int[] row) - { - if (row == null) - return false; - - if (row[0] != 43 || row[row.Length - 1] != 43) - return false; - - return Decode(r, row, 1, row != null ? row.Length - 1 : 0); - } - - internal virtual bool Decode(BarCodeRegion r, int[] row, int iStart, int iEnd) - { - string pre = ""; - if (row != null && row.Length > 2) - { - if (CheckSumMode == Mode.CheckMod43Checksum) - { - iEnd--; - int sum = 0; - for (int i = iStart; i < iEnd; i++) sum += row[i]; - sum = sum % 43; - if (sum != row[iEnd]) return false; - } - else if (CheckSumMode == Mode.CheckMod11ChecksumPZN8) - { - iEnd--; - int sum = 0; - int[] weights = new int[] { 1, 2, 3, 4, 5, 6, 7 }; - bool isPzn7 = false; - // check if it is PZN8 - if (iEnd - iStart != 8) - { - // check if it is PZN7 - isPzn7 = iEnd - iStart == 7; - - // otherwise exit - if (!isPzn7) - return false; - } - - if (row[iStart] != 36) return false; //PZN starts with - - iStart++; - - if (isPzn7) - // count weights as 2,3,4,5,6.. - for (int i = iStart; i < iEnd; i++) sum += row[i] * weights[i - iStart + 1]; - else - // PZN8, count weights as 1,2,3,4,5,6 - for (int i = iStart; i < iEnd; i++) sum += row[i] * weights[i - iStart]; - int checkdigit = sum % 11; - if (checkdigit == 10) checkdigit = 0; - if (checkdigit != row[iEnd]) return false; - pre = "";// "PZN-"; - } - else if (CheckSumMode == Mode.CheckMod11ChecksumISBN10) - { - iEnd--; - int sum = 0; - int[] weights = new int[] { 10, 9, 8, 7, 6, 5, 4, 3, 2 }; - if (iEnd - iStart != 9) return false; - for (int i = iStart; i < iEnd; i++) sum += row[i] * weights[i - iStart]; - int checkdigit = 11 - sum % 11; - if (checkdigit == 10) checkdigit = 0; - else if (checkdigit == 11) checkdigit = 5; - if (checkdigit != row[iEnd]) return false; - } - else if (CheckSumMode == Mode.CheckMod11ChecksumUPU) - { - iEnd--; - int sum = 0; - int[] weights = new int[] { 8, 6, 4, 2, 3, 5, 9, 7 }; - if (iEnd - iStart != 8) return false; //UPU has just 8 digits + Checksum - for (int i = iStart; i < iEnd; i++) sum += row[i] * weights[i - iStart]; - int checkdigit = 11 - sum % 11; - if (checkdigit == 10) checkdigit = 0; - else if (checkdigit == 11) checkdigit = 5; - if (checkdigit != row[iEnd]) return false; - } - - var s = new StringBuilder(); - for (int i = iStart; i < iEnd; i++) - if (row[i] >= 0 && row[i] < _patterns.Length) - s.Append(_alphabet.Substring(row[i], 1)); - else - return false; - - var res = s.ToString(); - if (DoDecodeExtended) - res = DecodeExtended(res); - - r.Data = new ABarCodeData[] { new StringBarCodeData(res) }; - return true; - } - return false; - } - - /// - /// Checks if value using extended alphabet and changes value accordingly. - /// - string DecodeExtended(string value) - { - StringBuilder result = new StringBuilder(); - char newChar; - - for (int i = 0; i < value.Length; i++) - { - char c = value[i]; - if (i < value.Length - 1 && "$/+%".IndexOf(c) != -1) - { - char next = value[i + 1]; - switch (c) - { - case '$': - if (next >= 'A' && next <= 'Z') - { - newChar = (char)(next - 64); - break; - } - - newChar = next; - result.Append(c); - break; - - case '/': - if (next >= 'A' && next <= 'O') - { - newChar = (char)(next - 32); - break; - } - else if (next == 'Z') - { - newChar = ':'; - break; - } - - newChar = next; - result.Append(c); - break; - - case '+': - if (next >= 'A' && next <= 'Z') - { - newChar = (char)(next + 32); - break; - } - - newChar = next; - result.Append(c); - break; - - case '%': - if (next >= 'A' && next <= 'E') - { - newChar = (char) (next - 38); - break; - } - else if (next >= 'F' && next <= 'J') - { - newChar = (char) (next - 11); - break; - } - else if (next >= 'K' && next <= 'O') - { - newChar = (char) (next + 16); - break; - } - else if (next >= 'P' && next <= 'T') - { - newChar = (char) (next + 43); - break; - } - else if (next == 'U') - { - newChar = (char) 0; - break; - } - else if (next == 'V') - { - newChar = '@'; - break; - } - else if (next == 'W') - { - newChar = '`'; - break; - } - else if (next >= 'X' && next <= 'Z') - { - newChar = (char) 127; // DEL - break; - } - - newChar = next; - result.Append(c); - break; - - default: - return null; - } - - result.Append(newChar); - i++; - } - else - { - result.Append(c); - } - } - - return result.ToString(); - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39_LinearReader/Code39LinearReader_Ext.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39_LinearReader/Code39LinearReader_Ext.cs deleted file mode 100644 index c975de07..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code39_LinearReader/Code39LinearReader_Ext.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using SkiaSharp; -using System.Text; -using BarcodeReader.Core.Common; - - -namespace BarcodeReader.Core.Code39 -{ - /// - /// Code39 reader with extended character set. - /// - [Obsolete("This class does not pass all tests.")] -#if CORE_DEV - public -#else - internal -#endif - class Code39ExtendedLinearReader : Code39LinearReader - { - public Code39ExtendedLinearReader() - { - DoDecodeExtended = true; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Code39Ext; - } - } - - /// - /// Code39 reader with CheckMod43Checksum. - /// - [Obsolete("This class does not pass all tests.")] -#if CORE_DEV - public -#else - internal -#endif - class Code39Mod43LinearReader : Code39LinearReader - { - public Code39Mod43LinearReader() - { - CheckSumMode = Mode.CheckMod43Checksum; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Code39Mod43; - } - } - - /// - /// Code39 reader with CheckMod43Checksuma and extended charset. - /// - [Obsolete("This class does not pass all tests.")] -#if CORE_DEV - public -#else - internal -#endif - class Code39Mod43ExtLinearReader : Code39LinearReader - { - public Code39Mod43ExtLinearReader() - { - DoDecodeExtended = true; - CheckSumMode = Mode.CheckMod43Checksum; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Code39Mod43Ext; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code93/Code93Reader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Code93/Code93Reader.cs deleted file mode 100644 index 9f63fc99..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Code93/Code93Reader.cs +++ /dev/null @@ -1,189 +0,0 @@ -using System; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Code93 -{ - //Main class to scan an image looking for code93 barcodes. It inherits from Reader2DNoise, - //responsible to find start and stop patterns in the image, and calling FindBarcode methods - //to read the barcode in between. -#if CORE_DEV - public -#else - internal -#endif - class Code93Reader : Reader2DNoise - { - public Code93Reader() - { - //Define start and stop patterns. Code93 has one unique start/stop pattern. - startPatterns = stopPatterns = _startStopPatterns; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Code93; - } - - //Method to scan a single row - protected override BarCodeRegion FindBarcode(ImageScaner scan, int startPattern, MyPoint start, MyPoint end, FoundPattern foundPattern) - { - BarSymbolReader reader = new BarSymbolReader(scan, 6, 9, true, true, foundPattern.moduleLength, _patterns, -1, false, null); - float error, maxError, confidence; - int[] row = reader.Read(start, end, out error, out maxError, out confidence); - if (error < 1f) - { - BarCodeRegion r = new BarCodeRegion(start, reader.Current, reader.Current + new MyPoint(0, 5), start + new MyPoint(0, 5)); - r.Confidence = confidence; - if (Decode(r, row)) return r; - } - return null; - } - - //Method to scan a region. It starts with a scanning the line in the middle of the region - //to be able to read quickly good quality barcodes. If it fails, then uses the slower - //loose projection reader. - int[] nBars = new int[] {6, 6}; - int[] nModules = new int[] { 9, 9 }; - int[] tIndexs = new int[] { 0, 1 }; - int[][][] tables = new int[][][] { _startStopPatterns, _patterns }; - override protected BarCodeRegion FindBarcode(ImageScaner scan, int startPattern, BarCodeRegion r, FoundPattern foundPattern) - { - BarSymbolReader reader = new BarSymbolReader(scan, nBars, nModules, tIndexs, true, true, foundPattern.moduleLength, tables, false, null); - MyPoint a = r.A * 0.5f + r.D * 0.5f; - MyPoint b = r.B * 0.5f + r.C * 0.5f; - float error, maxError, confidence; - int[] row = reader.Read(a, b, out error, out maxError, out confidence); r.Confidence = confidence; - if (Decode(r, row)) - return r; //if sampling is good enough, then use simple row scanning - - BarSymbolReaderLooseProjection reader2 = new BarSymbolReaderLooseProjection(scan, 6, 9, true, _patterns, Stop, true, useE); - float[] widths = new float[] { 0.5f, 0.9f }; - foreach (float w in widths) - { - row = reader2.Read(r, foundPattern.moduleLength, w); - if (Decode(r, row)) - return r; - } - - return null; - } - - //Method to decode bytecodes into the final string. - protected bool Decode(BarCodeRegion r, int[] rawData) - { - if (rawData == null) return false; - - int end = rawData.Length-1; while (end>0 && rawData[end] != Stop) end--; - int[] row = new int[end+1]; - Array.Copy(rawData, row, end+1); - - if (row != null && row.Length > 4) //start + 2 checksum + stop - if (checkChecksum(row)) - { - string s = "";; - for (int i = 1; i < row.Length - 3; i++) - if (row[i] >= 0 && row[i] < _patterns.Length) - if (row[i] < 43) s += _alphabet.Substring(row[i], 1); - else if (i < row.Length - 4) switch (row[i]) - { - case 43: s += "["+tableA[row[i + 1] - 10]+"]"; i++; break; - case 44: s += tableB[row[i + 1] - 10]; i++; break; - case 45: s += tableC[row[i + 1] - 10]; i++; break; - case 46: s += (char)(row[i + 1] - 10 + (int)'a'); i++; break; - default: s += "(" + _alphabet.Substring(row[i], 1) + ")"; break; - } - else s += "(" + _alphabet.Substring(row[i], 1) + ")"; - else s += "[???]"; - r.Data = new ABarCodeData[] { new StringBarCodeData(s) }; - return true; - } - return false; - } - - private static bool checkChecksum(int[] row) - { - return checkChecksum(row, row.Length - 3, 20) && checkChecksum(row, row.Length - 2, 15); - } - - private static bool checkChecksum(int[] row, int maxPos, int maxW) - { - int w = 1; - int sum = 0; - int i=maxPos-1; - while (i>=0) - { - sum += w * row[i--]; - if (w++ == maxW) w = 1; - } - return row[maxPos] == sum % 47; - } - - static readonly int[][] _startStopPatterns = new int[][] { new int[] { 1, 1, 1, 1, 4, 1 } }; - - static readonly string _alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%$%/+*********"; - static readonly string[] tableA = new string[] { "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM", "SUB" }; - static readonly string[] tableB = new string[] { "[ESC]", "[FS]", "[GS]", "[RS]", "[US]", ";", "<", "=", ">", "?", "[", "\\", "]", "^", "_", "{", "[???]", "}", "~", "[DEL]", "[NUL]", "@", "`", "[DEL]", "[DEL]", "[DEL]" }; - static readonly string[] tableC = new string[] { "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "[???]", "[???]", "/", "[???]", "[???]", "[???]", "[???]", "[???]", "[???]", "[???]", "[???]", "[???]", "[???]", ":" }; - - static readonly int Stop = 47; - - static readonly int[][] _patterns = new int[][] { - new int[]{1,3,1,1,1,2}, //0 - new int[]{1,1,1,2,1,3}, - new int[]{1,1,1,3,1,2}, - new int[]{1,1,1,4,1,1}, - new int[]{1,2,1,1,1,3}, - new int[]{1,2,1,2,1,2}, - new int[]{1,2,1,3,1,1}, - new int[]{1,1,1,1,1,4}, - new int[]{1,3,1,2,1,1}, - new int[]{1,4,1,1,1,1}, - new int[]{2,1,1,1,1,3},//10 - new int[]{2,1,1,2,1,2}, - new int[]{2,1,1,3,1,1}, - new int[]{2,2,1,1,1,2}, - new int[]{2,2,1,2,1,1}, - new int[]{2,3,1,1,1,1}, - new int[]{1,1,2,1,1,3}, - new int[]{1,1,2,2,1,2}, - new int[]{1,1,2,3,1,1}, - new int[]{1,2,2,1,1,2}, - new int[]{1,3,2,1,1,1},//20 - new int[]{1,1,1,1,2,3}, - new int[]{1,1,1,2,2,2}, - new int[]{1,1,1,3,2,1}, - new int[]{1,2,1,1,2,2}, - new int[]{1,3,1,1,2,1}, - new int[]{2,1,2,1,1,2}, - new int[]{2,1,2,2,1,1}, - new int[]{2,1,1,1,2,2}, - new int[]{2,1,1,2,2,1}, - new int[]{2,2,1,1,2,1},//30 - new int[]{2,2,2,1,1,1}, - new int[]{1,1,2,1,2,2}, - new int[]{1,1,2,2,2,1}, - new int[]{1,2,2,1,2,1}, - new int[]{1,2,3,1,1,1}, - new int[]{1,2,1,1,3,1}, - new int[]{3,1,1,1,1,2}, - new int[]{3,1,1,2,1,1}, - new int[]{3,2,1,1,1,1}, - new int[]{1,1,2,1,3,1},//40 - new int[]{1,1,3,1,2,1}, - new int[]{2,1,1,1,3,1}, - new int[]{1,2,1,2,2,1}, - new int[]{3,1,2,1,1,1}, - new int[]{3,1,1,1,2,1}, - new int[]{1,2,2,2,1,1},//46 - new int[]{1,1,1,1,4,1},//47 start, stop - new int[]{1,1,4,1,1,1},//48 reversed start - new int[]{4,1,1,1,1,1},// unused - new int[]{1,1,1,1,3,2},//50 - new int[]{1,1,1,2,3,1}, - new int[]{1,1,3,1,1,2}, - new int[]{1,1,3,2,1,1}, - new int[]{2,1,3,1,1,1}, - new int[]{2,1,2,1,2,1} - }; - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/AdaptiveGrid.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/AdaptiveGrid.cs deleted file mode 100644 index 214d5ece..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/AdaptiveGrid.cs +++ /dev/null @@ -1,110 +0,0 @@ -namespace BarcodeReader.Core.Common -{ - // Stores and manages a region to sample, defined by its 4 vertex + module width and height at each vertex - internal class AdaptiveGrid - { - int rows, cols; - MyPointF lu, ld, ru, rd; - float[] topSampling, bottomSampling, leftSampling, rightSampling; - float moduleLength; - - public AdaptiveGrid(int cols, int rows, - MyPointF lu, MyPointF ld, MyPointF ru, MyPointF rd, - float wLu, float wLd, float wRu, float wRd, - float hLu, float hLd, float hRu, float hRd) - { - this.rows = rows; this.cols = cols; - this.lu = lu; this.ld = ld; this.ru = ru; this.rd = rd; - - this.topSampling = adaptiveSampling(wLu, wRu, (lu - ru).Length, cols); - this.bottomSampling = adaptiveSampling(wLd, wRd, (ld - rd).Length, cols); - - this.leftSampling = adaptiveSampling(hLu, hLd, (lu - ld).Length, rows); - this.rightSampling = adaptiveSampling(hRu, hRd, (ru - rd).Length, rows); - - this.moduleLength = (lu - ru).Length / (float)cols; - } - - //x=0..cols-1 y=0..rows-1 - public MyPointF GetSamplePoint(int x, int y) - { - //interpolate between CornerUp and RightUp vectors - float tX = topSampling[x]; - MyPointF top = lu * (1.0F - tX) + ru * tX; - - float bX = bottomSampling[x]; - MyPointF bottom = ld * (1.0F - bX) + rd * bX; - - float lY = leftSampling[y]; - MyPointF left = lu * (1.0F - lY) + ld * lY; - - float rY = rightSampling[y]; - MyPointF right = ru * (1.0F - rY) + rd * rY; - - //intersection of two lines (top->bottom) and (left->right) - float det = (top.X - bottom.X) * (left.Y - right.Y) - (top.Y - bottom.Y) * (left.X - right.X); - float px = ((top.X * bottom.Y - top.Y * bottom.X) * (left.X - right.X) - (top.X - bottom.X) * (left.X * right.Y - left.Y * right.X)) / det; - float py = ((top.X * bottom.Y - top.Y * bottom.X) * (left.Y - right.Y) - (top.Y - bottom.Y) * (left.X * right.Y - left.Y * right.X)) / det; - return new MyPointF(px, py); - } - - - //extract points of the grid - public void ExtractPoints(ImageScaner scan, bool[][] result, MyPoint resultIndex) - { - for (int y = 0; y < this.rows; ++y) - { - for (int x = 0; x < this.cols; ++x) - { - MyPointF point = GetSamplePoint(x, y); - bool isBlack = scan.isBlackSample(point,moduleLength); - result[resultIndex.Y + y][resultIndex.X + x] = isBlack; - } - } - } - - - //w1 is the starting width - //w2 is the end width - //W is the total width - //n is the number of samples - //returns the module widths of samples - float[] adaptiveSampling(float w1, float w2, float W, int n) - { - float N = (float)n; - float M11 = 0f, M12 = 0f, M13 = 1f; - float M21 = (N + 1f) * (N + 1f), M22 = N + 1f, M23 = 1f; - float M31 = N * (N + 1f) * (2f * N + 1f) / 6f, M32 = N * (N + 1f) / 2f, M33 = N; - - //determinants - float det = M11 * M22 * M33 + M12 * M23 * M31 + M13 * M21 * M32 - M31 * M22 * M13 - M32 * M23 * M11 - M33 * M21 * M12; - if (Calc.Around(det, 0f, 0.0001f)) return null; - - float D11 = M22 * M33 - M32 * M23, D12 = M21 * M33 - M31 * M23, D13 = M21 * M32 - M31 * M22; - float D21 = M12 * M33 - M32 * M13, D22 = M11 * M33 - M31 * M13, D23 = M11 * M32 - M31 * M12; - float D31 = M12 * M23 - M22 * M13, D32 = M11 * M23 - M21 * M13, D33 = M11 * M22 - M21 * M12; - - //inverse matrix - float I11 = D11 / det, I12 = -D21 / det, I13 = D31 / det; - float I21 = -D12 / det, I22 = D22 / det, I23 = -D32 / det; - float I31 = D13 / det, I32 = -D23 / det, I33 = D33 / det; - - //solution - float a = I11 * w1 + I12 * w2 + I13 * W; - float b = I21 * w1 + I22 * w2 + I23 * W; - float c = I31 * w1 + I32 * w2 + I33 * W; - - //calculate sampling positions relative to the starting point - float[] r = new float[n]; - float acum=0f; - for (int i = 1; i <= n; i++) - { - float f = (float)i; - float w = a * f * f + b * f + c; //width of the current module - r[i-1]=(acum+w/2f)/W; //sample in the middle of the module (normalized to 0..1) - acum+=w; - } - return r; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BarSymbolReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BarSymbolReader.cs deleted file mode 100644 index 210ec003..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BarSymbolReader.cs +++ /dev/null @@ -1,469 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace BarcodeReader.Core.Common -{ - internal interface IBestMatch { int bestMatch(float[] e, LinkedList symbols);} - - //Class to read barcode symbols of nBars bars and match against a set of available symbols - //Optionally (set in parameter useE), converts the widths table to E table (sum of two consecutive bars, white and - //black or black and white). - internal class BarSymbolReader - { - internal IImageScaner scan; - Bresenham br; - MyPoint lastBlack; //last black pixel read. To determine the bounding box of the barcode if no stop pattern. - internal float moduleLength; - int[] nBars, nModules; - bool startsWithBlack, currentIsBlack, useLastBar; - bool checkEnd; - int[] tableIndexs; - int[][][] ttSymbols, ttE; - int stopSymbol; - bool useE; - IBestMatch iBestMatch; - bool oneTable, differentSymbolLengths; - - - //nBars - count of black and white "bars" per each symbol - //nModules - length in modules of each symbol - - //symbols with chars with the same number of nBars, and of the same length (nModules) - public BarSymbolReader(IImageScaner scan, int nBars, int nModules, bool useLastBar, bool startsWithBlack, float moduleLength, int[][] symbols, int stopSymbol, bool useE, IBestMatch iBestMatch) - { - this.oneTable = true; - this.differentSymbolLengths = false; - Initialize(scan, new int[] { nBars }, new int[] { nModules }, new int[] { 0 }, useLastBar, startsWithBlack, moduleLength, new int[][][] { symbols }, stopSymbol, useE, iBestMatch); - } - - //symbols with chars with different number of nBars, and different lengths (nModules) at a predefined position - public BarSymbolReader(IImageScaner scan, int[] nBars, int[] nModules, int[] tableIndexs, bool useLastBar, bool startsWithBlack, float moduleLength, int[][][] symbols, bool useE, IBestMatch iBestMatch) - { - this.oneTable = false; - this.differentSymbolLengths = false; - Initialize(scan, nBars, nModules, tableIndexs, useLastBar, startsWithBlack, moduleLength, symbols, -1, useE, iBestMatch); - } - - //symbols with chars with different number of nBars, and different lengths (nModules) at a predefined position - public BarSymbolReader(IImageScaner scan, int[] nBars, int[] nModules, int[] tableIndexs, bool useLastBar, bool startsWithBlack, float moduleLength, int[][][] symbols, bool useE, IBestMatch iBestMatch, bool oneTable) - { - this.oneTable = oneTable; - this.differentSymbolLengths = false; - Initialize(scan, nBars, nModules, tableIndexs, useLastBar, startsWithBlack, moduleLength, symbols, -1, useE, iBestMatch); - } - - //symbols with chars with the same number of nBars, and different lengths (nModules) at any position -->pharma - public BarSymbolReader(IImageScaner scan, int nBars, int[] nModules, bool useLastBar, bool startsWithBlack, float moduleLength, int[][] symbols, bool useE, IBestMatch iBestMatch) - { - this.oneTable = true; - this.differentSymbolLengths = true; - Initialize(scan, new int[] { nBars }, nModules, new int[] { 0 }, useLastBar, startsWithBlack, moduleLength, new int[][][] { symbols }, -1, useE, iBestMatch); - } - - - private void Initialize(IImageScaner scan, int[] nBars, int[] nModules, int[] tableIndexs, bool useLastBar, bool startsWithBlack, float moduleLength, int[][][] ttSymbols, int stopSymbol, bool useE, IBestMatch iBestMatch) - { - this.scan = scan; - this.nBars = nBars; - this.nModules = nModules; - this.useLastBar = useLastBar; - this.startsWithBlack = startsWithBlack; - this.moduleLength = moduleLength; - this.tableIndexs = tableIndexs; - this.ttSymbols = ttSymbols; - this.stopSymbol = stopSymbol; - this.useE = useE; - this.iBestMatch = iBestMatch; - - //initialize E table - if (useE) - { - this.ttE = new int[ttSymbols.Length][][]; - for (int i = 0; i < ttSymbols.Length; i++) //for each table of symbols - { - int[][] ts = ttSymbols[i]; - int[][] tE = ttE[i] = new int[ts.Length][]; - for (int j = 0; j < ts.Length; j++) //for each symbol in the table - { - int[] s = ts[j]; - int[] e = tE[j] = new int[s.Length]; - for (int k = 0; k < s.Length; k++) - e[k] = s[k] + s[(k + 1) % s.Length]; - } - } - } - } - - - //main method to read a barcode row between point a and b. Reads groups of nBars bars, and - //match them against the pattern table. - public int[] Read(MyPointF a, MyPointF b, int n, out float error, out float maxError, out float confidence) { return Read(new Bresenham(a, b), true, n, out error, out maxError, out confidence); } - public int[] Read(MyPointF a, MyPointF b, out float error, out float maxError, out float confidence) { return Read(new Bresenham(a, b), true, -1, out error, out maxError, out confidence); } - public int[] Read(MyPointF a, MyVectorF vd, out float error, out float maxError, out float confidence, out MyPoint last) { int[] row = Read(new Bresenham(a, vd), false, 1, out error, out maxError, out confidence); last = lastBlack; return row; } - - public int[] Read(IImageScaner scanner, float module, MyPointF a, MyPointF b, out float error, out float maxError, out float confidence) - { - this.scan = scanner; - this.moduleLength = module; - return Read(new Bresenham(a, b), true, -1, out error, out maxError, out confidence); - } - - public int[] Read(Bresenham br, bool checkEnd, int nCodewords, out float error, out float maxError, out float confidence) - { - float e; - this.checkEnd = checkEnd; - error = 0f; maxError = 0f; - this.br = br; - LinkedList symbols = new LinkedList(); - - confidence = 0f; - if (br.Steps > (scan.Width*6)/5) return null; - - //move to first black - if (checkEnd) while (!br.End() && scan.isBlack(br.Current) != startsWithBlack) br.Next(); - else while (scan.In(br.Current) && scan.isBlack(br.Current) != startsWithBlack) br.Next(); - currentIsBlack = startsWithBlack; - - int s = -1; - bool stop = false; - float symbolConfidence; - lastModuleLength = moduleLength; - if (checkEnd) - while (scan.In(br.Current) && !br.End() && (stopSymbol == -1 || s != stopSymbol) && nCodewords != 0) - { - s = ReadSymbol(symbols, out e, out symbolConfidence, out stop); - confidence += symbolConfidence; - if (!br.End()) { error += e; if (e > maxError) maxError = e; } - symbols.AddLast(s); - nCodewords--; - } - else - while (scan.In(br.Current) && (oneTable || symbols.Count < nBars.Length) && (stopSymbol == -1 || s != stopSymbol)) - { s = ReadSymbol(symbols, out e, out symbolConfidence, out stop); if (stopSymbol == -1 && s == -1) break; confidence += symbolConfidence; error += e; if (e > maxError) maxError = e; symbols.AddLast(s); if (stop) break; } - error /= (float)symbols.Count; //mean error - confidence /= (float)symbols.Count; //mean confidence - int[] ss = new int[symbols.Count]; - symbols.CopyTo(ss, 0); - return ss; - } - - /// - /// min match difference - /// - internal float MinMatchDifference = float.MinValue; - - float lastModuleLength; - int ReadSymbol(LinkedList symbols, out float bestDist, out float symbolConfidence, out bool stop) - { - stop = false; - bestDist = 1000; symbolConfidence = 0f; - float[] e = NextSymbolE(symbols.Count); //next block of nBars bars widths - if (e == null) return -1; - - int[][][] table = (useE ? ttE : ttSymbols); - int symbol = -1; - int i = (symbols.Count < tableIndexs.Length ? symbols.Count : tableIndexs.Length - 1); - if (tableIndexs[i] == -1) - { - if (iBestMatch != null) symbol = iBestMatch.bestMatch(e, symbols); - else symbol=0; //there is no table of symbols, so return a symbolic 0. - } - else symbol = BestMatch(MinMatchDifference, e, table[tableIndexs[i]], useE, useLastBar, out bestDist, out symbolConfidence, out stop); - - return symbol; - } - - public static int BestMatch(float minDifference, float[] e, int[][] table, bool useE, bool useLastBar, out float bestDist, out float confidence, out bool stop) - { - float distToNext = 1000f; - bestDist = 1000f; - confidence = 0f; - stop = false; - if (e != null) - { - //black and white compensation - float sumBlack = 0f, sumWhite = 0f, lastBlack = 0f, lastWhite = 0f; - if (!useE) - { - for (int i = 0; i < e.Length; i++) - if (i % 2 == 0) sumBlack += e[i]; else sumWhite += e[i]; - if (e.Length % 2 == 0) lastWhite = e[e.Length - 1]; - else lastBlack = e[e.Length - 1]; - } - - int bestMatch = -1; - float prevBestDist = 1000f; - for (int index = 0; index < table.Length; index++) if (table[index].Length == e.Length) - { - bool thisStop = false; - int[] E = table[index]; - - int N = e.Length; - if (!useLastBar && !Calc.Around(e[N - 1], E[N - 1], 1f)) - { - N--; //don't use the last white module to find the closest symbol - thisStop = true; //stop at the next symbol - } - - //black and white compensation - float blackCompensationMIN = 0f; - if (!useE) - { - float sumEBlack = 0f, sumEWhite=0f; - for (int i = 0; i < N; i++) - if (i % 2 == 0) sumEBlack += E[i]; else sumEWhite += E[i]; - blackCompensationMIN = (sumWhite - sumEWhite - sumBlack + sumEBlack -(thisStop?lastWhite+lastBlack:0f)) / (float)N; - } - - //if (Math.Abs(blackCompensationMIN) < 1) - { - float dist = 0f; - for (int i = 0; i < N; i++) - { - float d = e[i] - (float)E[i] + (i % 2 == 0 ? blackCompensationMIN : -blackCompensationMIN);; - d = d*d; - - if (d < 4f) dist += d; - else { dist = -1f; break; } - } - if (dist != -1f) - { - dist += blackCompensationMIN*blackCompensationMIN; - if (dist < bestDist) - { - prevBestDist = bestDist; - bestDist = dist; - bestMatch = index; - stop = thisStop; - } - else if (dist < prevBestDist) prevBestDist = dist; - } - } - } - distToNext = prevBestDist - bestDist; - confidence = 1f - bestDist / prevBestDist; - if (distToNext < minDifference) return -1; //if the difference to the previous best match is small, reject - return bestMatch; - } - return -1; - } - - public static int BestMatch2(float[] w, float[] e, int[][] tableW, int[][] tableE, bool useLastBar, out float bestDist, out float confidence) - { - bestDist = 1000f; - confidence = 0f; - if (e != null) - { - int bestMatch = -1; - for (int index = 0; index < tableW.Length; index++) if (tableW[index].Length == e.Length) - { - int[] W = tableW[index]; - int[] E = tableE[index]; - float dist = 0f; - int last = useLastBar ? E.Length : E.Length - 1; - for (int i = 0; i < last; i++) - { - float dW = w[i] - (float)W[i]; dW *= dW; - float dE = e[i] - (float)E[i]; dE *= dE; - float d = useLastBar ? dE + dW : dW; - - if (useLastBar && (dE > 3f || dE > 3f)) { dist = -1f; break; } - - if (d < 8f) dist += d; - else { dist = -1f; break; } - } - if (dist != -1f && dist < bestDist) { bestDist = dist; bestMatch = index; } - } - confidence = (bestDist < (float)e.Length ? 1f - bestDist / (float)e.Length : 0f); - return bestMatch; - } - return -1; - } - - public static int BestMatchInc(float[] e, int[][] table, bool useLastBar, out float bestDist, out float confidence) - { - bestDist = 1000f; - confidence = 0f; - if (e != null) - { - int bestMatch = -1; - for (int index = 0; index < table.Length; index++) if (table[index].Length == e.Length) - { - int[] E = table[index]; - float dist = 0f; - int last = useLastBar ? E.Length : E.Length - 1; - for (int i = 0; i < last; i++) - { - float d = (e[(i+1)%last] - e[i]) - (float)(E[(i+1)%last] - E[i]); - d = d * d; - - if (d < 8f) dist += d; - else { dist = -1f; break; } - } - if (dist != -1f && dist < bestDist) { bestDist = dist; bestMatch = index; } - } - confidence = (bestDist < (float)e.Length ? 1f - bestDist / (float)e.Length : 0f); - return bestMatch; - } - return -1; - } - - - - public float[] NextSymbolE(int nSymbol) - { - if (checkEnd && br.End()) return null; - int N = nBars.Length; - int cnBars = nBars[nSymbol < N ? nSymbol : N - 1]; - int cnModules = nModules[nSymbol < N ? nSymbol : N - 1]; - - int[] barLenghts = new int[cnBars]; - int nTransitions = 0; - int n = 0; - int sumBarLengths = 0; - Bresenham br2 = new Bresenham(br); //copy bresenham state - while (((checkEnd && !br.End()) || !checkEnd) && scan.In(br.Current) && nTransitions < cnBars) - { - //scans next pixel, only 1 pixel is samplingWidth=1 - float gray = 0f; - bool isBlack = scan.isBlack(br.Current); - gray = isBlack ? 1f : 0f; - - if (isBlack == currentIsBlack) n++; - else if (n > (int)(lastModuleLength / 3F)) //a valid module - { - barLenghts[nTransitions++] = n; - sumBarLengths += n; - n = 1; - if (currentIsBlack) lastBlack = br.Current; - currentIsBlack = !currentIsBlack; - } - else //consider as noise (join to the previous module) - { - if (nTransitions > 0) - { - nTransitions--; - n = barLenghts[nTransitions] + n + 1; - sumBarLengths -= barLenghts[nTransitions]; - } - else n++; - currentIsBlack = !currentIsBlack; - } - if (nTransitions == cnBars && !differentSymbolLengths) - { - //check if there are noisy bars - float currentModuleLength = (float)sumBarLengths / (float)cnModules; - if (!Calc.Around(currentModuleLength / lastModuleLength, 1.0f, 0.3f)) //0.14f - return null; - } - - if (nTransitions < cnBars) br.Next(); - } - - if (nTransitions < cnBars) - { - barLenghts[nTransitions++] = n; - sumBarLengths += n; - if (currentIsBlack) lastBlack = br.Current; - currentIsBlack = !currentIsBlack; - } - - if (nTransitions >= cnBars-1) - { - nTransitions = cnBars; - //check symbol length, that can vary incrementally - float currentModuleLength = differentSymbolLengths?lastModuleLength:(float)sumBarLengths / (float)cnModules; - lastModuleLength = currentModuleLength; - float[] E = new float[nTransitions]; - for (int i = 0; i < nTransitions; i++) - { - float e = 0f; - if (useE) - { - e = (float)(barLenghts[i] + barLenghts[(i + 1) % nTransitions]) / currentModuleLength; - if (e < 2f) e = 2f; - } - else - { - e = (float)(barLenghts[i]) / currentModuleLength; - //if (e < 1f) e = 1f; - } - E[i] = e; - } - return E; - } - return null; - } - - public MyPoint Current { get { return br.Current; } } - } - - - - - - - //Auxiliar class to store the number of occurrencies of a codeword. The most common one will - //be used to decode the barcode. - internal class SymbolSample : IComparable - { - public int symbol; - public int N; //number of repetitions - public SymbolSample(int symbol) { this.symbol = symbol; this.N = 1; } - public int CompareTo(object o) { SymbolSample s = (SymbolSample)o; return s.N - N; } - } - - //Auxiliar class to store samples of a codeword of the barcode. Each row in the barcode will be scanned - //several times, so each codeword is samples also several times. All samples values are stored, and the - //most common one is used to decode the barcode. - internal class SymbolSamples - { - public ArrayList samples = new ArrayList(); - public void add(int symbol) - { - foreach (SymbolSample s in samples) if (s.symbol == symbol) { s.N++; return; } - samples.Add(new SymbolSample(symbol)); - } - public void sort() { samples.Sort(); } - } - - //data structure to store all redundant samples of each row. - internal class RowSamples - { - Dictionary symbols = new Dictionary(); - public void Clear() { symbols.Clear(); } - - public void AddRow(int nRow, int[] codewords) - { - if (!symbols.ContainsKey(nRow)) - { - SymbolSamples[] s = symbols[nRow] = new SymbolSamples[codewords.Length]; - for (int i = 0; i < codewords.Length; i++) s[i] = new SymbolSamples(); - } - SymbolSamples[] samples = symbols[nRow]; - for (int i = 0; i < codewords.Length; i++) - if (codewords[i]!=-1 && i0? ((SymbolSample)row[i].samples[0]).symbol : -1); - } - return codewords; - } - - public int[] GetRowIndexs() { - int[] indexs=new int[symbols.Count]; - symbols.Keys.CopyTo(indexs, 0); - return indexs; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BarSymbolReaderLooseProjection.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BarSymbolReaderLooseProjection.cs deleted file mode 100644 index 5a299e84..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BarSymbolReaderLooseProjection.cs +++ /dev/null @@ -1,296 +0,0 @@ -using System.Collections.Generic; - -namespace BarcodeReader.Core.Common -{ - //Magic class to scan a barcode region area, not just a line. It projects the area to a - //single line, thus removing most of noise. The projection is loose, it means that it is - //not a strict mathematical projection, allowing +-1px margin. For each vertical segment - //of the area, the algorithm calculates the % of white pixels and the % of black pixels - //and the higher of them is the used value. - //The resulting projection is stored in proj[], a float array. This array is read - //using 2 different threshods, to be able to read high noise images. The first threshold - //works well for normal noise images. The second detect noisy narrow bars. - internal class BarSymbolReaderLooseProjection - { - internal IImageScaner scan; - BarCodeRegion r; - float moduleLength, lastModuleLength; //to detect missing bars, or noisy bars - int[] tNBars, tNModules; - bool startsWithBlack, useLastBar; - int[][][] ttSymbols, ttE; - int stopSymbol; - MyPointF ULsampling, URsampling; - float length; - float[] thresholds=new float[]{ 0.2f, 0.05f}; - bool useE = false; - - public BarSymbolReaderLooseProjection(IImageScaner scan, int nBars, int nModules, - bool startsWithBlack, int[][] tSymbols, int stopSymbol, bool useLastBar, bool useE) - { - initialize(scan, new int[] { nBars }, new int[] { nModules }, startsWithBlack, new int[][][] { tSymbols }, stopSymbol, useLastBar, useE); - } - - public BarSymbolReaderLooseProjection(IImageScaner scan, int[] nBars, int[] nModules, - bool startsWithBlack, int[][][] tSymbols, int stopSymbol, bool useLastBar, bool useE) - { - initialize(scan, nBars, nModules, startsWithBlack, tSymbols, stopSymbol, useLastBar, useE); - } - - private void initialize(IImageScaner scan, int[] nBars, int[] nModules, - bool startsWithBlack, int[][][] tSymbols, int stopSymbol, bool useLastBar, bool useE) - { - this.scan = scan; - this.tNBars = nBars; - this.tNModules = nModules; - this.startsWithBlack = startsWithBlack; - this.ttSymbols = tSymbols; - this.stopSymbol = stopSymbol; - this.useLastBar = useLastBar; - this.useE = useE; - - //initialize E table - if (useE) - { - this.ttE = new int[ttSymbols.Length][][]; - for (int i = 0; i < ttSymbols.Length; i++) //for each table of symbols - { - int[][] ts = ttSymbols[i]; - int[][] tE = ttE[i] = new int[ts.Length][]; - for (int j = 0; j < ts.Length; j++) //for each symbol in the table - { - int[] s = ts[j]; - int[] e = tE[j] = new int[s.Length]; - for (int k = 0; k < s.Length; k++) - e[k] = s[k] + s[(k + 1) % s.Length]; - } - } - } - } - - //Scan an area of the region defined by its height. - public int[] Read(BarCodeRegion r, float moduleLength, float height) - { - this.r = r; - this.lastModuleLength=this.moduleLength = moduleLength; - - //calculate region to sample - float cc = 0.5f + height / 2f; - ULsampling = r.A * cc + r.D * (1f - cc); - URsampling = r.B * cc + r.C * (1f - cc); - MyPointF a = r.A * (1f - cc) + r.D * cc; - MyPointF b = r.B * (1f - cc) + r.C * cc; - Bresenham br=new Bresenham(a,b); - if (br.Steps > (scan.Width*6)/5) return null; - - this.length = (float)(br.Steps); - - float[] proj=new float[br.Steps+1]; - int nProj = 0; - - //move to the first black - while (!br.End() && scan.isBlack(br.Current) != startsWithBlack) - { - proj[nProj++] = 1f; //assume skiped pixels are black - br.Next(); - } - - //project all pixels - while (!br.End() && scan.In(br.Current)) - { - float tpo = (float)br.Steps / (float)(length); - MyPoint p = ULsampling * tpo + URsampling * (1f - tpo); - - proj[nProj++] = SampleSegmentLoosely(new Bresenham(br.Current, p)); - br.Next(); - } - - int[] symbols = ReadSymbols(proj, true); //try removing noise pixels - if (symbols == null) symbols = ReadSymbols(proj, false); //try without removing noise pixels - return symbols; - } - - int[] ReadSymbols(float[] proj, bool removeNoise) - { - //read barcode using different thresholds - float confidence = 0f, symbolConfidence; - LinkedList symbols = new LinkedList(); - bool error=false; - foreach (float t in thresholds) - { - int pos = 0; - this.lastModuleLength = this.moduleLength; - symbols.Clear(); - confidence = 0; - error = false; - while (pos < proj.Length && !error) - { - int s = ReadSymbol(proj, ref pos, symbols, t, removeNoise, out symbolConfidence); - if (s == -1) error = true; //if a bytecode is not read, skip to the next threshold - else - { - symbols.AddLast(s); - confidence += symbolConfidence; - if (symbols.Count > 1 && s == stopSymbol) break; - } - } - if (!error) break;//if the whole barcode is read, stop - } - if (error) return null; - r.Confidence = confidence / (float)symbols.Count; - int[] ss = new int[symbols.Count]; - symbols.CopyTo(ss, 0); - return ss; - } - - /// - /// min matching difference - /// - internal float MinMatchDifference = float.MinValue; - - //reads a bytecode starting at pos. - int ReadSymbol(float[] proj, ref int pos, LinkedList symbols, float threshold, bool removeNoise, out float symbolConfidence) - { - symbolConfidence = 0f; - int startPos = pos; - int tableIndex = symbols.Count >= tNBars.Length ? tNBars.Length - 1 : symbols.Count; - int nBars = tNBars[tableIndex]; - int nModules = tNModules[tableIndex]; - int[] w = new int[nBars]; - float[] grays = new float[nBars]; - bool currentIsBlack = true; - bool moduleIsConfident = false; - int n = 0, nb = 0, length = 0; - float gray = 0f; - float peakThreshold = threshold * 0.7f; - while (pos < proj.Length && nb < nBars) - { - //Decide if the current pixel is white or black based on the threshold - float pr = proj[pos]; - bool isBlack = pr > 0.5f; - bool pixelIsConfident = false; - if (pr > 1f - threshold) { isBlack = true; pixelIsConfident = true; }//it is clearly black - else if (pr < threshold) { isBlack = false; pixelIsConfident = true; }//it is clearly white - else if (pos > 0 && pos < proj.Length - 1) //we are not sure - { - //detect peaks to decide if it is black or white. If it is not a peak, use 0.5. - float dl = pr - proj[pos - 1]; - float dr = pr - proj[pos + 1]; - if (dl > 0 && dr > 0) //max - { - int l = pos - 1; while (l > 0 && proj[l] > proj[l - 1]) l--; - int r = pos + 1; while (r < proj.Length - 1 && proj[r] > proj[r + 1]) r++; - dl = pr - proj[l]; - dr = pr - proj[r]; - if (dl > peakThreshold && dr > peakThreshold) isBlack = true; - } - else if (dl < 0 && dr < 0) //min - { - int l = pos - 1; while (l > 0 && proj[l] < proj[l - 1]) l--; - int r = pos + 1; while (r < proj.Length - 1 && proj[r] < proj[r + 1]) r++; - dl = pr - proj[l]; - dr = pr - proj[r]; - if (dl < -peakThreshold && dr < -peakThreshold) isBlack = false; - } - } - - float confidence = isBlack ? pr : 1f - pr; - if (isBlack == currentIsBlack) //another pixel of the same color - { - n++; gray += confidence; - length++; - pos++; - moduleIsConfident = moduleIsConfident || pixelIsConfident; - } - else if (nb == 0 || !removeNoise || removeNoise && n > (int)(lastModuleLength / 3.8F)) //a valid module - { - grays[nb] = gray; - w[nb++] = n; - currentIsBlack = !currentIsBlack; - n = 1; gray = confidence; - moduleIsConfident = pixelIsConfident; - - if (nb < nBars) { length++; pos++; } - } - else //consider as noise (join to the previous module) - { - nb--; - n = w[nb] + n + 1; gray = grays[nb] + gray + confidence; - length++; - pos++; - currentIsBlack = !currentIsBlack; - } - } - if (n > 0 && nb < nBars) - { - grays[nb] = gray; - w[nb++] = n; //add the last bar - } - - if (nb < nBars) return -1; - - //check disalignment - float currentModuleLength = useLastBar ? (float)length / (float)nModules : (float)(length - w[nBars - 1]) / (float)(nModules - 1); - if (!Calc.Around(currentModuleLength / lastModuleLength, 1f, 0.15f)) - return -1; - lastModuleLength = currentModuleLength; - - float[] E = new float[nBars]; - float[] W = new float[nBars]; - for (int i = 0; i < w.Length; i++) W[i] = (float)grays[i] / currentModuleLength; - for (int i = 0; i < w.Length; i++) E[i] = W[i] + W[(i + 1) % nBars]; - - float bestDist; - bool stop; - int symbol = useE ? - BarSymbolReader.BestMatch2(W, E, ttSymbols[tableIndex], ttE[tableIndex], useLastBar, out bestDist, out symbolConfidence) - : - BarSymbolReader.BestMatch(MinMatchDifference, E, (useE ? ttE[tableIndex] : ttSymbols[tableIndex]), useE, useLastBar, out bestDist, out symbolConfidence, out stop); - - return symbol; - } - - //Key method to project the region. Traces a bresenham segment, and count - //the number of white and black pixels in that segment, allowing a margin of - //+-1px (with penalisation). - const float K = 0.3f; - float SampleSegmentLoosely(Bresenham b) - { - float nBlack = 0f, nWhite = 0f; - int length = b.Steps+1; - int blackX = 0, whiteX = 0; - while (!b.End() && scan.In(b.Current)) - { - MyPoint p=b.Current; - if (scan.isBlack(p)) { blackX = 0; nBlack+=1f; } - else - { - p.X++; - if (blackX != -1 && scan.isBlack(p)) { blackX = 1; nBlack += K; } - else - { - p.X -= 2; - if (blackX != 1 && scan.isBlack(p)) { blackX = -1; nBlack += K; } else blackX=0; - } - } - p = b.Current; - if (!scan.isBlack(p)) { whiteX = 0; nWhite += 1f; } - else - { - p.X++; - if (whiteX != -1 && !scan.isBlack(p)) { whiteX = 1; nWhite += K; } - else - { - p.X -= 2; - if (whiteX != 1 && !scan.isBlack(p)) { whiteX = -1; nWhite += K; } else whiteX=0; - } - } - - b.Next(); - } - if (moduleLength>2f) - return (nBlack > nWhite ? nBlack / (float)length : 1f - nWhite / (float)length); - else - return (nBlack > nWhite ? nBlack / (nBlack+nWhite) : 1f - nWhite / (nBlack+nWhite)); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BarcodeData.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BarcodeData.cs deleted file mode 100644 index adb87815..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BarcodeData.cs +++ /dev/null @@ -1,359 +0,0 @@ -using System.Text; - -namespace BarcodeReader.Core.Common -{ -#if CORE_DEV - public -#else - internal -#endif - enum BarCodeDataType - { - String, Numeric, Base256, Control, Base900 - } - - // Stores any type of data extracted from the barcode -#if CORE_DEV - public -#else - internal -#endif - abstract class ABarCodeData - { - public abstract BarCodeDataType Type { get; } - public new abstract string ToString(); - - public virtual bool IsSimilar(ABarCodeData obj) - { - return obj.Type == this.Type; - } - - protected bool RawDataEquals(int[] left, int[] right) - { - if (left == right) - return true; - - if (left == null || right == null) - return false; - - if (left.Length != right.Length) - return false; - - for (int i = 0; i < left.Length; i++) - { - if (left[i] != right[i]) - return false; - } - - return true; - } - - protected bool RawDataEquals(byte[] left, byte[] right) - { - if (left == right) - return true; - - if (left == null || right == null) - return false; - - if (left.Length != right.Length) - return false; - - for (int i = 0; i < left.Length; i++) - { - if (left[i] != right[i]) - return false; - } - - return true; - } - } - - internal class StringBarCodeData : ABarCodeData - { - public override BarCodeDataType Type - { - get - { - return BarCodeDataType.String; - } - } - - public string Value; - - public StringBarCodeData(string value) - { - Value = value; - } - - public StringBarCodeData(byte[] value) - { - char[] userData = new char[value.Length]; - for (int i = 0; i < userData.Length; ++i) - { - userData[i] = (char) value[i]; - } - Value = new string(userData); - } - - public override string ToString() - { - return Value; - } - - public override bool IsSimilar(ABarCodeData obj) - { - if (obj.GetType() != this.GetType() || !base.IsSimilar(obj)) - return false; - - return this.Value == (obj as StringBarCodeData).Value; - } - } - - internal class NumericBarCodeData : ABarCodeData - { - public override BarCodeDataType Type - { - get - { - return BarCodeDataType.Numeric; - } - } - - public byte[] Value; - - public NumericBarCodeData(byte[] value) - { - Value = value; - } - - public override string ToString() - { - StringBuilder builder = new StringBuilder(); - foreach (byte b in Value) - { - builder.Append(b.ToString()); - } - - return builder.ToString(); - } - - public override bool IsSimilar(ABarCodeData obj) - { - if (obj.GetType() != this.GetType() || !base.IsSimilar(obj)) - return false; - - return RawDataEquals(this.Value, (obj as NumericBarCodeData).Value); - } - - } - - internal class Base900BarCodeData : ABarCodeData - { - public override BarCodeDataType Type - { - get - { - return BarCodeDataType.Base900; - } - } - - public int[] Value; - - public Base900BarCodeData(int[] value) - { - Value = value; - } - - public override string ToString() - { - StringBuilder builder = new StringBuilder(); - foreach (int b in Value) - { - builder.Append("\\"+b.ToString().PadLeft(3,'0')); - } - - return builder.ToString(); - } - - - public override bool IsSimilar(ABarCodeData obj) - { - if (obj.GetType() != this.GetType() || !base.IsSimilar(obj)) - return false; - - return RawDataEquals(this.Value, (obj as Base900BarCodeData).Value); - } - } - - internal class Base256BarCodeData : ABarCodeData - { - public override BarCodeDataType Type - { - get - { - return BarCodeDataType.Base256; - } - } - - public byte[] Value; - public System.Text.Encoding encoding = null; - - public Base256BarCodeData(byte[] value) - { - Value = value; - } - - public Base256BarCodeData(byte[] value, System.Text.Encoding encoding) - { - Value = value; - this.encoding = encoding; - } - - // This conversion routine cannot return the byte[] as string - string is UTF-8, and it would represent - // non-ASCII chars in a weird way - public override string ToString() - { - Encoding e = encoding ?? System.Text.Encoding.GetEncoding(28591); //default is iso-8859-1 (8-bit encoding) - - return encoding.GetString(Value); - } - - public override bool IsSimilar(ABarCodeData obj) - { - if (obj.GetType() != this.GetType() || !base.IsSimilar(obj)) - return false; - - return RawDataEquals(this.Value, (obj as Base256BarCodeData).Value); - } - } - - #region Datamatrix control characters - // These control chars represent various functions within the barcode itself. It is the user's task to - // interpret them, as their meaning is at higher level. - - class FNC1Symbol : ABarCodeData - { - public override BarCodeDataType Type - { - get - { - return BarCodeDataType.Control; - } - } - - public override string ToString() - { - return ""; - } - } - - class Macro05Symbol : ABarCodeData - { - public override BarCodeDataType Type - { - get - { - return BarCodeDataType.Control; - } - } - - public override string ToString() - { - return ""; - } - } - - class Macro06Symbol : ABarCodeData - { - public override BarCodeDataType Type - { - get - { - return BarCodeDataType.Control; - } - } - - public override string ToString() - { - return ""; - } - } - - // this tells that the barcode is used to program the reader - class ReaderProgramSymbol : ABarCodeData - { - public override BarCodeDataType Type - { - get - { - return BarCodeDataType.Control; - } - } - - public override string ToString() - { - return ""; - } - } - - // describes the ECI in effect for the rest of the data - class ECISwitchSymbol : ABarCodeData - { - public readonly int ECINumber; - - public override BarCodeDataType Type - { - get - { - return BarCodeDataType.Control; - } - } - - public ECISwitchSymbol(int eci) - { - ECINumber = eci; - } - - public override string ToString() - { - return ""; - } - } - - // This tells that the current barcode is the Position-th of BarcodeCount Datamatrices. - // The file Id is a user data, can be anything from 1-254 - internal class StructuredAppendSymbol : ABarCodeData - { - public readonly int Position; - - public readonly int BarcodeCount; - - public readonly int FileId1; - - public readonly int FileId2; - - public override BarCodeDataType Type - { - get - { - return BarCodeDataType.Control; - } - } - - public StructuredAppendSymbol(int position, int fileId1, int fileId2) - { - BarcodeCount = position%16; - Position = position >> 4; - - FileId1 = fileId1; - FileId2 = fileId2; - } - - public override string ToString() - { - return "<" + Position + " of " + BarcodeCount + ", file1: " + FileId1 + ", file2: " + FileId2 + ">"; - } - } - #endregion -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BigInt.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BigInt.cs deleted file mode 100644 index ce28671b..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BigInt.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System; -using System.Collections; - -namespace BarcodeReader.Core.Common -{ - internal class BigInt - { - bool[] bits; - - public BigInt(int N, int n) - { - this.bits = new bool[N]; - int i = 0; - while (n > 0) - { - this.bits[i++]=(n % 2) == 1; - n /= 2; - } - } - - public BigInt(int N, Decimal n) - { - this.bits = new bool[N]; - int i = 0; - while (n > 0) - { - this.bits[i++] = (n % 2) == 1; - n = Decimal.Floor(n/2); - } - } - - public BigInt(int n) - { - ArrayList b = new ArrayList(); - while (n > 0) - { - b.Add((n % 2)==1); - n /= 2; - } - this.bits = new bool[b.Count]; - for (int i = 0; i < b.Count; i++) this.bits[i] = (bool)b[i]; - } - - public BigInt(Decimal n) - { - ArrayList b = new ArrayList(); - while (n > 0) - { - b.Add((n % 2) == 1); - n = Decimal.Floor(n/2); - } - this.bits = new bool[b.Count]; - for (int i = 0; i < b.Count; i++) this.bits[i] = (bool)b[i]; - } - - public BigInt(BigInt n) - { - this.bits = new bool[n.bits.Length]; - Array.Copy(n.bits, this.bits, this.bits.Length); - } - - public int ArrayLength { get { return this.bits.Length; } } - - public int Length { get { for (int i = this.bits.Length - 1; i >= 0; i--) if (this.bits[i]) return i+1; return 0; } } - - private bool addBit(int nBit, bool n, bool carry) - { - int c = (this.bits[nBit] ? 1 : 0) + (n ? 1 : 0) + (carry?1:0); - switch (c) - { - case 0: this.bits[nBit] = false; carry = false; break; - case 1: this.bits[nBit] = true; carry = false; break; - case 2: this.bits[nBit] = false; carry = true; break; - case 3: this.bits[nBit] = true; carry = true; break; - } - return carry; - } - - private bool subsBit(int nBit, bool n, bool carry) - { - int c = (this.bits[nBit] ? 1 : 0) - (n ? 1 : 0) - (carry ? 1 : 0); - switch (c) - { - case -2: this.bits[nBit] = false; carry = true; break; - case -1: this.bits[nBit] = true; carry = true; break; - case 0: this.bits[nBit] = false; carry = false; break; - case 1: this.bits[nBit] = true; carry = false; break; - } - return carry; - } - - public static BigInt operator +(BigInt a, BigInt n) - { - BigInt r = new BigInt(a); - bool carry = false; - int i = 0; - while (i < n.ArrayLength) carry=r.addBit(i, n.bits[i++], carry); - while (carry && i < r.ArrayLength) carry = r.addBit(i++, false, carry); - - return r; - } - - public static BigInt operator -(BigInt a, BigInt n) - { - BigInt r = new BigInt(a); - bool carry = false; - int i = 0; - while (i < n.ArrayLength) carry = r.subsBit(i, n.bits[i++], carry); - while (carry && i < r.ArrayLength) carry = r.subsBit(i++, false, carry); - return r; - } - - public BigInt ShiftLeft(int n) - { - BigInt r = new BigInt(this.ArrayLength, 0); - for (int i = 0; i + n < this.ArrayLength; i++) r.bits[i + n] = this.bits[i]; - return r; - } - - public static BigInt operator *(BigInt a, BigInt n) - { - BigInt r = new BigInt(a.bits.Length,0); - for (int i = 0; i < n.Length; i++) if (n.bits[i]) r = r + a.ShiftLeft(i); - return r; - } - - public void Divide(BigInt n, out BigInt q, out BigInt r) - { - int thisN = this.Length; - int lengthN = n.Length; - Decimal iN = n.ToDecimal(); - BigInt current=new BigInt(lengthN+1,0); - if (lengthN < thisN) for (int i = 0; i < lengthN; i++) current.bits[i] = this.bits[thisN - lengthN + i]; - else for (int i = 0; i < thisN; i++) current.bits[i] = this.bits[i]; - q = new BigInt(this.ArrayLength, 0); - int pos = thisN - lengthN; - do { - Decimal iCurrent = current.ToDecimal(); - Decimal iQ = Decimal.Floor(iCurrent / iN); - Decimal iR = iCurrent % iN; - - //update quotient - q=q.ShiftLeft(1); q.bits[0] = (iQ == 1); - - //update remainder - r = new BigInt(lengthN + 1, iR); - if (pos--> 0) { current = r.ShiftLeft(1); current.bits[0] = this.bits[pos]; } - } while (pos>=0); - } - - - public override string ToString() - { - string s = ""; - for (int i = 0; i < this.bits.Length; i++) s = (this.bits[i] ? "1" : "0") + s; - return s; - } - - public int ToInt() - { - int r = 0, pow = 1; - for (int i = 0; i < this.bits.Length; i++, pow*=2) r += (this.bits[i] ? pow : 0); - return r; - } - - public Decimal ToDecimal() - { - Decimal r = 0, pow = 1; - for (int i = 0; i < this.Length; i++, pow *= 2) r += (this.bits[i] ? pow : 0); - return r; - } - - public int GetByte(int n) - { - int i0=8*n; - int b=0; - for (int i = 0; i < 8; i++) - b+= this.bits[i0 + i] ? 1 << i : 0; - return b; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BoundingBox.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BoundingBox.cs deleted file mode 100644 index 03d6cf50..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/BoundingBox.cs +++ /dev/null @@ -1,44 +0,0 @@ -using SkiaSharp; -using System.Drawing; - -namespace BarcodeReader.Core.Common -{ -#if CORE_DEV - public -#else - internal -#endif - class BoundingBox - { - int minX, minY, maxX, maxY; - FoundBarcode f; - public BoundingBox(FoundBarcode f) - { - this.f = f; - this.minX = this.minY = int.MaxValue; - this.maxX = this.maxY = 0; - foreach (SKPointI p in f.Polygon) - { - if (p.X < this.minX) this.minX = p.X; - if (p.X > this.maxX) this.maxX = p.X; - if (p.Y < this.minY) this.minY = p.Y; - if (p.Y > this.maxY) this.maxY = p.Y; - } - } - - public bool contains(SKPointI p) - { - return p.X >= this.minX && p.X <= this.maxX && p.Y >= this.minY && p.Y <= this.maxY; - } - - public bool contains(BoundingBox b) - { - int n = 0; - foreach (SKPointI p in b.f.Polygon) - if (this.contains(p)) n++; - return (n > 0); - } - - public FoundBarcode FoundBarcode { get { return f; } } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Bresenham.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Bresenham.cs deleted file mode 100644 index d831212a..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Bresenham.cs +++ /dev/null @@ -1,149 +0,0 @@ -using SkiaSharp; -using System.Drawing; - -namespace BarcodeReader.Core.Common -{ - //Calculates the x,y coordinates of pixels of an edge a-b - internal class Bresenham - { - MyPoint a, b; - MyVectorF vd; - int steps; - MyPoint p, k; - bool swapXY; - float error, incError; - - public Bresenham(Bresenham l) - { - this.a = l.a; - this.b = l.b; - this.vd = l.vd; - this.steps = l.steps; - this.p = l.p; - this.k = l.k; - this.swapXY = l.swapXY; - this.error = l.error; - this.incError = l.incError; - } - - //Horizontal line, left to right - public Bresenham(int xIn, int xEnd, int y) - { - a = new MyPoint(xIn, y); - b = new MyPoint(xEnd, y); - vd = ((MyVectorF)(b - a)).Normalized; - p = a; - k = new MyPoint(1, 0); - incError = error = 0F; - swapXY = false; - steps = xEnd - xIn; - } - - public Bresenham(MyPoint a, MyPoint b) - { - this.a = a; this.b = b; - this.vd = ((MyVectorF)(b - a)).Normalized; - int incX = b.X - a.X; - int incY = b.Y - a.Y; - if (incX >= 0) - if (incY > 0) - if (incX >= incY) { k = new SKPointI(1, 1); swapXY = false; steps = incX; } - else { k = new SKPointI(1, 1); swapXY = true; steps = incY; } - else - if (incX >= -incY) { k = new SKPointI(1, -1); swapXY = false; steps = incX; } - else { k = new SKPointI(1, -1); swapXY = true; steps = -incY; } - else - if (incY > 0) - if (-incX >= incY) { k = new SKPointI(-1, 1); swapXY = false; steps = -incX; } - else { k = new SKPointI(-1, 1); swapXY = true; steps = incY; } - else - if (-incX >= -incY) { k = new SKPointI(-1, -1); swapXY = false; steps = -incX; } - else { k = new SKPointI(-1, -1); swapXY = true; steps = -incY; } - p = a; - error = 0.0F; - incError = (swapXY ? (float)incX * k.X / incY * k.Y : (float)incY * k.Y / incX * k.X); - } - - public Bresenham(MyPointF p0, MyVectorF vd) - { - this.vd = vd; - if (vd.X >= 0F) - if (vd.Y > 0F) - if (vd.X >= vd.Y) { k = new SKPointI(1, 1); swapXY = false; } - else { k = new SKPointI(1, 1); swapXY = true; } - else - if (vd.X >= -vd.Y) { k = new SKPointI(1, -1); swapXY = false; } - else { k = new SKPointI(1, -1); swapXY = true;} - else - if (vd.Y > 0F) - if (-vd.X >= vd.Y) { k = new SKPointI(-1, 1); swapXY = false; } - else { k = new SKPointI(-1, 1); swapXY = true; } - else - if (-vd.X >= -vd.Y) { k = new SKPointI(-1, -1); swapXY = false; } - else { k = new SKPointI(-1, -1); swapXY = true; } - p = p0.Truncate(); - if (!swapXY) { - float alfa=((float)p.X - p0.X + 0.5F) / vd.X; - error=(p0.Y + vd.Y*alfa -((float)p.Y+0.5F) )*k.Y; - incError=vd.Y*k.Y/vd.X*k.X; - } else { - float alfa=((float)p.Y - p0.Y + 0.5F) / vd.Y; - error=(p0.X + vd.X*alfa - ((float)p.X+0.5F))*k.X; - incError=vd.X*k.X/vd.Y*k.Y; - } - } - - public MyPointF CurrentF - { - get - { - if (!swapXY) return (MyPointF)p + new MyVectorF(0.5F, 0.5F + error * k.Y); - else return (MyPointF)p + new MyVectorF(0.5F + error * k.X, 0.5F); - } - } - - public void MoveTo(MyPoint q) - { - p = q; - int incX = b.X - p.X; if (incX<0) incX=-incX; - int incY = b.Y - p.Y; if (incY<0) incY=-incY; - steps=incX>incY?incX:incY; - error = 0.0F; - } - - public bool End() { return steps < 0; } - public void Next() - { - if (swapXY) p.Y += k.Y; - else p.X += k.X; - error += incError; - if (error > 0.5F) - { - error -= 1.0F; - if (swapXY) p.X += k.X; - else p.Y += k.Y; - } - steps--; - } - - public void Previous() - { - if (swapXY) p.Y -= k.Y; - else p.X -= k.X; - error -= incError; - if (error < -0.5F) - { - error += 1.0F; - if (swapXY) p.X -= k.X; - else p.Y -= k.Y; - } - steps++; - } - - public MyPoint Current { get { return p; } } - public int Steps { get { return steps; } } - public MyVectorF Vd { get { return vd; } } - public float Length { get { return (a - b).Length; } } - public float CurrentLength { get { return (p - a).Length; } } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Calc.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Calc.cs deleted file mode 100644 index 873384e8..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Calc.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace BarcodeReader.Core.Common -{ - class Calc - { - public static float Modul(MyPoint a, MyPoint b) { return Modul(new MyPoint(a.X - b.X, a.Y - b.Y)); } - public static float Modul(MyPoint a) { return (float) Math.Sqrt(a.X * a.X + a.Y * a.Y); } - - //middle point between a bresenham line from a to b. When the middle is 0.5 adjust to a. - //IMPORTANT: Middle(a,b) can be != Middle(b,a), if division by 2 is not exact. In such - //a case, adjust the middle closer to a. - public static MyPoint Middle(MyPoint a, MyPoint b) { return new MyPoint(Middle(a.X, b.X), Middle(a.Y, b.Y)); } - public static int Middle(int a, int b) - { - int c = a + b; - if (c % 2 == 0) return c / 2; - return c / 2 + (b > a ? 0 : 1); - } - public static bool Around(float f, float v) { return Around(f, v, 0.15F); } - public static bool Around(float f, float v, float epsilon) { return f > v - epsilon && f < v + epsilon; } - public static bool Around(int f, int v, int epsilon) { return f > v - epsilon && f < v + epsilon; } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/DebugHelper.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/DebugHelper.cs deleted file mode 100644 index e7a8534c..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/DebugHelper.cs +++ /dev/null @@ -1,322 +0,0 @@ -using SkiaSharp; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Net; -using System.Text; - -namespace BarcodeReader.Core.Common -{ -#if CORE_DEV - public -#else - internal -#endif - static class DebugHelper - { - private static string saveFolder = "c:\\temp"; - private static int saveCounter = 0; - private static SKBitmap OriginalImage; - private static SKBitmap NoScaledImage; - public static SKBitmap ScaledImage; - private static int scale = 1; - private static SKCanvas gr; - private static SKCanvas gr_noScaled; - private static SKPaint fillPaint = new SKPaint { IsAntialias = false, Style = SKPaintStyle.Fill }; - private static SKPaint strokePaint = new SKPaint { IsAntialias = false, Style = SKPaintStyle.Stroke, StrokeWidth = 1 }; - - private static List Items = new List(); - - public static List GetItems() - { - var items = Items; - Items = new List(); - return items; - } - - public static void AddDebugItem(string name, IEnumerable list, params MyPointF[] polygon) - { - var item = new ListItem() { Name = name, Polygon = polygon }; - foreach (var v in list) - item.List.Add(v); - - Items.Add(item); - } - - -#if DEBUG - - public static void InitImage(SKBitmap bmp) - { - OriginalImage = bmp.Copy(); - ReInitImage(); - saveCounter = 0; - - //remove all images - if (Directory.Exists(saveFolder)) - foreach (var file in Directory.GetFiles(saveFolder, "*.png")) - File.Delete(file); - } - - public static void InitImageIfNotInit(SKImage img) - { - if (OriginalImage != null) - return; - - OriginalImage = new SKBitmap(img.Info); - ReInitImage(); - saveCounter = 0; - - //remove all images - if (Directory.Exists(saveFolder)) - foreach (var file in Directory.GetFiles(saveFolder, "*.png")) - File.Delete(file); - } - - public static void ReInitImage() - { - if (OriginalImage == null) - return; - - // Dispose old images - ScaledImage?.Dispose(); - NoScaledImage?.Dispose(); - - int sw = OriginalImage.Width * scale; - int sh = OriginalImage.Height * scale; - - // Scaled image - ScaledImage = new SKBitmap(sw, sh); - using (var canvas = new SKCanvas(ScaledImage)) - { - var destRect = new SKRect(0, 0, sw, sh); - var paint = new SKPaint - { - FilterQuality = SKFilterQuality.None, // NearestNeighbor equivalent - IsAntialias = false - }; - - canvas.DrawBitmap(OriginalImage, destRect, paint); - - // Overlay semi-transparent white - var overlayPaint = new SKPaint - { - Color = new SKColor(255, 255, 255, 180), - Style = SKPaintStyle.Fill - }; - canvas.DrawRect(destRect, overlayPaint); - } - - // No scaled image - NoScaledImage = new SKBitmap(OriginalImage.Width, OriginalImage.Height); - using (var canvas = new SKCanvas(NoScaledImage)) - { - var destRect = new SKRect(0, 0, OriginalImage.Width, OriginalImage.Height); - var paint = new SKPaint - { - FilterQuality = SKFilterQuality.None, - IsAntialias = false - }; - canvas.DrawBitmap(OriginalImage, destRect, paint); - } - - gr = new SKCanvas(ScaledImage); - gr_noScaled = new SKCanvas(NoScaledImage); - } - - public static void DrawSquare(float x, float y, SKColor color) - { - if (OriginalImage == null) return; - - fillPaint.Color = color; - gr.DrawRect(x * scale, y * scale, scale, scale, fillPaint); - } - - public static void FillInNoScaled(SKColor color, params MyPoint[] points) - { - if (OriginalImage == null) return; - - fillPaint.Color = color; - - foreach (var p in points) - gr_noScaled.DrawRect(p.X, p.Y, 1, 1, fillPaint); - } - - public static void FillInNoScaled(SKColor color, int radius, params MyPoint[] points) - { - if (OriginalImage == null) return; - - fillPaint.Color = color; - - foreach (var p in points) - gr_noScaled.DrawRect(p.X - radius, p.Y - radius, 2 * radius + 1, 2 * radius + 1, fillPaint); - } - - public static void DrawSquare(SKColor color, params MyPoint[] points) - { - if (OriginalImage == null) return; - - fillPaint.Color = color; - foreach (var p in points) - gr.DrawRect(p.X * scale, p.Y * scale, scale, scale, fillPaint); - } - - public static void DrawRegion(SKColor color, BarCodeRegion r) - { - if (OriginalImage == null || gr == null) return; - - strokePaint.Color = color; - - var path = new SKPath(); - path.MoveTo(r.A.X * scale, r.A.Y * scale); - path.LineTo(r.B.X * scale, r.B.Y * scale); - path.LineTo(r.C.X * scale, r.C.Y * scale); - path.LineTo(r.D.X * scale, r.D.Y * scale); - path.Close(); // Important to close the polygon - - gr.DrawPath(path, strokePaint); - } - - public static void DrawArrow(float x1, float y1, float x2, float y2, SKColor color) - { - if (OriginalImage == null || gr == null) return; - - strokePaint.Color = color; - strokePaint.Style = SKPaintStyle.Stroke; - strokePaint.StrokeWidth = 1; - - var offset = scale / 2f; - - var start = new SKPoint(x1 * scale + offset, y1 * scale + offset); - var end = new SKPoint(x2 * scale + offset, y2 * scale + offset); - - // Draw main line - gr.DrawLine(start, end, strokePaint); - - // Draw arrowhead - float arrowLength = 6f; - float arrowAngle = (float)(Math.PI / 6); // 30 degrees - - var angle = (float)Math.Atan2(end.Y - start.Y, end.X - start.X); - - var left = new SKPoint( - end.X - arrowLength * (float)Math.Cos(angle - arrowAngle), - end.Y - arrowLength * (float)Math.Sin(angle - arrowAngle)); - - var right = new SKPoint( - end.X - arrowLength * (float)Math.Cos(angle + arrowAngle), - end.Y - arrowLength * (float)Math.Sin(angle + arrowAngle)); - - gr.DrawLine(end, left, strokePaint); - gr.DrawLine(end, right, strokePaint); - } - - public static void SaveImage(string prefix = "") - { - if (OriginalImage == null) return; - - if (Directory.Exists(saveFolder)) - { - using (var image = SKImage.FromBitmap(ScaledImage)) - using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) - using (var stream = File.OpenWrite(saveFolder + "\\" + prefix + saveCounter + ".png")) - { - data.SaveTo(stream); - } - } - saveCounter++; - } - - public static void SaveNoScaledImage(string prefix = "") - { - if (OriginalImage == null) return; - - if (Directory.Exists(saveFolder)) - { - using (var image = SKImage.FromBitmap(NoScaledImage)) - using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) - using (var stream = File.OpenWrite(saveFolder + "\\NoScaled_" + prefix + saveCounter + ".png")) - { - data.SaveTo(stream); - } - } - saveCounter++; - } - - static Stopwatch SW1 = new Stopwatch(); - static Stopwatch SW2 = new Stopwatch(); - static Stopwatch SW3 = new Stopwatch(); - static Stopwatch SW4 = new Stopwatch(); - - static int countSW1 = 0; - static int countSW2 = 0; - static int countSW3 = 0; - static int countSW4 = 0; - - - public static void StartSW1(){ SW1.Start(); countSW1++; } - public static void StopSW1() { SW1.Stop(); } - - public static void StartSW2() { SW2.Start(); countSW2++; } - public static void StopSW2() { SW2.Stop(); } - - public static void StartSW3() { SW3.Start(); countSW3++; } - public static void StopSW3() { SW3.Stop(); } - - public static void StartSW4() { SW4.Start(); countSW4++; } - public static void StopSW4() { SW4.Stop(); } - - public static int Counter0 = 0; - - public static void WriteStopwath() - { - Console.WriteLine("SW1 Calls: {0} Time: {1:000.0} ms", countSW1.ToString().PadRight(10), SW1.ElapsedMilliseconds); - Console.WriteLine("SW2 Calls: {0} Time: {1:000.0} ms", countSW2.ToString().PadRight(10), SW2.ElapsedMilliseconds); - Console.WriteLine("SW3 Calls: {0} Time: {1:000.0} ms", countSW3.ToString().PadRight(10), SW3.ElapsedMilliseconds); - Console.WriteLine("SW4 Calls: {0} Time: {1:000.0} ms", countSW4.ToString().PadRight(10), SW4.ElapsedMilliseconds); - //Console.WriteLine("Counter0: " + Counter0); - - SW1.Reset(); - SW2.Reset(); - SW3.Reset(); - SW4.Reset(); - - countSW1 = 0; - countSW2 = 0; - countSW3 = 0; - countSW4 = 0; - - Counter0 = 0; - } - - public static void CopyArrayToClipboard(IEnumerable list) - { - var sb = new StringBuilder(); - foreach(var v in list) - { - sb.Append(v + "\t"); - } - //System.Windows.Forms.Clipboard.SetText(sb.ToString()); - } -#endif - } - -#if CORE_DEV - public -#else - internal -#endif - class ListItem - { - public List List = new List(); - public string Name; - public MyPointF[] Polygon; - - public override string ToString() - { - return Name; - } - } - -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/EdgeSlicer.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/EdgeSlicer.cs deleted file mode 100644 index 695aea74..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/EdgeSlicer.cs +++ /dev/null @@ -1,225 +0,0 @@ -using SkiaSharp; -using System; -using System.Collections.Generic; -using System.Drawing; - -namespace BarcodeReader.Core.Common -{ - internal class EdgeSlicer - { - int width, height; - int minRad, maxRad; - bool scanRows, scanColumns; - LinkedList edges; // array with results - Edge[] prevRow; //segment indexs from the last minDist rows, - //to allow connecting segments in the current row with those at minDist - Edge[] currentRow; //segment indexs of the current processing row - - public EdgeSlicer(int minRad, int maxRad) { newEdgeSlicer(minRad,maxRad,true,true); } - public EdgeSlicer(int minRad, int maxRad, bool scanColumns) { newEdgeSlicer(minRad, maxRad, true, scanColumns); } - public EdgeSlicer(int minRad, int maxRad, bool scanRows, bool scanColumns) { newEdgeSlicer(minRad, maxRad, scanRows, scanColumns); } - protected void newEdgeSlicer(int minRad, int maxRad, bool scanRows, bool scanColumns) - { - this.minRad = minRad; - this.maxRad = maxRad; - this.scanRows = scanRows; - this.scanColumns = scanColumns; - } - - public LinkedList GetEdges(BlackAndWhiteImage img) - { - this.width = img.Width; - this.height = img.Height; - edges = new LinkedList(); - - //scan rows - if (scanRows) - { - currentRow = new Edge[width]; - prevRow = new Edge[width]; - for (int i = 0; i < width; i++) prevRow[i] = null; - for (int y = 0; y < height; y += 1) - ScanRow(y, img.GetRow(y), true); - } - - //scan cols - if (scanColumns) - { - currentRow = new Edge[height]; - prevRow = new Edge[height]; - for (int i = 0; i < height; i++) prevRow[i] = null; - for (int x = 0; x < width; x += 1) - ScanRow(x, img.GetColumn(x), false); - } - - //filter parts - LinkedList filtered = new LinkedList(); - foreach (Edge p in edges) - { - int l = (int)p.Length; - if (p != null && l > minRad && l < maxRad) - filtered.AddLast(p); - } - return filtered; - } - - //Process a row. For each pixel in the row looks for a previously connected pixel. It if is - //found, the pixel is added to its segment (or Part object). Otherwise, a new id (Part) is created. - private void ScanRow(int y, XBitArray row, bool isHorizontal) - { - int length = row.Size; - bool isBlackPrev = false, isBlackCurrent; - for (int x = 0; x < length; x++) - { - isBlackCurrent=row[x]; - if (isBlackPrev ^ isBlackCurrent) //only process edges - { - - //look in the prev row for a connected edge. - Edge found=null; - for (int xi = x - 1; xi <= x + 1 && xi < length; xi++) if (xi>=0) - { - Edge e = prevRow[xi]; - if (e != null && e.WhiteToBlack == isBlackCurrent) - { - if (e.Add(x, y)) - { - found = e; - break; - } - } - } - - if (found == null) //no connected edge is found, so add a new edge - { - found = new Edge(x, y, isBlackCurrent, isHorizontal); - edges.AddLast(found); - } - - currentRow[x] = found; - } else currentRow[x] = null; - isBlackPrev = isBlackCurrent; - } - - //move index rows - Edge[] tmp = prevRow; - prevRow = currentRow; - currentRow = tmp; - } - } - - internal class Edge : IComparable - { - bool scaned; - float x, angle; - float xIn, xEnd, yIn, yEnd, min, max; - bool whiteToBlack, isHorizontal; - - public Edge(int x, int y, bool whiteToBlack, bool isHorizontal) - { - angle = float.MinValue; - scaned = false; - min = max = x; - this.whiteToBlack = whiteToBlack; - this.isHorizontal = isHorizontal; - if (!isHorizontal) { int tmp = x; x = y; y = tmp; }//swap x,y - xIn = xEnd = x; - yIn = yEnd = y; - } - - //for every new pixel added, the bounding box is updated - public bool Add(int x, int y) - { - if (isHorizontal) - { - if (x < xIn) { if (x > min + 2) return false; } - else if (x < max - 2) return false; - if (x > max) max = x; - if (x < min) min = x; - } - else - { - int tmp = x; x = y; y = tmp; //swap x,y - if (y < yIn) { if (y > min + 2) return false; } - else if (y < max - 2) return false; - if (y > max) max = y; - if (y < min) min = y; - } - - xEnd = x; - yEnd = y; - return true; - } - - public SKPointI[] GetBBox() - { - return new SKPointI[] { new SKPointI((int)xIn - 1, (int)yIn), new SKPointI((int)xEnd - 1, (int)yEnd)}; - } - - - public SKRect GetRectangle() - { - return new SKRect(xIn, yIn, xEnd + 1, yEnd + 1); - } - - public void SetRectangle(SKRect r) - { - this.xIn = r.Left; - this.yIn = r.Top; - this.xEnd = r.Right - 1; - this.YEnd = r.Bottom - 1; - } - - public float XIn { get { return xIn; } set { xIn = value; } } - public float XEnd { get { return xEnd; } set { xEnd = value; } } - public float YIn { get { return yIn; } set { yIn = value; } } - public float YEnd { get { return yEnd; } set { yEnd = value; } } - public float Length - { - get - { - float ix = (float)(xEnd - xIn); - float iy = (float)(yEnd - yIn); - return (float)Math.Sqrt(ix * ix + iy * iy); - } - } - - const float PI = (float)Math.PI; - public float Width { get { return xEnd - xIn + 1; } } - public float Height { get { return yEnd - yIn + 1; } } - public bool WhiteToBlack { get {return whiteToBlack; } } - public bool Scaned { get {return scaned; } set { scaned=value;} } - public float X { get { return x; } set { x = value; } } - public float Angle { get { - if (angle == float.MinValue) - { - angle = (float)Math.Atan2((double)(yEnd - yIn), (double)(xEnd - xIn)); - if (angle > PI) angle -= PI; - else if (angle < 0) angle += PI; - } - return angle; - } } - public float Perpendicular { get { float p = Angle + PI / 2F; if (p > PI) p -= PI; return p; } } - public SKPoint Center { get { return new SKPoint((xIn + xEnd) / 2, (yIn + yEnd) / 2); } } - public SKPoint In { get { return new SKPoint(xIn, yIn); } } - public SKPoint End { get { return new SKPoint(xEnd, yEnd); } } - - public float Dist(Edge p) - { - SKPoint b = p.Center; - SKPoint a = this.Center; - return (float)(Math.Sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y))); - } - - public override String ToString() - { - return "X:" + x + " In(" + xIn + "," + yIn + ") - Fi(" + xEnd + "," + yEnd + ")"; - } - - public int CompareTo(Object o) - { - Edge p = (Edge)o; - return (int)((p.x - this.x) * 10); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/EdgeTrack.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/EdgeTrack.cs deleted file mode 100644 index c629119e..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/EdgeTrack.cs +++ /dev/null @@ -1,290 +0,0 @@ -using System; - -namespace BarcodeReader.Core.Common -{ - //class used to track an edge. Uses "dual" linear regression, to adjust the - // regressed line to edges of both sides of the pattern. - // At each step finds the 4 edge-pixels (left-up, left-down, right-up, right-down) - // and adds the edge-pixel closest to the regression line. - internal class EdgeTrack - { - ImageScaner scan; - float moduleSize; - bool currentIsBlack; - - MyPoint A,B,C,D; - MyPointF fA, fB; - MyPoint lastA, lastB; - MyVector vdX, vdY; - float dA, dB; - Regression reg; - - public EdgeTrack(ImageScaner scan) - { - this.scan = scan; - } - - private bool DebugWriteConsoleMessages = false; - public void setDebugWriteConsoleMessages(bool debug) { this.DebugWriteConsoleMessages=debug; } - - //p0 --> starting pixel - //vd --> direction: (-1,0) for vertical left side, (1,0) for vertical right side - public Regression Track(MyPoint p0, MyVector vd, float moduleSize, bool currentIsBlack) - { - this.moduleSize = moduleSize; - this.currentIsBlack = currentIsBlack; - - //main directions - vdX = vd; //direction perpendicular to the edge (first approximation: usually 1,0 or 0,1 is enough) - vdY = new MyVector(-vdX.Y, vdX.X); //edge direction - - //Initial aproximation - A = scan.FindTransition(p0 + vdY, vdX, currentIsBlack, out fA); - B = scan.FindTransition(p0 - vdY, vdX, currentIsBlack, out fB); - lastA = A; - lastB = B; - - reg = new Regression(p0); - reg.AddPointL(fA); - reg.AddPointL(fB); - - A = scan.FindTransition(A + vdY, vdX, currentIsBlack, out fA); - B = scan.FindTransition(B - vdY, vdX, currentIsBlack, out fB); - - bool end = false; - float maxDist = moduleSize; - int maxSumErr = (int)(moduleSize * 2f); - //moduleSize = 0; - int errA=0, errB=0, sumErr=0; - while (!end ) - { - dA = reg.DistL(fA); if (dA < 0) dA = -dA; //absolute dist - dB = reg.DistL(fB); if (dB < 0) dB = -dB; - if (dA <= dB && dA < maxDist && scan.InBorder(A)) - { - reg.AddPointL(fA); - if (dA < maxDist / 2f) { lastA = A; errA = 0; } - A = scan.FindTransition(A + vdY, vdX, currentIsBlack, out fA); - } - else if (dB <= dA && dB < maxDist && scan.InBorder(B)) - { - reg.AddPointL(fB); - if (dB < maxDist / 2f) { lastB = B; errB = 0; } - B = scan.FindTransition(B - vdY, vdX, currentIsBlack, out fB); - } - else if (errA < moduleSize && sumErr< maxSumErr) - { - errA++; sumErr++; - A = scan.FindTransition(lastA + errA*vdY, vdX, currentIsBlack, out fA); - } - else if (errB < moduleSize && sumErr < maxSumErr) - { - errB++; sumErr++; - B = scan.FindTransition(lastB - errB * vdY, vdX, currentIsBlack, out fB); - } - else end = true; - } - return reg; - } - - public MyPointF Up() { return Up(1); } //default offset +1 - public MyPointF Up(int offset) - { - int n = 0; - float max = moduleSize; - int iMax = (int) Math.Round(max); - if (iMax <2) iMax = 2; - A = lastA; - while (scan.InBorder(A) && n < iMax) - { - if (dA < max) - { - lastA = A; n = 0; - A = scan.FindTransition(A + vdY, vdX, currentIsBlack, out fA); - } - else - { - n++; - A = scan.FindTransition(lastA + (n+offset)*vdY, vdX, currentIsBlack, out fA); - } - dA = reg.DistL(fA); if (dA < 0) dA = -dA; //absolute dist - } - try { return reg.Project(lastA); } catch (Exception) { return MyPointF.Empty; } - } - - public MyPointF Down() { return Down(1); } //default offset +1 - public MyPointF Down(int offset) - { - int n = 0; - float max = moduleSize; - int iMax = (int)Math.Round(max); - if (iMax <2) iMax = 2; - B = lastB; - while (scan.InBorder(B) && n < iMax) - { - if (dB < max) - { - lastB = B; n = 0; - B = scan.FindTransition(B - vdY, vdX, currentIsBlack, out fB); - } - else - { - n++; - B = scan.FindTransition(lastB - (n+offset) * vdY, vdX, currentIsBlack, out fB); - } - dB = reg.DistL(fB); if (dB < 0) dB = -dB; //absolute dist - } - try { return reg.Project(lastB); } catch (Exception) { return MyPointF.Empty; } - } - - public RegressionLine GetLine() - { - return reg.LineL; - } - - float abs(float f) - { - if (f < 0) return -f; //absolute dist - return f; - } - - //p0, p1 --> starting pixel (dual edge track, used in QR and Aztec (square finders) - //vd --> direction: (-1,0) for vertical left side, (1,0) for vertical right side - public Regression Track(MyPoint p0, MyPoint p1, MyVector vd, float moduleSize, bool currentIsBlack) - { - //main directions - MyVector vdX = vd; //direction of the edge (first approximation: usually 1,0 or 0,1 is enough) - MyVector vdY = new MyVector(-vdX.Y, vdX.X); - - //Initial aproximation - MyPointF fA, fB, fC, fD; - //p0 = scan.FindTransition(p0, vdX, currentIsBlack, out fA); - A = scan.FindTransition(p0 + vdY, vdX, currentIsBlack, out fA); - B = scan.FindTransition(p0 - vdY, vdX, currentIsBlack, out fB); - C = scan.FindTransition(p1 + vdY, vdX, !currentIsBlack, out fC); - D = scan.FindTransition(p1 - vdY, vdX, !currentIsBlack, out fD); - Regression reg = new Regression(p0, C-A); - //if (DebugWriteConsoleMessages) reg.setDebug(true); - reg.AddPointL(fA); - reg.AddPointL(fB); - reg.AddPointR(fC); - reg.AddPointR(fD); - - A = scan.FindTransition(A + vdY, vdX, currentIsBlack, out fA); - B = scan.FindTransition(B - vdY, vdX, currentIsBlack, out fB); - C = scan.FindTransition(C + vdY, vdX, !currentIsBlack, out fC); - D = scan.FindTransition(D - vdY, vdX, !currentIsBlack, out fD); - - bool end = false; - float maxDist = moduleSize * 2F; - float dA, dB, dC, dD; - update(reg, fA, fB, fC, fD, out dA, out dB, out dC, out dD); - while (!end) - { - if (dA <= dB && dA <= dC && dA <= dD && dA - 1F < maxDist / (float)reg.NL) - { - reg.AddPointL(fA); - A = scan.FindTransition(A + vdY, vdX, currentIsBlack, out fA); - update(reg, fA, fB, fC, fD, out dA, out dB, out dC, out dD); - } - else if (dB <= dA && dB <= dC && dB <= dD && dB -1F< maxDist / (float)reg.NL) - { - reg.AddPointL(fB); - B = scan.FindTransition(B - vdY, vdX, currentIsBlack, out fB); - update(reg, fA, fB, fC, fD, out dA, out dB, out dC, out dD); - } - else if (dC <= dA && dC <= dB && dC <= dD && dC - 1F < maxDist / (float)reg.NR) - { - reg.AddPointR(fC); - C = scan.FindTransition(C + vdY, vdX, !currentIsBlack, out fC); - update(reg, fA, fB, fC, fD, out dA, out dB, out dC, out dD); - } - else if (dD <= dA && dD <= dB && dD <= dC && dD - 1F < maxDist / (float)reg.NR) - { - reg.AddPointR(fD); - D = scan.FindTransition(D - vdY, vdX, !currentIsBlack, out fD); - update(reg, fA, fB, fC, fD, out dA, out dB, out dC, out dD); - } - else end = true; - } - return reg; - } - - void update(Regression reg, MyPointF fA, MyPointF fB, MyPointF fC, MyPointF fD, out float dA, out float dB, out float dC, out float dD) - { - dA = abs(reg.DistL(fA)); - dB = abs(reg.DistL(fB)); - dC = abs(reg.DistR(fC)); - dD = abs(reg.DistR(fD)); - } - - //p0 --> starting pixel - //vd --> direction: (-1,0) or (0,-1) - public void TrackBar(MyPoint p0, MyVector vd, float moduleSize, bool currentIsBlack, out MyPointF lu, out MyPointF ld, out MyPointF ru, out MyPointF rd) - { - this.moduleSize = moduleSize; - this.currentIsBlack = currentIsBlack; - - //main directions - vdX = vd; //direction perpendicular to the edge (first approximation: usually 1,0 or 0,1 is enough) - vdY = new MyVector(-vdX.Y, vdX.X); //edge direction - - float ud, dd; - MyPoint UM,DM, lastUM, lastDM; - MyPoint UL, UR, DL, DR; - scan.FindModule(p0+vdY, vdX, currentIsBlack, out UL, out UR, out UM, out ud); - scan.FindModule(p0-vdY, vdX, currentIsBlack, out DL, out DR, out DM, out dd); - - Regression reg = new Regression(p0); - reg.AddPointL(UM); - reg.AddPointL(DM); - lastUM = UM; - lastDM = DM; - - bool Uend = false, Dend = false; - int Uhole, Dhole,hole; - Uhole = Dhole = hole=Convert.ToInt32(ud); - - while (!Uend || !Dend) - { - if (!Uend) - { - UM = reg.project(UM + vdY, vdX); - scan.FindModule(UM, vdX, currentIsBlack, out UL, out UR, out UM, out ud); - if (ud == 0f) { if (--Uhole <= 0) Uend = true; } - else if (Calc.Around(ud/hole,1f,0.1f)) - { - Uhole = hole; - reg.AddPointL(UM); - lastUM = UM; - } - } - - if (!Dend) - { - DM = reg.project(DM - vdY, vdX); - scan.FindModule(DM, vdX, currentIsBlack, out DL, out DR, out DM, out dd); - if (dd == 0f) { if (--Dhole <= 0) Dend = true; } - else if (Calc.Around(dd/hole,1f,0.1f)) - { - Dhole = 0; - reg.AddPointL(DM); - lastDM = DM; - } - } - } - - MyPointF um = reg.Project(lastUM); - MyPointF dm = reg.Project(lastDM); - - MyVectorF x = reg.VdY.Normalized; - //correct x to go in the same direction of vdX - if (vdX.X != 0) { if (vdX.X<0 && x.X > 0 || vdX.X>0 && x.X < 0 ) x = x * -1f; } - else { if (vdX.Y < 0 && x.Y > 0 || vdX.Y > 0 && x.Y < 0) x = x * -1f; } - lu = um + x * (hole / 2f); - ld = dm + x * (hole / 2f); - ru = um - x * (hole / 2f); - rd = dm - x * (hole / 2f); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/EdgeVarSlicer.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/EdgeVarSlicer.cs deleted file mode 100644 index a7402929..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/EdgeVarSlicer.cs +++ /dev/null @@ -1,279 +0,0 @@ -using SkiaSharp; -using System; -using System.Collections.Generic; -using System.Drawing; - -namespace BarcodeReader.Core.Common -{ - internal class EdgeVarSlicer - { - int width, height; - int minRad, maxRad; - bool scanRows, scanColumns; - LinkedList edges; // array with results - EdgeVar[] prevRow; //segment indexs from the last minDist rows, - //to allow connecting segments in the current row with those at minDist - EdgeVar[] currentRow; //segment indexs of the current processing row - float maxVariance = 0.1f; - - int edgeId = 0; - - public EdgeVarSlicer(int minRad, int maxRad, float maxVariance) { newEdgeSlicer(minRad,maxRad, true,true, maxVariance); } - public EdgeVarSlicer(int minRad, int maxRad, bool scanColumns, float maxVariance) { newEdgeSlicer(minRad, maxRad, true, scanColumns, maxVariance); } - public EdgeVarSlicer(int minRad, int maxRad, bool scanRows, bool scanColumns, float maxVariance) { newEdgeSlicer(minRad, maxRad, scanRows, scanColumns, maxVariance); } - protected void newEdgeSlicer(int minRad, int maxRad, bool scanRows, bool scanColumns, float maxVariance) - { - this.minRad = minRad; - this.maxRad = maxRad; - this.scanRows = scanRows; - this.scanColumns = scanColumns; - this.maxVariance = maxVariance; - } - - public LinkedList GetEdges(BlackAndWhiteImage img) - { - this.width = img.Width; - this.height = img.Height; - edges = new LinkedList(); - - //scan rows looking for vertical lines - if (scanRows) - { - currentRow = new EdgeVar[width]; - prevRow = new EdgeVar[width]; - for (int i = 0; i < width; i++) prevRow[i] = null; - for (int y = 0; y < height; y += 1) - ScanRow(y, img.GetRow(y), false); - } - - //scan cols looking for horizontal lines - if (scanColumns) - { - currentRow = new EdgeVar[height]; - prevRow = new EdgeVar[height]; - for (int i = 0; i < height; i++) prevRow[i] = null; - for (int x = 0; x < width; x += 1) - ScanRow(x, img.GetColumn(x), true); - } - - //filter parts - LinkedList filtered = new LinkedList(); - foreach (EdgeVar p in edges) - { - int l = (int)p.Length; - if (p != null && l > minRad && l < maxRad) - filtered.AddLast(p); - } - return filtered; - } - - //Process a row. For each pixel in the row looks for a previously connected pixel. It if is - //found, the pixel is added to its segment (or Part object). Otherwise, a new id (Part) is created. - private void ScanRow(int y, XBitArray row, bool isHorizontal) - { - int length = row.Size; - bool isBlackPrev = false, isBlackCurrent; - for (int x = 0; x < length; x++) - { - isBlackCurrent=row[x]; - if (!isBlackPrev && isBlackCurrent) - { - - //look in the prev row for a connected edge. - EdgeVar found = null; - for (int xi = x - 1; xi <= x + 1 && xi < length; xi++) if (xi>=0) - { - EdgeVar e = prevRow[xi]; - if (e != null && e.WhiteToBlack == isBlackCurrent) - { - if (isHorizontal && e.Add(y,x) || !isHorizontal && e.Add(x,y)) - { - found = e; - break; - } - } - } - - if (found == null) //no connected edge is found, so add a new edge - { - found = isHorizontal?new EdgeVar(y, x, isBlackCurrent, true, maxVariance, edgeId++):new EdgeVar(x, y, isBlackCurrent, false, maxVariance, edgeId++); - edges.AddLast(found); - } - - currentRow[x] = found; - } else currentRow[x] = null; - isBlackPrev = isBlackCurrent; - } - - //move index rows - EdgeVar[] tmp = prevRow; - prevRow = currentRow; - currentRow = tmp; - } - } - -#if CORE_DEV - public -#else - internal -#endif - class EdgeVar : IComparable - { - int id; - float x = 0; - int xIn, xEnd, yIn, yEnd; - bool whiteToBlack, isHorizontal; - float maxVariance; - - //to calculate variance - int N; - float mean, variance, angle; - bool recalcAngle; - - protected EdgeVar(int edgeId) { this.id = edgeId; } - - public EdgeVar(int x, int y, bool whiteToBlack, bool isHorizontal, float maxVariance, int edgeId) - { - this.id = edgeId; - this.whiteToBlack = whiteToBlack; - this.isHorizontal = isHorizontal; - if (!isHorizontal) { int tmp = x; x = y; y = tmp; }//swap x,y - xIn = xEnd = x; - yIn = yEnd = y; - this.variance = this.mean= 0.0f; - this.N = 0; - this.recalcAngle = true; - this.maxVariance = maxVariance; - } - - public EdgeVar mirror(int edgeId) - { - EdgeVar m = new EdgeVar(edgeId); - m.xIn = this.xEnd; m.yIn = this.yEnd; - m.xEnd = this.xIn; m.yEnd = this.yIn; - m.isHorizontal = this.isHorizontal; - m.variance = this.variance; - m.angle = this.Angle+(float)Math.PI; - m.recalcAngle = false; - m.maxVariance = this.maxVariance; - return m; - } - - public override int GetHashCode() - { - return this.id; - } - - //for every new pixel added, the bounding box is updated - //LinkedList slopes = new LinkedList(); - public bool Add(int x, int y) - { - if (!isHorizontal) { int tmp = x; x = y; y = tmp; }//swap x,y - - //update variance - if (x == xEnd) return false; - float slope = (float)(y - yIn) / (float)(x - xIn); //yEnd, xEnd - float nextMean, nextVariance; - NextVariance(slope, out nextMean, out nextVariance); - - if (nextVariance > maxVariance) return false; - mean = nextMean; - variance = nextVariance; - - xEnd = x; - yEnd = y; - N++; - this.recalcAngle = true; - return true; - } - - void NextVariance(float x, out float nextMean, out float nextVariance) - { - if (N == 0) { nextMean = x; nextVariance = 0.0f; } - else - { - float n = (float)N; - nextMean = (mean * n + x) / (n + 1f); - nextVariance = n * variance / (n + 1) + (float)Math.Pow(x - mean, 2) / (n + 1) - (float)Math.Pow(nextMean - mean, 2); - } - } - - public SKPointI[] GetBBox() - { - return isHorizontal?new SKPointI[] { new SKPointI(xIn - 1, yIn), new SKPointI(xEnd - 1, yEnd)}: - new SKPointI[] { new SKPointI(yIn - 1, xIn), new SKPointI(yEnd - 1, xEnd) }; ; - } - - - public SKRect GetRectangle() - { - return isHorizontal? new SKRect(xIn, yIn, xEnd + 1, yEnd + 1): - new SKRect(yIn, xIn, yEnd + 1, xEnd + 1); ; - } - - public float Length - { - get - { - float ix = (float)(xEnd - xIn); - float iy = (float)(yEnd - yIn); - return (float)Math.Sqrt(ix * ix + iy * iy); - } - } - - const float PI = (float)Math.PI; - public bool WhiteToBlack { get {return whiteToBlack; } } - public SKPointI Center { get { - return isHorizontal? new SKPointI((xIn + xEnd) / 2, (yIn + yEnd) / 2): - new SKPointI((yIn + yEnd) / 2, (xIn + xEnd) / 2); - }} - public SKPointI In { get { - return isHorizontal? new SKPointI(xIn, yIn) : new SKPointI(yIn, xIn); - } } - - public SKPointI End { get { return isHorizontal?new SKPointI(xEnd, yEnd):new SKPointI(yEnd, xEnd); } } - - public float Angle - { - get - { - if (this.recalcAngle) - { - MyPoint a = In; - MyPoint b = End; - MyVector vd = b-a; - this.angle = vd.Angle; - this.recalcAngle = false; - } - return this.angle; - } - } - - public override String ToString() - { - return (isHorizontal? - "In(" + xIn + "," + yIn + ") - Fi(" + xEnd + "," + yEnd + ")": - "In(" + yIn + "," + xIn + ") - Fi(" + yEnd + "," + xEnd + ")" - )+"var:"+this.variance; - } - - public int CompareTo(Object o) - { - EdgeVar p = (EdgeVar)o; - return (int)((p.x - this.x) * 10); - } - - public float Variance { get { return this.variance; } } - } - - class EdgeLengthComparer :System.Collections.IComparer - { - public int Compare(object a, object b) - { - EdgeVar aa = (EdgeVar)a; - EdgeVar bb = (EdgeVar)b; - - return aa.Length>bb.Length?1:aa.Length==bb.Length?0:-1; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/EncodingUtils.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/EncodingUtils.cs deleted file mode 100644 index c4c3004b..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/EncodingUtils.cs +++ /dev/null @@ -1,287 +0,0 @@ -using System; -using System.Diagnostics; -using System.Text; - -namespace BarcodeReader.Core.Common -{ - /// - /// Guess encoding by bytes - /// - internal static class EncodingUtils - { -#if (WINDOWS_PHONE || SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || PORTABLE || NETSTANDARD) - private const String PLATFORM_DEFAULT_ENCODING = "UTF-8"; -#else - private static readonly String PLATFORM_DEFAULT_ENCODING = Encoding.Default.WebName; -#endif - /// - /// SJIS - /// - public static String SHIFT_JIS = "SJIS"; - /// - /// GB2312 - /// - public static String GB2312 = "GB2312"; - private const String EUC_JP = "EUC-JP"; - private const String UTF8 = "UTF-8"; - private const String ISO88591 = "ISO-8859-1"; - private static readonly bool ASSUME_SHIFT_JIS = - String.Compare(SHIFT_JIS, PLATFORM_DEFAULT_ENCODING, StringComparison.OrdinalIgnoreCase) == 0 || - String.Compare(EUC_JP, PLATFORM_DEFAULT_ENCODING, StringComparison.OrdinalIgnoreCase) == 0; - - /// - /// Guesses the encoding. - /// - /// bytes encoding a string, whose encoding should be guessed - /// decode hints if applicable - /// name of guessed encoding; at the moment will only guess one of: - /// {@link #SHIFT_JIS}, {@link #UTF8}, {@link #ISO88591}, or the platform - /// default encoding if none of these can possibly be correct - public static String GuessEncoding(byte[] bytes) - { - // For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS, - // which should be by far the most common encodings. - int length = bytes.Length; - bool canBeISO88591 = true; - bool canBeShiftJIS = true; - bool canBeUTF8 = true; - int utf8BytesLeft = 0; - int utf2BytesChars = 0; - int utf3BytesChars = 0; - int utf4BytesChars = 0; - int sjisBytesLeft = 0; - int sjisKatakanaChars = 0; - int sjisCurKatakanaWordLength = 0; - int sjisCurDoubleBytesWordLength = 0; - int sjisMaxKatakanaWordLength = 0; - int sjisMaxDoubleBytesWordLength = 0; - int isoHighOther = 0; - int isoHighOther2 = 0; - - bool utf8bom = bytes.Length > 3 && - bytes[0] == 0xEF && - bytes[1] == 0xBB && - bytes[2] == 0xBF; - - for (int i = 0; - i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8); - i++) - { - - int value = bytes[i] & 0xFF; - - // UTF-8 stuff - if (canBeUTF8) - { - if (utf8BytesLeft > 0) - { - if ((value & 0x80) == 0) - { - canBeUTF8 = false; - } - else - { - utf8BytesLeft--; - } - } - else if ((value & 0x80) != 0) - { - if ((value & 0x40) == 0) - { - canBeUTF8 = false; - } - else - { - utf8BytesLeft++; - if ((value & 0x20) == 0) - { - utf2BytesChars++; - } - else - { - utf8BytesLeft++; - if ((value & 0x10) == 0) - { - utf3BytesChars++; - } - else - { - utf8BytesLeft++; - if ((value & 0x08) == 0) - { - utf4BytesChars++; - } - else - { - canBeUTF8 = false; - } - } - } - } - } - } - - // ISO-8859-1 stuff - if (canBeISO88591) - { - if (value > 0x7F && value < 0xA0) - { - canBeISO88591 = false; - } - else if (value > 0x9F) - { - if (value < 0xC0 || value == 0xD7 || value == 0xF7) - { - isoHighOther++; - } - isoHighOther2++; - } - } - - // Shift_JIS stuff - if (canBeShiftJIS) - { - if (sjisBytesLeft > 0) - { - if (value < 0x40 || value == 0x7F || value > 0xFC) - { - canBeShiftJIS = false; - } - else - { - sjisBytesLeft--; - } - } - else if (value == 0x80 || value == 0xA0 || value > 0xEF) - { - canBeShiftJIS = false; - } - else if (value > 0xA0 && value < 0xE0) - { - sjisKatakanaChars++; - sjisCurDoubleBytesWordLength = 0; - sjisCurKatakanaWordLength++; - if (sjisCurKatakanaWordLength > sjisMaxKatakanaWordLength) - { - sjisMaxKatakanaWordLength = sjisCurKatakanaWordLength; - } - } - else if (value > 0x7F) - { - sjisBytesLeft++; - //sjisDoubleBytesChars++; - sjisCurKatakanaWordLength = 0; - sjisCurDoubleBytesWordLength++; - if (sjisCurDoubleBytesWordLength > sjisMaxDoubleBytesWordLength) - { - sjisMaxDoubleBytesWordLength = sjisCurDoubleBytesWordLength; - } - } - else - { - //sjisLowChars++; - sjisCurKatakanaWordLength = 0; - sjisCurDoubleBytesWordLength = 0; - } - } - } - - if (canBeUTF8 && utf8BytesLeft > 0) - { - canBeUTF8 = false; - } - if (canBeShiftJIS && sjisBytesLeft > 0) - { - canBeShiftJIS = false; - } - - // Easy -- if there is BOM or at least 1 valid not-single byte character (and no evidence it can't be UTF-8), done - if (canBeUTF8 && (utf8bom || utf2BytesChars + utf3BytesChars + utf4BytesChars > 0)) - { - return UTF8; - } - // Easy -- if assuming Shift_JIS or at least 3 valid consecutive not-ascii characters (and no evidence it can't be), done - if (canBeShiftJIS && (ASSUME_SHIFT_JIS || sjisMaxKatakanaWordLength >= 3 || sjisMaxDoubleBytesWordLength >= 3)) - { - return SHIFT_JIS; - } - // Distinguishing Shift_JIS and ISO-8859-1 can be a little tough for short words. The crude heuristic is: - // - If we saw - // - only two consecutive katakana chars in the whole text, or - // - at least 10% of bytes that could be "upper" not-alphanumeric Latin1, - // - then we conclude Shift_JIS, else ISO-8859-1 - if (canBeISO88591 && canBeShiftJIS) - { - return (sjisMaxKatakanaWordLength == 2 && sjisKatakanaChars == 2) || isoHighOther * 10 >= length - ? SHIFT_JIS : ISO88591; - } - - // Otherwise, try in order ISO-8859-1, Shift JIS, UTF-8 and fall back to default platform encoding - if (canBeISO88591) - { - if (isoHighOther2 * 2 >= length) - return PLATFORM_DEFAULT_ENCODING;//too many letters with grave/tilde/acute - else - return ISO88591; - } - if (canBeShiftJIS) - { - return SHIFT_JIS; - } - if (canBeUTF8) - { - return UTF8; - } - // Otherwise, we take a wild guess with platform encoding - return PLATFORM_DEFAULT_ENCODING; - } - - public static Encoding GetEncodingFromECI(string eci) - { - if (!string.IsNullOrEmpty(eci) && eci.StartsWith("]Q2\\0000", StringComparison.Ordinal)) - { - if (int.TryParse(eci.Substring(eci.Length - 6, 6), out int code)) - { - try - { - switch (code) - { - case 3: return Encoding.GetEncoding("iso-8859-1"); - case 4: return Encoding.GetEncoding("iso-8859-2"); - case 5: return Encoding.GetEncoding("iso-8859-3"); - case 6: return Encoding.GetEncoding("iso-8859-4"); - case 7: return Encoding.GetEncoding("iso-8859-5"); - case 8: return Encoding.GetEncoding("iso-8859-6"); - case 9: return Encoding.GetEncoding("iso-8859-7"); - case 10: return Encoding.GetEncoding("iso-8859-8"); - case 11: return Encoding.GetEncoding("iso-8859-9"); - case 12: return Encoding.GetEncoding("iso-8859-10"); - case 13: return Encoding.GetEncoding("iso-8859-11"); - case 15: return Encoding.GetEncoding("iso-8859-13"); - case 16: return Encoding.GetEncoding("iso-8859-14"); - case 17: return Encoding.GetEncoding("iso-8859-15"); - case 18: return Encoding.GetEncoding("iso-8859-16"); - case 20: return Encoding.GetEncoding("shift_jis"); - case 21: return Encoding.GetEncoding("windows-1250"); - case 22: return Encoding.GetEncoding("windows-1251"); - case 23: return Encoding.GetEncoding("windows-1252"); - case 24: return Encoding.GetEncoding("windows-1256"); - case 25: return Encoding.GetEncoding("utf-16BE"); - case 26: return Encoding.UTF8; - case 27: return Encoding.ASCII; - case 28: return Encoding.GetEncoding("big5"); - case 29: return Encoding.GetEncoding("GB18030"); - case 30: return Encoding.GetEncoding("euc-kr"); - } - } - catch (Exception e) - { - Debug.WriteLine("GetEncodingFromECI(): Unregistered encoding?"); - } - } - } - - return null; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/FoundPattern.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/FoundPattern.cs deleted file mode 100644 index f52cdc9c..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/FoundPattern.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace BarcodeReader.Core.Common -{ -#if CORE_DEV - public -#else - internal -#endif - class FoundPattern - { - public int nPattern; - public float blackCompensation, whiteCompensation; - public float moduleLength; - - /// - /// Length of last module - /// - public int lastModule; - - public FoundPattern(int nPattern, float blackCompensation, float whiteCompensation, float moduleLength) - { - this.nPattern = nPattern; - this.blackCompensation = blackCompensation; - this.whiteCompensation = whiteCompensation; - this.moduleLength = moduleLength; - } - - public FoundPattern(int nPattern, float blackCompensation, float whiteCompensation, float moduleLength, int lastModule) : this (nPattern, blackCompensation, whiteCompensation, moduleLength) - { - this.lastModule = lastModule; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Grid.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Grid.cs deleted file mode 100644 index 36e5d595..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Grid.cs +++ /dev/null @@ -1,116 +0,0 @@ -namespace BarcodeReader.Core.Common -{ - - // Stores and manages rectangular regions to sample. - // A rectangular region can be defined based on 3 vertexs (assuming no deformation). We call regular sampling - // 4 corners: this allows deformation - - internal class Grid - { - int Cols, Rows; - MyPointF Corner, Right, CornerUp, RightUp; - MyVectorF CornerUpVD, RightUpVD, BottomRightVD; - float BottomWidth, CornerHeight, RightHeight; - float moduleLength; - bool areCorners; - - //grid with perspective deformation. Points are corners if areCorners is true, - //or module centers otherwise - public Grid(int cols, int rows, MyPointF corner, MyPointF cornerUp, MyPointF right, - MyPointF rightUp, bool areCorners) - { - Cols = cols; - Rows = rows; - Corner = corner; - CornerUp = cornerUp; - Right = right; - RightUp = rightUp; - - MyVectorF vCUp = (cornerUp - corner); - CornerHeight = vCUp.Length; - CornerUpVD = vCUp.Normalized * (CornerHeight / rows); - - MyVectorF vRUp = (rightUp - right); - RightHeight = vRUp.Length; - RightUpVD = vRUp.Normalized * (RightHeight / rows); - - MyVectorF vBRight = (right - corner); - BottomWidth = vBRight.Length; - BottomRightVD = vBRight.Normalized * (BottomWidth / cols); - - this.areCorners = areCorners; - if (areCorners) moduleLength = BottomWidth / (float)cols; - else moduleLength = BottomWidth / (float)(cols - 1); - } - - //grid without perspective deformation --> called regular - public Grid(MyPointF center, MyVectorF vdX, MyVectorF vdY) - { - CornerUp = center; - CornerUpVD = vdY; - BottomRightVD = vdX; - } - - public MyPointF GetSamplePointRegular(int x, int y) - { - return CornerUp + CornerUpVD * y + BottomRightVD * x; - } - - public MyPointF GetSamplePointRegular(float x, float y) - { - return CornerUp + CornerUpVD * y + BottomRightVD * x; - } - - //x=0..cols-1 y=0..rows-1 - public MyPointF GetSamplePoint(int x, int y) - { - //interpolate between CornerUp and RightUp vectors - float fX=((float)x+0.5F)/(float)(Cols); //]0..1[ - MyVectorF up = CornerUpVD * (1.0F-fX) + RightUpVD * fX; - - MyPointF p = Corner + BottomRightVD * ((float)x + (areCorners?0.5F:0F)); - MyVectorF q = up * ((float)y + (areCorners ? 0.5F : 0F)); - p += q; - return p; - } - - public MyPointF GetSamplePoint(float x, float y) - { - //interpolate between CornerUp and RightUp vectors - float fX = x / (float)(Cols); //]0..1[ - MyVectorF up = CornerUpVD * (1.0F - fX) + RightUpVD * fX; - - MyPointF p = Corner + BottomRightVD *x; - MyVectorF q = up * y; - p += q; - return p; - } - - public void ExtractPointsRegular(ImageScaner scan, bool[][] result, MyPoint sourceIndex, MyPoint resultIndex, int cx, int cy) - { - for (int y = 0; y < cy; ++y) - { - for (int x = 0; x < cx; ++x) - { - MyPointF point = GetSamplePointRegular(sourceIndex.X + x, sourceIndex.Y + y); - bool isBlack = scan.isBlackSample(point,moduleLength); - result[resultIndex.Y + y][resultIndex.X + x] = isBlack; - } - } - } - //extract points of a rectangular area of the grid - public void ExtractPoints(ImageScaner scan, bool[][] result, MyPoint sourceIndex, MyPoint resultIndex, int cx, int cy) - { - //Console.WriteLine("sampled grid:"); - for (int y = 0; y < cy; ++y) - { - for (int x = 0; x < cx; ++x) - { - MyPointF point = GetSamplePoint(sourceIndex.X + x, sourceIndex.Y + y); - bool isBlack = scan.isBlackSample(point,moduleLength); - result[resultIndex.Y + y][resultIndex.X + x] = isBlack; - } - } - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Hough.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Hough.cs deleted file mode 100644 index 9b934aed..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Hough.cs +++ /dev/null @@ -1,354 +0,0 @@ -using SkiaSharp; -using System; -using System.Collections.Generic; -using System.Drawing; - -namespace BarcodeReader.Core.Common -{ - //Implementation of the discrete Hough transform used to find aligned points in - // a 2D plane. - internal class Hough - { - int width, height, diagonal; - int hWidth, hHeight; - float angleInc, distInc; - int count; - HoughCell[][] h; - int maxH; - float[] cosO, sinO; - - //width and height: image size - //hWidth, hHeight: level of discretization of the hough transform - //hWidth: angle discretization - //hHeight: distance discretization - public Hough(int width, int height, int hWidth, int hHeight) - { - this.width = width; - this.height = height; - this.diagonal = (int) Math.Ceiling(Math.Sqrt(width * width + height * height)); - - this.hWidth = hWidth; - this.hHeight = hHeight; - - this.angleInc = (float)Math.PI / (float)hWidth; - this.distInc = (float) diagonal * 2F / (float)hHeight; - - this.h = new HoughCell[hWidth][]; //initialize hough resulting array - this.cosO = new float[hWidth]; //precompute sin and cos tables - this.sinO = new float[hWidth]; - double angle = 0.0, inc = Math.PI / (double)hWidth; - for (int i = 0; i < hWidth; i++) - { - h[i] = new HoughCell[hHeight]; - cosO[i] = (float)Math.Cos(angle); - sinO[i] = (float)Math.Sin(angle); - angle += inc; - } - this.maxH = 1; - this.count = 0; - } - - public void Clean() - { - for (int i=0;i 0.8F) Add(i, d + 1, inc-1, o); - } - } - - //Add all points of an edge from which we know its angle - const int ANGLE_INTERVAL = 3; - public void Add(SKPointI pIn, SKPointI pEnd, int inc, object o, int iAngle) - { - int angleIn = iAngle - ANGLE_INTERVAL; if (angleIn < 0) angleIn = 0; - int angleEnd = iAngle + ANGLE_INTERVAL + 1; if (angleEnd > hWidth) angleEnd = hWidth; - - for (int i = angleIn; i < angleEnd; i++) - { - float DIn = ((float)pIn.X * cosO[i] + (float)pIn.Y * sinO[i]) / distInc; - float DEnd = ((float)pEnd.X * cosO[i] + (float)pEnd.Y * sinO[i]) / distInc; - int dIn = (int)(Math.Floor((double)(DIn))); - int dEnd = (int)(Math.Floor((double)(DEnd))); - if (dIn > dEnd) { int tmp = dIn; dIn = dEnd; dEnd = tmp; } - - dIn += hHeight / 2; - dEnd += hHeight / 2; - - int dist = i - iAngle; dist *= dist; - //int iInc = inc / (1+dist); - - int iInc = ANGLE_INTERVAL*ANGLE_INTERVAL + 1 - dist; - for (int d=dIn; d<=dEnd; d++) Add(i, d, iInc, o); - } - } - - void Add(int angle, int d, int inc, object o) - { - if (h[angle][d] == null) { h[angle][d] = new HoughCell(angle, d); count++; } - int n = h[angle][d].Add(inc, o); - if (n > maxH) maxH = n; - } - - //Add a point to the candidate cells knowing its direction - public void Add(SKPointI pIn, SKPointI pEnd, float angle, int inc, object o) - { - if (angle<0F) angle+=(float)Math.PI; - int iAngle = (int)Math.Floor(angle * (float)hWidth / (float)Math.PI); - Add(pIn, pEnd, inc, o, iAngle); - } - - //debug method to get an image of the hough transform - public SKBitmap GetImage() - { - var bmp = new SKBitmap(hWidth, hHeight); - using (var canvas = new SKCanvas(bmp)) - { - canvas.Clear(SKColors.Black); - - // Draw yellow cross lines - using (var yellowPaint = new SKPaint - { - Color = SKColors.Yellow, - StrokeWidth = 1, - IsAntialias = true - }) - { - canvas.DrawLine(new SKPoint(0, hHeight / 2), new SKPoint(hWidth, hHeight / 2), yellowPaint); - canvas.DrawLine(new SKPoint(hWidth / 2, 0), new SKPoint(hWidth / 2, hHeight), yellowPaint); - } - - int minAng = -1, minCount = hHeight; - - for (int i = 0; i < hWidth; i++) - { - int count = 0; - for (int d = 0; d < hHeight; d++) - { - if (h[i][d] != null && h[i][d].Count > 0) - { - count++; - int g = h[i][d].Count * 255 / maxH; - g *= 4; - if (g > 255) g = 255; - if (g > 0) - { - // Draw pixel in grayscale - bmp.SetPixel(i, d, new SKColor((byte)g, (byte)g, (byte)g)); - } - } - } - if (count < minCount) - { - minCount = count; - minAng = i; - } - } - - // Draw the angle text - using (var textPaint = new SKPaint - { - Color = SKColors.Blue, - TextSize = 14, - IsAntialias = true, - Typeface = SKTypeface.FromFamilyName("Arial") - }) - { - string text = ((float)minAng * 180F / (float)hWidth).ToString("F2", System.Globalization.CultureInfo.InvariantCulture); - canvas.DrawText(text, 10, 20, textPaint); - } - - return bmp; - } - } - /* - public SKBitmap GetImage() - { - SKBitmap bmp = new SKBitmap(hWidth, hHeight); - Graphics gc = Graphics.FromImage(bmp); - gc.FillRectangle(Brushes.Black, 0, 0, hWidth, hHeight); - gc.DrawLine(Pens.Yellow, new SKPointI(0, hHeight / 2), new SKPointI(hWidth, hHeight / 2)); - gc.DrawLine(Pens.Yellow, new SKPointI(hWidth / 2, 0), new SKPointI(hWidth / 2, hHeight)); - int minAng = -1, minCount = hHeight; - for (int i = 0; i < hWidth; i++) - { - int count = 0; - for (int d = 0; d < hHeight; d++) if (h[i][d] != null && h[i][d].Count > 0) - { - count++; - int g = h[i][d].Count * 255 / maxH; - g *= 4; if (g > 255) g = 255; - if (g > 0) bmp.SetPixel(i, d, Color.FromArgb(g, g, g)); - } - if (count= 0F && a <= PI_2) - { - float cos = (float)Math.Cos(a); if (cos < 0) cos = 1e-15F; - float sin = (float)Math.Sin(a); if (sin < 0) sin = 1e-15F; - float dw = (float)width * cos; - float dh = (float)height * sin; - if (dw < dh) - if (d < dw) { yIn = (int)(d / sin); xEnd = (int)(d / cos); } - else if (d < dh) - { - yIn = (int)(d / sin); - yEnd = (int)((d - (float)width * cos) / sin); xEnd = width; - } - else - { - xIn = (int)((d - (float)height * sin) / cos); yIn = height; - yEnd = (int)((d - (float)width * cos) / sin); xEnd = width; - } - else //dh PI_2 && a <= PI) - { - float cos = (float)Math.Cos(a); if (cos > 0) cos = -1e-15F; - float sin = (float)Math.Sin(a); if (sin < 0) sin = 1e-15F; - float dwh = (float)width * cos + (float)height * sin; - - if (dwh<0F) - if (d < dwh) { - xIn=(int)(d/cos) ; yIn=0; - xEnd = width; yEnd=(int)((d-(float)width*cos)/sin); - } - else if (d < 0F) - { - xIn = (int)(d / cos); yIn = 0; - xEnd = (int)((d - (float)height * sin) / cos); yEnd = height; - } - else - { - xIn = 0; yIn = (int)(d / sin); - xEnd = (int)((d - (float)height * sin) / cos); yEnd = height; - } - else - if (d < 0F) { - xIn=(int)(d/cos); yIn=0; - xEnd=width; yEnd=(int)((-d-(float)width*cos)/sin); - } - else if (d < dwh) - { - xIn=0; yIn=(int)(d/sin); - xEnd=width; yEnd=(int)((d-(float)width*cos)/sin); - } - else - { - xIn=0;yIn=(int)(d/sin); - xEnd=(int)((d-(float)height*sin)/cos); yEnd=height; - } - } - return new SKPointI[] { new SKPointI(xIn, yIn), new SKPointI(xIn, yIn + (int)distInc), new SKPointI(xEnd, yEnd + (int)distInc), new SKPointI(xEnd, yEnd), new SKPointI(xIn, yIn) }; - } - - } - - //A cell in the hough space, defined by its angle (x coord) and dist (y coord) - //Holds a list of all objects (Parts) and an object counter and a weight accumulator. - //Weight allows to sort cells not only for the number of contained objects (for example, - //if we want to priorize big sized parts...). - internal class HoughCell : IComparable - { - int angle,dist; - int count, weight; - LinkedList objects; - public HoughCell(int angle, int dist) - { - this.angle = angle; - this.dist = dist; - this.count = this.weight = 0; - this.objects = new LinkedList(); - } - public int Add(int n, object o) - { - count += 1; - weight+= n; - if (o!=null) objects.AddLast(o); - return count; - } - - public int CompareTo(object o) { - HoughCell c = (HoughCell)o; - return c.weight- this.weight; - } - - public int Count { get { return count; } } - public int Angle { get { return angle; } } - public int Distance { get { return dist; } } - public LinkedList Objects { get { return objects; } } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/IDecoderAscDescBars.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/IDecoderAscDescBars.cs deleted file mode 100644 index 8bdd948d..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/IDecoderAscDescBars.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace BarcodeReader.Core.Common -{ -#if CORE_DEV - public -#else - internal -#endif - interface IDecoderAscDescBars - { - string Decode(bool[][] samples, out float confidence); - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/ImageScaner.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/ImageScaner.cs deleted file mode 100644 index f63423fd..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/ImageScaner.cs +++ /dev/null @@ -1,906 +0,0 @@ -using SkiaSharp; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Drawing; - -namespace BarcodeReader.Core.Common -{ -#if CORE_DEV - public -#else - internal -#endif - interface IImageScaner - { - int Width { get; } - int Height { get; } - bool In(MyPoint p); - bool isBlack(MyPoint p); - float[] GetPixels(MyPoint a, MyPoint b); - } - -#if CORE_DEV - public -#else - internal -#endif - class ImageScaner : IImageScaner - { - public static readonly int LEFT = -1; - public static readonly int RIGHT = 1; - public static readonly int UP = -1; - public static readonly int DOWN = 1; - public static readonly MyPoint LU = new MyPoint(LEFT, UP); - public static readonly MyPoint LD = new MyPoint(LEFT, DOWN); - public static readonly MyPoint RU = new MyPoint(RIGHT, UP); - public static readonly MyPoint RD = new MyPoint(RIGHT, DOWN); - public static readonly MyPoint[] DIRS = new MyPoint[] { LU, LD, RU, RD }; -#if DEBUG_IMAGE - SKBitmap bb = null; - Graphics g = null; -#endif - - BlackAndWhiteImage image; - float bwThreshold; - - public ImageScaner(BlackAndWhiteImage image) - { - this.image = image; -#if DEBUG_IMAGE - image.GetAsBitmap().Save(@"outRaw.png"); - Reset(); -#endif - } - public float BWThreshold { get { return bwThreshold; } set { bwThreshold = value; } } - public int Width { get { return image.Width; } } - public int Height { get { return image.Height; } } - public bool InX(int x) { return x >= 0 && x < image.Width; } - public bool InY(int y) { return y >= 0 && y < image.Height; } - public bool InX(float x) { return x >= 0F && x < (float)image.Width; } - public bool InY(float y) { return y >= 0F && y < (float)image.Height; } - public bool In(MyPoint p) { return InX(p.X) && InY(p.Y); } - public bool In(MyPointF p) { return InX(p.X) && InY(p.Y); } - - public bool InBorderX(int x) { return x >= -1 && x <= image.Width; } - public bool InBorderY(int y) { return y >= -1 && y <= image.Height; } - public bool InBorder(MyPoint p) { return InBorderX(p.X) && InBorderY(p.Y); } - - public XBitArray GetColumn(int x) { return image.GetColumn(x); } - public XBitArray GetRow(int y) { return image.GetRow(y); } - - public bool isBlack(MyPoint p) { return isBlack(p.X, p.Y); } - public bool isBlack(int x, int y) - { - if (y >= 0 && y < image.Height && x >= 0 && x < image.Width) - return image.GetRow(y)[x]; - return false; //by default returns white - } - - public float getGray(MyPoint p) - { - return isBlack(p) ? 0F : 1F; - //Comment the previous lines and uncomment lines below to sample the original RGB image - //if (In(p)) return colorImage.GetPixel(p.X, p.Y).GetBrightness(); - //return 1.0F; - } - - public bool getGrayIsBlack(MyPoint p) - { - return isBlack(p); - //Comment the previous lines and uncomment lines below to sample the original RGB image - //return getGray(p) < bwThreshold; - } - - public bool getSampleSimple(MyPoint p) - { -#if DEBUG_IMAGE - if (In(p)) - { - setPixel(p, Color.Orange); //paint pixel center - setPixel(p+new MyPoint(1,0), isBlack(p) ? Color.Black : Color.White); //paint pixel center - } -#endif - return !isBlack(p); - } - - public bool isBlackSample(MyPointF point, float moduleLength) - { - return getSample(point, moduleLength) < this.bwThreshold; - } - - //Main sample function: uses 4 neighbours and ponderate them according to its distance to point - public float getSample(MyPointF point, float moduleLength) - { - - MyPoint p00 = new MyPoint((int)Math.Truncate(point.X), (int)Math.Truncate(point.Y)); - float c = 0f; - float incX = point.X - p00.X; - float incY = point.Y - p00.Y; - if (moduleLength < 5f) - { - MyPoint[] samples = new MyPoint[4]; - if (incX < 0.5F) - if (incY < 0.5F) - { - samples[0] = new MyPoint(p00.X, p00.Y); - samples[1] = new MyPoint(p00.X - 1, p00.Y); - samples[2] = new MyPoint(p00.X, p00.Y - 1); - samples[3] = new MyPoint(p00.X - 1, p00.Y - 1); - } - else - { - samples[0] = new MyPoint(p00.X, p00.Y); - samples[1] = new MyPoint(p00.X - 1, p00.Y); - samples[2] = new MyPoint(p00.X, p00.Y + 1); - samples[3] = new MyPoint(p00.X - 1, p00.Y + 1); - incY = 1F - incY; - } - else - if (incY < 0.5F) - { - samples[0] = new MyPoint(p00.X, p00.Y); - samples[1] = new MyPoint(p00.X + 1, p00.Y); - samples[2] = new MyPoint(p00.X, p00.Y - 1); - samples[3] = new MyPoint(p00.X + 1, p00.Y - 1); - incX = 1F - incX; - } - else - { - samples[0] = new MyPoint(p00.X, p00.Y); - samples[1] = new MyPoint(p00.X + 1, p00.Y); - samples[2] = new MyPoint(p00.X, p00.Y + 1); - samples[3] = new MyPoint(p00.X + 1, p00.Y + 1); - incX = 1F - incX; - incY = 1F - incY; - } - float c00 = getGray(samples[0]); - float c10 = getGray(samples[1]); - float c01 = getGray(samples[2]); - float c11 = getGray(samples[3]); - c = c00 * (0.5F + incX) * (0.5F + incY) + - c10 * (0.5F - incX) * (0.5F + incY) + - c01 * (0.5F + incX) * (0.5F - incY) + - c11 * (0.5F - incX) * (0.5F - incY); - } - else - { - MyPoint[] samples = new MyPoint[5]; - incX = incY = 0f; - int inc = (int)Math.Round(moduleLength / 5f); - samples[0] = new MyPoint(p00.X+inc, p00.Y+inc); - samples[1] = new MyPoint(p00.X + inc, p00.Y-inc); - samples[2] = new MyPoint(p00.X-inc, p00.Y - inc); - samples[3] = new MyPoint(p00.X - inc, p00.Y + inc); - samples[4] = new MyPoint(p00.X , p00.Y ); - - c = 0f; - for (int i = 0; i < samples.Length; i++) c += getGray(samples[i]); - c /= (float)samples.Length; - } - - -#if DEBUG - /*if (In(p00) && false) - { - //foreach (MyPoint p in samples) - // if (In(p)) bb.SetPixel(5 * p.X - 2, 5 * p.Y - 2, Color.Orange); - MyPoint pm = point * 5F; - if (In(point)) - { - setPixel(point, Color.Yellow); - setPixel(point,new MyPoint(1,0), c > bwThreshold ? Color.White : Color.Black); - } - }*/ -#endif -#if DEBUG_IMAGE - if (In(p00)) - { - setPixel(point, Color.Orange); //paint pixel center - setPixel(point + new MyPointF(1f, 0f), c<0.5F ? Color.Black : Color.White); //paint pixel center - } -#endif - return c; - } - - - static readonly float c0 = 2F / 3F; - static readonly float c45 = (float)Math.Sqrt(2) / 3F; - static readonly MyPointF[] moduleSamples = new MyPointF[]{new MyPointF(0F,0F), - new MyPointF(c0,0F), new MyPointF(-c0,0F), - new MyPointF(0F,c0), new MyPointF(0F,-c0), - new MyPointF(c45,c45), new MyPointF(-c45,c45), - new MyPointF(c45,-c45), new MyPointF(-c45,-c45) - }; - public float getModuleGrayLevel(MyPointF p, float W) - { - int count = 0; - float grayLevel = 0F; - float w2 = W / 3F; - foreach(MyPointF offset in moduleSamples) { - MyPointF pi = p + offset * w2; - grayLevel += 1F-getSample(pi,0f); - count++; -#if DEBUG - //setPixel(pi, System.Drawing.Color.Yellow); -#endif - } - float k = grayLevel / (float)count; - return k; - } - -#if DEBUG_IMAGE - const int SCALE = 5; - public void Reset() - { - //save image with zoom *5 and sample and 4 points (orange), and the decimal sample point (yellow) - bb = new SKBitmap(image.Width * SCALE, image.Height * SCALE); - g = Graphics.FromImage(bb); - g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; - g.DrawImage(image.GetAsBitmap(), + SCALE / 2, + SCALE / 2, bb.Width, bb.Height); - //g.DrawImage(colorImage, 0, 0, bb.Width, bb.Height); - } - - public void Save(string path) - { - try - { - bb.Save(path); - } - catch { } - } - - public void setPixel(MyPointF p, Color color) - { - setPixel(p, new MyPoint(0, 0), color); - } - public void setPixel(MyPointF p, MyPoint inc, Color color) - { - MyPoint ip=new MyPoint((int)Math.Round(p.X * SCALE/*+2.5F*/), (int)Math.Round(p.Y * SCALE/*+2.5F*/)); - ip = ip + inc; - if (ip.X>=0 && ip.X=0 && ip.Y= 0 && !end) - { - XBitArray nextCol = image.GetColumn(pIn.X); - int next = FindTransition(nextCol, pIn.Y, current, Ydir); - if (next == -1) { end = true; pIn.X++; } - else - { - int expected = p.Y + acum * (n + 1) / n; - d = expected - next; - if (d < MAX_INC_SLOPE && d > -MAX_INC_SLOPE) - { - acum += (next - pIn.Y); - n++; - pIn.X--; pIn.Y = next; - } - else { end = true; pIn.X++; } - } - } - - //to right - end = false; - acum = 0; n = 1; - pEnd = new MyPoint(p.X + 1, p.Y); - while (pEnd.X < image.Width && !end) - { - XBitArray nextCol = image.GetColumn(pEnd.X); - int next = FindTransition(nextCol, pEnd.Y, current, Ydir); - if (next == -1) { end = true; pEnd.X--; } - else - { - int expected = p.Y + acum * (n + 1) / n; - d = expected - next; - if (d < MAX_INC_SLOPE && d > -MAX_INC_SLOPE) - { - acum += (next - pEnd.Y); - n++; - pEnd.X++; pEnd.Y = next; - } - else { end = true; pEnd.X--; } - } - } - } - - //starting from p point, follows a "vertical" edge (135º..45º) until the end. - //p: start point - //pIn, pOut: found segment - //yDir: y direction - const int GOOD_INC_SLOPE = 1; - const int MAX_INC_SLOPE = 6; - public void FindVLine(MyPoint p, out MyPoint q, bool current, MyPoint dir) - { - bool end = false; - MyPoint lastGood = q = new MyPoint(p.X, p.Y + dir.Y); - int acum = 0, n = 1, d; - while (q.Y >= 0 && q.Y < image.Height && !end) - { - XBitArray nextCol = (XBitArray)image.GetRow(q.Y); - int next = FindTransition(nextCol, q.X, current, dir.X); - if (next == -1) { end = true; q.Y -= dir.Y; } - else - { - float expected = (float)p.X + ((float)(2F * acum) / (float)(n)); - d = (int)Math.Round(expected) - next; - if (d < MAX_INC_SLOPE && d > -MAX_INC_SLOPE) - { - if (d <= GOOD_INC_SLOPE && d >= -GOOD_INC_SLOPE) lastGood = new MyPoint(next, q.Y); - acum += (next - p.X); - n++; - q.Y += dir.Y; q.X = next; - } - else { end = true; q.Y -= dir.Y; } - } - } - q = lastGood; - if (q.Y == image.Height) q.Y--; - else if (q.Y == -1) q.Y++; - } - - //starting from p point, follows a "horizontal" edge (-45º..45º) until the end. - //p: start point - //current: true is p is black - public void FindHLine(MyPoint p, out MyPoint q, bool current, MyPoint dir) - { - bool end = false; - MyPoint lastGood = q = new MyPoint(p.X + dir.X, p.Y); - int acum = 0, n = 1, d; - while (q.X >= 0 && q.X < image.Width && !end) - { - XBitArray nextCol = (XBitArray)image.GetColumn(q.X); - int next = FindTransition(nextCol, q.Y, current, dir.Y); - if (next == -1) { end = true; q.X -= dir.X; } - else - { - float expected = (float)p.Y + ((float)(2F * acum) / (float)(n)); - d = (int)Math.Round(expected) - next; - if (d < MAX_INC_SLOPE && d > -MAX_INC_SLOPE) - { - if (d <= GOOD_INC_SLOPE && d >= -GOOD_INC_SLOPE) lastGood = new MyPoint(q.X, next); - acum += (next - p.Y); - n++; - q.X += dir.X; q.Y = next; - } - else { end = true; q.X -= dir.X; } - } - } - q = lastGood; - if (q.X == image.Width) q.X--; - else if (q.X == -1) q.X++; - } - - - //current: current pixel is white or black - //previous bits[start]==current - //previous bits[start+dir]==!current - const int MAX_SLOPE = 4; - int FindTransition(XBitArray bits, int start, bool current, int dir) - { - int pos = start; - int inc = 1; - if (pos + dir < 0 || pos + dir >= bits.Size) return -1; - if (bits[pos] == current && bits[pos + dir] == !current) return pos; //tribial solution - if (bits[pos] == !current) //search backwards - { - pos -= dir; - while (inc < MAX_SLOPE && pos >= 0 && pos < bits.Size && bits[pos] != current) { pos -= dir; inc++; } - if (pos < 0 || pos >= bits.Size || inc >= MAX_SLOPE) return -1; - } - else //search forward - { - pos += dir; - while (inc < MAX_SLOPE && pos >= 0 && pos < bits.Size && bits[pos] == current) { pos += dir; inc++; } - if (pos <= 0 || pos >= bits.Size || inc >= MAX_SLOPE) return -1; - pos -= dir; //one pixel back to point agains to current - } - return pos; - } - - - //Percetage of fixels of onBlack color from point a to b, for continuous side - public float GetPercentOn(bool onBlack, SKPointI a, SKPointI b) - { - int total = 0, on = 0; - Bresenham br = new Bresenham(a, b); - while (!br.End()) - { - bool next = isBlack(br.Current.X, br.Current.Y); - if (!next && !onBlack || next && onBlack) on++; - total++; - br.Next(); - } - return (float)on / (float)total; - } - - //Percetage of fixels of onBlack color from point a to b, for dashed side - public float GetPercentOn(bool onBlack, bool skipFirsts, MyPoint a, MyPoint b, out int nPairs, out int most, out float regularity) - { - int total = 0, on = 0; - SortedDictionary bars = new SortedDictionary(); - int n = 0; - nPairs = 0; - bool current = false, first = false, next = false; - Bresenham br = new Bresenham(a, b); - if (skipFirsts) while (!br.End() && isBlack(br.Current) != onBlack) { br.Next(); total++; }//move to the first pixel onBlack - if (!br.End()) - { - first = current = isBlack(br.Current.X, br.Current.Y); - while (!br.End()) - { - next = isBlack(br.Current.X, br.Current.Y); - //histogram pairs - if (next != current) //new transition - { - if (next == first) //only record each pair to avoid different saturation of black or white - { - if (bars.ContainsKey(n)) bars[n]++; - else bars.Add(n, 1); - nPairs += 1; //increment total number of pairs - n = 0; - } - current = next; - } - - //count pixels on - if (!next && !onBlack || next && onBlack) on++; - n++; - total++; - br.Next(); - } - } - if (n > 0 && next != first) //add last pair - { - if (bars.ContainsKey(n)) bars[n]++; - else bars.Add(n, 1); - nPairs += 1; - } - - //find the most common - most = -1; - ArrayList repeated = new ArrayList(); - foreach (int i in bars.Keys) - if (most == -1 || bars[i] > bars[most]) - { - most = i; - repeated.Clear(); - repeated.Add(i); - } - else if (most != -1 && bars[i] == bars[most]) repeated.Add(i); - if (most != -1) - { - most = (int)repeated[repeated.Count / 2]; //if repeated, take the middle - int inc = most * 10 / 100 + 1; - float m = 0f; - for (int i = most - inc; i <= most + inc; i++) - if (bars.ContainsKey(i)) - { - var weight = 1f / (Math.Abs(i - most) + 1); - m += bars[i] * weight; - } - regularity = (float)m / (float)nPairs; - } - else regularity = 0.0F; - - return (float)on / (float)total; - } - - - public void FindEnd(MyPoint A, MyPoint B, MyPoint C, out MyPoint AB) - { - MyVector vdAB = B - A; - MyVector vdAC = C - A; - int sAB = (int)vdAB.Length; - int sAC = (int)vdAC.Length; - int sideAB = (int)(vdAB.Length * 2.5F); - int sideAC = (int)(vdAC.Length * 2.5F); - MyVector normAC = vdAC.NormInt; - MyVector normAB = vdAB.NormInt; - Bresenham ab = new Bresenham(A, B), abW = new Bresenham(A, B); - Bresenham ac = new Bresenham(A, C), acW = new Bresenham(A, C); - ab.MoveTo(B); - ac.MoveTo(C); - int n; - AB = A; - bool found = false; - while (!found && In(ab.Current)) - { - AB = ab.Current; - ab.Next(); - while (isBlack(ab.Current)) ab.MoveTo(ab.Current - normAC); - acW.MoveTo(ab.Current); - n = sideAC; - while (n > 0 && !isBlack(acW.Current)) { acW.Next(); n--; } - found = (n == 0); - } - if (!found) AB = A; - else - { - //adjust to fall on black - n = sAC; - bool black = false; - while (n > 0 && !black) - { - ab.MoveTo(AB); - int m = sAB; - while (m > 0 && !isBlack(ab.Current)) { ab.Previous(); m--; } - if (m == 0) { AB += normAC; n--; } - else black = true; - } - } - } - - public void AdjustToWhite(MyPoint A, ref MyPoint B, MyPoint C, int N, int MAX) - { - MyVector vdCB = C - B; - MyVector normCB = vdCB.NormInt; - Bresenham ab = new Bresenham(A, B); - - //adjust to fall on white - int n = MAX; - bool white = false; - while (n > 0 && !white) - { - ab.MoveTo(B); - int m = N; - int count = 0; - while (m > 0 && count < MAX) - { - if (isBlack(ab.Current)) count++; - ab.Previous(); - m--; - } - if (m == 0) white = true; - else { B -= normCB; n--; } //move away - } - } - - public void AdjuntToEdge(MyPoint A, ref MyPoint B, MyPoint C, int N) - { - MyVector vdCB = C - B; - MyVector normCB = vdCB.NormInt; - Bresenham ab = new Bresenham(A, B); - - //adjust to fall on black - int n = N; - bool black = false; - while (n > 0 && !black) - { - ab.MoveTo(B); - int m = N; - while (m > 0 && !isBlack(ab.Current)) { ab.Previous(); m--; } - if (m == 0) { B += normCB; n--; } - else black = true; - } - } - - //A and B define an edge on one of the DM dashed L pattern sides. - //These points are adjusted to the border of this DM side, moving them to the dir direction. - //Uses linear regression. - public void Regression(ref MyPointF A, ref MyPointF B, bool current, bool isHorizontal, int dir) - { - //order points - bool reverse = false; - MyPointF a = A, b = B; - if (isHorizontal && B.X < A.X || !isHorizontal && B.Y < A.Y) { reverse = true; b = A; a = B; }; - - //get function - MyPoint iA = a, iB = b; //convert to int coords - int incX = iB.X > iA.X ? iB.X - iA.X : iA.X - iB.X; - int incY = iB.Y > iA.Y ? iB.Y - iA.Y : iA.Y - iB.Y; - int inc = (incY < incX ? incX : incY) + 1; - int[] f = new int[inc]; - int i = 0; - XBitArray pixels; - Bresenham br = new Bresenham(iA, iB); - while (!br.End()) - { - if (isHorizontal) pixels = InX(br.Current.X) ? image.GetColumn(br.Current.X) : null; - else pixels = InY(br.Current.Y) ? image.GetRow(br.Current.Y) : null; - int start = isHorizontal ? br.Current.Y : br.Current.X; - int N = pixels == null ? 0 : pixels.Size; - if (pixels != null && start >= 0 && start < N && pixels[start] == current) - { - start += dir; - int n = start; - while (n >= 0 && n < pixels.Size && pixels[n] == current) n += dir; - if (n-start<5) f[i++] = n - start; - } //else does not take into acount (discontinuos L pattern) - br.Next(); - } - inc = i; //use only black pixels - - //linear regression - int xy, x, y, x2; - xy = x = y = x2 = 0; - for (i = 0; i < inc; i++) - { - x += i; - y += f[i]; - xy += i * f[i]; - x2 += i * i; - } - float fx = (float)x / (float)inc; - float fy = (float)y / (float)inc; - float fxy = (float)xy / (float)inc; - float fx2 = (float)x2 / (float)inc; - - float m = (fxy - fx * fy) / (fx2 - fx * fx); - float k = fy - m * fx + 0.5F * (float)dir; - - if (Calc.Around(m, 0.0F, 0.5F)) - { - if (isHorizontal) { a.Y += k; b.Y += k + m * (float)inc; } - else { a.X += k; b.X += k + m * (float)inc; } - if (reverse) { A = b; B = a; } else { A = a; B = b; } - } - } - - - public MyPointF NextBlack(MyPointF p, MyVectorF dir, int minLength) - { - Bresenham br = new Bresenham(p, p + dir * 20); - MyPoint last = br.Current; - int count = 0; - bool prev = isBlack(br.Current); - while (In(br.Current)) - { - bool current = isBlack(br.Current); - if (prev == current) count = 0; - else if (!prev && current) count++; - if (count > minLength) break; - else if (count == 0) - { - prev = current; - last = br.Current; - } - br.Next(); - } - MyPointF r = last; - MyVector d = last - br.Current; - if (d.X < 0) r.X += 1.0F; - else if (d.X == 0) r.X += 0.5F; - if (d.Y < 0) r.Y += 1.0F; - else if (d.Y == 0) r.Y += 0.5F; - return r; - } - - public MyPointF NextTransition(MyPointF p, MyVectorF dir, float moduleLength, bool currentIsBlack) - { - Bresenham br = new Bresenham(p, dir); - MyPointF last; - //if it is wrong placed - if (getGrayIsBlack(br.Current) != currentIsBlack) - { - br.Next(); - while (In(br.Current) && getGrayIsBlack(br.Current) != currentIsBlack) br.Next(); - } - last = br.CurrentF; - - //now find transition - br.Next(); - while (In(br.Current) && getGrayIsBlack(br.Current) == currentIsBlack) - { last = br.CurrentF; br.Next(); } - - return (br.CurrentF + last) / 2F; - } - - public MyPoint FindTransition(MyPoint p, MyVector dir, bool currentIsBlack, out MyPointF fP) - { - fP = MyPointF.Empty; - Bresenham br = new Bresenham(p, p + dir); - MyPoint last = MyPoint.Empty; - if (!In(p)) //try to move again on the image - { - int n = 10; // stop after 10 pixels - while (!In(br.Current) && n >0) { last = br.Current; br.Next(); n--; } - if (n == 0) return MyPoint.Empty; //we are far away from the image - - if (getGrayIsBlack(br.Current) != currentIsBlack) - { - fP = SideOut(last, dir); - return last; - } - } - if (getGrayIsBlack(br.Current) == currentIsBlack) - { - while (In(br.Current) && getGrayIsBlack(br.Current) == currentIsBlack) { last = br.Current; br.Next(); } - if (!In(last)) return MyPoint.Empty; - fP = SideOut(last, dir); - return last; - } - else - { - while (In(br.Current) && getGrayIsBlack(br.Current) != currentIsBlack) { br.Previous(); } - if (!In(br.Current)) return MyPoint.Empty; - fP = SideOut(br.Current, dir); - return br.Current; - } - } - - MyPointF SideOut(MyPoint p, MyVector vd) - { - MyPointF fp = (MyPointF)p; - if (vd.X == -1) fp.Y += 0.5F; - else if (vd.X == 1) fp = fp + new MyVectorF(1F, 0.5F); - else if (vd.Y == 1) fp = fp + new MyVectorF(0.5F, 1F); - else fp.X += 0.5F; - return fp; - } - - MyPointF SideOut(MyPoint p, MyVectorF vd) - { - MyPointF fp = (MyPointF)p; - if (vd.X < 0) { } - else if (vd.X == 0F) fp.X += 0.5F; - else fp.X+=1F; - if (vd.Y < 0) { } - else if (vd.Y == 0F) fp.Y += 0.5F; - else fp.Y+=1F; - return fp; - } - //TODO do more samples - public bool ModuleIsBlack(MyPointF p, float width, float height, MyVectorF right, MyVectorF down) - { - return isBlackSample(p,0f); - } - - const float LIM = 0.85F; - public MyPointF FindModuleCenter(MyPointF p, bool isBlack, float width, float height, MyVectorF right, MyVectorF down) - { - //X center - bool isBlackLeft = ModuleIsBlack(p - right, width, height, right, down); - bool isBlackRight = ModuleIsBlack(p + right, width, height, right, down); - - if (isBlackLeft != isBlack && isBlackRight != isBlack) - { - MyPointF pLeft = NextTransition(p, -right, width, isBlack); - MyPointF pRight = NextTransition(p, right, width, isBlack); - if ((pLeft - p).Length < width * LIM && (pRight - p).Length < width * LIM) - p = (pLeft + pRight) / 2F; - } - else if (isBlackLeft != isBlack) - { - MyPointF pLeft = NextTransition(p, -right, width, isBlack); - if ((pLeft - p).Length < width * LIM ) - p = pLeft + right / 2F; - } - else if (isBlackRight != isBlack) - { - MyPointF pRight = NextTransition(p, right, width, isBlack); - if ((pRight - p).Length < width * LIM) - p = pRight - right / 2F; - } - - - //Y center - bool isBlackUp = ModuleIsBlack(p - down, width, height, right, down); - bool isBlackDown = ModuleIsBlack(p + down, width, height, right, down); - if (isBlackUp!=isBlack && isBlackDown!=isBlack) - { - MyPointF pUp = NextTransition(p, -down, height, isBlack); - MyPointF pDown = NextTransition(p, down, height, isBlack); - if ((pUp - p).Length < height*LIM && (pDown - p).Length < height*LIM) - p = (pUp + pDown) / 2F; - } - else if (isBlackUp!=isBlack) - { - MyPointF pUp = NextTransition(p, -down, height, isBlack); - if ((pUp - p).Length < height*LIM) - p = pUp + down / 2F; - } - else if (isBlackDown!=isBlack) - { - MyPointF pDown = NextTransition(p, down, height, isBlack); - if ((pDown - p).Length < height*LIM) - p = pDown - down / 2F; - } - return p; - } - - public void setBWThreshold(MyPoint a, MyPoint b) - { - Bresenham br = new Bresenham(a, b); - float grayWhite = 0F, grayBlack = 1F; - while (!br.End()) - { - float g = getGray(br.Current); - if (g < grayBlack) grayBlack = g; - if (g > grayWhite) grayWhite = g; - br.Next(); - } - BWThreshold = (grayWhite + grayBlack) / 2F; - } - - public void setBWThreshold(float f) - { - BWThreshold = f; - } - - //dir =(-1,0) or (0,-1)... - public void FindModule(MyPoint center, MyVector dir, bool currentIsBlack, out MyPoint left, out MyPoint right, out MyPoint middle, out float dist) - { - left = center; - right = center; - while (In(left) && getGrayIsBlack(left) == currentIsBlack) left += dir; //move left - while (In(right) && getGrayIsBlack(right) == currentIsBlack) right -= dir; //move right - middle = (left + right) / 2; - dist = (left - right).Length; - } - - public float[] GetPixels(MyPoint a, MyPoint b) - { - Bresenham br = new Bresenham(a, b); - var res = new float[br.Steps]; - - var i = 0; - while (br.Steps > 0) - { - var p = br.Current; - res[i++] = image.GetRow(p.Y)[p.X] ? -1 : 1; - br.Next(); - } - - return res; - } - - public float[] GetPixels(MyPoint a, MyPoint b, float scale) - { - var dir = (b - a); - var len = dir.Length; - var steps = (int)(len * scale); - var res = new float[steps]; - float x = a.X; - float y = a.Y; - var dx = dir.X / (float)steps; - var dy = dir.Y / (float)steps; - for (int i = 0; i < res.Length; i++) - { - res[i] = image.GetPixelInterpolated(x, y); - x += dx; - y += dy; - } - - return res; - } - - public float[] GetPixels3Lines(MyPoint a, MyPoint b, float scale, float width = 1) - { - var dir = (b - a); - var len = dir.Length; - var steps = (int)(len * scale); - var res = new float[steps]; - float x = a.X; - float y = a.Y; - var dx = dir.X / (float)steps; - var dy = dir.Y / (float)steps; - var n = (dir / len).Perpendicular * width; - - for (int i = 0; i < res.Length; i++) - { - var v1 = image.GetPixelInterpolated(x + n.X, y+ n.Y); - var v2 = image.GetPixelInterpolated(x, y); - var v3 = image.GetPixelInterpolated(x - n.X, y - n.Y); - res[i] = (v1 + v2 + v3); - x += dx; - y += dy; - } - - return res; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/LineSlicer.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/LineSlicer.cs deleted file mode 100644 index c87dc4e4..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/LineSlicer.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace BarcodeReader.Core.Common -{ - internal class LineSlicer - { - BlackAndWhiteImage bwImage; - int distToJoin; - ArrayList[] angles; - float maxVariance = 0.1f; - - int edgeId = 0; - - public float MaxVariance { get { return maxVariance; } set { maxVariance = value; } } - - public LineSlicer(BlackAndWhiteImage bwImage) { - this.bwImage = bwImage; - } - - public LinkedList GetEdges(int minRad, int maxRad, bool findHorizontalLines, bool findVerticalLines) - { - EdgeVarSlicer slicer = new EdgeVarSlicer(minRad, maxRad, findVerticalLines, findHorizontalLines, maxVariance); - return slicer.GetEdges(bwImage); - } - - //minRad,maxRad: min and max segment length during segmentation - //distToJoin: min distance to join aligned segments to create larger ones. - //minLength: min distance to remove short lines - public ArrayList[] GetLineEdges(int minRad, int maxRad, int distToJoin, int minLength, bool findHorizontalLines, bool findVerticalLines) - { - this.distToJoin = distToJoin; - LinkedList edges = GetEdges(minRad, maxRad, findHorizontalLines, findVerticalLines); - - //classify segments by angle - float PI = (float)Math.PI; - float DISCRET = 4f; - int N_ANGLES = (int)Math.Round(PI * 2f * DISCRET) + 1; - angles = new ArrayList[N_ANGLES]; - for (int i = 0; i < angles.Length; i++) angles[i] = new ArrayList(); - - foreach (EdgeVar f in edges) - { - MyPoint a = f.In; - MyPoint b = f.End; - MyVector vd = b - a; - float angle = vd.Angle; - if (angle < 0) angle += PI * 2f; - int iAngle = (int)Math.Round(angle * DISCRET); - angles[iAngle].Add(new LineEdge(a, b, f.Variance)); - } - - //join segments - int ii, jj; - for (int i = 0; i < angles.Length; i++) - { - for (int j = 0; j < angles[i].Count; j++) - { - while (JoinsWith(i, j, out ii, out jj)) - { - //JoinsWith(i, j, out ii, out jj); - ((LineEdge)angles[i][j]).Join((LineEdge)angles[ii][jj]); - angles[ii].RemoveAt(jj); - if (i == ii && jj < j) j--; - } - } - } - - //remove short segments - for (int i = 0; i < angles.Length; i++) - for (int j = 0; j < angles[i].Count; j++) - { - LineEdge s = (LineEdge)angles[i][j]; - if (s.length < minLength || s.TpcWhite>0.1f) - { - angles[i].RemoveAt(j); - j--; - } - } - - return angles; - } - - int OFFSET = 1; - bool JoinsWith(int i, int j, out int ii, out int jj) - { - ii = jj = 0; - LineEdge a = (LineEdge)angles[i][j]; - MyVector aVD = a.VD; - for (int o = -OFFSET; o <= OFFSET; o++) //try to join with prev and next discrete angles - { - ii = (i + o + angles.Length) % angles.Length; - for (jj = 0; jj < angles[ii].Count; jj++) - if (i != ii || j != jj) - { - LineEdge b = (LineEdge)angles[ii][jj]; - //if (a.variance + b.variance < 0.1f) - { - if (Calc.Around(a.Angle, b.Angle, 0.1f)) - { - float dist1 = (a.b - b.a).Length; - float dist2 = (b.b - a.a).Length; - if (dist1 < distToJoin || dist2 < distToJoin) - { - //check alignment - if (a.length != 0f && b.length != 0) - { - float dist; - MyVector vdIn, vdEnd; - if (dist1 < dist2) { dist = dist1; vdIn = b.a - a.b; vdEnd = b.b - a.b; } - else { dist = dist2; vdIn = b.b - a.a; vdEnd = b.a - a.a; } - - float crossIn = MyVector.crossProduct(aVD, vdIn); //cross product is the area of the parallelapipede formed by the two vectors - float angleIn = crossIn / a.length / vdIn.Length; - - float crossEnd = MyVector.crossProduct(aVD, vdEnd); //cross product is the area of the parallelapipede formed by the two vectors - float angleEnd = crossEnd / a.length / vdEnd.Length; - - float meanError = crossEnd / 2f / (a.length + vdEnd.Length); - - float threshold = 1f / dist + 0.2f; - - - if (//Calc.Around(angleIn, 0f, threshold) && - Calc.Around(angleEnd, 0f, 0.2f) && - Calc.Around(meanError, 0f, 1f)) return true; - } - } - } - } - } - } - return false; - } - - public EdgeVar mirror(EdgeVar e) { - return e.mirror(edgeId++); - } - } - - internal class LineEdge : IComparable - { - public MyPoint a, b; - public float length, angle, variance, whitePixels; - - public LineEdge(MyPoint a, MyPoint b, float variance) - { - this.a = a; - this.b = b; - this.length = (a - b).Length; - this.angle = (float)Math.Atan2(this.b.Y - this.a.Y, this.b.X - this.a.X); - this.variance = variance; - this.whitePixels = 0f; - } - - public void Join(LineEdge b) - { - //check if need to join to the left or to the right - float dist1 = (this.b - b.a).Length; - float dist2 = (b.b - this.a).Length; - float dist=dist2; - if (dist1 < dist2) { this.b = b.b; dist=dist1; } - else this.a = b.a; - this.length = (this.a - this.b).Length; - this.angle = (float)Math.Atan2(this.b.Y - this.a.Y, this.b.X - this.a.X); - this.variance += b.variance; - this.whitePixels += b.whitePixels + dist; - } - - public override string ToString() - { - return a.ToString() + " -- " + b.ToString()+ "L:"+length+" ("+angle+"º) var:"+variance; - } - - public int CompareTo(object b) - { - LineEdge s = (LineEdge)b; - return this.a.Y - s.a.Y; - } - - public float Angle { get { return this.angle; } } - - public MyVector VD{ get { return b-a; } } - - public MyVectorF NormalizedVdY() - { - MyVectorF vdy = this.VD.Perpendicular; - return vdy.Normalized; - } - - public float Variance { get { return this.variance; } } - - public float TpcWhite { get { return whitePixels / (a - b).Length; } } - - public float Dist(MyPoint p) - { - float m = (this.a - this.b).Length; - float d=((b.X-a.X)*(a.Y-p.Y)-(a.X-p.X)*(b.Y-a.Y))/m; - return d > 0 ? d : -d; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/MyPoints.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/MyPoints.cs deleted file mode 100644 index f88517f4..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/MyPoints.cs +++ /dev/null @@ -1,578 +0,0 @@ -using SkiaSharp; -using System; - -namespace BarcodeReader.Core -{ - // Represents a vector with integer coordinates -#if CORE_DEV - public -#else - internal -#endif - struct MyVector - { - public int X; - - public int Y; - - public static readonly MyVector Zero = new MyVector(0, 0); - - public MyVector(int x, int y) - { - X = x; - Y = y; - } - - // length^2 - public float LengthSq - { - get - { - return (float)X*X + Y*Y; - } - } - - // length - public float Length - { - get - { - return (float)Math.Sqrt(LengthSq); - } - } - - public float Angle - { - get - { - return (float)Math.Atan2(Y, X); - } - } - - // returns another vector which is perpendicular to this one - public MyVector Perpendicular - { - get - { - return new MyVector(Y, -X); - } - } - - public MyVector NormInt - { - get - { - int x = X > 0 ? X : -X; - int y = Y > 0 ? Y : -Y; - if (x > y) return new MyVector(X>0?1:-1, 0); - return new MyVector(0, Y>0?1:-1); - } - } - - public void swap(ref MyVector v) - { - int x = X, y = Y; - X = v.X; Y = v.Y; - v.X = x; v.Y = y; - } - - public bool isHorizontal() - { - if (X > 0) - if (Y > 0) return X > Y; - else return X>-Y; - else - if (Y > 0) return -X > Y; - else return -X>-Y; - } - - public static bool operator ==(MyVector a, MyVector b) - { - return a.X == b.X && a.Y == b.Y; - } - - public static bool operator !=(MyVector a, MyVector b) - { - return !(a.X == b.X && a.Y == b.Y); - } - - public static MyVector operator + (MyVector a, MyVector b) - { - return new MyVector(a.X + b.X, a.Y + b.Y); - } - - public static MyVector operator - (MyVector a, MyVector b) - { - return new MyVector(a.X - b.X, a.Y - b.Y); - } - - // scalar multiply - public static MyVector operator *(int i, MyVector a) - { - return new MyVector(a.X * i, a.Y * i); - } - - public static MyPoint operator +(MyPoint p, MyVector a) - { - return new MyPoint(p.X + a.X, p.Y + a.Y); - } - - public static MyPoint operator -(MyPoint p, MyVector a) - { - return new MyPoint(p.X - a.X, p.Y - a.Y); - } - - public static MyVectorF operator /(MyVector a, float d) - { - return new MyVectorF(a.X / d, a.Y / d); - } - - public static float operator *(MyVector a, MyVector b) - { - return (float)(a.X*b.X+a.Y*b.Y); - } - - public static float crossProduct(MyVector a, MyVector b) - { - return a.X * b.Y - a.Y * b.X; - } - - public static implicit operator MyVectorF(MyVector vector) - { - return new MyVectorF(vector.X, vector.Y); - } - - public override string ToString() - { - return "(" + X + "," + Y + ")"; - } - - public bool Equals(MyVector other) - { - return other.X == X && other.Y == Y; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (obj.GetType() != typeof(MyVector)) return false; - return Equals((MyVector) obj); - } - - public override int GetHashCode() - { - unchecked - { - return (X * 397) ^ Y; - } - } - } - - // Represents a vector with fractional coordinates -#if CORE_DEV - public -#else - internal -#endif - struct MyVectorF - { - public float X; - - public float Y; - - public MyVectorF(float x, float y) - { - X = x; - Y = y; - } - - // length^2 - public float LengthSq - { - get - { - return X * X + Y * Y; - } - } - - // length - public float Length - { - get - { - return (float)Math.Sqrt(LengthSq); - } - } - - /// - /// Angle from (-PI ; PI] - /// - public float Angle - { - get - { - return (float)Math.Atan2(Y, X); - } - } - - private const float PI = 3.14159f; - - /// - /// Minimal angle between vectors - /// - public float AngleWith(MyVectorF c2) - { - var a = Angle - c2.Angle; - a += (a > PI) ? -2 * PI : (a < -PI) ? 2 * PI : 0; - - return a; - } - - /// - /// Normal from the line to the point (the line is defined by normalized direction) - /// - public MyVectorF NormalFromLine(MyVectorF normalizedLineDirection) - { - var cosAngle = X * normalizedLineDirection.X + Y * normalizedLineDirection.Y; - var x = normalizedLineDirection.X * cosAngle; - var y = normalizedLineDirection.Y * cosAngle; - return new MyVectorF(X - x, Y - y); - } - - // returns a vector which points the same direction but has a length of 1 - public MyVectorF Normalized - { - get - { - return this / Length; - } - } - - public MyVectorF Perpendicular - { - get - { - return new MyVectorF(Y, -X); - } - } - - public MyVectorF Rotate(float angle) - { - float ca = (float)Math.Cos(angle); - float sa = (float)Math.Sin(angle); - return new MyVectorF(X * ca - Y * sa, X * sa + Y * ca); - } - - public static MyVectorF operator +(MyVectorF a, MyVectorF b) - { - return new MyVectorF(a.X + b.X, a.Y + b.Y); - } - - public static MyVectorF operator -(MyVectorF a, MyVectorF b) - { - return new MyVectorF(a.X - b.X, a.Y - b.Y); - } - - public static MyVectorF operator -(MyVectorF v) - { - return new MyVectorF(-v.X, -v.Y); - } - - // scalar multiply - public static MyVectorF operator *(MyVectorF a, float d) - { - return new MyVectorF(a.X * d, a.Y * d); - } - - public static MyPointF operator +(MyPointF p, MyVectorF a) - { - return new MyPointF(p.X + a.X, p.Y + a.Y); - } - - public static MyPointF operator -(MyPointF p, MyVectorF a) - { - return new MyPointF(p.X - a.X, p.Y - a.Y); - } - - public static MyVectorF operator /(MyVectorF a, float d) - { - return new MyVectorF(a.X / d, a.Y / d); - } - - // dot product - public static float operator * (MyVectorF a, MyVectorF b) - { - return a.X * b.X + a.Y * b.Y; - } - - public static float crossProduct(MyVectorF a, MyVectorF b) - { - return a.X * b.Y - a.Y * b.X; - } - - public static explicit operator MyVector(MyVectorF vectorF) - { - return new MyVector((int)Math.Round(vectorF.X), (int)Math.Round(vectorF.Y)); - } - - public bool isNaN() { return float.IsNaN(X) || float.IsNaN(Y); } - - public override string ToString() - { - return string.Format("({0}:{1})", X.ToString("F2", System.Globalization.CultureInfo.InvariantCulture), Y.ToString("F2", System.Globalization.CultureInfo.InvariantCulture)); - } - } - - // Represents a point with fractional coordinates -#if CORE_DEV - public -#else - internal -#endif - struct MyPointF - { - private bool empty; - - public float X; - - public float Y; - - public bool IsEmpty - { - get - { - return empty || float.IsNaN(X) || float.IsNaN(Y); - } - } - - public bool IsInfinity - { - get - { - - if (float.IsNaN(X) || float.IsInfinity(X) || float.IsNaN(Y) || float.IsInfinity(Y)) - return true; - return false; - } - } - - public static MyPointF Empty - { - get - { - MyPointF e = new MyPointF(); - e.empty = true; - return e; - } - } - - public MyPointF(float x, float y) - { - X = x; - Y = y; - empty = false; - } - - public void Add(MyVectorF vector) - { - X += vector.X; - Y += vector.Y; - } - - public void swap(ref MyPointF v) - { - float x = X, y = Y; - X = v.X; Y = v.Y; - v.X = x; v.Y = y; - } - - public static MyPointF Lerp(MyPointF a, MyPointF b, float k) - { - return a * (1 - k) + b * k; - } - - public static MyVectorF operator -(MyPointF a, MyPointF b) - { - return new MyVectorF(a.X - b.X, a.Y - b.Y); - } - - public static MyPointF operator +(MyPointF a, MyPointF b) - { - return new MyPointF(a.X + b.X, a.Y + b.Y); - } - - public static MyPointF operator *(MyPointF a, float b) - { - return new MyPointF(a.X * b, a.Y * b); - } - - public static MyPointF operator /(MyPointF a, float b) - { - return new MyPointF(a.X / b, a.Y / b); - } - - public static implicit operator MyPoint(MyPointF pointF) - { - return new MyPoint((int)Math.Round(pointF.X), (int)Math.Round(pointF.Y)); - } - - public MyPoint Truncate() - { - return new MyPoint((int)Math.Truncate(X), (int)Math.Truncate(Y)); - } - - public static implicit operator MyPointF(MyVectorF p) - { - return new MyPointF(p.X, p.Y); - } - - public static implicit operator MyPointF(SKPointI p) - { - return new MyPointF((float)p.X, (float)p.Y); - } - - public static implicit operator SKPointI(MyPointF p) - { - return new SKPointI((int)Math.Round(p.X), (int)Math.Round(p.Y)); - } - - public override string ToString() - { - return string.Format("({0}:{1})", X.ToString("F2", System.Globalization.CultureInfo.InvariantCulture), Y.ToString("F2", System.Globalization.CultureInfo.InvariantCulture)); - } - } - - // Represents a point with integer coordinates -#if CORE_DEV - public -#else - internal -#endif - struct MyPoint : IEquatable - { - private bool empty; - - public int X; - - public int Y; - - public bool IsEmpty - { - get - { - return empty; - } - } - - public static MyPoint Empty - { - get - { - MyPoint e = new MyPoint(); - e.empty = true; - return e; - } - } - - public MyPoint(int x, int y) - { - X = x; - Y = y; - empty = false; - } - - public void swap(ref MyPoint v) - { - int x = X, y = Y; - X = v.X; Y = v.Y; - v.X = x; v.Y = y; - } - - public static MyPoint operator +(MyPoint a, MyPoint b) - { - return new MyPoint(a.X + b.X, a.Y + b.Y); - } - - public static MyVector operator -(MyPoint a, MyPoint b) - { - return new MyVector(a.X - b.X, a.Y - b.Y); - } - - public static MyPoint operator *(MyPoint a, int d) - { - return new MyPoint(a.X * d, a.Y * d); - } - - public static MyPoint operator /(MyPoint a, int d) - { - return new MyPoint(a.X / d, a.Y / d); - } - - public static implicit operator MyPoint(SKPointI p) - { - return new MyPoint(p.X, p.Y); - } - - public static implicit operator SKPointI(MyPoint p) - { - return new SKPointI(p.X, p.Y); - } - - public static implicit operator MyPointF(MyPoint point) - { - if (point.IsEmpty) - { - return MyPointF.Empty; - } - return new MyPointF(point.X, point.Y); - } - - public static bool operator ==(MyPoint a, MyPoint b) - { - if (a.IsEmpty != b.IsEmpty) - { - return false; - } - - return (a.IsEmpty && b.IsEmpty) || a.X == b.X && a.Y == b.Y; - } - - public static bool operator !=(MyPoint a, MyPoint b) - { - if (a.IsEmpty && b.IsEmpty) - { - return false; - } - - return (a.IsEmpty != b.IsEmpty) || a.X != b.X || a.Y != b.Y; - } - - public override string ToString() - { - return empty ? "empty" : "(" + X + "," + Y + ")"; - } - - public bool Equals(MyPoint other) - { - return empty == other.empty && X == other.X && Y == other.Y; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - return obj is MyPoint && Equals((MyPoint) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = empty.GetHashCode(); - hashCode = (hashCode * 397) ^ X; - hashCode = (hashCode * 397) ^ Y; - return hashCode; - } - } - } - -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/NotUniformGrid.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/NotUniformGrid.cs deleted file mode 100644 index bfb62a3d..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/NotUniformGrid.cs +++ /dev/null @@ -1,100 +0,0 @@ -namespace BarcodeReader.Core.Common -{ - internal class NotUniformGrid - { - SquareFinder LU, LD, RU; - int cols, rows, modulesFinder; - float h, w, moduleLength; - MyPointF lu, ld, ru, rd; - - public NotUniformGrid(int cols, int rows, int modulesFinder, SquareFinder LU, SquareFinder LD, SquareFinder RU) - { - this.cols = cols; - this.rows = rows; - this.modulesFinder = modulesFinder; - this.LU = LU; - this.LD = LD; - this.RU = RU; - w = (LU.C - RU.D).Length; - h = (LU.C - LD.A).Length; - this.moduleLength = w / (float)cols; - lu = LU.C; - ld = LD.A; - ru = RU.D; - //MyPointF l = interpolation(lu, ld, RU.Height / LU.Height); - //rd = l + (ru - lu)*(RU.Width/LD.Width); - rd = ld + (RU.B - LU.A); - } - - public MyPoint GetSamplePoint(int xx, int yy) - { - float x = (float)xx + 0.5f; - float y = (float)yy + 0.5f; - - float betaL = interpolation(h, rows, LU.Height, LD.Height, modulesFinder, y); - float betaR = interpolation(h, rows, RU.Height, LD.Height, modulesFinder, y); - - float wl=interpolation(LU.Width, LD.Width, betaL); - float wr=interpolation(RU.Width, LD.Width, betaR); - - float alfa = interpolation(w, cols, wl, wr, modulesFinder, x); - - MyPointF l = interpolation(lu, ld, betaL); - MyPointF r = interpolation(ru, rd, betaR); - - MyPointF p = interpolation(l, r, alfa); - return p; - } - - public void ExtractPoints(ImageScaner scan, bool[][] result) - { - for (int y = 0; y < rows; ++y) - { - for (int x = 0; x < cols; ++x) - { - MyPointF point = GetSamplePoint(x, y); - bool isBlack = moduleLength < 4f ? scan.isBlackSample(point,0f) : scan.isBlack(point); - result[y][x] = isBlack; - } - } - } - - float interpolation(float a, float b, float alfa) - { - return a * (1f-alfa) + b * alfa; - } - - MyPointF interpolation(MyPointF a, MyPointF b, float alfa) - { - return a * (1f-alfa) + b*alfa; - } - - //l-> length of the segment - //N-> number of modules - //la, lb-> length of the left and right finders - //M-> number of modules of the finders - //x-> coordinate from 0..N to convert to 0..1 - float interpolation(float l, int N, float lA, float lB, int M, float x) - { - if (x <= (float)M) - { - float alfa = lA / l; - return alfa * x / (float)M; - } - else if (x >= (float)(N - M)) - { - float beta = (l -lB) / l; - return beta *((float)N-x)/(float)M + 1f*(x-(float)(N-M))/(float)M; - } - else - { - float alfa = lA / l; - float beta = (l - lB) / l; - float L = (float)(N - 2 * M); - return alfa * ((float)(N - M) - x) / L + beta* (x - (float)M) / L; - } - } - - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Parallel.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Parallel.cs deleted file mode 100644 index d76e0523..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Parallel.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; - -namespace BarcodeReader.Core.Common -{ -#if !DISABLE_PARALLEL - internal static class Parallel - { - internal static bool DisableParallelProcessing = false; - - static int coreCount; - - static Parallel() - { - coreCount = 4; - } - - public static void For(int fromInclusive, int toExclusive, Action act) - { - if (DisableParallelProcessing) - { - for (int i = fromInclusive; i < toExclusive; i++) - act(i); - } - else - { - For(fromInclusive, toExclusive, -1, 1, (pair) => - { - var from = pair.From; - var to = pair.To; - for (int i = from; i < to; i++) - act(i); - }); - } - } - - public static void For(int fromInclusive, int toExclusive, int maxThreadsCount, int minPartitionSize, Action act) - { - if (DisableParallelProcessing) - { - act(new Pair { From = fromInclusive, To = toExclusive }); - } - else - { - var threadCount = maxThreadsCount < 1 ? coreCount : maxThreadsCount; - var partSize = Math.Max(minPartitionSize, 1 + (toExclusive - fromInclusive) / threadCount); - - var waits = new AutoResetEvent[threadCount]; - - for (int iThread = 0; iThread < threadCount; iThread++) - { - var iT = iThread; - var from = fromInclusive + iThread * partSize; - var to = Math.Min(toExclusive, fromInclusive + (iThread + 1) * partSize); - - waits[iT] = new AutoResetEvent(false); - - if (from >= to) - { - waits[iT].Set(); - continue; - } - - new Thread( - (o) => - { - var pair = new Pair { From = from, To = to }; - act(pair); - waits[iT].Set(); - } - ) - { IsBackground = true }.Start(); - } - - //wait all threads - for (int iThread = 0; iThread < threadCount; iThread++) - waits[iThread].WaitOne(); - } - } - - public struct Pair - { - public int From; - public int To; - - public override string ToString() - { - return From + " - " + To; - } - } - } -#endif - -#if DISABLE_PARALLEL - internal static class Parallel - { - public static void For(int fromInclusive, int toExclusive, Action act) - { - for (int i = fromInclusive; i < toExclusive; i++) - act(i); - } - - public static void For(int fromInclusive, int toExclusive, int maxThreadsCount, int minPartitionSize, Action act) - { - act(new Pair { From = fromInclusive, To = toExclusive }); - } - - public struct Pair - { - public int From; - public int To; - - public override string ToString() - { - return From + " - " + To; - } - } - } - -#endif - -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/PatternFinderNoise.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/PatternFinderNoise.cs deleted file mode 100644 index 2be01920..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/PatternFinderNoise.cs +++ /dev/null @@ -1,503 +0,0 @@ -using System.Collections.Generic; - -namespace BarcodeReader.Core.Common -{ -#if CORE_DEV - public -#else - internal -#endif - class Pattern - { - public FoundPattern foundPattern; - public int xIn, xEnd, y; - - public Pattern(FoundPattern foundPattern, int xIn, int xEnd, int y) - { - this.foundPattern = foundPattern; - this.xIn = xIn; - this.xEnd = xEnd; - this.y = y; - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - - - public override bool Equals(object obj) - { - if (obj.GetType() == typeof(Pattern)) - { - Pattern p = (Pattern)obj; - return (this.foundPattern==null || p.foundPattern==null || this.foundPattern.nPattern==p.foundPattern.nPattern) - && dist(p.xIn, xIn) <= 3 && dist(p.xEnd, xEnd) <= 3; - } - return base.Equals(obj); - } - - int dist(int a, int b) - { - int d = a - b; - if (d < 0) return -d; - return d; - } - - public static LinkedList RemoveOldPatterns(LinkedList foundPatterns, int y) - { - /*LinkedList removed = new LinkedList(); - foreach (Pattern p in foundPatterns) - if (p.y < y - 2) removed.AddLast(p); - foreach (Pattern p in removed) - foundPatterns.Remove(p); - return removed;*/ - - LinkedList removed = new LinkedList(); - - var node = foundPatterns.First; - - while (node != null) - { - var next = node.Next; - if (node.Value.y < y - 2) - { - foundPatterns.Remove(node); - removed.AddLast(node.Value); - } - node = next; - } - - return removed; - } - - public static LinkedList RemoveOldPatterns(LinkedList foundPatterns, int y, float tpc) - { - LinkedList removed = new LinkedList(); - foreach (StackedPattern p in foundPatterns) - if ((y - p.y)>(int)((float)(p.y-p.startY)*tpc)) removed.AddLast(p); - foreach (Pattern p in removed) - foundPatterns.Remove(p); - return removed; - } - } - - class StackedPattern : Pattern - { - public int nPattern; - public int startXIn, startXEnd, startY; - LinkedList starts, ends; - int sumWidths; - - public StackedPattern(FoundPattern foundPattern, int xIn, int xEnd, int y): base(foundPattern, xIn, xEnd, y) - { - this.nPattern = foundPattern==null?0:foundPattern.nPattern; - startXIn = xIn; - startXEnd = xEnd; - startY = y; - starts = new LinkedList(); - starts.AddLast(new MyPoint(xIn, y)); - ends = new LinkedList(); - ends.AddLast(new MyPoint(xEnd, y)); - sumWidths = (xEnd - xIn); - } - - public void NewRow(int xIn, int xEnd, int y) - { - this.xIn = xIn; - this.xEnd = xEnd; - this.y = y; - starts.AddLast(new MyPoint(xIn, y)); - ends.AddLast(new MyPoint(xEnd, y)); - sumWidths += (xEnd - xIn); - } - - public void Center(out MyPoint left, out MyPoint right) - { - LinkedList.Enumerator l = starts.GetEnumerator(); - LinkedList.Enumerator r = ends.GetEnumerator(); - for (int i = 0; i < starts.Count / 2; i++) - { - l.MoveNext(); - r.MoveNext(); - } - left = l.Current; - right = r.Current; - } - - public void MidPoints(out MyPoint start, out MyPoint end) - { - LinkedList.Enumerator s = starts.GetEnumerator(); - LinkedList.Enumerator e = ends.GetEnumerator(); - e.MoveNext(); - s.MoveNext(); //move to the first element - for (int i = 0; i < starts.Count / 2; i++) { s.MoveNext(); e.MoveNext(); } - start=s.Current; - end = e.Current; - } - - public float MeanWidth() - { - return (float)sumWidths / (float)starts.Count; - } - - public LinkedList LPoints { get { return this.starts; } } - public LinkedList RPoints { get { return this.ends; } } - } - - //Class to find bit patterns allowing noise removing. Noise level definies the number of pixels considered as noise. - //noiseLevel=1: accept modules 1 pixel wide. - //noiseLevel=2: accept modules 2 pixel wide. - //... - //PatternFinder mantains an state. When a pattern is found the state is saved and the method returns. When - // NextPattern is called, the state is restored and the search continues. - class PatternFinderNoise - { - BlackAndWhiteImage image; - int[][] pattern; - bool startsWithBlack; - int noiseLevel, defaultNoiseLevel; - - int[][] patternE; - int patternLength, patternELength; - int[] N; //number of modules of each pattern - - Bresenham l; - bool end; //stops at the end of l or at the end of the image - - LevelStatus[] status; - int foundAtLevel; - int currentY; - XBitArray row; - - private void Initialize(BlackAndWhiteImage image, int[][] pattern, bool startsWithBlack, int noiseLevel, Dictionary hash) - { - this.image = image; - this.pattern = pattern; - this.startsWithBlack = startsWithBlack; - this.defaultNoiseLevel = noiseLevel; - - this.patternLength = pattern[0].Length; - this.patternELength = pattern[0].Length-1; - this.patternE = new int[pattern.Length][]; - this.N = new int[pattern.Length]; - for (int j = 0; j < pattern.Length; j++) - { - int[] P = pattern[j]; - int[] E = patternE[j] = new int[patternELength]; - int n = 0; - for (int i = 0; i < patternELength; i++) - { - E[i] = P[i] + P[i + 1]; - n += P[i]; - } - N[j]= n + P[patternELength]; - } - this.status = new LevelStatus[defaultNoiseLevel]; - } - - public PatternFinderNoise(BlackAndWhiteImage image, int[] pattern, bool startsWithBlack, int noiseLevel) - { - Initialize(image, new int[][] { pattern }, startsWithBlack, noiseLevel, new Dictionary(1000)); - } - - public PatternFinderNoise(BlackAndWhiteImage image, int[] pattern, bool startsWithBlack, int noiseLevel, Dictionary hash) - { - Initialize(image, new int[][]{pattern}, startsWithBlack, noiseLevel, hash); - } - - public PatternFinderNoise(BlackAndWhiteImage image, int[][] pattern, bool startsWithBlack, int noiseLevel) - { - Initialize(image, pattern, startsWithBlack, noiseLevel, new Dictionary(1000)); - } - - public PatternFinderNoise(BlackAndWhiteImage image, int[][] pattern, bool startsWithBlack, int noiseLevel, Dictionary hash) - { - Initialize(image, pattern, startsWithBlack, noiseLevel, hash); - } - - public PatternFinderNoise NewFinder() - { - return new PatternFinderNoise(image, pattern, startsWithBlack, noiseLevel); - } - - bool End() - { - if (end) return l.End() || !image.In(l.Current.X, l.Current.Y); - return !image.In(l.Current.X, l.Current.Y); - } - - int minModuleLength; - - //end=true if the searchs stops at the end of l or until the end of the image size. - public void NewSearch(Bresenham l) { NewSearch(l, true, -1); } - public void NewSearch(Bresenham l, bool end, int minModuleLength) - { - this.l = l; - this.end = end; - this.minModuleLength = minModuleLength; - - //moves bresenham into the image - while (!l.End() && !image.In(l.Current.X, l.Current.Y)) l.Next(); - - //jump to the first pixel equals to startsWithBlack - currentY = -1; - bool found = false; - while (!End() && !found) - { - if (l.Current.Y != currentY) row = image.GetRow(currentY = l.Current.Y); - if (row[l.Current.X] ^ startsWithBlack) l.Next(); - else found = true; - } - - //prepare search - if (minModuleLength > 0) - { - noiseLevel = 1; - status[0] = new LevelStatus(minModuleLength, patternLength, l.Current, startsWithBlack); - } - else - { - noiseLevel = defaultNoiseLevel; - for (int i = 0; i < noiseLevel; i++) - status[i] = new LevelStatus(i, patternLength, l.Current, startsWithBlack); - } - - foundAtLevel = -1; - } - - public int NextPattern() - { - //terminate last step - if (foundAtLevel != -1) status[foundAtLevel].skipCandidate(); - - //continue... - int nPattern = -1; - while (!End()) - { - if (l.Current.Y != currentY) row = image.GetRow(currentY = l.Current.Y); - nPattern=processPixel(l.Current, row[l.Current.X], foundAtLevel+1); - if (nPattern!=-1) return nPattern; - l.Next(); - foundAtLevel = -1; - } - - //finalize (last module is not processed in the main loop - nPattern=terminate(foundAtLevel, l.Current); - if (nPattern!=-1) return nPattern; - - return -1; - } - - int processPixel(MyPoint p, bool isBlack, int startLevel) - { - for (int i = startLevel; i < this.noiseLevel; i++) - { - if (status[i].isCandidate(p, isBlack)) //detect new transition that ends a candidate pattern - { - int found = IsPattern(i); - - if (found!=-1) - { - this.foundAtLevel = i; - return found; - } - status[i].skipCandidate(); - } - } - return -1; - } - - - int terminate(int startLevel, MyPoint p) - { - for (int i = startLevel+1; i < this.noiseLevel; i++) - { - if (status[i].terminate(p)) - { - int found = IsPattern(i); - - if (found!=-1) - { - this.foundAtLevel = i; - return found; - } - } - } - return -1; - } - - - int IsPattern(int statusLevel) - { - int[] elementWidths = status[statusLevel].elementWidths; - - //total width - int p = 0; - for (int i = 0; i < patternLength; i++) p += elementWidths[i]; - - int iMin = -1; - float minE = float.MaxValue; - status[statusLevel].error = -1f; - for (int j = 0; j < patternE.Length; j++) - { - int[] patE = patternE[j]; - //compute normalized pair widths - bool found = true; - float acumE = 0f; - for (int i = 0; i < patternELength && found; i++) - { - float e = (float)(elementWidths[i] + elementWidths[i + 1]) * N[j] / p; - float err = e - (float)patE[i]; //real part - acumE += err * err; - if (err > 1F || err < -1F) found=false; - } - if (found && acumE < minE) { iMin = j; minE = acumE; status[statusLevel].error = acumE; } - } - return iMin; -/* - if (i > 0) - { - //compensate neigbour - if (errPrev < 0F && err > 0F) - if (-errPrev < err) { err += errPrev; errPrev = 0F; } - else { errPrev += err; err = 0F; } - else if (errPrev > 0F && err < 0F) - if (errPrev < -err) { err += errPrev; errPrev = 0F; } - else { errPrev += err; err = 0F; } - - //recalculate - if (errPrev<-0.5F || errPrev>0.5F) return false; - } - errPrev = err; - - } - //check last E - if (errPrev < -0.5F || errPrev > 0.5F) return false; -*/ - - } - - //public int[] ElementWidths { get { return elementWidths; } } - public MyPoint First { get { return status[this.foundAtLevel].points[0]; } } - public MyPoint Last { get { return status[this.foundAtLevel].points[patternLength];} } - public int PatternLength { get { return status[this.foundAtLevel].patternLength; } } - public float Error { get { return status[this.foundAtLevel].error; } } - - public void setBWThreshold(ImageScaner scan) - { - MyPoint p = First; - float grayWhite = 0F, grayBlack = 1F; - for (int x = First.X; x < Last.X; x++) - { - p.X = x; - float g=scan.getGray(p); - if (scan.isBlack(p) && g grayWhite) grayWhite = g; - } - scan.BWThreshold = (grayWhite + grayBlack) / 2F; - } - } - - class LevelStatus - { - int noiseLevel; - public int patternLength; - int currentElement; - int n; - MyPoint nextPoint; - public bool processingWhite; - public int[] elementWidths; - public MyPoint[] points; - public float error; - public LevelStatus(int noiseLevel, int patternLength, MyPoint startPoint, bool startsWithBlack) - { - this.noiseLevel = noiseLevel; - this.patternLength = patternLength; - this.elementWidths = new int[patternLength]; - this.points = new MyPoint[patternLength+1]; - this.processingWhite = !startsWithBlack; - - this.n = 0; - this.nextPoint = startPoint; - this.currentElement = 0; - this.points[0] = startPoint; - } - - public bool isCandidate(MyPoint p, bool isBlack) - { - if (isBlack ^ processingWhite) //no transition - { - n++; - } - else //transition at this noise level - { - if (currentElement == -1) //searching for the first module - { - currentElement = 0; - n = 1; - points[0] = p; - } - else - { - //check if module is not noise at this level - if (n > noiseLevel) - { - if (currentElement < patternLength) //first modules - { - elementWidths[currentElement++] = n; - points[currentElement] = p; - n = 1; - } - else - { - nextPoint = p; - return true; //elementWidths ready to check if it is a pattern - } - } - else //if it is noise, add to the previous module - { - if (currentElement > 0) - { - n = elementWidths[currentElement - 1] + n +1; - currentElement--; - } - else //invalidate the start of the module, it is too narrow - currentElement = -1; - } - } - processingWhite = !processingWhite; - } - return false; - } - - public bool terminate(MyPoint p) - { - if (currentElement == patternLength-1) - { - elementWidths[currentElement++] = n; - points[currentElement] = p; - } - if (n > noiseLevel && currentElement == patternLength) - { - return true; //elementWidths ready to check if it is a pattern - } - return false; - } - - public void skipCandidate() - { - for (int i = 0; i < patternLength - 2; i++) - { - elementWidths[i] = elementWidths[i + 2]; - points[i] = points[i + 2]; - } - elementWidths[patternLength - 2] = n; - points[patternLength - 2] = points[patternLength]; - points[patternLength - 1] = nextPoint; - currentElement = patternLength - 1; - n = 1; - processingWhite = !processingWhite; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/PatternFinderNoiseRow.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/PatternFinderNoiseRow.cs deleted file mode 100644 index 0f5b3f0f..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/PatternFinderNoiseRow.cs +++ /dev/null @@ -1,416 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace BarcodeReader.Core.Common -{ - interface IPatternFinderNoiseRow - { - void NewSearch(XBitArray row); - void NewSearch(XBitArray row, int startX, int endX, int inc, int minModuleLength); - FoundPattern NextPattern(); - bool HasQuietZone { get; } - int First { get; } - int Last { get; } - int Center { get; } - } - - //Optimized version of PatternFinderNois for horizontal or vertical lines. - //It receives an array of bits. - - //Class to find bit patterns allowing noise removing. Noise level definies the number of pixels considered as noise. - //noiseLevel=1: accept modules 1 pixel wide. - //noiseLevel=2: accept modules 2 pixel wide. - //... - //PatternFinder mantains an state. When a pattern is found the state is saved and the method returns. When - //NextPattern is called, the state is restored and the search continues. - class PatternFinderNoiseRow : IPatternFinderNoiseRow - { - bool useE = true; - int[][] pattern; //main patterns - bool startsWithBlack; - int defaultNoiseLevel, noiseLevel; - - int[][] patternE; //pattern of black-white and white-black added modules - int nPatterns, maxPatternLength, maxPatternELength; - int[] N; //number of modules of each pattern - int x, inc; - - LevelStatusRow[] status; //status of each noise level scan. - int foundAtLevel; //local at which the last pattern was found - XBitArray row; - - private void Initialize(int[][] pattern, bool useE, bool startsWithBlack, int noiseLevel, Dictionary hash) - { - this.pattern = pattern; - this.useE = useE; - this.startsWithBlack = startsWithBlack; - this.defaultNoiseLevel = noiseLevel; - - this.nPatterns = pattern.Length; - this.maxPatternLength = -1; //the max length - this.patternE = new int[nPatterns][]; - this.N = new int[nPatterns]; - for (int i = 0; i < nPatterns; i++) - { - int n = 0; - int[] currentPattern = pattern[i]; - int ELength = useE ? currentPattern.Length - 1 : currentPattern.Length; - patternE[i] = new int[ELength]; - for (int j = 0; j < ELength; j++) - { - if (useE) patternE[i][j] = currentPattern[j] + currentPattern[j + 1]; - else patternE[i][j] = currentPattern[j]; - n += currentPattern[j]; - } - if (useE) N[i] = n + currentPattern[ELength]; - else N[i] = n; - if (currentPattern.Length > maxPatternLength) - maxPatternLength = currentPattern.Length; - } - this.maxPatternELength = maxPatternLength - 1; - - - this.status = new LevelStatusRow[defaultNoiseLevel]; - } - - public PatternFinderNoiseRow(int[][] pattern, bool useE, bool startsWithBlack, int noiseLevel, float maxEAcumDist = -1, float maxEDist = -1) - { - if(maxEAcumDist >= 0) - this.maxEAcumDist = maxEAcumDist; - if (maxEDist >= 0) - this._maxEDist = maxEDist; - Initialize(pattern, useE, startsWithBlack, noiseLevel, new Dictionary(1000)); - } - - public PatternFinderNoiseRow(int[][] pattern, bool useE, bool startsWithBlack, int noiseLevel, Dictionary hash) - { - Initialize(pattern, useE, startsWithBlack, noiseLevel, hash); - } - - public PatternFinderNoiseRow NewFinder() - { - return new PatternFinderNoiseRow(pattern, useE, startsWithBlack, defaultNoiseLevel); - } - - bool End() - { - return x>=row.Size; - } - - int minModuleLength; - int startX, endX; - - public void NewSearch(XBitArray row) { NewSearch(row, 0, row.Size, 1, 0); } - public void NewSearch(XBitArray row, int startX, int endX, int inc, int minModuleLength) - { - this.row = row; - this.inc = inc; - if (inc > 0) { this.startX = startX; this.endX = endX; } - else { this.startX = endX; this.endX = startX; } - this.minModuleLength = minModuleLength; - - //jump to the first pixel equals to startsWithBlack - bool found = false; - x = startX; - while (x >= this.startX && x < endX && !found) - { - if (row[x] ^ startsWithBlack) x += inc; - else found = true; - } - - //prepare search - if (minModuleLength > 0) - { - noiseLevel = 0; - status[noiseLevel++] = new LevelStatusRow(minModuleLength, maxPatternLength, x, startsWithBlack); - if (minModuleLength>0) status[noiseLevel++] = new LevelStatusRow(minModuleLength-1, maxPatternLength, x, startsWithBlack); - } - else - { - noiseLevel = defaultNoiseLevel; - for (int i = 0; i < noiseLevel; i++) - status[i] = new LevelStatusRow(i, maxPatternLength, x, startsWithBlack); - } - - foundAtLevel = -1; - } - - public FoundPattern NextPattern() - { - //terminate last step - if (foundAtLevel != -1) status[foundAtLevel].skipCandidate(); - - //continue... - while (x>=startX && x maxEDist || acum < -maxEAcumDist || acum > maxEAcumDist) { found = false; break; } - } - float compensation=blackCompensation>1f?blackCompensation/1f:1f/blackCompensation; - absAcum *= compensation*compensation; - if (found && (bestMatch == null || absAcum < bestDist)) - { - var points = status[statusLevel].points; - var nextX = status[statusLevel].nextX; - var lastPoint = points.Length > 0 ? points[points.Length - 1] : nextX; - bestMatch = new FoundPattern(np, blackCompensation, whiteCompensation, (float)p/(float)n, Math.Abs(nextX - lastPoint)); - bestDist = absAcum; - } - } - - return bestMatch; - } - - public bool HasQuietZone - { - get - { - int[] w=status[this.foundAtLevel].elementWidths; - int l = 0; for (int i = 0; i < w.Length; i++) l += w[i]; - - int[] pattern=this.pattern[status[this.foundAtLevel].foundPattern.nPattern]; - int modules=0; for (int i = 0; i < pattern.Length; i++) modules += pattern[i]; - l = (int)((float)l * 5f / (float)modules); - return status[this.foundAtLevel].previousWidth > l; - } - } - - public int First { get { return status[this.foundAtLevel].points[0]; } } - public int Last - { - get - { - LevelStatusRow s = status[this.foundAtLevel]; - return s.points[pattern[s.foundPattern.nPattern].Length]; - } - } - public int Center - { - get - { - LevelStatusRow s = status[this.foundAtLevel]; - int mid = pattern[s.foundPattern.nPattern].Length / 2; - int[] points = s.points; - return (points[mid] + points[mid + 1]) / 2; - } - } - } - - class LevelStatusRow - { - int noiseLevel; - public FoundPattern foundPattern; - int patternLength; - int currentElement; - int n; - public int nextX; - public bool processingWhite; - public int[] elementWidths; - public int[] points; - public int previousWidth; //to check if it has quiet zone - public LevelStatusRow(int noiseLevel, int patternLength, int startX, bool startsWithBlack) - { - this.noiseLevel = noiseLevel; - this.patternLength = patternLength; - this.elementWidths = new int[patternLength]; - this.points = new int[patternLength+1]; - this.processingWhite = !startsWithBlack; - this.previousWidth = 1000; - - this.n = 0; - this.nextX = startX; - this.currentElement = 0; - this.points[0] = startX; - this.foundPattern = null; - } - - public bool isCandidate(int p, bool isBlack) - { - if (isBlack ^ processingWhite) //no transition - { - n++; - } - else //transition at this noise level - { - if (currentElement == -1) //searching for the first module - { - currentElement = 0; - n = 1; - points[0] = p; - } - else - { - //check if module is not noise at this level - if (n > noiseLevel) - { - if (currentElement < patternLength) //first modules - { - elementWidths[currentElement++] = n; - points[currentElement] = p; - n = 1; - } - else - { - nextX = p; - return true; //elementWidths ready to check if it is a pattern - } - } - else //if it is noise, add to the previous module - { - if (currentElement > 0) - { - n = elementWidths[currentElement - 1] + n +1; - currentElement--; - } - else //invalidate the start of the module, it is too narrow - currentElement = -1; - } - } - processingWhite = !processingWhite; - } - return false; - } - - public bool terminate() - { - if (n > noiseLevel && currentElement == patternLength) - { - return true; //elementWidths ready to check if it is a pattern - } - return false; - } - - public void skipCandidate() - { - previousWidth = elementWidths[1]; //remember last white module to check quiet zone in the next fount pattern - for (int i = 0; i < patternLength - 2; i++) - { - elementWidths[i] = elementWidths[i + 2]; - points[i] = points[i + 2]; - } - elementWidths[patternLength - 2] = n; - points[patternLength - 2] = points[patternLength]; - points[patternLength - 1] = nextX; - currentElement = patternLength - 1; - n = 1; - processingWhite = !processingWhite; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/PatternFinderNoiseRowEx.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/PatternFinderNoiseRowEx.cs deleted file mode 100644 index 6e248d09..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/PatternFinderNoiseRowEx.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace BarcodeReader.Core.Common -{ - // Faster version of PatternFinderNoiseRow - [Obsolete("This class does not pass all tests.")] - class PatternFinderNoiseRowEx : IPatternFinderNoiseRow - { - private bool _hasQuietZone; - private int _first; - private int _last; - private int _center; - private int[] pattern; - private StartPatternFinder finder; - private bool UseE; - private IEnumerator enumerator; - - /// - ///004 - ///010 - ///017 - ///021 - ///022 - ///023 - /// - /// - - public PatternFinderNoiseRowEx(int[] pattern) - { - this.pattern = pattern; - finder = new StartPatternFinder(pattern); - } - - public void NewSearch(XBitArray row) - { - NewSearch(row, 0, row.Size, 1, 1); - } - - public void NewSearch(XBitArray row, int startX, int endX, int inc, int minModuleLength) - { - var line = new LineScanner(startX, endX); - line.FindBars(row, 0); - enumerator = finder.FindPattern(line).GetEnumerator(); - } - - public FoundPattern NextPattern() - { - if (enumerator.MoveNext()) - return enumerator.Current.foundPattern; - - return null; - } - - public bool HasQuietZone - { - get { return true; } - } - - public int First - { - get { return enumerator.Current.xIn; } - } - - public int Last - { - get { return enumerator.Current.xEnd; } - } - - public int Center - { - get - { - var p = enumerator.Current; - return (p.xIn + p.xEnd) / 2; - } - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/RS.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/RS.cs deleted file mode 100644 index 36a984d8..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/RS.cs +++ /dev/null @@ -1,228 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace BarcodeReader.Core.Common -{ - class RS - { - GF gf; //Galois Field used for this Reed Solomon - int[] msg; //Message to validate/correct - int ecw; //number of error correcting words in msg - bool generatorPolyHasPower0; - public int[] correctedData; - - //msg: message to validate/correct - //ecw: number of error correcting words - public RS(GF gf, int[] msg, int ecw, bool generatorPolyHasPower0) - { - this.gf = gf; - this.msg = msg; - this.ecw = ecw; - this.generatorPolyHasPower0 = generatorPolyHasPower0; - this.correctedData = new int[msg.Length - ecw]; - Array.Copy(msg, correctedData, correctedData.Length); - } - - public bool correct() { - int[] syndromes; - if (!validateSyndromes(msg, ecw, generatorPolyHasPower0, out syndromes)) - { - int[] pos = findErrors(syndromes, msg.Length); - if (pos != null) - { - correctErrors(msg, syndromes, pos); - return true; - } - return false; - } - Array.Copy(msg, correctedData, msg.Length - ecw); - return true; - } - - bool validateSyndromes(int[] msg, int ecw, bool generatorHasPower0, out int[] synd) - { - bool validate=true; - int power0=generatorHasPower0?0:1; - synd = new int[ecw]; - for (int i = 0; i < ecw; i++) - { - if ((synd[i] = gf.polyEval(msg, gf.exp[i + power0])) != 0) validate = false; - } - return validate; - } - - //Berlekamp-Massey algorithm to find error positions - //find error locator polynomial with Berlekamp-Massey algorithm - int[] findErrors(int[] synd, int nmess) - { - int[] err_poly = new int[] { 1 }; - int[] old_poly = new int[] { 1 }; - - for (int i = 0; i < synd.Length; i++) - { - old_poly = ArrayAdd(old_poly, 0); - int delta = synd[i]; - for (int j = 1; j < err_poly.Length; j++) - delta ^= gf.mult(err_poly[err_poly.Length - 1 - j], synd[i - j]); - if (delta != 0) - { - if (old_poly.Length > err_poly.Length) - { - int[] new_poly = gf.polyScale(old_poly, delta); - old_poly = gf.polyScale(err_poly, gf.div(1, delta)); - err_poly = new_poly; - } - err_poly = gf.polyAdd(err_poly, gf.polyScale(old_poly, delta)); - } - - } - int errs = err_poly.Length-1; - if (errs*2 > synd.Length) return null; //too many errors to correct - - - //find zeros of error polynomial - LinkedList err_pos = new LinkedList(); - for (int i=0;i b.Length ? a.Length : b.Length; - int[] r = new int[max]; - for (int i=0;i candidates = new LinkedList(); //list of found barcodes - - abstract protected int getFinderNElements(); //number of black, or white bars of the finder - abstract protected bool IsFinder(int[] widths, out float meanWidth); //check if widths are a valid finder - abstract protected bool LeftFree(int x0, int y, float meanWidth); //check if finder left side is blank - abstract protected BarCodeRegion FindBarcode(int x0, int y, float meanWidth); //try to read the barcode - - public bool StartsWithBlack { get { return startsWithBlack; } set { startsWithBlack = value; } } - - - //main method: scans the image, row by row looking for the finder. Once found, call FindBarcode to try to read it. - protected override FoundBarcode[] DecodeBarcode() - { -#if DEBUG_IMAGE - //image.Save(@"d:\out.png"); -#endif - scan = new ImageScaner(BWImage); - candidates.Clear(); - - for (int y = 0; y < BWImage.Height; y += ScanStep) - { - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - XBitArray row = BWImage.GetRow(y); - int startX = 0, endX = BWImage.Width - 10; - - //skip black (if finder starts with white) or white(if black) pixels - while (startX < endX && (row[startX] ^ (startsWithBlack))) startX++; - - //look for the finder - int currentElement = 0, n = 0; - int[] elementWidths = new int[getFinderNElements()]; - int symbolStart = startX; - float mean; - bool processingWhite = !startsWithBlack; - for (int x = startX; x < endX; x++) - { - if (row[x] ^ processingWhite) n++; - else - { - elementWidths[currentElement++] = n; - if (currentElement == getFinderNElements()) - { - bool done = false; MyPoint p = new MyPoint(x, y); - foreach (BarCodeRegion r in candidates) if (r.In(p)) { done = true; break; } - if (!done && IsFinder(elementWidths, out mean)) - if(LeftFree(symbolStart,y,mean)) - { -#if FINDERS - candidates.AddLast(new BarCodeRegion(new MyPoint(startX, y), new MyPoint(startX+2, y), new MyPoint(startX+2, y + 1), new MyPoint(startX, y + 1))); -#else - BarCodeRegion result = FindBarcode(symbolStart, y, mean); - if (result != null) candidates.AddLast(result); -#endif - } - SkipTwoModules(ref symbolStart, elementWidths); - currentElement -= 2; - } - n = 1; - processingWhite = !processingWhite; - } - } - } - - //set up results - FoundBarcode[] results = new FoundBarcode[candidates.Count]; - int nn = 0; - foreach (BarCodeRegion r in candidates) - { - FoundBarcode f = new FoundBarcode(); - - if (this is PostCodeReader) - f.BarcodeFormat = SymbologyType.AustralianPostCode; - else if (this is KIXReader) - f.BarcodeFormat = SymbologyType.RoyalMailKIX; - else if (this is RMReader) - f.BarcodeFormat = SymbologyType.RoyalMail; - else if (this is IMReader) - f.BarcodeFormat = SymbologyType.IntelligentMail; - else if (this is PostNetReader) - f.BarcodeFormat = SymbologyType.PostNet; - - f.Polygon = new SKPointI[] { r.A, r.B, r.C, r.D, r.A }; - f.Color = SKColors.Blue; - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(f.Polygon, pointTypes); - //f.Rect = Rectangle.Round(path.GetBounds()); - f.Rect = Utils.DrawPath(f.Polygon); - f.Value = (r.Data != null ? r.Data[0].ToString() : "?"); - f.Confidence = r.Confidence; - results[nn++] = f; - } - return results; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Reader2DNoise.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Reader2DNoise.cs deleted file mode 100644 index f2da6589..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Reader2DNoise.cs +++ /dev/null @@ -1,557 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using SkiaSharp; -using BarcodeReader.Core.Code39; -using BarcodeReader.Core.Code93; - -namespace BarcodeReader.Core.Common -{ -#if CORE_DEV - public -#else - internal -#endif - abstract class Reader2DNoise : SymbologyReader2D - { - protected bool startsWithBlack = true; - protected bool useE = false; - protected bool requireQuietZones = true; - - //Process start patterns of mínim 2 pixels height - protected int stackedPatternMinHeight = 2; - - //Reject start patterns with and edge out of -45º..45º --> cos(45)=0.707 - protected float barcodeMaxCosAngle = 0.7f; - - //When check for start quiet zone, check 4 * modules with white pixels - protected float startPatternQuietZone = 4f; - - //When check for stop quiet zone, check 7 * modules with white pixels - protected float stopPatternQuietZone = 7f; - - //If all patterns are different versions of the same pattern - protected bool singlePattern = false; //by default, all patterns are considered differents - - //For each start pattern found, try to find a valid stop pattern tracing perpendicular lines - // at 50%, 25%, 75%,... and at diferent angles from the perpendicular: 0rad, 0.2rad, -0.2rad - protected float[] crossPoints = new float[] { 0.5f, 0.25f, 0.75f, 0.12f, 0.37f, 0.62f, 0.87f }; //needed for really damaged barcodes - protected float[] angles = new float[] { 0f, 0.2f, -0.2f }; //nedded for skewed barcodes - - //Reject start/stop patterns that are not +/- parallel. Set to 0 for strict parallel or bigger to process - //skewed barcodes with start/stop patterns bars not parallels - protected float maxDifferenceStartStopAngle = 0.2f; - - //Reject start/stop patterns that have not the same length. Set to 0 for strict same length, or bigger - //to process skewed barcodes. - protected float maxRatioStartStopLength = 0.4F; - - protected ImageScaner scan; //object to sample BW image - - //PatternFinderRow startFinder; - IPatternFinderNoiseRow startFinder, simpleStopFinder; //object to find finders in a horizontal row - PatternFinderNoise stopFinder; //object to find stop finders in any direction - LinkedList candidates = new LinkedList(); //found barcodes - - /// found regions (recognized and not recognized) - protected LinkedList foundRegions = new LinkedList(); - - protected int[][] startPatterns = null, stopPatterns = null; - virtual protected BarCodeRegion FindBarcode(ImageScaner scan, int startPattern, MyPoint start, MyPoint end, FoundPattern foundPattern) { return null; } - virtual protected BarCodeRegion FindBarcode(ImageScaner scan, int startPattern, BarCodeRegion r, FoundPattern foundPattern) { return null; } - //method to read a barcode without stop pattern - virtual protected BarCodeRegion FindBarcode(ImageScaner scan, int startPattern, MyPoint start, MyVectorF vd, FoundPattern foundPattern) { return null; } - - public bool StartsWithBlack { get { return startsWithBlack; } set { startsWithBlack = value; } } - public bool UseE { get { return useE; } set { useE = value; } } - public bool RequireQuietZones { get { return requireQuietZones; } set { requireQuietZones = value; } } - public bool SinglePattern { get { return singlePattern; } set { singlePattern = value; } } - - public bool UsePatternFinderNoiseRowEx { get; set; } = false; - public int NoiseLevel { get; set; } = 2; - - //Scans the image row by row, looking for start patterns. For each pattern found, try to - //add this patterns to previously found patterns. This is implemented in the StackedPattern. - //StackedPatterns are useful to track better the edge and detect the angle of the barcode. - protected override FoundBarcode[] DecodeBarcode() - { - foundRegions.Clear(); - scan = new ImageScaner(BWImage); - var maxEAcumDist = 1.6f; // moved to 1.6f due to #151 1.5f; //relax error for start pattern, see #138 (927-code39-with-noise.PNG) - - if (UsePatternFinderNoiseRowEx) - { - //if (startPatterns.Length != 1) - // throw new Exception("PatternFinderNoiseRowEx does not support several start patterns"); - startFinder = new PatternFinderNoiseRowEx(startPatterns[0]); - } - else - startFinder = new PatternFinderNoiseRow(startPatterns, useE, startsWithBlack, NoiseLevel, maxEAcumDist); - - //simpleStopFinder = new PatternFinderNoiseRow(startPatterns, useE, startsWithBlack, 2); - //startFinder = new PatternFinderRow(startPatterns); - if (stopPatterns != null) stopFinder = new PatternFinderNoise(BWImage, stopPatterns, startsWithBlack, NoiseLevel); - LinkedList foundPatterns = new LinkedList(); - LinkedList removedPatterns; - processedRegions = new LinkedList(); - - // check against the timeout - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - candidates.Clear(); // clear candidates to avoid unwanted caching of results - - for (int y = 0; y < BWImage.Height; y += ScanStep) - { - XBitArray row = BWImage.GetRow(y); - - // check against the timeout - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - startFinder.NewSearch(row); - FoundPattern foundPattern; - while ((foundPattern = startFinder.NextPattern()) != null) - { - if (requireQuietZones && !startFinder.HasQuietZone) continue; - //foundPattern is the index of the found pattern - MyPoint a = new MyPoint(startFinder.First, y); - MyPoint b = new MyPoint(startFinder.Last, y); - //Check if another pattern was found in the last row. - if (singlePattern) foundPattern.nPattern = 0; //all patterns are different versions of the same pattern - Pattern p = new Pattern(foundPattern, a.X, b.X, y); - LinkedListNode prev = foundPatterns.Find(p); - if (prev != null) - { //pattern already processed the last row - if (prev.Value.y != y) - { - StackedPattern sp = (StackedPattern)prev.Value; - sp.NewRow(a.X, b.X, y); - } - } - else - { //new - StackedPattern sp = new StackedPattern(foundPattern, a.X, b.X, y); - foundPatterns.AddLast(sp); - } - if (StopOnFirstFoundBarcodeInTheRow) break; - } - - // check against the timeout - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - //clean old patterns and process them - removedPatterns = Pattern.RemoveOldPatterns(foundPatterns, y); - foreach (Pattern p in removedPatterns) - { - // check against the timeout - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - ProcessPattern((StackedPattern)p); - - if (CheckStop()) break; - - } - - if (CheckStop()) break; - - } - - //clean stackedPatterns - foreach (Pattern p in foundPatterns) - { - ProcessPattern((StackedPattern)p); - - // check against the timeout - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - } - - // check against the timeout - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - FoundBarcode[] results = new FoundBarcode[candidates.Count]; - int nn = 0; - foreach (BarCodeRegion r in candidates) - { - FoundBarcode f = new FoundBarcode() {ParentRegion = r}; - - if (this is PZNReader) - f.BarcodeFormat = SymbologyType.PZN; - else if (this is UPUReader) - f.BarcodeFormat = SymbologyType.UPU; - else if (this is Code39ExtendedReader) - f.BarcodeFormat = SymbologyType.Code39Ext; - else if (this is Code39Mod43ExtendedReader) - f.BarcodeFormat = SymbologyType.Code39Mod43Ext; - else if (this is Code39Mod43Reader) - f.BarcodeFormat = SymbologyType.Code39Mod43; - else if (this is Code39.Code39Reader) - f.BarcodeFormat = SymbologyType.Code39; - else if (this is Code93Reader) - f.BarcodeFormat = SymbologyType.Code93; - else if (this is Code128.Code128Reader) - f.BarcodeFormat = SymbologyType.Code128; - else if (this is MSI.MSIReader) - f.BarcodeFormat = SymbologyType.MSI; - else if (this is Pharmacode.PharmaReader) - f.BarcodeFormat = SymbologyType.Pharmacode; - else - f.BarcodeFormat = GetBarCodeType(); - - f.Polygon = new SKPointI[] { r.A, r.B, r.C, r.D, r.A }; - f.Color = SKColors.Blue; - //byte[] pointTypes = new byte[5] { (byte)PathPointType.Start, (byte)PathPointType.Line, (byte)PathPointType.Line, (byte)PathPointType.Line, (byte)PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(f.Polygon, pointTypes); - //f.Rect = Rectangle.Round(path.GetBounds()); - f.Rect = Utils.DrawPath(f.Polygon); - f.Value = (r.Data != null ? r.Data[0].ToString() : "?"); - f.Confidence = r.Confidence; - results[nn++] = f; - } - - // check against the timeout - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - return RemoveDuplicates(results); - } - - // Removes duplicates from same region. - // Adding regions to list foundRegions - zone of responsibility of classes inherited from this class. For example - see Code128Reader. - private FoundBarcode[] RemoveDuplicates(FoundBarcode[] foundBarcodes) - { - var bestByRegions = new Dictionary(); - var result = new List(); - - foreach (var bar in foundBarcodes) - { - //find region for the found barcode - BarCodeRegion regionForBarcode = null; - - foreach (var reg in foundRegions) - { - if (reg.IntersectsWith(bar.ParentRegion, 10)) - { - regionForBarcode = reg; - break; - } - } - - //if not found region - just return barcode as result - if (regionForBarcode == null) - result.Add(bar); - else - { - //if found region that contains other barcodes - choose best from them (by Confidence) - FoundBarcode prevBar = null; - if (!bestByRegions.TryGetValue(regionForBarcode, out prevBar)) - bestByRegions.Add(regionForBarcode, bar); - else - { - if (prevBar.Confidence < bar.Confidence) - bestByRegions[regionForBarcode] = bar; - } - } - } - - //add best barcode from regions to result list - foreach (var bar in bestByRegions.Values) - result.Add(bar); - - return result.ToArray(); - } - - //Check if the algorithm must stop when the first number of found barcodes reach the parameter ExpectedNumberOfBarcodes - private bool CheckStop() - { - // check against the timeout - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - return this.ExpectedNumberOfBarcodes > 0 && candidates.Count >= this.ExpectedNumberOfBarcodes; - } - - - //Traces a line starting at p in vdX direction during moduleLength pixels. Count the number of black pixles in this segment. - //If it is bigger than the half of a module length, then quiet zone is considered a false quiet zone. - protected bool CheckQuietZone(MyPoint p, MyVectorF vdX, float moduleLength) - { - Bresenham br = new Bresenham(new MyPointF(0.5f, 0.5f) + (MyPointF)p, vdX); - while (scan.In(br.Current) && scan.isBlack(br.Current)) br.Next(); - - //max allowed noise - int quietZone = (int)Math.Round(moduleLength * 0.5f); - if (quietZone < 1) quietZone = 1; - int steps = (int)Math.Ceiling(moduleLength * (vdX.X > 0 ? stopPatternQuietZone : startPatternQuietZone)); //less restrictive for start patterns - while (quietZone > 0 && scan.In(br.Current) && steps > 0) - { - if (scan.isBlack(br.Current)) quietZone--; - br.Next(); - steps--; - } - return quietZone > 0; - } - - protected virtual void TrackEdge(MyPoint left, MyPoint right, float moduleLength, out MyPointF up, out MyPointF down) - { - EdgeTrack et = new EdgeTrack(scan); - et.Track(left, new MyVector(-1, 0), moduleLength, true); - - //find the top and bottom points of the edge - up = et.Up(); - down = et.Down(); - } - - //Find the top and bottom corners of the finder, starting at the center point of the pattern, - //and tracking the edge. Then traces a perpendicular line to the left direction, and check the - //quiet zone of 10 modules length (this step rejects most of the false candidates). - bool ProcessPattern(StackedPattern p) - { - //estimate moduleLength (in pixels) - int nModules = 0; - foreach (int w in startPatterns[p.nPattern]) nModules += w; - float moduleLength = p.MeanWidth() / (float)nModules; - - //check minimum aspect ratio - if (p.y - p.startY <= stackedPatternMinHeight) return false; //invalid start, not tall enough - - MyPoint lCenter, rCenter; p.Center(out lCenter, out rCenter); - MyPoint midCenter = lCenter; - midCenter.X += (int)p.MeanWidth(); - - foreach (BarCodeRegion c in candidates) - if (c.In(midCenter)) return true; //valid but already processed - - //Track edge - MyPointF up, down; - TrackEdge(lCenter, rCenter, moduleLength, out up, out down); - - //Calculate main directions - MyVectorF vdY = (up - down); - vdY = vdY.Normalized; - MyVectorF vdX = new MyVectorF(-vdY.Y, vdY.X); - - //Calculate rotated module length - float cosAngle = (float)Math.Cos(vdX.Angle); - if (cosAngle < barcodeMaxCosAngle) return false; //invalid - - float rotatedModuleLength = moduleLength * cosAngle * cosAngle; //projected module length X axis - float rotatedLength = p.MeanWidth() * cosAngle * cosAngle; - if (requireQuietZones && !CheckQuietZone(lCenter, -vdX, rotatedModuleLength)) return false; //invalid start - p.foundPattern.moduleLength = rotatedModuleLength; - - BarCodeRegion region = null; - if (stopPatterns == null) //barcode without stop patterns - { //proceed to decode - BarCodeRegion r = FindBarcode(scan, p.foundPattern == null ? p.nPattern : p.foundPattern.nPattern, new BarCodeRegion(up, up + vdX * rotatedLength, down + vdX * rotatedLength, down), p.foundPattern); - if (r != null) { candidates.AddLast(r); return true; } - } - else - { - region = FindStopPattern(p.foundPattern == null ? p.nPattern : p.foundPattern.nPattern, up, down, vdX, vdY, p.foundPattern); - if (region != null && region.Data != null && region.Data.Length > 0) { candidates.AddLast(region); return true; } - } - - //if no region is detected, try to read barcode without stop pattern - region = FindBarcode(scan, p.foundPattern == null ? p.nPattern : p.foundPattern.nPattern, up*0.5f+down*0.5f, vdX, p.foundPattern); - if (region != null) { candidates.AddLast(region); return true; } - - - //if no region is detected, we try to find barcodes for each found start pattern - //just scanning a single row - /*foreach (MyPoint q in p.LPoints) - { - XBitArray row = BWImage.GetRow(q.Y); - simpleStopFinder.NewSearch(row, q.X, row.Size, 1, -1); - while (simpleStopFinder.NextPattern() != null) - if (CheckQuietZone(new MyPoint(simpleStopFinder.Last, q.Y), new MyVectorF(1f, 0f), rotatedModuleLength)) - { - BarCodeRegion r = FindBarcode(scan, p.foundPattern.nPattern, q, new MyPoint(simpleStopFinder.Last + (int)(rotatedModuleLength * 1.5f), q.Y), p.foundPattern); - if (r != null) { candidates.AddLast(r); return true; } - } - }*/ - - bool checkEndLine = MaxDistanceBetweenStartAndStopPatternsInModules >= 0; - - if (stopFinder != null) - { - foreach (MyPoint q in p.LPoints) - { - float[] angles = new float[] { 0f, 0.1f, -0.1f }; - foreach (float angle in angles) - { - MyVectorF vd = vdX.Rotate(angle); - MyVectorF vdy = vdY.Rotate(angle); - - //create Bresenham - - //Bresenham br = new Bresenham(q, vd); - MyPointF c = q + vd * p.foundPattern.moduleLength * MinDistanceBetweenStartAndStopPatternsInModules * 0.7f; - Bresenham br; - if (MaxDistanceBetweenStartAndStopPatternsInModules < 0) - br = new Bresenham(c, vd); - else - { - var end = q + vd * p.foundPattern.moduleLength * MaxDistanceBetweenStartAndStopPatternsInModules * 1.3f; - br = new Bresenham(c, end); - } - // - - stopFinder.NewSearch(br, checkEndLine, -1); - while (stopFinder.NextPattern() != -1) - { - var stripWidth = 2; - //if (CheckQuietZone(stopFinder.Last, vd, rotatedModuleLength)) - if (CheckQuietZone(stopFinder.Last + vdy * moduleLength * stripWidth, vd, rotatedModuleLength) && - CheckQuietZone(stopFinder.Last - vdy * moduleLength * stripWidth, vd, rotatedModuleLength)) - { - BarCodeRegion r = FindBarcode(scan, p.foundPattern.nPattern, q, stopFinder.Last, p.foundPattern); - if (r != null) - { - if (region != null) //if previously we had detected a full region with start/stop patterns - { - region.Data = r.Data; - candidates.AddLast(region); - } - else //otherwise, we return a small region around the successfully scaned line - { - candidates.AddLast(r); - } - return true; - } - } - } - } - } - } - return true; //not found, but valid start of barcode - } - - LinkedList processedRegions; //list of previously processed regions to avoid doing the work twice. - - protected int MinDistanceBetweenStartAndStopPatternsInModules = 0; - protected int MaxDistanceBetweenStartAndStopPatternsInModules = -1; - protected float StopPatternExtraShift = 1.5f; - - //A start pattern is found with corners at up and down. This method traces a line at different points of the start pattern - //going in vdX rotated at different angles. This is a force method to find stop pattern for skewed or damaged barcodes. - //Tracing only one line works in good quality, no noise, no skewed barcodes. Once one stop pattern that leads to a readable - //barcode is found, the method stops. - protected BarCodeRegion FindStopPattern(int startPattern, MyPointF up, MyPointF down, MyVectorF vdX, MyVectorF vdY, FoundPattern foundPattern) - { - var checkEndLine = MaxDistanceBetweenStartAndStopPatternsInModules >= 0; - - BarCodeRegion bestResult = null; - foreach (float angle in angles) - { - MyVectorF vd = vdX.Rotate(angle); - foreach (float f in crossPoints) - { - //create Bresenham - - //MyPointF c = up * f + down * (1f - f);// +vd * foundPattern.moduleLength * startPatterns[startPattern].Length; - var c = up * f + down * (1f - f); - MyPoint ci = c + vd * foundPattern.moduleLength * MinDistanceBetweenStartAndStopPatternsInModules * 0.7f; - Bresenham br; - if (MaxDistanceBetweenStartAndStopPatternsInModules < 0) - br = new Bresenham(ci, vd); - else - { - var end = c + vd * foundPattern.moduleLength * MaxDistanceBetweenStartAndStopPatternsInModules * 1.3f; - br = new Bresenham(ci, end); - } - - BarCodeRegion lastResult = null; - stopFinder.NewSearch(br, checkEndLine, -1); - while (stopFinder.NextPattern() != -1) - { - if (stopFinder.First == ci) - continue; - MyPoint end = stopFinder.Last; - //Once a stop pattern is found, check if it has a valid quiet zone or if its error is very low (this way we can process stop patterns of good quality without quiet zone!) - bool hasQuietZone = CheckQuietZone(end, vd, foundPattern.moduleLength) || CheckQuietZone(end + vdY, vd, foundPattern.moduleLength) || CheckQuietZone(end - vdY, vd, foundPattern.moduleLength); - - if (hasQuietZone || !requireQuietZones) - if (stopFinder.PatternLength > 5 && stopFinder.Error < 1f || hasQuietZone) - { - //Find the two stop corners - MyPointF endUp = MyPointF.Empty, endDown = MyPointF.Empty; - EdgeTrack et = new EdgeTrack(scan); - try - { - et.Track(end, new MyVector(-1, 0), foundPattern.moduleLength, false); - endUp = et.Up(); - endDown = et.Down(); - } - catch (Exception) { break; } - if (!endUp.IsEmpty && !endDown.IsEmpty) //in both corners are found - { - //check left and right sides are parallel - MyVectorF left = (up - down).Normalized; - MyVectorF right = (endUp - endDown).Normalized; - float cosAngle = left * right; - if (Calc.Around(cosAngle, 1.0f, maxDifferenceStartStopAngle)) - { - //check ratio - float dstart = (up - down).Length; - float dstop = (endUp - endDown).Length; - BarCodeRegion r = null; - if (Calc.Around(dstart / dstop, 1F, maxRatioStartStopLength)) //this coeficient must be hight to allow skewed barcodes - { - r = new BarCodeRegion(up, endUp + vd * foundPattern.moduleLength * StopPatternExtraShift, endDown + vd * foundPattern.moduleLength * StopPatternExtraShift, down); - } - else //if aspect ratio is wrong, try cut large side - { - float lud = (up - c).Length; - float ldd = (down - c).Length; - float eud = (endUp - (MyPointF)end).Length; - float edd = (endDown - (MyPointF)end).Length; - - float u = (lud > eud ? eud : lud); - float d = (ldd > edd ? edd : ldd); - - MyVectorF eY = (endUp - endDown).Normalized; - - r = new BarCodeRegion(c + vdY * u, (MyPointF)end + eY * u, (MyPointF)end - eY * d, c - vdY * d); - } - r.startPattern = startPattern; - bool repeated = false; - foreach (BarCodeRegion dd in processedRegions) - if (dd.SimilarTo(r)) - repeated = true; - if (repeated) - if (!hasQuietZone) - continue; - else - //if (bestResult != null) // refs #145, commented out as it fixes the issue with pattent.jpg returning null BUT causing issues with Code 128 barcodes from TestCase - return bestResult;// refs #145, this line was causing returning null so the final result was empty too - - processedRegions.AddLast(r); - BarCodeRegion rr = FindBarcode(scan, startPattern, r, foundPattern); - if (rr != null) - if (hasQuietZone) - return rr; - else - lastResult = rr; //remember region with Data - else if (lastResult==null || lastResult.Data==null) lastResult= r; //remember region without Data - } - } - } - //if (hasQuietZone) return null; //stop search of stop pattern, but it does not work because for damaged barcodes! - } - if (lastResult != null) - if (bestResult == null) - bestResult = lastResult; - else if ((bestResult.B - bestResult.A).Length < (lastResult.B - lastResult.A).Length) - bestResult = lastResult; - } - } - return bestResult; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/ReedSolomon.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/ReedSolomon.cs deleted file mode 100644 index 0b2998c6..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/ReedSolomon.cs +++ /dev/null @@ -1,456 +0,0 @@ -using System; - -namespace BarcodeReader.Core.Common -{ - class ReedSolomon - { - private readonly int modulus; - private readonly int initZero; - private const int ErasureCount = 0; - private readonly int[] poly = new int[13]; - private readonly int[] g; - - private readonly int wordWidth; - private readonly int fullLength; - private readonly int dataLength; - private readonly int ecwHalf; - private readonly int ecw; - private readonly int[] exp; - private readonly int[] log; - private readonly int[] inputData; - private bool success; - private int errorCount; - - public int[] CorrectedData - { - get - { - int[] correctedData = new int[inputData.Length]; - Array.Copy(inputData, correctedData, inputData.Length); - Array.Reverse(correctedData); - return correctedData; - } - } - - public bool CorrectionSucceeded - { - get - { - return success; - } - } - - public int ErrorCount - { - get - { - return errorCount; - } - } - - public ReedSolomon(int[] data, int ecwCount, int wW, int polynomCoefficients, int iZero) - { - fullLength = data.Length; - dataLength = fullLength - ecwCount; - ecwHalf = ecwCount / 2; - ecw = 2 * ecwHalf; - wordWidth = wW; - initZero = iZero; - - g = new int[(1 << (wordWidth + 2))]; - modulus = (1 << wordWidth) - 1; - exp = new int[modulus + 1]; - log = new int[modulus + 1]; - - for (int i = 0; i < 10; i++) - { - poly[i] = (polynomCoefficients & (1 << i)) != 0 ? 1 : 0; - } - GenerateGaloisField(); - - GeneratePoly(); - inputData = new int[data.Length]; - Array.Copy(data, inputData, inputData.Length); - Array.Reverse(inputData); - success = false; - errorCount = 0; - } - - // http://en.wikipedia.org/wiki/Galois_field - private void GenerateGaloisField() - { - int window = 1; - exp[wordWidth] = 0; - for (int i = 0; i < wordWidth; i++) - { - exp[i] = window; - log[exp[i]] = i; - if (poly[i] != 0) - { - exp[wordWidth] ^= window; - } - window <<= 1; - } - - log[exp[wordWidth]] = wordWidth; - window >>= 1; - for (int i = wordWidth + 1; i < modulus; i++) - { - if (exp[i - 1] >= window) - { - exp[i] = exp[wordWidth] ^ ((exp[i - 1] ^ window) << 1); - } - else - { - exp[i] = exp[i - 1] << 1; - } - log[exp[i]] = i; - } - log[0] = -1; - } - - // Compute the generator polynomial of the t-error correcting, length - // n=(2^m -1) Reed-Solomon code from the product of (X+alpha^i), for - // i = InitZero, InitZero + 1, ..., InitZero+length-k-1 - private void GeneratePoly() - { - g[0] = exp[initZero]; // <--- vector form of alpha^InitZero - g[1] = 1; // g(x) = (X+alpha^InitZero) - for (int i = 2; i <= fullLength - dataLength; i++) - { - g[i] = 1; - for (int j = i - 1; j > 0; j--) - { - if (g[j] != 0) - { - g[j] = g[j - 1] ^ exp[(log[g[j]] + i + initZero - 1) % modulus]; - } - else - { - g[j] = g[j - 1]; - } - } - g[0] = exp[(log[g[0]] + i + initZero - 1) % modulus]; - } - - // convert g[] to log form for quicker encoding - for (int i = 0; i <= fullLength - dataLength; i++) - { - g[i] = log[g[i]]; - } - - } - - // http://en.wikipedia.org/wiki/Berlekamp%E2%80%93Massey_algorithm - public void Correct(out float confidence) - { - int[] d = new int[(1 << (wordWidth + 2)) + 10]; - int[] l = new int[(1 << (wordWidth + 2)) + 10]; - int[] uLu = new int[(1 << (wordWidth + 2)) + 10]; - int[] syndrome = new int[(1 << (wordWidth + 2)) + 10]; - int[] forney = new int[(1 << (wordWidth + 2)) + 10]; - int[] tau = new int[(1 << (wordWidth + 1)) + 10]; - int[] root = new int[(1 << (wordWidth + 1)) + 10]; - int[] loc = new int[(1 << (wordWidth + 1)) + 10]; - int[] err = new int[(1 << (wordWidth + 2)) + 10]; - int[] reg = new int[(1 << (wordWidth + 1)) + 10]; - int[] omega = new int[(1 << (wordWidth + 2)) + 10]; - int[] phi = new int[(1 << (wordWidth + 2)) + 10]; - int[] phiprime = new int[(1 << (wordWidth + 2)) + 10]; - confidence = 0F; - - int[][] errorLocatorPolynom = new int[modulus + 1][]; - for (int x = 0; x < errorLocatorPolynom.Length; ++x) - { - errorLocatorPolynom[x] = new int[(1 << (wordWidth + 2)) + 10]; - } - - // Compute the syndromes - int synError = 0; - for (int k = 1; k <= ecw; k++) - { - syndrome[k] = 0; - for (int j = 0; j < fullLength; j++) - { - if (inputData[j] != 0) - { - syndrome[k] ^= exp[(log[inputData[j]] + (k + initZero - 1) * j) % modulus]; - } - } - // convert syndrome from vector form to log form */ - if (syndrome[k] != 0) - { - synError++; // set flag if non-zero syndrome => error - } - syndrome[k] = log[syndrome[k]]; - } - - if (synError <= 0) - { - errorCount = 0; - success = true; - confidence = 1F; //100% right - return; - } - syndrome[0] = 0; // S(x) = 1 + s_1x + ... - - tau[0] = 0; - for (int k = 1; k <= modulus - dataLength; k++) - { - forney[k] = syndrome[k]; - } - - // THE BERLEKAMP-MASSEY ALGORITHM FOR ERRORS AND ERASURES - // initialize table entries - d[0] = 0; // log form - d[1] = forney[ErasureCount + 1]; // log form - errorLocatorPolynom[0][0] = 0; // log form - errorLocatorPolynom[1][0] = 1; // vector form - for (int k = 1; k < ecw; k++) - { - errorLocatorPolynom[0][k] = -1; // log form - errorLocatorPolynom[1][k] = 0; // vector form - } - - l[0] = 0; - l[1] = 0; - uLu[0] = -1; - uLu[1] = 0; - int u = 0; - int q; - if (ErasureCount < ecw) - { - // If errors can be corrected - do - { - u++; - if (d[u] == -1) - { - l[u + 1] = l[u]; - for (int k = 0; k <= l[u]; k++) - { - errorLocatorPolynom[u + 1][k] = errorLocatorPolynom[u][k]; - errorLocatorPolynom[u][k] = log[errorLocatorPolynom[u][k]]; - } - } - else - // search for words with greatest u_lu[q] for which d[q]!=0 - { - q = u - 1; - while ((d[q] == -1) && (q > 0)) q--; - // have found first non-zero d[q] - if (q > 0) - { - int j = q; - do - { - j--; - if ((d[j] != -1) && (uLu[q] < uLu[j])) - { - q = j; - } - } while (j > 0); - } - - // have now found q such that d[u]!=0 and u_lu[q] is maximum - // store degree of new elp polynomial - if (l[u] > l[q] + u - q) - { - l[u + 1] = l[u]; - } - else - { - l[u + 1] = l[q] + u - q; - } - - // compute new elp(x) - for (int k = 0; k < ecw; k++) - { - errorLocatorPolynom[u + 1][k] = 0; - } - for (int k = 0; k <= l[q]; k++) - { - if (errorLocatorPolynom[q][k] != -1) - { - errorLocatorPolynom[u + 1][k + u - q] = - exp[(d[u] + modulus - d[q] + errorLocatorPolynom[q][k])%modulus]; - } - } - for (int k = 0; k <= l[u]; k++) - { - errorLocatorPolynom[u + 1][k] ^= errorLocatorPolynom[u][k]; - errorLocatorPolynom[u][k] = log[errorLocatorPolynom[u][k]]; - } - } - - uLu[u + 1] = u - l[u + 1]; - // compute (u+1)th discrepancy - if (u < (ecw - ErasureCount)) // no discrepancy computed on last iteration - { - d[u + 1] = forney[ErasureCount + u + 1] != -1 ? exp[forney[ErasureCount + u + 1]] : 0; - for (int k = 1; k <= l[u + 1]; k++) - { - if ((forney[ErasureCount + u + 1 - k] != -1) && (errorLocatorPolynom[u + 1][k] != 0)) - { - d[u + 1] ^= exp[(forney[ErasureCount + u + 1 - k] - + log[errorLocatorPolynom[u + 1][k]])%modulus]; - } - } - d[u + 1] = log[d[u + 1]]; // put d[u+1] into index form - } - } while ((u < (ecw - ErasureCount)) && (l[u + 1] <= ((ecw - ErasureCount)/2))); - } - - u++; - - if (l[u] <= ecwHalf - ErasureCount/2) // can correct errors - { - confidence = (float)(ecwHalf - l[u]) / (float)(ecwHalf); - // put elp into index form - for (int k = 0; k <= l[u]; k++) - { - errorLocatorPolynom[u][k] = log[errorLocatorPolynom[u][k]]; - } - - // find roots of the error location polynomial - for (int k = 1; k <= l[u]; k++) - { - reg[k] = errorLocatorPolynom[u][k]; - } - - int count = 0; - for (int k = 1; k <= modulus; k++) - { - q = 1; - for (int j = 1; j <= l[u]; j++) - { - if (reg[j] != -1) - { - reg[j] = (reg[j] + j)%modulus; - q ^= exp[reg[j]]; - } - } - if (q == 0) // store root and error location number indices - { - root[count] = k; - loc[count] = modulus - k; - - count++; - } - } - - if (count == l[u]) // no. roots = degree of elp hence <= t errors - { - // Compute the errata evaluator polynomial, omega(x) - forney[0] = 0; // as a log, to construct 1+T(x) - for (int k = 1; k <= ecw; k++) - { - omega[k] = 0; - } - for (int k = 0; k <= ecw; k++) - { - for (int j = 0; j <= l[u]; j++) - { - { - if (k + j <= ecw) - if ((forney[k] != -1) && (errorLocatorPolynom[u][j] != -1)) - omega[k + j] ^= exp[(forney[k] + errorLocatorPolynom[u][j])%modulus]; - } - } - } - for (int k = 0; k <= ecw; k++) - { - omega[k] = log[omega[k]]; - } - - // Compute the errata locator polynomial, phi(x) - int degphi = ErasureCount + l[u]; - for (int k = 1; k <= degphi; k++) - { - phi[k] = 0; - } - for (int k = 0; k <= ErasureCount; k++) - { - for (int j = 0; j <= l[u]; j++) - { - if ((tau[k] != -1) && (errorLocatorPolynom[u][j] != -1)) - { - phi[k + j] ^= exp[(tau[k] + errorLocatorPolynom[u][j])%modulus]; - } - } - } - for (int k = 0; k <= degphi; k++) - { - phi[k] = log[phi[k]]; - } - - - // Compute the "derivative" of phi(x): phiprime - for (int k = 0; k <= degphi; k++) - { - phiprime[k] = -1; // as a log - } - for (int k = 0; k <= degphi; k++) - { - if (k%2 != 0) // Odd powers of phi(x) give terms in phiprime(x) - { - phiprime[k - 1] = phi[k]; - } - } - - bool hadError = false; - // evaluate errors at locations given by errata locations, loc[i] - for (int k = 0; k < degphi; k++) - { - // compute numerator of error term - err[loc[k]] = 0; - for (int j = 0; j <= ecw; j++) - { - if ((omega[j] != -1) && (root[k] != -1)) - { - err[loc[k]] ^= exp[(omega[j] + j*root[k])%modulus]; - } - } - - // The term loc[i]^{2-InitZero} - if ((err[loc[k]] != 0) && (loc[k] != -1)) - { - err[loc[k]] = exp[(log[err[loc[k]]] - + loc[k]*(2 - initZero + modulus))%modulus]; - } - if (err[loc[k]] != 0) - { - err[loc[k]] = log[err[loc[k]]]; - // compute denominator of error term - q = 0; - for (int j = 0; j <= degphi; j++) - { - if ((phiprime[j] != -1) && (root[k] != -1)) - { - q ^= exp[(phiprime[j] + j*root[k])%modulus]; - } - } - - // Division by q - err[loc[k]] = exp[(err[loc[k]] - log[q] + modulus)%modulus]; - - if (loc[k] < inputData.Length) - { - inputData[loc[k]] ^= err[loc[k]]; - } - else - { - hadError = true; - } - } - } - - errorCount = degphi; - success = !hadError; - } - // no. roots != degree of elp => >t errors and cannot solve - } - // elp has degree has degree >t hence cannot solve - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Region.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Region.cs deleted file mode 100644 index 277ca676..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Region.cs +++ /dev/null @@ -1,161 +0,0 @@ -using SkiaSharp; -using System; -using System.Drawing; - -namespace BarcodeReader.Core.Common -{ - //Base class to hold a found barcode defined by 4 corners. -#if CORE_DEV - public -#else - internal -#endif - class BarCodeRegion - { - public int startPattern; - public MyPointF A, B, C, D; - protected float minX, minY, maxX, maxY; - public ABarCodeData[] Data; - public float Confidence, angle; - public bool Reversed; - - public BarCodeRegion() { } - - public BarCodeRegion(BarCodeRegion r) - { - this.startPattern = r.startPattern; - this.A = r.A; - this.B = r.B; - this.C = r.C; - this.D = r.D; - this.minX = r.minX; - this.minY = r.minY; - this.maxX = r.maxX; - this.maxY = r.maxY; - this.Data = null; - this.angle = r.angle; - this.Reversed = r.Reversed; - } - - public BarCodeRegion(MyPointF A, MyPointF B, MyPointF C, MyPointF D) - { - SetCorners(A,B,C,D); - } - - static float Min(float a, float b, float c, float d) - { - return a < b ? (a < c ? (a < d ? a : d) : (c < d ? c : d)) : (b < c ? (b < d ? b : d) : (c < d ? c : d)); - } - - static float Max(float a, float b, float c, float d) - { - return a > b ? (a > c ? (a > d ? a : d) : (c > d ? c : d)) : (b > c ? (b > d ? b : d) : (c > d ? c : d)); - } - - public void SetCorners(MyPointF A, MyPointF B, MyPointF C, MyPointF D) - { - this.A = A; - this.B = B; - this.C = C; - this.D = D; - minX = Min(A.X,B.X,C.X,D.X); - minY = Min(A.Y, B.Y, C.Y, D.Y); - maxX = Max(A.X, B.X, C.X, D.X); - maxY = Max(A.Y, B.Y, C.Y, D.Y); - angle=(A - C).Angle; - } - - public bool In(MyPoint pp) - { - MyPointF p = (MyPointF)pp; - return p.Y >= minY && p.Y <= maxY && p.X >= minX && p.X <= maxX; - } - - public override string ToString() - { - return "[" + A + "-" + B + "-" + C + "-" + D+"]"; - } - - public bool SimilarTo(BarCodeRegion r) - { - if (this.startPattern==r.startPattern) return FindNumberOfSimilarPoints(r) == 4; - return false; - /* - return Calc.Around(r.A.X, A.X, 2f) && Calc.Around(r.A.Y, A.Y, 2f) && - Calc.Around(r.B.X, B.X, 2f) && Calc.Around(r.B.Y, B.Y, 2f) && - Calc.Around(r.C.X, C.X, 2f) && Calc.Around(r.C.Y, C.Y, 2f) && - Calc.Around(r.D.X, D.X, 2f) && Calc.Around(r.D.Y, D.Y, 2f); - */ - } - - public bool IntersectsWith(BarCodeRegion r, float epsilon) - { - // based on RectangleF.IntersectsWith - - return (r.A.X-epsilon < epsilon + this.A.X + (this.B.X - this.A.X)) && - (this.A.X - epsilon < epsilon + r.A.X + (r.B.X - r.A.X)) && - (r.A.Y - epsilon < epsilon+this.A.Y + (this.D.Y - this.A.Y)) && - (this.A.Y - epsilon < epsilon + r.A.Y + (r.D.Y - r.A.Y)); - } - - - public int FindNumberOfSimilarPoints(BarCodeRegion r) - { - int i = 0; - - if (Calc.Around(r.A.X, A.X, 2f) && Calc.Around(r.A.Y, A.Y, 2f)) - i++; - - if (Calc.Around(r.B.X, B.X, 2f) && Calc.Around(r.B.Y, B.Y, 2f)) - i++; - - if (Calc.Around(r.C.X, C.X, 2f) && Calc.Around(r.C.Y, C.Y, 2f)) - i++; - - if (Calc.Around(r.D.X, D.X, 2f) && Calc.Around(r.D.Y, D.Y, 2f)) - i++; - - return i; - } - - public bool SimilarToData(BarCodeRegion r) - { - if (this.Data.Length != r.Data.Length) - return false; - - for (int i=0; i pointsL = new LinkedList(); - LinkedList pointsR = new LinkedList(); - bool debug = false; - - public Regression(MyPointF l0) - { - this.l0 = l0; - this.r0 = l0; //right side should not be used! - Initialize(); - } - - public Regression(MyPointF l0, MyPointF r0) - { - this.l0 = l0; - this.r0 = r0; - Initialize(); - } - public Regression(MyPointF p0, MyVectorF dist) - { - this.l0 = p0; - this.r0 = l0 + dist; - Initialize(); - } - - void Initialize() - { - lsumX = lsumY = lsumXY = lsumX2 = lsumY2 = 0F; - rsumX = rsumY = rsumXY = rsumX2 = rsumY2 = 0F; - minX = minY = float.MaxValue; - maxX = maxY = float.MinValue; - nL = nR = 0; - solved = false; - } - - - public void AddPointL(MyPointF p) - { - float x = p.X - l0.X; - float y = p.Y - l0.Y; - pointsL.AddLast(new MyPointF(x, y)); - - nL++; - lsumX += x; - lsumY += y; - lsumXY += x * y; - lsumX2 += x * x; - lsumY2 += y * y; - UpdateMaxMin(p); - solved = false; - } - - public void AddPointR(MyPointF p) - { - float x = p.X - r0.X; - float y = p.Y - r0.Y; - pointsR.AddLast(new MyPointF(x, y)); - - nR++; - rsumX += x; - rsumY += y; - rsumXY += x * y; - rsumX2 += x * x; - rsumY2 += y * y; - solved = false; - } - - void UpdateMaxMin(MyPointF p) - { - if (p.X < minX) minX = p.X; - if (p.X > maxX) maxX = p.X; - if (p.Y < minY) minY = p.Y; - if (p.Y > maxY) maxY = p.Y; - } - - public void Solve() - { - if (nL > 0) _solve(lsumX, lsumY, lsumXY, lsumX2, lsumY2, nL, pointsL, out la, out lb, out lc, out lerror); - if (nR > 0) _solve(rsumX, rsumY, rsumXY, rsumX2, rsumY2, nR, pointsR, out ra, out rb, out rc, out rerror); - - //mean of both lines - if (nL == 0) { a = ra; b = rb; } - else if (nR == 0) { a = la; b = lb; } - else if (rerror / nR < lerror / nL) { a = ra; b = rb; } - else if (rerror / nR > lerror / nL) { a = la; b = lb; } - else - { - a = (la + ra) / 2f; - b = (lb + rb) / 2f; - } - - modul = (float)Math.Sqrt(a * a + b * b); - a /= modul; b /= modul; - - lC = -a * (l0.X + lsumX / nL) - b * (l0.Y + lsumY / nL); - rC = -a * (r0.X + rsumX / nR) - b * (r0.Y + rsumY / nR); - - solved = true; - } - - - void _solve(float sumX, float sumY, float sumXY, float sumX2, float sumY2, int n, LinkedList points, out float a, out float b, out float c, out float error) - { - if (maxX - minX > maxY - minY) - { //horizontal line - dX = (sumX2 * n - sumX * sumX); - if (Calc.Around(dX, 0.0F, 0.00001F)) //vertical line - { - dY = a = 1F; - dX = b = 0F; - c = -sumX / n; if (n == 0) throw new Exception("div by 0"); - } - else - { - dY = (sumXY * n - sumX * sumY); - a = dY; - b = -dX; - c = (dX * sumY - dY * sumX) / n; if (n == 0) throw new Exception("div by 0"); - } - } - else //Vertical line --> invert axis - { - dY = (sumY2 * n - sumY * sumY); - if (Calc.Around(dY, 0.0F, 0.00001F)) //horizontal line - { - dY = a = 0F; - dX = b = 1F; - if (n == 0) throw new Exception("div by 0"); c = -sumY / n; - } - else - { - dX = (sumXY * n - sumX * sumY); - a = -dY; - b = dX; - if (n == 0) throw new Exception("div by 0"); c = (dY * sumX - dX * sumY) / n; - } - } - - float modul = (float)Math.Sqrt(a * a + b * b); - a /= modul; b /= modul; c /= modul; //normalize result - - error = 0f; - foreach (MyPointF p in points) - { - float d = a * p.X + b * p.Y + c; - error += d * d; - } - } - - public float DistL(MyPointF p) - { - if (p.IsEmpty) return float.MaxValue; - if (!solved) Solve(); - if (modul == 0f) throw new Exception("Div by 0"); - //float x = p.X - l0.X; - //float y = p.Y - l0.Y; - return (a * p.X + b * p.Y + lC) / modul; - } - - public float DistR(MyPointF p) - { - if (p.IsEmpty) return float.MaxValue; - if (!solved) Solve(); - if (modul == 0f) throw new Exception("Div by 0"); - //float x = p.X - r0.X; - //float y = p.Y - r0.Y; - return (a * p.X + b * p.Y + rC) / modul; - } - - //project to Left line - public MyPointF Project(MyPoint p) { return Project((MyPointF)p); } - public MyPointF Project(MyPointF p) - { - float a2 = la * la; - float b2 = lb * lb; - float d = b2 + a2; - float x = 0F, y = 0F; - float xx = p.X - l0.X; - float yy = p.Y - l0.Y; - if (a2 < b2) - { - if (d == 0f) throw new Exception("Div by 0"); - x = (lb * (lb * xx - la * yy) - la * lc) / d; - if (lb == 0f) throw new Exception("Div by 0"); - y = (-la * x - lc) / lb; - } - else - { - if (d == 0f) throw new Exception("Div by 0"); - y = (la * (la * yy - lb * xx) - lb * lc) / d; - if (la == 0f) throw new Exception("Div by 0"); - x = (-lb * y - lc) / la; - } - return new MyPointF(x + l0.X, y + l0.Y); - } - - //intersection with y=p.Y or x=p.X based on vd direction - public MyPoint project(MyPoint p, MyVector vd) - { - if (!solved) Solve(); - if (vd.Y == 0) //vd is horizontal - p.X = Convert.ToInt32(l0.X - (lb * (p.Y - l0.Y) + lc) / la); - else - p.Y = Convert.ToInt32(l0.Y - (la * (p.X - l0.X) + lc) / lb); - return p; - } - - public float A { get { if (!solved) Solve(); return a; } } - public float B { get { if (!solved) Solve(); return b; } } - //public float C { get { if (!solved) Solve(); return c; } } - public float Dx { get { if (!solved) Solve(); return dX; } } - public float Dy { get { if (!solved) Solve(); return dY; } } - public MyVectorF VdX { get { if (!solved) Solve(); return new MyVectorF(dX, dY); } } - public MyVectorF VdY { get { if (!solved) Solve(); return new MyVectorF(-dY, dX); } } - public int NR { get { return pointsR.Count; } } - public int NL { get { return pointsL.Count; } } - public RegressionLine LineL { get { if (!solved) Solve(); return new RegressionLine(a, b, lC); } } - public RegressionLine LineR { get { if (!solved) Solve(); return new RegressionLine(a, b, rC); } } - - public void setDebug(bool deb) { solved = false; debug = deb; } - public LinkedList PointsL { get { return pointsL; } } - public LinkedList PointsR { get { return pointsR; } } - } - - //class that represents the equation of a line ax+by+c=0 - //Used to calculate intersections and find corners. - class RegressionLine - { - float a, b, c; - public RegressionLine(float a, float b, float c) - { - this.a = a; - this.b = b; - this.c = c; - } - - public RegressionLine(MyPointF A, MyPointF B) - { - c = -1f; - float det = A.X * B.Y - A.Y * B.X; - a = (B.Y - A.Y) / det; - b = (A.X - B.X) / det; - } - - public MyVectorF GetNormal() - { - return new MyVectorF(a, b); - } - - public MyPointF Intersection(RegressionLine r) - { - float max = 1F; - if (Math.Abs(a) > Math.Abs(b)) - { - if (Math.Abs(a) > Math.Abs(c)) max = a; - else max = c; - } - else - { - if (Math.Abs(b) > Math.Abs(c)) max = b; - else max = c; - } - if (max == 0f) return MyPointF.Empty; - if (Math.Abs(a / max) > Math.Abs(b / max)) - { - float d = -r.a * b + r.b * a; - if (d == 0f) return MyPointF.Empty; - float y = (r.a * c - r.c * a) / d; - if (a == 0f) return MyPointF.Empty; - float x = (-b * y - c) / a; - return new MyPointF(x, y); - } - else - { - float d = r.a * b - r.b * a; - if (d == 0f) return MyPointF.Empty; - float x = (r.b * c - r.c * b) / d; - if (b == 0f) return MyPointF.Empty; - float y = (-a * x - c) / b; - return new MyPointF(x, y); - } - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Slicer.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Slicer.cs deleted file mode 100644 index b2c1f8fe..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Slicer.cs +++ /dev/null @@ -1,421 +0,0 @@ -using SkiaSharp; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Drawing; - -namespace BarcodeReader.Core.Common -{ - /// - /// Slicer extract connected black pixels segments (or Parts) from a black/white image. - /// Main parameters are: - /// - minRad and maxRad: to discard too small or too big segments - /// - minDist: to connect unconnected segments - /// - maxEntropy: to discard too noisy segments - /// - /// Scans the image row by row and assign an identifier to each pixel. Connected pixels - /// are assigned with the same identifier. The algorithm uses a cache of the last minDist - /// processed rows (prevRows) to find pixel connections and assign the appropiate id. - /// An important step in this algorithm is when two different segments A and B joins. When it happens, - /// B's index is moved to A's index, and B's index in no more used. - /// - internal class Slicer - { - int minDist, minRad, maxRad, maxEntropy; //main parameters - float aspect; // min incX/incY - float aspectMax; // max incX/incY - bool recordPoints; - int width, height; //image size - ArrayList parts; // array with results - int[][] prevRows; //segment indexs from the last minDist rows, - //to allow connecting segments in the current row with those at minDist - int[] currentRow; //segment indexs of the current processing row - - /// - /// Slicer object - /// Set aspectMax = 0f to skip checking for maxAspect - /// - /// - /// - /// - /// - /// - /// - /// - public Slicer(int minDist, int minRad, int maxRad, int maxEntropy, float aspect, float aspectMax, bool recordPoints) - { - this.minRad = minRad; - this.maxRad = maxRad; - this.minDist = minDist; - this.maxEntropy = maxEntropy; - this.prevRows = new int[minDist][]; - this.aspect = aspect; - this.aspectMax = aspectMax; - this.recordPoints = recordPoints; - } - - public ArrayList GetParts(BlackAndWhiteImage img) - { - this.width = img.Width; - this.height = img.Height; - - for (int k = 0; k < minDist; k++) - { - prevRows[k] = new int[width]; - for (int i = 0; i < width; i++) prevRows[k][i] = -1; - } - - currentRow = new int[width]; - parts = new ArrayList(); - - for (int y = 0; y < height; y += 1) - ScanRow(y, img.GetRow(y)); - - //filter parts - ArrayList filtered = new ArrayList(); - foreach (Segment p in parts) - { - if (p == null) - continue; - - bool widthAndHeightAreEqual = p.Width == p.Height; - float aspectCalculated = (float)p.Width / (float)p.Height; - float aspectCalculated2 = (float)p.Height / (float)p.Width; - - if ( - (p.Width > minRad || p.Height > minRad) && - (p.Width < maxRad && p.Height < maxRad) - ) - { - - bool canAdd = false; - - // if aspectMax is set to zero so not checking it - if (aspectMax aspect) || - (p.Width >= p.Height && (float)p.Height / (float)p.Width > aspect)) - && p.GetEntropy(img, maxEntropy) < maxEntropy); - } - else - { - // checking if we can add based on AspectMax - canAdd = - // check for min ratio - ( - ((p.Width > p.Height || widthAndHeightAreEqual) && (aspectCalculated > aspect || widthAndHeightAreEqual)) - || - ((p.Width < p.Height || widthAndHeightAreEqual) && (aspectCalculated2 > aspect || widthAndHeightAreEqual)) - ) - && - // check for max ratio - ( - - ((p.Width > p.Height || widthAndHeightAreEqual) && (aspectCalculated < aspectMax || widthAndHeightAreEqual)) - || - ((p.Height > p.Width || widthAndHeightAreEqual) && (aspectCalculated2 < aspectMax || widthAndHeightAreEqual)) - ) - && p.GetEntropy(img, maxEntropy) < maxEntropy; - }; - - if (canAdd) - filtered.Add(p); - } - } - return filtered; - } - - static bool FloatsAreEqual(float a, float b) - { - return Math.Abs(a - b) < float.Epsilon; - } - - - //Process a row. For each pixel in the row looks for a previously connected pixel. It if is - //found, the pixel is added to its segment (or Part object). Otherwise, a new id (Part) is created. - private void ScanRow(int y, XBitArray row) - { - for (int x = 0; x < width; x++) - { - int nPart = -1; - if (row[x]) //only process black pixels - { - nPart = -1; //by now, no connected pixel is found - - //look in the left of the current row for a connected pixel. - for (int xi = x - 1; xi >= x - minDist && xi >= 0; xi--) - if (currentRow[xi] != -1) - { - nPart = currentRow[xi]; //a connected pixel if found, and assign its id - ((Segment)parts[nPart]).Add(x, y); //add the new pixel to the segment (Part). - break; - } - - //look in the minDist previous rows for a connected pixel. - for (int iRow = 0; iRow < minDist; iRow++) - for (int xi = x - minDist; xi <= x + minDist; xi++) if (xi >= 0 && xi < width) - { - int prevPart = prevRows[iRow][xi]; - if (prevPart != -1) //a connected pixel if found - if (nPart == -1) - {//if it is the first connected pixel found, add the current pixel to it. - nPart = prevPart; - ((Segment)parts[nPart]).Add(x, y); - } - else if (nPart != prevPart) - {//if the current pixel is connected to + than 1 pixel, join parts - ((Segment)parts[nPart]).Join((Segment)parts[prevPart]); - //remove prevPart id, and move prevPart id to nPart in the prevRows - parts[prevPart] = null; - for (int k = 0; k < minDist; k++) - for (int j = 0; j < width; j++) - if (prevRows[k][j] == prevPart) prevRows[k][j] = nPart; - for (int j = 0; j < x; j++) if (currentRow[j] == prevPart) currentRow[j] = nPart; - - } - } - - if (nPart == -1) //no connected pixel is found, so add a new part - { - nPart = parts.Count; - parts.Add(new Segment(new MyPoint(x, y),recordPoints)); - } - } - currentRow[x] = nPart; - } - //move index rows - int[] tmp = prevRows[minDist - 1]; - for (int k = minDist - 1; k > 0; k--) prevRows[k] = prevRows[k - 1]; - prevRows[0] = currentRow; currentRow = tmp; - } - } - - //class to hold connected pixels. Actually it does not store the pixels, but its bounding box (xin, xend, yin, yend) -#if CORE_DEV - public -#else - internal -#endif - class Segment : IComparable - { - bool scaned = false; - int xIn, xEnd, yIn, yEnd; - float x; - int blackPixelsCounter=0; - LinkedList points; - - public Segment(MyPoint p, bool recordPoints) - { - xIn = xEnd = p.X; - yIn = yEnd = p.Y; - blackPixelsCounter = 1; - if (recordPoints) - { - points = new LinkedList(); - points.AddLast(p); - } - } - - //for every new pixel added, the bounding box is updated - public void Add(int x, int y) - { - if (xIn > x) xIn = x; - else if (xEnd < x) xEnd = x; - - if (yEnd < y) yEnd = y; - - blackPixelsCounter++; - if (points != null) points.AddLast(new MyPoint(x, y)); - } - - //joining parts means updating their bounding boxes - public void Join(Segment p) - { - if (xIn > p.xIn) xIn = p.xIn; - if (xEnd < p.xEnd) xEnd = p.xEnd; - if (yIn > p.yIn) yIn = p.yIn; - if (yEnd < p.yEnd) yEnd = p.yEnd; - blackPixelsCounter += p.blackPixelsCounter; - if (points != null && p.points != null) foreach (MyPoint pp in p.points) points.AddLast(pp); - } - - public SKPointI[] GetBBox() - { - return new SKPointI[] { new SKPointI((int)xIn - 1, (int)yIn - 1), new SKPointI((int)xEnd + 1, (int)yIn - 1), new SKPointI((int)xEnd + 1, (int)yEnd + 1), new SKPointI((int)xIn - 1, (int)yEnd + 1), new SKPointI((int)xIn - 1, (int)yIn - 1) }; - } - - //A simple measure of noise (or entropy). Simply count horizontal and vertical black-white intervals, - //for each row and column. For example, a totally black segment returns entropy 0... - public int GetEntropy(BlackAndWhiteImage img, int maxEntropy) - { - int hIntervals = 0; - int max = maxEntropy * (yEnd - yIn +1)/ 100; - for (int y = yIn; y <= yEnd; y++) - { - XBitArray row= img.GetRow(y); - bool isBlack=row[xIn]; - for (int x = xIn+1; x <= xEnd; x++) - { - if (row[x]^isBlack) - { - hIntervals++; - if (hIntervals > max) return maxEntropy; - isBlack = !isBlack; - } - } - } - int vIntervals = 0; - - for (int x = xIn ; x <= xEnd; x++) - { - XBitArray col = img.GetColumn(x); - bool isBlack = col[yIn]; - for (int y = yIn +1; y <= yEnd; y++) - { - if (col[y] ^ isBlack) - { - vIntervals++; - if (vIntervals > max) return maxEntropy; - isBlack = !isBlack; - } - } - } - //returns the average of vertical and horizontal entropy - return (hIntervals * 100 / (yEnd - yIn + 1) + vIntervals * 100 / (xEnd - xIn + 1)) / 2; - } - - public SKRect GetRectangle() - { - return new SKRect(xIn, yIn, xEnd + 1, yEnd + 1); - } - - public void SetRectangle(SKRect r) - { - this.xIn = (int)r.Left; - this.yIn = (int)r.Top; - this.xEnd = (int)r.Right - 1; - this.YEnd = (int)r.Bottom - 1; - } - - public int XIn { get { return xIn; } set { xIn = value; } } - public int XEnd { get { return xEnd; } set { xEnd = value; } } - public int YIn { get { return yIn; } set { yIn = value; } } - public int YEnd { get { return yEnd; } set { yEnd = value; } } - - public int Width { get { return xEnd - xIn + 1; } } - public int Height { get { return yEnd - yIn + 1; } } - public bool Scaned { get { return scaned; } set { scaned = value; } } - public float X { get { return x; } set { x = value; } } - public int BlackPixelCounter { get { return blackPixelsCounter; } } - - public SKPointI Center { get { return new SKPointI((xIn + xEnd) / 2, (yIn + yEnd) / 2); } } - public MyPointF CenterF { get { return new MyPointF((float)(xIn + xEnd + 1) / 2f, (float)(yIn + yEnd + 1) / 2f); } } - public SKPointI LU { get { return new SKPointI(xIn, yIn); } } - public SKPointI LD { get { return new SKPointI(xIn, yEnd); } } - public SKPointI RU { get { return new SKPointI(xEnd, yIn); } } - public SKPointI RD { get { return new SKPointI(xEnd, yEnd); } } - public LinkedList Points { get { return points; } } - - public float Dist(Segment p) - { - SKPointI b = p.Center; - SKPointI a = this.Center; - return (float)(Math.Sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y))); - } - - public int CompareTo(Object o) - { - Segment p = (Segment)o; - return (int)((p.x - this.x) * 10); - } - - public override String ToString() - { - return "X:" + x + " LU(" + XIn + "," + YIn + ")"; - } - } - - class BBox - { - float d; - MyPointF center; - MyVectorF vd, vdY; - float minX, maxX, minY, maxY; - bool first; - - public BBox(MyPoint center, MyVector vd, float d) - { - this.d = d; - this.center = center; - this.vd = ((MyVectorF)vd) / vd.Length; //normalized - this.vdY = this.vd.Perpendicular; - this.first = true; - } - - //returns the distance from p to the main axis, i.e the Y coordinate - public float GetY(MyPointF p) - { - MyVectorF pc = (MyPointF)p - center; - float dy = pc.X * vd.Y - pc.Y * vd.X; - return dy; - } - - public float GetHeight() { return maxY - minY; } - - //add a new point to the bounding box, i.e. simply update minx, miny, maxx, maxy when needed. - public void Update(MyPoint p, ref float ymax, ref float ymin) - { - MyVectorF pc = (MyPointF)p - center; - float dx = pc * vd; - if (dx < minX || first) minX = dx; - if (dx > maxX || first) maxX = dx; - float dy = pc.X * vd.Y - pc.Y * vd.X; - if (dy < minY || first) minY = dy; - if (dy > maxY || first) maxY = dy; - if (dy < ymin) ymin = dy; - if (dy > ymax) ymax = dy; - first = false; - } - - public float GetArea() - { - return (float)(maxX-minX+1)*(float)(maxY-minY+1); - } - - public SKRect GetRectangle() - { - return new SKRect(0, 0, (maxX - minX + 4*d) + 1, (maxY - minY) + 1); - } - - bool axisDone = false; - MyPointF O; - - // Returns the coordinate of a point (x,y) using the bbox axis. - public MyPointF GetPoint(int x, int y) - { - if (!axisDone) - { - O = center + vd * ( minX - d*2F) + vdY * maxY + new MyPointF(0.5F, 0.5F); - axisDone = true; - } - return O + vd * (float)x - vdY * (float)y; - } - - //returns the corners of the bounding box - public SKPointI[] GetBBox() - { - MyVectorF up = vdY * maxY; - MyVectorF down = vdY * minY; - - MyPointF l = center + vd * (minX - d); - MyPointF lu = l + up; - MyPointF ld = l + down; - - MyPointF r = center + vd * (maxX + d); - MyPointF ru = r + up; - MyPointF rd = r + down; - - return new SKPointI[] { (SKPointI)lu, (SKPointI)ld, (SKPointI)rd, (SKPointI)ru, (SKPointI)lu }; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/SquareFinder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/SquareFinder.cs deleted file mode 100644 index a095a5a3..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/SquareFinder.cs +++ /dev/null @@ -1,249 +0,0 @@ -namespace BarcodeReader.Core.Common -{ - interface FinderFactory - { - SquareFinder IsFinder(ImageScaner scan, MyPointF left, MyPointF right, MyPointF top, MyPointF bottom); - float GetOffsetFactor(); - float GetCenterRatio(); - int GetNumModules(); - bool CanHaveHoles(); - } - - class SquareFinder: BarCodeRegion - { - //Max accepted ratio Y/X for finders - protected static readonly float patternMaxRatio = 0.3F; - - //Max difference coeficient between the 4 edges of the finder - protected static readonly float maxFinderEdgeLenghtDifference = 0.3F; - - //Max angle difference for finder edges - protected static readonly float maxFinderAngleDifference = 0.25F; - - //Max height difference for finder edges - protected static readonly float maxFinderHeightDifference = 0.3F; - - - - public float Width; - public float Height; - public MyVectorF RightNormal; - public MyVectorF DownNormal; - public float ModuleWidth, ModuleHeight; - public MyVectorF ModuleRight, ModuleDown; - - public SquareFinder(SquareFinder f): base(f) - { - this.Width = f.Width; - this.Height = f.Height; - this.RightNormal = f.RightNormal; - this.DownNormal = f.DownNormal; - this.ModuleWidth = f.ModuleWidth; - this.ModuleHeight = f.ModuleHeight; - this.ModuleRight = f.ModuleRight; - this.ModuleDown = f.ModuleDown; - } - - //fast check to see if a and b (start and end points of a horizontal pattern) - //has a vertical pattern crossing the center. - //Used to fast accept/reject horizontal patterns found in the scan line process. - public static bool CheckCrossPattern(ImageScaner scan, MyPoint a, MyPoint b, MyPoint center, IPatternFinderNoiseRow patternFinder, FinderFactory factory, out MyPoint minpUp, out MyPoint minpDown) - { - //cross pattern search - MyPoint pUp, pDown; - pUp = pDown = center; - minpUp = minpDown = MyPoint.Empty; - - int d = b.X - a.X; - //for big patterns, scan the border of the mid black square, to avoid holes in the center produced by the BW conversion - // d<350 defines how large square finders could be - // refs #147, see 1028-qrcode-rotated-45-LARGE-x2.png and 1028-qrcode-rotated-45-LARGE.png - XBitArray col = (d < 350 || !factory.CanHaveHoles() ? scan.GetColumn(center.X) : scan.GetColumn(center.X - d / 7)); - - d = (int)((float)(d) * factory.GetOffsetFactor()) + 1; - int start = center.Y - d; - if (start < 0) start = 0; - int end = center.Y + d; - //if (end > col.Size) end = col.Size;//!!!!! - int minNumModules =d/factory.GetNumModules()/3; - patternFinder.NewSearch(col, start, end, 1, minNumModules>0?minNumModules:-1); - int minDist = int.MaxValue; - while (patternFinder.NextPattern()!=null) - { - pUp.Y = patternFinder.First; - pDown.Y = patternFinder.Last; - float dh = (float)(b.X - a.X); - float dv = (float)(pDown.Y - pUp.Y); - if (Calc.Around(dh / dv, 1.0F, patternMaxRatio)) //valid ratio - { - int dist=patternFinder.Center-a.Y; - if (dist < 0) dist = -dist; - if (dist < minDist) { minDist = dist; minpUp = pUp; minpDown = pDown; } - } - } - if ((float)minDist <= (float)(b.X - a.X) / factory.GetCenterRatio()) return true; - return false; - } - - //checks if points left, right, top and bottom lays on the 4 edges of a square finder - public static SquareFinder IsFinder(ImageScaner scan, MyPoint left, MyPoint right, MyPoint top, MyPoint bottom, FinderFactory factory) - { - //Detect if the left-right are horizontal or vertical - MyVector vd = right - left; - bool isHorizontal = vd.isHorizontal(); - MyVector vdH, vdV; - if (isHorizontal) - { - if (left.X < right.X) { vdH = new MyVector(-1, 0); vdV = new MyVector(0, -1); } - else { vdH = new MyVector(1, 0); vdV = new MyVector(0, 1); } - } - else - { - if (left.Y < right.Y) { vdH = new MyVector(0, -1); vdV = new MyVector(1, 0); } - else { vdH = new MyVector(0, 1); vdV = new MyVector(0, -1); } - } - - MyPointF A, B, C, D; - Regression rV, rH; - EdgeTrack et = new EdgeTrack(scan); - float moduleLength = vd.Length / factory.GetNumModules(); - bool startWithBlack = scan.isBlack(left); - - rV = et.Track(left, right, vdH, moduleLength, startWithBlack); - rH = et.Track(top, bottom, vdV, moduleLength, startWithBlack); - findVertexs(rV, rH, out A, out B, out C, out D); - if (A.IsEmpty || B.IsEmpty || C.IsEmpty || D.IsEmpty) return null; - - //Checks if corner distances are right (they should approximate a square) - MyVectorF ba = B - A, ca = C - A; - float ac = ca.Length; - float ab = ba.Length; - float bd = (B - D).Length; - float cd = (C - D).Length; - float epsilon = ab * maxFinderEdgeLenghtDifference; - if (ac >= 6F && ab >= 6F && bd >= 6F && cd >= 6F && Calc.Around(ac, bd, epsilon) && Calc.Around(ab, cd, epsilon) - && Calc.Around(ab, ac, epsilon * 3)) - { - //check angle - float cos = (ba * ca) / (ab * ac); - if (Calc.Around(cos, 0.0F, maxFinderAngleDifference)) - { - SquareFinder finder = factory.IsFinder(scan, A, B, C, D); - return finder; - } - } - return null; - } - - static void findVertexs(Regression rV, Regression rH, out MyPointF A, out MyPointF B, out MyPointF C, out MyPointF D) - { - RegressionLine rLeft = rV.LineL; - RegressionLine rRight = rV.LineR; - RegressionLine rTop = rH.LineL; - RegressionLine rBottom = rH.LineR; - - A = rLeft.Intersection(rBottom); - B = rBottom.Intersection(rRight); - C = rLeft.Intersection(rTop); - D = rTop.Intersection(rRight); - } - - - //checks if a and b (start and end points of a pattern in ANY DIRECTION) - //has a vertical pattern crossing the center. - public static SquareFinder IsFinder(ImageScaner scan, MyPoint a, MyPoint b, PatternFinderNoise patternFinder, FinderFactory factory) - { - MyPoint pUp, pDown; - MyPoint center=pUp=pDown = (a + b) / 2; - MyVector vd=(b-a); - MyVector up = new MyVector(vd.Y*3/4, -vd.X*3/4); - if (vd.Length > 50f && factory.CanHaveHoles()) center = center - vd / (float)factory.GetNumModules(); - Bresenham br = new Bresenham(center + up, center - up); - int minNumModules=(int)vd.Length/factory.GetNumModules()/3; - patternFinder.NewSearch(br, true, minNumModules>0?minNumModules:-1); - while (patternFinder.NextPattern()!=-1) - { - pUp = patternFinder.First; - pDown = patternFinder.Last; - - float dh = (b - a).Length; - float dv = (pDown - pUp).Length; - if (Calc.Around(dh / dv, 1.0F, maxFinderHeightDifference)) - { - SquareFinder finder = IsFinder(scan, a, b, pUp, pDown, factory); - if (finder != null) return finder; - } - } - return null; - } - - - //checks if a and b (start and end points of a HORIZONTAL pattern found during scan line) - //PRE: a and b has a cross pattern (previously checked). - public static SquareFinder IsFinder(ImageScaner scan, MyPoint left, MyPoint right, MyPoint center, PatternFinderNoise finder, PatternFinderNoise crossFinder, FinderFactory factory) - { - //Detect if the left-right are horizontal or vertical - MyVector vd = right - left; - MyVector vdH = new MyVector(-1, 0); - MyVector vdV = new MyVector(0, -1); - - //Calculate angle using the left edge - float moduleLength = vd.Length / factory.GetNumModules(); - bool startWithBlack = scan.isBlack(left); - - EdgeTrack le = new EdgeTrack(scan); - le.Track(left, vdH, moduleLength, startWithBlack); - MyVectorF mainH=le.GetLine().GetNormal().Normalized; - float d = mainH * vdH; - if (d > 0) mainH = mainH * -1F; - else d = -d; - float midLength = vd.Length *0.75F / d; - - //find good edge points - finder.NewSearch(new Bresenham(center- mainH*midLength ,center + mainH * midLength), true, 0); - while (finder.NextPattern()!=-1) - { - MyPoint l = finder.First; - MyPoint r = finder.Last; - SquareFinder f = IsFinder(scan, l, r, crossFinder, factory); - if (f != null) - if (f.In(center)) - return f; - } - return null; - } - - - public SquareFinder(MyPointF A, MyPointF B, MyPointF C, MyPointF D, int NumModules): base(A,B,C,D) - { - Width = ( (A - B).Length + (C - D).Length )/2.0F; - Height = ((A - C).Length +(B - D).Length )/2.0F; - - RightNormal = (B - A).Normalized; - DownNormal = (A - C).Normalized; - - ModuleWidth = Width / (float)NumModules; - ModuleHeight = Height / (float)NumModules; - - ModuleRight = RightNormal * ModuleWidth; - ModuleDown = DownNormal * ModuleHeight; - } - - //Calculate the center of the finder - public MyPointF Center() - { - MyPointF m = ((A + B) / 2.0F + (C + D) / 2.0F) / 2.0F; - return m; - } - - //Rotate 90º clockwise the 4 corners of the finder. Used to normalize finders of a QR code. - public void Rotate() - { - MyPointF p = A; A = B; B = D; D = C; C = p; - float w = Width; Width = Height; Height = w; - MyVectorF v = RightNormal; RightNormal = DownNormal *-1F; DownNormal = v; - float m = ModuleWidth; ModuleWidth = ModuleHeight; ModuleHeight = m; - v = ModuleRight; ModuleRight = ModuleDown * -1F; ModuleDown = v; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/TwoFourStateBarcodesReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/TwoFourStateBarcodesReader.cs deleted file mode 100644 index 622cd6cf..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/TwoFourStateBarcodesReader.cs +++ /dev/null @@ -1,432 +0,0 @@ -using System; -using System.Collections; - -namespace BarcodeReader.Core.Common -{ - //Abstract class to find 4-state or 2-state barcodes (RM, IM, KIX, PostCode, PostNet) - //Extends Reader2D, a generic 4state barcode reader - //Finder: 4 or 2-state barcodes have not a pattern, but the track region can be used as a pattern. - //Large patterns are very sensible to rotations (can only be detected for small rotations), thus - //we look for a small part of the track region. Exactly 5 black + 5 white bars. This allows to detect - //barcodes rotated +-10º. - //Once found, we still need to detect the rotation of the barcode. Since there is not a main edge - //in the barcode, and because track region has short bars, detecting the rotation based on edge - //detection fails easily. Instead, we trace lines at different angles (0..10º every 1º) counting - //bars for each angle. If we are able to count as many bars as the length of the barcode, we - //have found the rotation of the barcode. Then we are ready to read and decode it. -#if CORE_DEV - public -#else - internal -#endif - abstract class TwoFourStateBarcodesReader: Reader2D - { - //Number of elements of a finder: 5 black bars + 5 white spaces - //Actually, does not search a finder, but a track region (i.e. 101010101010) - protected static readonly int finderElements = 10; - - //All bars in the finder must be more or less of the same width. This is the max error of their widths. - protected float finderMaxWidthError = 0.3f; - - //When check for start quiet zone, check 2 * modules with white pixels - protected float startPatternQuietZone = 2f; - - //Max scan angle in order to find the line crossing the whole barcode - float scanMaxAngle = (float)Math.PI / 4f; - - //Max and Min allowed difference between width bars of the finder and the rest of bars of the barcode - float maxBarWidthDifference = 0.2f; - float minBarWidthDifference = 2f; - - //Max number of black pixels (noise) accepted when finding a white line on top/bottom of the barcode to find the barcode height - int whiteLineMaxNoise = 5; - - //Max offset difference between consecutive bars - float maxDifferenceBetweenBarOffsets = 0.1f; - - //If the larger bar is too short compared to the bounding box heigth then reject the barcode - float maxDifferenceBetweenLargerBarAndBoundingBox = 0.75f; - - //Tri and four state bar length thresholds, to decide if a bar is a large or shor bar. - float fourStateHeightThreshold = 0.33f; - float triStateHeightThreshold = 0.5f; - - //Tri state barcodes have all bars aligned to the base. This is the max allowed error. - float maxBaseAlignmentError = 0.25f; - - //methods defined in derived classes - protected abstract bool IsFourState(); //true for four state barcodes, false for two state barcodes - protected abstract bool CheckNBars(int nBars); - protected abstract bool CheckRatio(float ratio); //checks if ratio is in the expected range - protected abstract IDecoderAscDescBars GetDecoder(); - - protected override int getFinderNElements() { return finderElements; } - - Hashtable h = new Hashtable(); - int[] w = new int[finderElements / 2]; - - //Method to check if widths are a valid finder for four-state or two-state barcodes - //Actually, does not search a finder, but a track region (i.e. 101010101010) - protected override bool IsFinder(int[] widths, out float meanWidth) - { - //calculate 2 bars widths (to compensate white-black image brightness) - for (int i = 0, j = 0; i < finderElements; i += 2, j++) w[j] = widths[i] + widths[i + 1]; - - //search the median width bar (not the mean!) - h.Clear(); - for (int i = 0; i < finderElements / 2; i++) - if (h.ContainsKey(w[i])) { int n = (int)h[w[i]]; h[w[i]] = (n + 1); } - else h[w[i]] = 1; - int m = 0, mid = finderElements / 4; - float mean = -1f; - foreach (int i in h.Keys) - { - m += (int)h[i]; - if (m >= mid) { mean = (float)i; break; } - } - - //check if all bars are 1 module length. Otherwise return false. - int numModules = 0; - meanWidth = 0f; - for (int i = 0; i < finderElements / 2; i++) - { - meanWidth += (float)w[i]; - double proportional = ((double)w[i]) / mean; - if (!Calc.Around((float)proportional, 1f, finderMaxWidthError)) return false; - numModules++; - } - meanWidth /= (float)numModules; - return true; - } - - - //check if the left side of the finder is white area, for 2 modules (meanWidth=1 module length) - protected override bool LeftFree(int x0, int y, float meanWidth) - { - XBitArray row = BWImage.GetRow(y); - int d = (int)(meanWidth * startPatternQuietZone); - for (int x = x0 - 1, i = 0; i < d && x >= 0; i++, x--) if (!(row[x] ^ startsWithBlack)) return false; - return true; - } - - - //check if pixel x in r is black - int isBlack(XBitArray r, int x) - { - if (r == null || x < 0 || x >= r._size) return 0; //defaults to white - return r[x] ? 1 : 0; - } - - //Algorithm to detect the rotation of the barcode. Traces several lines at 0..10º each 1º - //looking for CheckNBars bars. Seems slow, but works pretty fast. - protected override BarCodeRegion FindBarcode(int x0, int y, float meanWidth) - { - - //first estimate gradient to reject not vertical -10..10º edges - XBitArray prevRow = (y == 0 ? null : BWImage.GetRow(y - 1)); - XBitArray row = BWImage.GetRow(y); - XBitArray nextRow = (y == BWImage.Height - 1 ? null : BWImage.GetRow(y + 1)); - - int dy = isBlack(prevRow, x0 - 1) + 2 * isBlack(prevRow, x0) + isBlack(prevRow, x0 + 1) - - isBlack(nextRow, x0 - 1) - 2 * isBlack(nextRow, x0) - isBlack(nextRow, x0 + 1); - - //double angle = Math.Atan2((double)dy, (double)dx); - if (dy < -1 || dy > 1) return null; - - //trace several lines at 0..10º each 1º and, if not found, the same for 0..-10º - float incAngle = (float)Math.PI / 360f; - MyPoint pIn = new MyPoint(x0, y), pEnd; - - float angle = 0f; - int nBars = countBars(pIn, meanWidth, startsWithBlack, angle, out pEnd); - int nBars0 = nBars; - while (!CheckNBars(nBars) && nBars >= nBars0 && angle < scanMaxAngle) - { - angle += incAngle; - nBars = countBars(pIn, meanWidth, startsWithBlack, angle, out pEnd); - } - if (!CheckNBars(nBars)) - { - // check timeout - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - angle = 0f; - nBars = countBars(pIn, meanWidth, startsWithBlack, angle, out pEnd); - while (!CheckNBars(nBars) && nBars >= nBars0 && angle > -scanMaxAngle) - { - angle -= incAngle; - nBars = countBars(pIn, meanWidth, startsWithBlack, angle, out pEnd); - - // check timeout - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - } - } - - //if we have found all bars then read the barcode - if (CheckNBars(nBars)) - { - //First detect the height of the barcode, looking for a white line parallel to the track region - MyVector incX = pEnd - pIn; - MyVectorF vdX = new MyVectorF((float)Math.Cos(angle), -(float)Math.Sin(angle)); - MyVectorF vdY = new MyVectorF(-(float)Math.Sin(angle), -(float)Math.Cos(angle)); - - Bresenham brup = new Bresenham(pIn, vdY); - while (BWImage.In(brup.Current)) if (WhiteLine(brup.Current, incX)) break; else brup.Next(); - Bresenham brdown = new Bresenham(pIn, -vdY); - while (BWImage.In(brdown.Current)) if (WhiteLine(brdown.Current, incX)) break; else brdown.Next(); - MyPoint pUp = brup.Current; - MyPoint pDown = brdown.Current; - - //Now check ratio and read the barcode - float ratio = (pEnd - pIn).Length / (pUp - pDown).Length; - if (CheckRatio(ratio)) - { - BarCodeRegion region = new BarCodeRegion(pUp, pUp + incX, pDown + incX, pDown); - return Decode(pIn, pEnd, region, nBars); - } - } - return null; - } - - //count bars starting at pIn, with module length=meanWidth, current=true is starts with black bar, - //and a given angle. - //Returns the number of found bars and the end point pEnd - //Allows noisy pixels - private int countBars(MyPoint pIn, float meanWidth, bool current, float angle, out MyPoint pEnd) - { - int nBars = 0, n = 0; - bool processing = !current; - float margin = meanWidth * maxBarWidthDifference; - if (margin < minBarWidthDifference) margin = minBarWidthDifference; //min 2 pixels difference - MyVectorF vdX = new MyVectorF((float)Math.Cos(angle), -(float)Math.Sin(angle)); - - pEnd = pIn; - Bresenham br = new Bresenham(pIn, vdX); - while (BWImage.In(br.Current)) - { - XBitArray row = BWImage.GetRow(br.Current.Y); - if (row[br.Current.X] ^ processing) - { - n++; //current equals processing - } - else //transition detected - { - //n++; //move below - if (processing == current) //2 bars read (BW or WB) - { - if (Calc.Around(meanWidth, (float)n, margin)) n = 1; - else if ((float)n > meanWidth) break; - } - else - { - n++; - pEnd = br.Current; nBars++; - } - processing = !processing; - } - br.Next(); - - // check timeout - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - } - if (!BWImage.In(br.Current) && processing != current) - { - pEnd = br.Current; nBars++; - } - return nBars; - } - - - //check if the line starting at p in vd direction is white, or has only a few black pixels - protected bool WhiteLine(MyPoint p, MyVector vd) - { - Bresenham br = new Bresenham(p, p + vd); - int n= whiteLineMaxNoise; //allow max 5 black noisy pixels - while (!br.End()) if (scan.isBlack(br.Current) && n-- == 0) return false; - else br.Next(); - return true; - } - - - - //region: A------B - // | | - // D------C - //Sample the barcode region and calls the decoder - protected BarCodeRegion Decode(MyPoint pIn, MyPoint pEnd, BarCodeRegion region, int nBars) - { - bool[][] samples = Sample(pIn, pEnd, region, nBars); - if (samples == null) return null; - IDecoderAscDescBars decoder = GetDecoder(); - float confidence; - string code = decoder.Decode(samples, out confidence); - if (code != null) - { - region.Data = new ABarCodeData[] { new StringBarCodeData(code) }; - region.Confidence = confidence; - } - else region = null; - return region; - } - - - - - //Since the rotation angle is not exact, we can not sample bars in a regular way. Instead, we - //trace bars starting from the track region. Then we can distingish between long up, short, or - //long down bars, which sets the 4-state bar state. - protected bool[][] Sample(MyPoint pIn, MyPoint pEnd, BarCodeRegion region, int N) - { - int iSample = 0; - MyVector vdUp = new MyVector(0, -1), vdDown = new MyVector(0, 1); - float[][] bars = new float[N][]; - bool[][] samples = new bool[N][]; - float max = 0f, min = 1000f, minBase = 0f; - float length=(pIn-pEnd).Length; - int noise = (int)Math.Round(length / (float)(2 * N - 1)); //level of accepted noise for tracking bars - if (noise < 1) noise = 1; - float moduleLength=2f*length/(float)(2*N-1); - - bool current = BWImage.GetRow(pIn.Y)[pIn.X]; - bool processing = !current; - float lastD = 0f; - Bresenham br = new Bresenham(pIn, pEnd); - while (!br.End()) - { - XBitArray row = BWImage.GetRow(br.Current.Y); - if (row[br.Current.X] ^ processing) - { - if (processing != current) //for each white-black transition trace vertical up and down bars - { - float d = (br.Current - pIn).Length; - float x=d/moduleLength; - bool goodDistFromPrevious=Calc.Around(d-lastD, moduleLength, moduleLength*maxDifferenceBetweenBarOffsets); - //if x rounds to the correct position, or the distance from the previous bar is good ->proceed with this bar - if (Calc.Around(x, (float)Math.Round(x), 0.4f) || goodDistFromPrevious) - { - MyPoint up = trackVLine(br.Current, current, vdUp, noise); - MyPoint down = trackVLine(br.Current, current, vdDown, noise); - - float dup = (up - br.Current).Length; //up bar length - float ddown = (down - br.Current).Length; //down bar length - if (ddown > minBase) minBase = ddown; - float l = dup + ddown; - if (l > max) max = l; - if (l < min) min = l; - - int pos = (int)Math.Round(x); - if (pos == iSample || goodDistFromPrevious) - { - if (iSample < bars.Length) - { - bars[iSample] = new float[2]; - bars[iSample][0] = dup; - bars[iSample][1] = ddown; - } - lastD = d; - iSample++; - } - else return null; - } - } - processing = !processing; - } - br.Next(); - } - - //convert up and down bar lengths to binary. - //For 4-state barcodes we use the max bar lenght/3 as a threshold - //For 2-state barcodes use length/2 - float maxHeight = (region.A - region.D).Length; - if (max> maxHeight) max = maxHeight; - if (max < maxHeight * maxDifferenceBetweenLargerBarAndBoundingBox) return null; - float step = (IsFourState() ? max * fourStateHeightThreshold : max *triStateHeightThreshold); - for (int i = 0; i < N; i++) if (bars[i] != null) - { - //if 2 state barcode, check if bases are aligned - if (!IsFourState() && minBase - bars[i][1] > max * maxBaseAlignmentError) return null; - - float l = bars[i][0] + bars[i][1]; - float ls = l / step; - int type = (IsFourState()?(ls < 1.5f ? 0 : ls < 2.5f ? 1 : 2):(ls>1.5f?1:0)); -#if DEBUG - //Console.Write("-" + type); -#endif - - - samples[i] = new bool[2]; - if (IsFourState()) - { - if (type == 2) samples[i][0] = samples[i][1] = true; - else if (type == 0) samples[i][0] = samples[i][1] = false; - else if (bars[i][0] > bars[i][1]) - { - samples[i][0] = true; samples[i][1] = false; - } - else - { - samples[i][0] = false; samples[i][1] = true; - } - } - else - { - if (type == 1) samples[i][0] = true; - else samples[i][0] = false; - } - } - else samples[i] = new bool[2]; -#if DEBUG - //Console.WriteLine("\nSamples:"); - //for (int i = 0; i < N; i++) Console.Write((samples[i][0] ? "1" : " ")); - //Console.WriteLine(""); - //for (int i = 0; i < N; i++) Console.Write((samples[i][1] ? "1" : " ")); - //Console.WriteLine(""); -#endif - return samples; - } - - - - //Find a vertical transition (from current color to !current) starting at point q - //current=true --> white to black transition - private bool findTransition(ref MyPoint q, bool current, int offset) - { - XBitArray row = (XBitArray)BWImage.GetRow(q.Y); - int x = q.X; - int xMin = x - offset; - int xMax = x + offset; - //move to left WHITE pixel (if current is BLACK) - while (x >= 0 && row[x] == current && x >= xMin) x--; - if (x >= 0 && row[x] == current) return false; //if not found return false - - //move to the right BLACK pixel - while (x < 0 || x < BWImage.Width && row[x] != current && x <= xMax) x++; - if (current && x >= BWImage.Width) return false; // if we are looking for a black pixel and we are out of the image, return false - int d = q.X - x; - if (d < offset && d > -offset) - { - q.X = x; - return true; - } - return false; - } - - //Trace a vertical edge and stops at the vertex, starting at point q in dir direction - //Allows noise pixels - private MyPoint trackVLine(MyPoint q, bool current, MyVector dir, int noise) - { - bool end = false; - MyPoint prev = q; - int offset = 1; - while (q.Y >= 0 && q.Y < BWImage.Height && !end) - if (!findTransition(ref q, current, offset)) { if (offset++ > noise) end = true; } - else { prev = q; q.Y += dir.Y; offset = 1; } - return prev; - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Utils.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Utils.cs deleted file mode 100644 index 1d5c8fc6..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Common/Utils.cs +++ /dev/null @@ -1,280 +0,0 @@ -using SkiaSharp; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Drawing; -using System.IO; -using System.Text; - -namespace BarcodeReader.Core.Common -{ - internal class Utils - { - // changes the bit order per byte in the given array - public static byte[] ReverseBitsPerByte(byte[] rawData) - { - byte[] data = new byte[rawData.Length]; - for (int i = 0; i < rawData.Length; ++i) - { - for (int b = 0; b < 8; ++b) - { - bool isOne = (rawData[i] & (1 << b)) > 0; - if (isOne) - { - data[i] = (byte)(data[i] | (1 << (7 - b))); - } - } - } - - return data; - } - - // Copies elements from the source to the destination array at the given indices - public static void ReverseSelect(int[] sourceArray, int[] destinationArray, int[] indices) - { - for (int i = 0; i < indices.Length; ++i) - { - destinationArray[indices[i]] = sourceArray[i]; - } - } - - // implements the missing Copy(from, index, length) for BitArrays - public static BitArray BitArrayPart(BitArray srcArray, int from, int length) - { - BitArray result = new BitArray(length); - for (int i = 0; i < length; ++i) - { - result[i] = srcArray[i + from]; - } - - return result; - } - - public static bool[][] NewBoolArray(int cols, int rows) - { - bool[][] a=new bool[rows][]; - for (int i = 0; i < rows; i++) - { - a[i] = new bool[cols]; - } - return a; - } - - public static T[][] NewJaggedArray(int cols, int rows) - { - var a = new T[rows][]; - for (int i = 0; i < rows; i++) - { - a[i] = new T[cols]; - } - return a; - } - - public static string BitArrayToString(BitArray arr) - { - var sb = new StringBuilder(); - foreach (bool d in arr) - { - sb.Append(d ? 1 : 0); - } - return sb.ToString(); - } - - public static bool[][] CloneJaggedArray(bool[][] arr) - { - var res = new bool[arr.Length][]; - for (int i = 0; i < arr.Length; i++) - { - res[i] = (bool[]) arr[i].Clone(); - } - - return res; - } - - public static ulong GetArrayHash(int[] arr) - { - unchecked - { - ulong hash = 17; - - // get hash code for all items in array - for (int i = 0; i < arr.Length; i++) - { - hash = hash * 23 + (ulong)arr[i]; - } - - return hash; - } - } - - public static int Sum(int[] arr) - { - var res = 0; - for (int i = 0; i < arr.Length; i++) - res += arr[i]; - - return res; - } - - public static ArrayComparer ArrayComparerInt = new ArrayComparer(); - - public static bool IsMirrored(int[] arr1, int[] arr2) - { - if (arr1.Length != arr2.Length) return false; - for(int i=0;i(ref T v1, ref T v2) - { - T temp = v1; - v1 = v2; - v2 = temp; - } - - public static SKRect DrawPath(SKPointI[] polygon) - { - // Create a path from the points - using (var path = new SKPath()) - { - path.MoveTo(polygon[0]); - for (int i = 1; i < polygon.Length; i++) - { - path.LineTo(polygon[i]); - } - path.Close(); // optional: close the shape - - // Get bounds - SKRect bounds = path.Bounds; - - return bounds; - } - } - - public static void SaveSKBitmap(SKBitmap bitmap, string filePath) - { - using (var image = SKImage.FromBitmap(bitmap)) - using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) - using (var stream = File.OpenWrite(filePath)) - { - data.SaveTo(stream); - } - } - - public static int Clamp(int value, int min, int max) - { - return (value < min) ? min : (value > max) ? max : value; - } - - public static float Clamp(float value, float min, float max) - { - return (value < min) ? min : (value > max) ? max : value; - } - } - - /// - /// Compares two arrays to see if the values inside of the array are the same. This is - /// dependent on the type contained in the array having a valid Equals() override. - /// - /// The type of data stored in the array - internal class ArrayComparer : IEqualityComparer - { - /// - /// Gets the hash code for the contents of the array since the default hash code - /// for an array is unique even if the contents are the same. - /// - /// - /// See Jon Skeet (C# MVP) response in the StackOverflow thread - /// http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode - /// - /// The array to generate a hash code for. - /// The hash code for the values in the array. - public int GetHashCode(int[] array) - { - // if non-null array then go into unchecked block to avoid overflow - if (array != null) - { - unchecked - { - int hash = 17; - - // get hash code for all items in array - for (int i = 0; i < array.Length; i++) - { - hash = hash * 23 + array[i]; - } - - return hash; - } - } - - // if null, hash code is zero - return 0; - } - - /// - /// Compares the contents of both arrays to see if they are equal. This depends on - /// typeparameter T having a valid override for Equals(). - /// - /// The first array to compare. - /// The second array to compare. - /// True if firstArray and secondArray have equal contents. - public bool Equals(int[] firstArray, int[] secondArray) - { - // if same reference or both null, then equality is true - if (object.ReferenceEquals(firstArray, secondArray)) - { - return true; - } - - // otherwise, if both arrays have same length, compare all elements - if (firstArray != null && secondArray != null && - (firstArray.Length == secondArray.Length)) - { - for (int i = 0; i < firstArray.Length; i++) - { - // if any mismatch, not equal - if (!object.Equals(firstArray[i], secondArray[i])) - { - return false; - } - } - - // if no mismatches, equal - return true; - } - - // if we get here, they are not equal - return false; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/Configurations.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/Configurations.cs deleted file mode 100644 index ec0ff65d..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/Configurations.cs +++ /dev/null @@ -1,195 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace BarcodeReader.Core.Datamatrix -{ - internal enum DatamatrixType - { - ECC200, NonECC200 - } - - internal class Configuration - { - // total module count in the X direction (except L and dotted L patterns, ie -2) - public readonly int FullX; - - // total module count in the Y direction - public readonly int FullY; - - //total module count except alignement patterns (L) - public readonly int FullDataX, FullDataY; - - // full symbol count (not bit, symbol = byte) - public readonly int FullSymbolCount; - - // number of data symbols - public readonly int DataSymbolCount; - - // region count in the X direction - public readonly int RegionCountX; - - // region count in the Y direction - public readonly int RegionCountY; - - // modules per region in the X direction - public readonly int SubX; - - // modules per region in the Y direction - public readonly int SubY; - - // number of error correction symbols - public readonly int ErrorCodeWords; - - // number of RS blocks - public readonly int ReedSolomonBlocks; - - public readonly DatamatrixType Type; - - public Configuration(int fullX, int fullY, int rCntX, int rCntY, int errorCodeWords, int rsBlocks, DatamatrixType dmType) - { - FullX = fullX; - FullY = fullY; - RegionCountX = rCntX; - RegionCountY = rCntY; - ErrorCodeWords = errorCodeWords; - ReedSolomonBlocks = rsBlocks; - FullDataX = FullX - 2 * RegionCountX; - FullDataY = FullY - 2 * RegionCountY; - SubX = FullX / RegionCountX; - SubY = FullY/RegionCountY; - FullSymbolCount = FullDataX*FullDataY/8; - DataSymbolCount = FullSymbolCount - ErrorCodeWords; - Type = dmType; - } - - // generate the indices of the data symbols for each RS block - public int[][] GetDataBlocks() - { - int[][] result = new int[ReedSolomonBlocks][]; - for (int i = 0; i < ReedSolomonBlocks; ++i) - { - int count = (DataSymbolCount - i)/ReedSolomonBlocks; - if ((DataSymbolCount - i)%ReedSolomonBlocks > 0) - { - count++; - } - result[i] = new int[count]; - int nr = i; - for (int j = 0; j < result[i].Length; ++j) - { - result[i][j] = nr; - nr += ReedSolomonBlocks; - } - } - - return result; - } - - // generate the indices of the error correction symbols for each RS block - public int[][] GetRSBlocks() - { - int[][] result = new int[ReedSolomonBlocks][]; - for (int i = 0; i < ReedSolomonBlocks; ++i) - { - int count = (ErrorCodeWords - i) / ReedSolomonBlocks; - if ((ErrorCodeWords - i) % ReedSolomonBlocks > 0) - { - count++; - } - result[i] = new int[count]; - int nr = i; - for (int j = 0; j < result[i].Length; ++j) - { - result[i][j] = DataSymbolCount + nr; - nr += ReedSolomonBlocks; - } - } - - return result; - } - - public static LinkedList FindConfiguration(float cols, float rows, bool isECC200) - { - float minDist = 1000F; - int iCols = (int)Math.Round(cols); - int iRows = (int)Math.Round(rows); - int i, iMin=-1; - for (i=0;i l = new LinkedList(); - if (minDist < (cols + rows) * 0.2F) - { - l.AddLast(AllConfigurations[iMin]); - if (iMin>0) l.AddLast(AllConfigurations[iMin-1]); - if (iMin c == '1').ToArray(); - var input = new BitArray(bits); - - var decoder = new ConvolutionDecoder(input); - decoder.Correct(); - Console.WriteLine(decoder.CorrectionSucceeded); - } - }*/ - - // values are in line with the ones found in the actual bitstream - enum ErrorCorrectionLevel - { - ECC000 = -1, ECC050 = 0, ECC080 = 1, ECC100 = 2, ECC140 = 3 - } - - // Convolution code decoder & corrector, based on sequential decoding. - // http://en.wikipedia.org/wiki/Sequential_decoding - internal class ConvolutionDecoder - { - #region ECC prefixes - // prefixes used to identify the correction level in place - private static readonly BitArray ECC000Prefix = - new BitArray(new bool[] {false, true, true, true, true, true, true}); - - private static readonly BitArray NonECC000Prefix = - new BitArray(new bool[] {false, true, true, true, false, false, false}); - - private static readonly BitArray ECC050Postfix = - new BitArray(new bool[] {false, false, false, false, false, false, true, true, true, false, false, false}); - - private static readonly BitArray ECC080Postfix = - new BitArray(new bool[] {false, false, false, true, true, true, false, false, false, true, true, true}); - - private static readonly BitArray ECC100Postfix = - new BitArray(new bool[] {false, false, false, true, true, true, true, true, true, true, true, true}); - - private static readonly BitArray ECC140Postfix = - new BitArray(new bool[] {true, true, true, false, false, false, true, true, true, true, true, true}); - - private static readonly BitArray[] ProtectionModes = new BitArray[] - {ECC050Postfix, ECC080Postfix, ECC100Postfix, ECC140Postfix}; - - #endregion - - #region Generator Matrices - // generator matrices - assembled them from the DM specification - private static readonly bool[][][] GeneratorMatrixECC050 = new bool[][][] - { - new bool[][] - { - new bool[] {true, false, false}, new bool[] {false, true, false}, - new bool[] {false, false, true}, new bool[] {true, true, true} - }, - new bool[][] - { - new bool[] {false, false, true}, new bool[] {false, true, false}, - new bool[] {true, true, true}, new bool[] {true, true, true} - }, - new bool[][] - { - new bool[] {false, false, true}, new bool[] {true, false, false}, - new bool[] {true, false, false}, new bool[] {false, true, false} - }, - new bool[][] - { - new bool[] {false, true, true}, new bool[] {true, true, false}, - new bool[] {true, false, false}, new bool[] {false, false, true} - } - }; - - private static readonly bool[][][] GeneratorMatrixECC080 = new bool[][][] - { - new bool[][] - { - new bool[] {true, false}, new bool[] {false, true}, - new bool[] {true, true} - }, - new bool[][] - { - new bool[] {true, false}, new bool[] {true, false}, - new bool[] {false, true} - }, - new bool[][] - { - new bool[] {false, false}, new bool[] {false, false}, - new bool[] {false, true} - }, - new bool[][] - { - new bool[] {true, true}, new bool[] {false, true}, - new bool[] {false, false} - }, - new bool[][] - { - new bool[] {false, false}, new bool[] {true, false}, - new bool[] {false, true} - }, - new bool[][] - { - new bool[] {true, false}, new bool[] {true, false}, - new bool[] {true, false} - }, - new bool[][] - { - new bool[] {true, false}, new bool[] {false, true}, - new bool[] {true, false} - }, - new bool[][] - { - new bool[] {true, true}, new bool[] {false, false}, - new bool[] {true, true} - }, - new bool[][] - { - new bool[] {false, true}, new bool[] {true, true}, - new bool[] {false, false} - }, - new bool[][] - { - new bool[] {false, false}, new bool[] {true, true}, - new bool[] {false, true} - }, - new bool[][] - { - new bool[] {true, false}, new bool[] {true, false}, - new bool[] {false, false} - }, - new bool[][] - { - new bool[] {false, true}, new bool[] {false, false}, - new bool[] {false, true} - } - }; - - private static readonly bool[][][] GeneratorMatrixECC100 = new bool[][][] - { - new bool[][] { new bool[] { true }, new bool[] { true } }, - new bool[][] { new bool[] { false }, new bool[] { true } }, - new bool[][] { new bool[] { true }, new bool[] { false } }, - new bool[][] { new bool[] { false }, new bool[] { true } }, - new bool[][] { new bool[] { false }, new bool[] { true } }, - new bool[][] { new bool[] { true }, new bool[] { false } }, - new bool[][] { new bool[] { true }, new bool[] { true } }, - new bool[][] { new bool[] { true }, new bool[] { false } }, - new bool[][] { new bool[] { true }, new bool[] { false } }, - new bool[][] { new bool[] { true }, new bool[] { false } }, - new bool[][] { new bool[] { true }, new bool[] { false } }, - new bool[][] { new bool[] { false }, new bool[] { true } }, - new bool[][] { new bool[] { false }, new bool[] { false } }, - new bool[][] { new bool[] { false }, new bool[] { true } }, - new bool[][] { new bool[] { false }, new bool[] { true } }, - new bool[][] { new bool[] { true }, new bool[] { true } } - }; - - private static readonly bool[][][] GeneratorMatrixECC140 = new bool[][][] - { - new bool[][] { new bool[] {true}, new bool[] {true}, new bool[] {true}, new bool[] {true}}, - new bool[][] { new bool[] {false}, new bool[] {false}, new bool[] {true}, new bool[] {true}}, - new bool[][] { new bool[] {false}, new bool[] {false}, new bool[] {true}, new bool[] {true}}, - new bool[][] { new bool[] {false}, new bool[] {true}, new bool[] {false}, new bool[] {false}}, - new bool[][] { new bool[] {true}, new bool[] {true}, new bool[] {true}, new bool[] {true}}, - new bool[][] { new bool[] {false}, new bool[] {false}, new bool[] {true}, new bool[] {true}}, - new bool[][] { new bool[] {false}, new bool[] {false}, new bool[] {false}, new bool[] {false}}, - new bool[][] { new bool[] {true}, new bool[] {true}, new bool[] {true}, new bool[] {true}}, - new bool[][] { new bool[] {false}, new bool[] {true}, new bool[] {false}, new bool[] {false}}, - new bool[][] { new bool[] {false}, new bool[] {true}, new bool[] {true}, new bool[] {true}}, - new bool[][] { new bool[] {true}, new bool[] {true}, new bool[] {false}, new bool[] {true}}, - new bool[][] { new bool[] {false}, new bool[] {true}, new bool[] {true}, new bool[] {true}}, - new bool[][] { new bool[] {true}, new bool[] {false}, new bool[] {true}, new bool[] {true}}, - new bool[][] { new bool[] {true}, new bool[] {true}, new bool[] {true}, new bool[] {true}} - }; - - - private static readonly bool[][][][] GeneratorMatrices = new bool[][][][] - { - GeneratorMatrixECC050, GeneratorMatrixECC080, - GeneratorMatrixECC100, GeneratorMatrixECC140 - }; - - #endregion - - private readonly BitArray inputData; - private bool correctionSuceeded; - private BitArray correctedData; - private readonly ErrorCorrectionLevel correctionLevel; - private readonly bool[][][] generatorMatrix; - private readonly int k; - private readonly int n; - - public ConvolutionDecoder(BitArray array) - { - inputData = array; - int ecc000Diff = 0; - int nonEcc000Diff = 0; - for (int i = 0; i < 7; ++i) - { - if (ECC000Prefix[i] != inputData[i]) - { - ecc000Diff++; - } - - if (NonECC000Prefix[i] != inputData[i]) - { - nonEcc000Diff++; - } - } - - if (ecc000Diff < nonEcc000Diff) - { - correctionLevel = ErrorCorrectionLevel.ECC000; - inputData = Common.Utils.BitArrayPart(inputData, 7, inputData.Length - 7); - return; - } - - // we skip some of the cases for multiple winners - if (nonEcc000Diff < ecc000Diff) - { - int[] indices = new int[4]; - int[] errors = new int[4]; - for (int i = 0; i < 4; ++i) - { - indices[i] = i; - errors[i] = 0; - for (int b = 0; b < 12; ++b) - { - if (inputData[b + 7] != ProtectionModes[i][b]) - { - errors[i]++; - } - } - } - - // we skip some of the cases for multiple winners - Array.Sort(errors, indices); - correctionLevel = (ErrorCorrectionLevel) indices[0]; - generatorMatrix = GeneratorMatrices[indices[0]]; - n = generatorMatrix[0].Length; - k = generatorMatrix[0][0].Length; - inputData = Common.Utils.BitArrayPart(inputData, 19, inputData.Length - 19); - - //Debug.WriteLine("Unrandomised Bit Stream (w/o header): " + Utils.BitArrayToString(inputData)); - - return; - } - } - - public bool CorrectionSucceeded - { - get { - return correctionSuceeded; - } - } - - public BitArray CorrectedData - { - get { - return correctedData; - } - } - - public ErrorCorrectionLevel CorrectionLevel - { - get - { - return correctionLevel; - } - } - - // Sequential decoding algorithm. Try the most appealing input, and fall back if too much error accumulates. - public void Correct() - { - if (correctionLevel == ErrorCorrectionLevel.ECC000) - { - correctedData = inputData; - correctionSuceeded = true; - return; - } - - int blockCount = inputData.Length/n; - int dataLength = blockCount*k; - int bitIndex = 0; - var encoder = new ConvolutionEncoder(generatorMatrix); - Node currentNode = new Node(encoder, 0, null); - while (currentNode != null && currentNode.InputCandidate.Count < dataLength) - { - // too many error - fall back - if (currentNode.Distance > 5) - { - currentNode = currentNode.LastNode; - continue; - } - - // haven't passed this node yet, create the possible routes - if (currentNode.NextNodes == null) - { - bool[] chunk = new bool[n]; - for (int b = 0; b < n; ++b, ++bitIndex) - { - if (bitIndex >= inputData.Length) - { - correctionSuceeded = false; - return; - } - chunk[b] = inputData[bitIndex]; - } - currentNode.ApplyOutput(chunk); - } - - // atthis point we _know_ that NextNodes is not null, so let's proceed with the next route - if (currentNode.NextNodes.Count == 0) - { - // no more ways, go back - currentNode = currentNode.LastNode; - } - else - { - Node nextNode = (Node)currentNode.NextNodes[0]; - currentNode.NextNodes.RemoveAt(0); - currentNode = nextNode; - } - } - - // we got back to the start - decoding failed - if (currentNode == null) - { - correctionSuceeded = false; - return; - } - - // yay - we got the input right - correctedData = new BitArray(currentNode.InputCandidate.Count); - for (int i = 0; i < currentNode.InputCandidate.Count; ++i) - { - correctedData[i] = (bool) currentNode.InputCandidate[i]; - } - correctionSuceeded = true; - } - - // represents one node in the decode chain - private class Node - { - private readonly ConvolutionEncoder convolutionEncoder; - - public readonly Node LastNode; - - public ArrayList NextNodes; - - public ArrayList InputCandidate; - - public readonly int Distance; - - public Node(ConvolutionEncoder encoder, int dist, Node lastNode) - { - convolutionEncoder = encoder; - Distance = dist; - LastNode = lastNode; - InputCandidate = new ArrayList(); - } - - // generate possibilities for proceeding, based on the given output block - public void ApplyOutput(bool[] outputBlock) - { - int[] distances; - NextNodes = new ArrayList(); - bool[][] inputCandidates = GenerateInputCandidates(outputBlock, out distances); - for (int i = 0; i < inputCandidates.Length; ++i) - { - ConvolutionEncoder newCe = (ConvolutionEncoder) convolutionEncoder.Clone(); - newCe.EncodeInput(inputCandidates[i]); - Node newNode = new Node(newCe, distances[i] + Distance, this); - newNode.InputCandidate = new ArrayList(InputCandidate); - newNode.InputCandidate.AddRange(inputCandidates[i]); - NextNodes.Add(newNode); - } - } - - private bool[][] GenerateInputCandidates(bool[] outputBlock, out int[] distances) - { - // get all the possible variations for the given input length - int inputCount = (1 << convolutionEncoder.K); - int[] dists = new int[inputCount]; - bool[][] inputVariations = new bool[inputCount][]; - for (int i = 0; i < inputCount; ++i) - { - inputVariations[i] = new bool[convolutionEncoder.K]; - for (int b = 0; b < convolutionEncoder.K; ++b) - { - inputVariations[i][b] = (i & (1 << b)) != 0; - } - - bool[] output = convolutionEncoder.SimulateInput(inputVariations[i]); - dists[i] = HammingDistance(outputBlock, output); - } - - const int maxResultCount = 3; - int resultCount = Math.Min(maxResultCount, inputVariations.Length); - Array.Sort(dists, inputVariations); - distances = new int[resultCount]; - bool[][] result = new bool[resultCount][]; - Array.Copy(inputVariations, result, resultCount); - Array.Copy(dists, distances, resultCount); - return result; - } - - private static int HammingDistance(bool[] a, bool[] b) - { - int dist = 0; - for (int i = 0; i < a.Length; i++) - { - if (a[i] != b[i]) - { - dist++; - } - } - - return dist; - } - } - } - - // Convolution encoder. Used to get the possible outputs for a given input. - // http://en.wikipedia.org/wiki/Convolutional_code - internal class ConvolutionEncoder : ICloneable - { - private readonly bool[][][] generatorMatrices; - - public readonly int N; - - public readonly int K; - - // in fact, the real M is m-1, but we include the inputs as T=0 storage time registers - public readonly int M; - - private bool[][] registers; - - public ConvolutionEncoder(bool[][][] gms) - { - generatorMatrices = gms; - M = generatorMatrices.Length; - if (M == 0) - { - throw new Exception("Memory register width is 0 for Convolution Encoder"); - } - - N = generatorMatrices[0].Length; - K = generatorMatrices[0][0].Length; - - if (K == 0) - { - throw new Exception("Input width is 0 for Convolution Encoder"); - } - if (N == 0) - { - throw new Exception("Output width is 0 for Convolution Encoder"); - } - - registers = new bool[M][]; - for (int i = 0; i < M; ++i) - { - registers[i] = new bool[K]; - - for (int j = 0; j < K; ++j) - { - registers[i][j] = false; - } - } - } - - public object Clone() - { - ConvolutionEncoder clone = new ConvolutionEncoder(generatorMatrices); - clone.registers = Utils.CloneJaggedArray(registers); - - return clone; - } - - public bool[] SimulateInput(bool[] input) - { - ValidateInput(input); - bool[][] localRegs = Utils.CloneJaggedArray(registers); - ShiftRegisters(localRegs); - ApplyInput(localRegs, input); - return CalculateOutput(localRegs); - } - - public void ApplyInput(bool[] input) - { - ValidateInput(input); - ApplyInput(registers, input); - } - - public bool[] EncodeInput(bool[] input) - { - bool[] result = SimulateInput(input); - ShiftRegisters(registers); - ApplyInput(registers, input); - return result; - } - - private void ValidateInput(bool[] input) - { - if (input.Length != K) - { - throw new Exception("Invalid input length"); - } - } - - // shift the memory registers to accomodate the next input - private static void ShiftRegisters(bool[][] r) - { - for (int i = r.Length - 1; i > 0; --i) - { - for (int j = 0; j < r[0].Length; ++j) - { - r[i][j] = r[i - 1][j]; - } - } - } - - // apply the input to the first register - private static void ApplyInput(bool[][] r, bool[] input) - { - for (int j = 0; j < r[0].Length; ++j) - { - r[0][j] = input[j]; - } - } - - // get output values based on the passed register contents - private bool[] CalculateOutput(bool[][] localRegs) - { - bool[] outputs = new bool[N]; - // for each register, calculate the output contribution - for (int r = 0; r < M; ++r) - { - for (int o = 0; o < N; ++o) - { - for (int i = 0; i < K; ++i) - { - if (generatorMatrices[r][o][i]) - { - outputs[o] ^= localRegs[r][i]; - } - } - } - } - - return outputs; - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMConnectReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMConnectReader.cs deleted file mode 100644 index 31c818b6..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMConnectReader.cs +++ /dev/null @@ -1,268 +0,0 @@ -using System; -using System.Collections.Generic; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Datamatrix -{ - internal class DMConnectReader: DMSimpleReader - { - LinkedList allEdges, lastEdges; - - //allow connect 10 pixels holes - internal double MaxHoleSizeInsideLines; - - internal override FoundBarcode[] Scan() - { - CreateLists(); - - //main loop to scan horizontal lines - lastEdges = new LinkedList(); - allEdges = new LinkedList(); - XBitArray rowPrev = bwSourceImage.GetRow(0); - XBitArray row = bwSourceImage.GetRow(1); - for (int y = 2; y < height; y += scanRowStep) - { - XBitArray rowNext = bwSourceImage.GetRow(y); - ScanBits(y - 1, rowPrev, row, rowNext, true); - rowPrev = row; - row = rowNext; - } - //connect vertical edges - LinkedList verticalEdges = allEdges; - - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - //main loop to scan vertical lines - lastEdges = new LinkedList(); - allEdges = new LinkedList(); - XBitArray colPrev = bwSourceImage.GetColumn(0); - XBitArray col = bwSourceImage.GetColumn(1); - for (int x = 2; x < width; x += scanRowStep) - { - XBitArray colNext = bwSourceImage.GetColumn(x); - ScanBits(x - 1, colPrev, col, colNext, false); - colPrev = col; - col = colNext; - } - //connect horizontal edges - LinkedList horizontalEdges = allEdges; - - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - //classify edges by length - ledges = new Dictionary>(); // (new ReverseInt2()); - classifyEdges(verticalEdges); - classifyEdges(horizontalEdges); - - LinkedList newVerticalEdges = new LinkedList(); - foreach (Edge e in verticalEdges) e.ScanConnections(e.end, true, MIN_SIZE, ref newVerticalEdges, this.MaxHoleSizeInsideLines); - if (newVerticalEdges.Count < 2 * verticalEdges.Count) classifyEdges(newVerticalEdges); - - LinkedList newHorizontalEdges = new LinkedList(); - foreach (Edge e in horizontalEdges) e.ScanConnections(e.end, false, MIN_SIZE, ref newHorizontalEdges, this.MaxHoleSizeInsideLines); - if (newHorizontalEdges.Count < 2 * horizontalEdges.Count) classifyEdges(newHorizontalEdges); - - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - return FindDM(ledges, false); - } - - void classifyEdges(LinkedList l) - { - foreach (Edge e in l) - { - int k = (int)(e.end - e.start).Length; - if (!ledges.ContainsKey(k)) - { - ledges.Add(k, new LinkedList()); - } - ledges[k].AddLast(e); - } - } - - - internal override void NewLPattern(Edge A, Edge B) - { -#if FIND_FINDER - float d1 = (A.start - B.start).Length; - float d2 = (A.start - B.end).Length; - float d3 = (A.end - B.start).Length; - float d4 = (A.end - B.end).Length; - BarCodeRegion reg=null; - if (d1 < MAX_DIST_TO_CONSIDER_LPATTERN) reg = new BarCodeRegion(A.start, A.end, B.end, B.end+(A.end-A.start)); - else if (d2 < MAX_DIST_TO_CONSIDER_LPATTERN) reg = new BarCodeRegion(A.start, A.end, B.start, B.start+(A.end-A.start)); - else if (d3 < MAX_DIST_TO_CONSIDER_LPATTERN) reg = new BarCodeRegion(A.end, A.start, B.end, B.end+(A.start-A.end)); - else reg = new BarCodeRegion(A.end, A.start, B.start, B.start+(A.start-A.end)); - if (reg!=null) candidates.AddLast(reg); -#else - RegressionLine lineA, lineB; - CrossEdges(A, B, out lineA, out lineB); - bool found = ReadLPattern(lineA, A.start, A.end, lineB, B.start, B.end); - if (!found) - { - MyPointF Aup, Adown, Bup, Bdown; - TrackEdges(A, B, out lineA, out Aup, out Adown, out lineB, out Bup, out Bdown); - found=ReadLPattern(lineA, Aup, Adown, lineB, Bup, Bdown); - } - -#endif - } - - void TrackEdges(Edge A, Edge B, out RegressionLine lineA, out MyPointF Aup, out MyPointF Adown, - out RegressionLine lineB, out MyPointF Bup, out MyPointF Bdown) - { - lineA = lineB = null; - Aup = Adown = Bup = Bdown = MyPointF.Empty; - - //Track edge crossing the center point of the start/reversed stop pattern - EdgeTrack etA = new EdgeTrack(scan); - MyPoint centerA = A.Center(); - MyVector vdY = A.start - A.end; - MyVector vdX = (vdY.isHorizontal() ? new MyVector(0, A.isBlack ? -1 : 1) : new MyVector(A.isBlack ? -1 : 1, 0)); - etA.Track(centerA, vdX, 3F, true); - Aup = etA.Up(); - Adown = etA.Down(); - Adown = Adown - ((MyVectorF)vdY).Normalized; - if (Aup.IsInfinity || Adown.IsInfinity) return; - lineA = etA.GetLine(); - - EdgeTrack etB = new EdgeTrack(scan); - MyPoint centerB = B.Center(); - vdY = B.start - B.end; - vdX = (vdY.isHorizontal() ? new MyVector(0, B.isBlack ? -1 : 1) : new MyVector(B.isBlack ? -1 : 1, 0)); - etB.Track(centerB, vdX, 3, true); - Bup = etB.Up(); - Bdown = etB.Down(); - if (Bup.IsInfinity || Bdown.IsInfinity) return; - Bdown = Bdown - ((MyVectorF)vdY).Normalized; - lineB = etB.GetLine(); - } - - void CrossEdges(Edge A, Edge B, out RegressionLine lineA, out RegressionLine lineB) - { - lineA = new RegressionLine(A.start, A.end); - lineB = new RegressionLine(B.start, B.end); - } - - - bool ReadLPattern(RegressionLine lineA, MyPointF Aup, MyPointF Adown, RegressionLine lineB, MyPointF Bup, MyPointF Bdown) - { - //find intersection - MyPointF cross = lineA.Intersection(lineB); - - //check if point is out of image's borders (with tolerance) - if (cross.IsEmpty) return false; - if (!bwSourceImage.In(cross, (width + height) / 4)) - return false; - - //detect orientation of A and B edges - MyPointF a, b; - float d1 = (Aup - cross).Length; - float d2 = (Adown - cross).Length; - if (d1 > d2) { a = Aup;} else { a = Adown; } - float d3 = (Bup - cross).Length; - float d4 = (Bdown - cross).Length; - if (d3 > d4) { b = Bup; } else { b = Bdown; } - - //clockwise? - MyVectorF v1 = a - cross; - MyVectorF v2 = b - cross; - float k = v1.X * v2.Y - v1.Y * v2.X; - if (k > 0) { MyPointF d = a; a = b; b = d; } - MyPointF c = b + (a - cross); - - return ReadBarcode(cross, a, b, c); - } - - //Scans a horizontal - internal override void ScanBits(int id, XBitArray bitsPrev, XBitArray bits, XBitArray bitsNext, bool isHorizontal) - { - bool prevIsBlack = bits[1]; - int i=2; - while (i < bits.Size-1) - { - bool currentIsBlack = bits[i]; - if (prevIsBlack ^ currentIsBlack) - { - //sobel angle - if (currentIsBlack) i--; //calculate sobel from a white pixel - int c00 = bitsPrev[i - 1] ? 1 : 0, c01 = bitsPrev[i] ? 1 : 0, c02 = bitsPrev[i+1] ? 1 : 0; - int c10 = bits[i - 1] ? 1 : 0, c11 = bits[i ] ? 1 : 0, c12 = bits[i+1] ? 1 : 0; - int c20 = bitsNext[i - 1] ? 1 : 0, c21 = bitsNext[i] ? 1 : 0, c22 = bitsNext[i+1] ? 1 : 0; - if (currentIsBlack) i++; //remove offset from i - //else i--; //move to the previous black pixel - - int Gx = c02 + c22 - c00 - c20 + 2 * (c12 - c10); - int Gy = c20 + c22 - c00 - c02 + 2 * (c21 - c01); - int G = (Gx > 0 ? Gx : -Gx) + (Gy > 0 ? Gy : -Gy); - if (G >= 2) //4 - { - //Hough - double Gangle = isHorizontal ? Math.Atan2(Gy, Gx) : Math.Atan2(Gx, Gy); - //if (Gangle < 0) Gangle += Math.PI; - //else while (Gangle + 0.01 > Math.PI) Gangle -= Math.PI; - MyPoint p = isHorizontal ? new MyPoint(i, id) : new MyPoint(id, i); - int best = int.MaxValue, j=0; - Edge eBest=null; - foreach(Edge e in edges) { - int d=e.Belongs(p, (float)Gangle, currentIsBlack, isHorizontal); - if (d!=-1 && d< best) - { - best = d; - eBest = e; - } - j++; - } - if (eBest != null) - { - eBest.Add(p, (float)Gangle); - } - else - { - Edge e=new Edge(p, (float)Gangle, currentIsBlack); - edges.AddLast(e); - - //find if the new edge connect with previous ones - e.FindConnections(lastEdges, isHorizontal,MIN_SIZE, this.MaxHoleSizeInsideLines); - e.FindConnections(edges, isHorizontal,MIN_SIZE, this.MaxHoleSizeInsideLines); - } - } - } - prevIsBlack = currentIsBlack; - i++; - } - - //purge terminated edges - LinkedList removed=new LinkedList(); - foreach (Edge e in edges) - if (isHorizontal && (id== scan.Height-2 || e.end.Y < id - 3) || - !isHorizontal && (id == scan.Width-2 || e.end.X < id - 3)) - removed.AddLast(e); - - foreach (Edge e in removed) - { - edges.Remove(e); - if (e.Length(isHorizontal) > MIN_SIZE) - { - MyPoint end = e.end; - if (isHorizontal) end.Y++; - else end.X++; - e.Add(end, e.angle); //add last point prepared to be converted to float pixel MyPixelF - - lastEdges.AddLast(e); - allEdges.AddLast(e); - } - } - - //clean cache of last found edges - LinkedList toRemove = new LinkedList(); - if (lastEdges != null) - foreach (Edge e in lastEdges) if (isHorizontal && id - e.end.Y > 25 || !isHorizontal && id-e.end.X>25) toRemove.AddLast(e); - foreach (Edge e in toRemove) - lastEdges.Remove(e); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMDecoder.cs deleted file mode 100644 index d00c7d5b..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMDecoder.cs +++ /dev/null @@ -1,235 +0,0 @@ -using System; -using System.Collections; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Datamatrix -{ - internal class DMDecoder - { - // decode method for ecc200 barcodes - public static ABarCodeData[] DecodeECC200Data(int[] endcodedData, System.Text.Encoding encoding) - { - int index = 0; - ArrayList decodedData = new ArrayList(); - //this is for indicating that we detected a pad char in ASCII mode -> end of data. - bool finished = false; - while (index < endcodedData.Length && !finished) - { - int symbol = endcodedData[index]; - if (symbol < 230 && symbol != 129 || symbol == SymbolDecoder.ASCIIUpperShift) - { - // ASCII encodation mode - ABarCodeData[] plusData = SymbolDecoder.DecodeASCII(endcodedData, ref index); - if (plusData.Length > 0) - { - decodedData.AddRange(plusData); - } - continue; - } - switch (symbol) - { - case 129: // pad char found, end of data - finished = true; - break; - case 230: // C40 mode - ++index; - ABarCodeData[] c40Data = SymbolDecoder.DecodeC40Text(endcodedData, ref index, SymbolDecoder.C40Set); - if (c40Data.Length > 0) - { - decodedData.AddRange(c40Data); - } - break; - case 231: // Base256 mode - ++index; - ABarCodeData[] base256Data = SymbolDecoder.DecodeBase256(endcodedData, ref index, encoding); - if (base256Data.Length > 0) - { - decodedData.AddRange(base256Data); - } - break; - case 238: // X12 mode - ++index; - ABarCodeData[] x12Data = SymbolDecoder.DecodeX12(endcodedData, ref index); - if (x12Data.Length > 0) - { - decodedData.AddRange(x12Data); - } - break; - case 239: // Text mode - ++index; - ABarCodeData[] textData = SymbolDecoder.DecodeC40Text(endcodedData, ref index, - SymbolDecoder.TextSet); - if (textData.Length > 0) - { - decodedData.AddRange(textData); - } - break; - case 240: // Edifact mode - ++index; - ABarCodeData[] edifactData = SymbolDecoder.DecodeEdifact(endcodedData, ref index); - if (edifactData.Length > 0) - { - decodedData.AddRange(edifactData); - } - break; - case 242: - ++index; //ECI - decodedData.Add(SymbolDecoder.DecodeECI(endcodedData, ref index)); - break; - case 232: - ++index; //FNC - decodedData.Add(new FNC1Symbol()); - break; - case 233: - ++index; //Structured append - decodedData.Add(SymbolDecoder.DecodeStructuredAppend(endcodedData, ref index)); - break; - case 234: - ++index; //Reader prog - decodedData.Add(new ReaderProgramSymbol()); - break; - case 236: - ++index; //Macro 05 - decodedData.Add(new Macro05Symbol()); - break; - case 237: - ++index; //Macro 06 - decodedData.Add(new Macro06Symbol()); - break; - default: - ++index; //Error - break; - } - } - - ABarCodeData[] result = new ABarCodeData[decodedData.Count]; - decodedData.CopyTo(result); - return result; - } - - // decode method for non-ecc200 barcodes - public static ABarCodeData[] DecodeNonECC200Data(BitArray bitArray, int dataLength, NonECC200DataFormat dataFormat, ushort crc, System.Text.Encoding encoding) - { - byte[] userData; - switch (dataFormat) // switch on dataformat - Base256 and ASCII are easy, the rest of them are similar - { - case NonECC200DataFormat.Base256: - userData = SliceBitStream(bitArray, dataLength, 8); - break; - case NonECC200DataFormat.ASCII: - userData = SliceBitStream(bitArray, dataLength, 7); - break; - case NonECC200DataFormat.Base11: - case NonECC200DataFormat.Base27: - case NonECC200DataFormat.Base37: - case NonECC200DataFormat.Base41: - userData = SymbolDecoder.DecodeBaseX(bitArray, dataLength, dataFormat); - break; - default: - userData = new byte[0]; - break; - } - - // if the userData is null, we probably have invalid data - if (userData == null) - { - return null; - } - - // get the CRC to validate the source data - ushort userCrc = NonECC200CRC.CalculateCRC(dataFormat, userData); - if (userCrc != crc) - { - return null; - } - - // Base256 data is returned as byte, the rest of them are strings - return dataFormat == NonECC200DataFormat.Base256 - ? new ABarCodeData[] {new Base256BarCodeData(userData, encoding)} - : new ABarCodeData[] {new StringBarCodeData(userData)}; - } - - // slices a bitstream width the given size, and returns the chunks as bytes - static byte[] SliceBitStream(BitArray bitArray, int dataLength, int chunkSize) - { - if (bitArray.Length < dataLength*chunkSize) - { - return null; - } - - byte[] userData = new byte[dataLength]; - int bitIndex = 0; - for (int i = 0; i < dataLength; ++i) - { - for (int b = 0; b < chunkSize; ++b) - { - if (bitArray[bitIndex++]) - { - userData[i] = (byte)(userData[i] | (1 << b)); - } - } - } - - return userData; - } - - // calculates CRC for non-ecc200 bitstreams - class NonECC200CRC - { - private static readonly Hashtable CRCPrexifes = new Hashtable(); - - static NonECC200CRC() - { - CRCPrexifes.Add(NonECC200DataFormat.Base11, 0x0100); - CRCPrexifes.Add(NonECC200DataFormat.Base27, 0x0200); - CRCPrexifes.Add(NonECC200DataFormat.Base41, 0x0300); - CRCPrexifes.Add(NonECC200DataFormat.Base37, 0x0400); - CRCPrexifes.Add(NonECC200DataFormat.ASCII, 0x0500); - CRCPrexifes.Add(NonECC200DataFormat.Base256, 0x0600); - } - - // Input data in normal byte order, this method will reverse it for internal use, as per DM spec. - public static ushort CalculateCRC(NonECC200DataFormat format, byte[] rawData) - { - byte[] data = new byte[rawData.Length + 2]; - // add the required data prefix (CRC register initial value) - int prefix = (int)CRCPrexifes[format]; - data[0] = (byte)(prefix / 0x100); - data[1] = (byte)(prefix % 0x100); - Array.Copy(rawData, 0, data, 2, rawData.Length); - data = Common.Utils.ReverseBitsPerByte(data); - - uint crcSum = 0; - for (int i = 0; i < data.Length; i++) - { - uint byteValue = ((uint)data[i] << 8); - for (int bi = 0; bi < 8; bi++) - { - if (((crcSum ^ byteValue) & 0x8000) != 0) - { - crcSum = (crcSum << 1) ^ 0x1021; - } - else - { - crcSum <<= 1; - } - byteValue <<= 1; - } - } - - // reverse the bitorder (again, and again...) - ushort crc = 0; - for (int i = 0; i < 16; ++i) - { - if ((crcSum & (1 << i)) != 0) - { - crc = (ushort)(crc | (1 << (15 - i))); - } - } - - return crc; - } - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMEncoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMEncoder.cs deleted file mode 100644 index 8551a3a4..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMEncoder.cs +++ /dev/null @@ -1,442 +0,0 @@ -using System; -using System.Collections; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Datamatrix -{ - internal class DMEncoded - { - public int[] symbols; - public BitArray bitArray; - public int dataLength; - public NonECC200DataFormat dataFormat; - public ushort crc; - public float Confidence; - public DMEncoded() { } - public DMEncoded(int[] symbols) { this.symbols = symbols; } - public DMEncoded(BitArray bitArray, int dataLength, NonECC200DataFormat dataFormat, ushort crc) - { - this.bitArray = bitArray; - this.dataLength = dataLength; - this.dataFormat = dataFormat; - this.crc = crc; - } - } - - internal class DMEncoder - { - Configuration cfg; - Grid[][] regions; - bool[][] symbolData; - int bitmapHeight, bitmapWidth; - private ImageScaner scan = null; - - - public DMEncoder(ImageScaner scan, Configuration cfg, Grid[][] regions) - { - this.scan = scan; - this.bitmapHeight = scan.Height ; - this.bitmapWidth = scan.Width; - - this.cfg = cfg; - this.regions = regions; - - } - - float[] GetWidths(MyPoint a, MyPoint b, int nModules) - { - float moduleLength = (a - b).Length / nModules; - int minLength=(int)Math.Round(moduleLength*0.5f); - Bresenham br = new Bresenham(a, b); - while (!br.End() && !scan.isBlack(br.Current)) br.Next(); - - bool processingBlack = true, isNoise = false; - int n = 0, N=0, l=0; - int[] widths = new int[nModules]; - while (!br.End() && N= minLength) - { - widths[N++] = n; - n = 1; - } - else - { - isNoise = true; - n++; - } - processingBlack = !processingBlack; - } - else n++; - br.Next(); - } - if (N < nModules && n > 0) widths[N++] = n; - - float fl = (float)l, fModules = (float)nModules; - float[] coords=new float[nModules]; - if (N < nModules) - for (int i = 0; i < nModules; i++) coords[i] = (float)(i) / (float)nModules; - else - { - float sum = 0f; - for (int i = 0; i < nModules; i++) - { - coords[i] = fModules * sum / fl; - sum += (float)widths[i]; - } - } - return coords; - } - - // Read in the symbol from the image according to the available configuration(s). Once the bits are read, - // try to perform error correction. If the correction was unsuccessful, try the next configuration. - public DMEncoded Encode(bool adaptiveSampling) - { -#if DEBUG_IMAGE - scan.Reset(); -#endif - symbolData = new bool[cfg.FullDataY][]; - for (int y = 0; y < cfg.FullDataY; y++) - symbolData[y] = new bool[cfg.FullDataX]; - - Grid dmLocation = regions[0][0]; - - float[] widths = null, heights = null; - if (adaptiveSampling) - { - //Prepare adaptive sampling - MyPoint a = dmLocation.GetSamplePoint(0f, (float)cfg.FullY - 0.5f); - MyPoint b = dmLocation.GetSamplePoint(cfg.FullX, (float)cfg.FullY - 0.5f); - widths = GetWidths(a, b, cfg.FullX); - MyPoint va = dmLocation.GetSamplePoint((float)cfg.FullX - 0.5f, 0f); - MyPoint vb = dmLocation.GetSamplePoint((float)cfg.FullX - 0.5f, cfg.FullY); - heights = GetWidths(va, vb, cfg.FullY); - } - - //Sample each region - for (int ry = 0; ry < cfg.RegionCountY; ++ry) - { - for (int rx = 0; rx < cfg.RegionCountX; ++rx) - { - float[][] regionPoints = new float[cfg.SubY][]; - for (int y = 0; y < cfg.SubY; ++y) - { - MyPoint P0 = new MyPoint(rx * cfg.SubX, ry * cfg.SubY); - regionPoints[y] = new float[cfg.SubX]; - for (int x = 0; x < cfg.SubX; ++x) - { - MyPointF point=adaptiveSampling?dmLocation.GetSamplePoint(widths[P0.X + x] + 0.5f, heights[P0.Y + y] + 0.5f): - dmLocation.GetSamplePoint(P0.X + x, P0.Y + y); - float c = scan.getSample(point,0f); - regionPoints[y][x] = c; - } - } - - // fill the data matrix - for (int y = 1; y < cfg.SubY-1 ; ++y) - { - for (int x = 1; x < cfg.SubX -1; ++x) - { - symbolData[ry * (cfg.SubY - 2) + y - 1][rx * (cfg.SubX - 2) + x - 1] = (regionPoints[y][x] < 0.5F); - } - } - } - } - -#if DEBUG_IMAGE - /*scan.Save(@"outscan.png"); - for (int i = symbolData.Length-1; i >=0 ; i--) - { - bool[] r=symbolData[i]; - for (int j = 0; j < r.Length; j++) - Console.Write(r[j] ? "X" : " "); - Console.WriteLine(); - } - scan.Reset();*/ -#endif - - if (cfg.Type == DatamatrixType.ECC200) return EncodeECC200(); - else if (cfg.Type == DatamatrixType.NonECC200) return EncodeNonECC200(); - return null; - } - - - DMEncoded EncodeECC200() - { - // get the placement grid for the given module size - ModulePlacementECC200 ecc200=new ModulePlacementECC200(); - MyPoint[][] placementGrid = ecc200.GetPlacementGridP(cfg.FullDataY, cfg.FullDataX); - if (placementGrid == null) return null; - - // fill the array of symbols - DMEncoded result = new DMEncoded(new int[cfg.FullSymbolCount]); - for (int y = 0; y < cfg.FullDataY; ++y) - { - for (int x = 0; x < cfg.FullDataX; ++x) - { - if (placementGrid[y][x].IsEmpty) - { - continue; - } - int symbolIndex = placementGrid[y][x].X; - int bitIndex = placementGrid[y][x].Y; - byte bitValue = (byte)(symbolData[y][x] ? 1 : 0); - result.symbols[symbolIndex] = result.symbols[symbolIndex] | (bitValue << bitIndex); - } - } - - // do the RS correction on the symbols - int[][] dataBlocks = cfg.GetDataBlocks(); - int[][] rsBlocks = cfg.GetRSBlocks(); - float meanConfidence = 0F, confidence; - int successfulDecodes = 0; - - // run 2 tries (2nd one is for 144x144 barcodes only) - for (int tries = 0; tries < 2; tries++) - { - // if we are runing second time then we should check for - // possible need for RS data code inversion, see #159 - if (tries == 1) - { - // see #159 - // [0123456789] - // becomes - // [234567801] - // copying 2 first bytes as last 2 bytes in the dest array - - // check if we have configurations with 144x144 DM size (10 or more blocks) - int blockCount = cfg.ReedSolomonBlocks; - if (blockCount >= 10) - { - int dataLengthOffset = cfg.DataSymbolCount; - - int numOfIterations = (cfg.FullSymbolCount - dataLengthOffset) / blockCount; - - for (int n = 0; n < numOfIterations; n++) - { - - // save first 2 codewords from the block - int c1 = result.symbols[dataLengthOffset + n * blockCount]; - int c2 = result.symbols[dataLengthOffset + n * blockCount + 1]; - // copying 2 first bytes as last 2 bytes in the dest array - Array.Copy(result.symbols, dataLengthOffset + n * blockCount + 2, result.symbols, dataLengthOffset + n * blockCount, blockCount - 2); - // set 1st and 2nd symbols from saved vars - // as last and pre-last codewords - result.symbols[dataLengthOffset + n * blockCount + blockCount - 2] = c1; - result.symbols[dataLengthOffset + n * blockCount + blockCount - 1] = c2; - - } - } - else return null; // if not supported size for byte swaps with RS then exit - } - - successfulDecodes = 0; - for (int i = 0; i < dataBlocks.Length; ++i) - { - // DM ECC200 can have multiple RS blocks, handle each of them separately - int[] data = Select(result.symbols, dataBlocks[i]); - int[] rs = Select(result.symbols, rsBlocks[i]); - int[] rsData = new int[data.Length + rs.Length]; - Array.Copy(data, 0, rsData, 0, data.Length); - Array.Copy(rs, 0, rsData, data.Length, rs.Length); - ReedSolomon corrector = new ReedSolomon(rsData, rs.Length, 8, 301, 1); - corrector.Correct(out confidence); - meanConfidence += confidence; - - if (!corrector.CorrectionSucceeded) - { - // reset successful decodes! - successfulDecodes = 0; - break; - } - - successfulDecodes++; - Common.Utils.ReverseSelect(corrector.CorrectedData, result.symbols, dataBlocks[i]); - } - - if (successfulDecodes > 0) - break; - else - continue; - } - - if (successfulDecodes == 0) - return null; - - meanConfidence /= (float)dataBlocks.Length; - result.Confidence = meanConfidence; - - // happy case: correction suceeded, proceed to the next step - int[] finalData = new int[cfg.DataSymbolCount]; - Array.Copy(result.symbols, 0, finalData, 0, finalData.Length); - result.symbols = finalData; - - // check if we have valid data - bool nonZero = false; - foreach (int i in result.symbols) - if (i != 0) - nonZero = true; - if (!nonZero) - return null; - return result; - } - - DMEncoded EncodeNonECC200() - { - // load up the placement grids (if haven't done so already) - ModulePlacementNonECC200.Init("mpnonecc200.dat"); - ushort[][] placementGrid = ModulePlacementNonECC200.GetPlacementGrid(cfg.FullY-2, cfg.FullX-2); - if (placementGrid == null) return null; - - // get the binary data - DMEncoded result = new DMEncoded(); - result.bitArray = new BitArray((cfg.FullX-2) * (cfg.FullY-2)); - for (int y = 0; y < cfg.FullY-2; ++y) - { - for (int x = 0; x < cfg.FullX-2; ++x) - { - ushort bitIndex = placementGrid[y][x]; - result.bitArray[bitIndex] = symbolData[y][x]; - } - } - - // unrandomise it - result.bitArray = UnRandomise(result.bitArray); - // try to correct it - ConvolutionDecoder corrector = new ConvolutionDecoder(result.bitArray); - corrector.Correct(); - if (!corrector.CorrectionSucceeded) return null; - result.Confidence = 1F; - - // extract user data & other paramterers - result.bitArray = ExtractNonECC200Data(corrector.CorrectedData, out result.dataFormat, out result.crc, out result.dataLength); - if (result.bitArray == null) return null; - return result; - } - - - // Selects the elements at the given indices in the source array. - public static int[] Select(int[] sourceArray, int[] indices) - { - int[] result = new int[indices.Length]; - for (int i = 0; i < indices.Length; ++i) - { - result[i] = sourceArray[indices[i]]; - } - - return result; - } - - // search order of the regions in case of multi-region DM - public static readonly MyPoint[] RegionSearchOrder = new MyPoint[] - { - new MyPoint(0, 0), new MyPoint(1, 0), new MyPoint(0, 1), - new MyPoint(1, 1), new MyPoint(2, 0), new MyPoint(2, 1), - new MyPoint(0, 2), new MyPoint(1, 2), new MyPoint(2, 2), - new MyPoint(3, 0), new MyPoint(3, 1), new MyPoint(3, 2), - new MyPoint(0, 3), new MyPoint(1, 3), new MyPoint(2, 3), - new MyPoint(3, 3), new MyPoint(4, 0), new MyPoint(4, 1), - new MyPoint(4, 2), new MyPoint(4, 3), new MyPoint(0, 4), - new MyPoint(1, 4), new MyPoint(2, 4), new MyPoint(3, 4), - new MyPoint(4, 4), new MyPoint(5, 0), new MyPoint(5, 1), - new MyPoint(5, 2), new MyPoint(5, 3), new MyPoint(5, 4), - new MyPoint(0, 5), new MyPoint(1, 5), new MyPoint(2, 5), - new MyPoint(3, 5), new MyPoint(4, 5), new MyPoint(5, 5) - }; - - // used to un/randomise the protected bitstream - private static readonly byte[] MasterRandomBytestream = new byte[] - { - 0x05, 0xff, 0xc7, 0x31, 0x88, 0xa8, 0x83, 0x9c, 0x64, 0x87, 0x9f, - 0x64, 0xb3, 0xe0, 0x4d, 0x9c, 0x80, 0x29, 0x3a, 0x90, 0xb3, 0x8b, - 0x9e, 0x90, 0x45, 0xbf, 0xf5, 0x68, 0x4b, 0x08, 0xcf, 0x44, 0xb8, - 0xd4, 0x4c, 0x5b, 0xa0, 0xab, 0x72, 0x52, 0x1c, 0xe4, 0xd2, 0x74, - 0xa4, 0xda, 0x8a, 0x08, 0xfa, 0xa7, 0xc7, 0xdd, 0x00, 0x30, 0xa9, - 0xe6, 0x64, 0xab, 0xd5, 0x8b, 0xed, 0x9c, 0x79, 0xf8, 0x08, 0xd1, - 0x8b, 0xc6, 0x22, 0x64, 0x0b, 0x33, 0x43, 0xd0, 0x80, 0xd4, 0x44, - 0x95, 0x2e, 0x6f, 0x5e, 0x13, 0x8d, 0x47, 0x62, 0x06, 0xeb, 0x80, - 0x82, 0xc9, 0x41, 0xd5, 0x73, 0x8a, 0x30, 0x23, 0x24, 0xe3, 0x7f, - 0xb2, 0xa8, 0x0b, 0xed, 0x38, 0x42, 0x4c, 0xd7, 0xb0, 0xce, 0x98, - 0xbd, 0xe1, 0xd5, 0xe4, 0xc3, 0x1d, 0x15, 0x4a, 0xcf, 0xd1, 0x1f, - 0x39, 0x26, 0x18, 0x93, 0xfc, 0x19, 0xb2, 0x2d, 0xab, 0xf2, 0x6e, - 0xa1, 0x9f, 0xaf, 0xd0, 0x8a, 0x2b, 0xa0, 0x56, 0xb0, 0x41, 0x6d, - 0x43, 0xa4, 0x63, 0xf3, 0xaa, 0x7d, 0xaf, 0x35, 0x57, 0xc2, 0x94, - 0x4a, 0x65, 0x0b, 0x41, 0xde, 0xb8, 0xe2, 0x30, 0x12, 0x27, 0x9b, - 0x66, 0x2b, 0x34, 0x5b, 0xb8, 0x99, 0xe8, 0x28, 0x71, 0xd0, 0x95, - 0x6b, 0x07, 0x4d, 0x3c, 0x7a, 0xb3, 0xe5, 0x29, 0xb3, 0xba, 0x8c, - 0xcc, 0x2d, 0xe0, 0xc9, 0xc0, 0x22, 0xec, 0x4c, 0xde, 0xf8, 0x58, - 0x07, 0xfc, 0x19, 0xf2, 0x64, 0xe2, 0xc3, 0xe2, 0xd8, 0xb9, 0xfd, - 0x67, 0xa0, 0xbc, 0xf5, 0x2e, 0xc9, 0x49, 0x75, 0x62, 0x82, 0x27, - 0x10, 0xf4, 0x19, 0x6f, 0x49, 0xf7, 0xb3, 0x84, 0x14, 0xea, 0xeb, - 0xe1, 0x2a, 0x31, 0xab, 0x47, 0x7d, 0x08, 0x29, 0xac, 0xbb, 0x72, - 0xfa, 0xfa, 0x62, 0xb8, 0xc8, 0xd3, 0x86, 0x89, 0x95, 0xfd, 0xdf, - 0xcc, 0x9c, 0xad, 0xf1, 0xd4, 0x6c, 0x64, 0x23, 0x24, 0x2a, 0x56, - 0x1f, 0x36, 0xeb, 0xb7, 0xd6, 0xff, 0xda, 0x57, 0xf4, 0x50, 0x79, - 0x08, 0x0 - }; - - - private static readonly BitArray MasterRandomBitstream = new BitArray(Common.Utils.ReverseBitsPerByte(MasterRandomBytestream)); - - // restore the protected bitstream using the MasterRandomBitstream - public static BitArray UnRandomise(BitArray randomStream) - { - BitArray masterChunck = new BitArray(MasterRandomBitstream); - masterChunck.Length = randomStream.Length; - return randomStream.Xor(masterChunck); - } - - // extracts the user data from the unprotected bitstream of non-ecc200 - public static BitArray ExtractNonECC200Data(BitArray bitArray, out NonECC200DataFormat dataFormat, out ushort crc, out int dataLength) - { - dataFormat = NonECC200DataFormat.Base11; - crc = 0; - dataLength = 0; - if (bitArray.Length < 30) - { - return null; - } - - int formatId = 0; - for (int i = 0; i < 5; ++i) - { - if (bitArray[i]) - { - formatId |= (1 << (4 - i)); - } - } - - dataFormat = (NonECC200DataFormat)formatId; - if (formatId < 0 || formatId > 5) - { - return null; - } - - for (int i = 0; i < 16; ++i) - { - if (bitArray[i + 5]) - { - crc = (ushort)(crc | (1 << i)); - } - } - - for (int i = 0; i < 9; ++i) - { - if (bitArray[i + 21]) - { - dataLength |= (1 << i); - } - } - - return Common.Utils.BitArrayPart(bitArray, 30, bitArray.Length - 30); - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMLPatternReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMLPatternReader.cs deleted file mode 100644 index 205b3573..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMLPatternReader.cs +++ /dev/null @@ -1,304 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Datamatrix -{ - internal class DMLPatternReader - { - ImageScaner scan; - Encoding encoding; - bool checkMirrored; - - public DMLPatternReader(ImageScaner scan, Encoding encoding, bool checkMirrored) - { - this.scan = scan; - this.encoding = encoding; - this.checkMirrored = checkMirrored; - } - - //pA is the L pattern Edge. pB is the right end of the L pattern. - //pC is the top of the L pattern. pD is the estimated fourth corner. - //This methods tries to adjust pD to the right position and reads the barcode. - //Moves pD until the edges pD-pB and pD-pC lays on white pixels. - public BarCodeRegion ReadBarcode(MyPointF pA, MyPointF pB, MyPointF pC, MyPointF pD) - { - MyPointF oD = pD; - MyVectorF vdX = (pB - pA).Normalized; - MyVectorF vdY = (pC - pA).Normalized; - - if (vdX.isNaN() || vdY.isNaN()) return null; - - int d = (int)((pB - pA).Length); - if (d > scan.Width && d > scan.Height) return null; - - - int comptExp, maxComptExp = (int)((pB - pA).Length * 0.15F); //maximum of pixels adjustments, proportional to the edge length. - if (maxComptExp < 5) maxComptExp = 5; //for short edges, set 5 pixels of adjustments. - bool onWhite = false; - int nPairs; - float pH = 0F, pV = 0F; - - - - //expand D to lay on white - comptExp = maxComptExp; - onWhite = false; - while (!onWhite && comptExp > 0) - { - int w; - float r; - onWhite = true; - pV = scan.GetPercentOn(true, false, pD.Truncate(), Calc.Middle(pD, pB), out nPairs, out w, out r); - if (pV > 0.2F && scan.In(pD + vdX)) { pD += vdX; onWhite = false; } - pH = scan.GetPercentOn(true, false, pD.Truncate(), Calc.Middle(pD, pC), out nPairs, out w, out r); - if (pH > 0.2F && scan.In(pD + vdY)) { pD += vdY; onWhite = false; } - comptExp--; - } - - if (comptExp == 0) pD = oD; - - BarCodeRegion reg=null; - float fRows=0f,fCols=0f; - if (EstimateRowsCols(pA,pB,pC,pD, vdX, vdY, ref fCols, ref fRows)) - { - reg = ReadBarcodeWithOrientation(pA, pB, pC, pD, vdX, vdY, fCols, fRows, maxComptExp); - if (reg == null && checkMirrored) reg=ReadBarcodeWithOrientation(pA, pC, pB, pD, vdY, vdX, fRows, fCols, maxComptExp); - } - return reg; - } - - BarCodeRegion ReadBarcodeWithOrientation(MyPointF pA, MyPointF pB, MyPointF pC, MyPointF pD, MyVectorF vdX, MyVectorF vdY, float fCols, float fRows, int maxComptExp) - { - BarCodeRegion reg=ReadBarcodeAdjusted(pA, pB, pC, pD, vdX, vdY, fCols, fRows); - - if (reg == null) - { - float moduleLength = (pB - pA).Length / fCols; - if (moduleLength < 5) - { - //for low resolution barcodes, try a small offset - if (reg == null) reg = ReadBarcodeAdjusted(pA, pB, pC, pD - vdX - vdY, vdX, vdY, fCols, fRows); - } - else - { - bool onWhite; - int comptExp; - //for good resolution barcodes, try adjust B and C corners to lay on white - //since noisy edges have B and C on the black area - - //expand B to lay on white - comptExp = maxComptExp; - onWhite = false; - while (!onWhite && comptExp > 0) - { - int w,nPairs; - float r; - onWhite = true; - float pH = this.scan.GetPercentOn(true, false, pB.Truncate(), Calc.Middle(pB, pA), out nPairs, out w, out r); - if (pH > 0.8F && scan.In(pB + vdY)) { pB -= vdY; onWhite = false; } - comptExp--; - } - - - //expand C to lay on white - comptExp = maxComptExp; - onWhite = false; - while (!onWhite && comptExp > 0) - { - int w,nPairs; - float r; - onWhite = true; - float pV = scan.GetPercentOn(true, false, pC.Truncate(), Calc.Middle(pC, pA), out nPairs, out w, out r); - if (pV > 0.8F && scan.In(pC + vdX)) { pC -= vdX; onWhite = false; } - comptExp--; - } - - reg = ReadBarcodeAdjusted(pA, pB, pC, pD, vdX, vdY, fCols, fRows); - } - } - return reg; - } - - bool EstimateRowsCols(MyPointF pA, MyPointF pB, MyPointF pC, MyPointF pD, MyVectorF vdX, MyVectorF vdY, ref float fCols, ref float fRows) { - //estimate rows and cols - fCols = CountModules(pC, pD, vdY); - if (fCols != 0F) - { - fRows = CountModules(pB, pD, vdX); - if (fRows != 0F) - { - if (Calc.Around(fRows / fCols, 1.0F)) //for square DM's take average of them - { - float f = (fCols + fRows) / 2.0F; - fCols = fRows = f; - } - return true; - } - } - return false; - } - - - //Critical method to read the barcode. First tries to get the number of modules - //of the DM. It is done for vertical and horizontal edges. - BarCodeRegion ReadBarcodeAdjusted(MyPointF pA, MyPointF pB, MyPointF pC, MyPointF pD, MyVectorF vdX, MyVectorF vdY, float fCols, float fRows) - { - int cols = (int)Math.Round(fCols); - int rows = (int)Math.Round(fRows); - - //sample the upper-right module to see if it is a ECC200 DM or non ECC200 - Grid l = new Grid(cols, rows, pA, pC, pB, pD, true); - MyPointF upRightCorner = l.GetSamplePoint(cols - 1, rows - 1); - MyPoint upRC = (MyPoint)upRightCorner; - bool isECC200 = !scan.isBlack(upRC.X, upRC.Y); - - //Find the best matching configuration, thus defining the final number of - //rows and cols - foreach (Configuration cfg in Configuration.FindConfiguration(fCols, fRows, isECC200)) - { - //initilize the object to calculate intermediate x,y sample coordinates - Grid[][] loc = new Grid[][] { new Grid[] { new Grid(cfg.FullX, cfg.FullY, pA, pC, pB, pD, true) } }; - - //and start the sampler. In ECC200 the result is an array of bytes (symbols) - // For non ECC200 the result is a bitStream, crc... - DMEncoder encoder = new DMEncoder(scan, cfg, loc); - DMEncoded encoded = encoder.Encode(false); - if (encoded == null) encoded = encoder.Encode(true); - if (encoded != null && (!isECC200 || encoded.symbols != null)) - { - ABarCodeData[] r = null; - if (isECC200) r = DMDecoder.DecodeECC200Data(encoded.symbols, encoding); - else r = DMDecoder.DecodeNonECC200Data(encoded.bitArray, encoded.dataLength, encoded.dataFormat, encoded.crc, encoding); - if (r != null) - { - BarCodeRegion reg = new BarCodeRegion(pA, pB, pC, pD); - reg.Data = r; - reg.Confidence = encoded.Confidence; - return reg; - } - } - } - return null; - } - - //look for module width: scan a dashed DM side - //PRE: a and b are the corners - const int MIN_MODULES = 8; //actually the smallest DM is 9x9 - float CountModules(MyPointF a, MyPointF b, MyVectorF vdY) - { - int N = 10; //max iterations - int most,nPairs; - float regularity = 0F, tpcOn = 0F; - MyPointF bestA = a, bestB = b; - float length = (b - a).Length; - int nGood = 0; - float maxReg = 0f, maxOn = 0f; - while (N > 0 && nGood < 20) - { - tpcOn = scan.GetPercentOn(true, true, a, b, out nPairs, out most, out regularity); - float aproxCount = length * 2f / (float)most; - if (nPairs>3 && aproxCount > MIN_MODULES && (regularity > maxReg || regularity == maxReg && tpcOn > maxOn)) - { - maxReg = regularity; - maxOn = tpcOn; - bestA = a; bestB = b; - nGood++; - } - /* - if (regularity > 0.5F && Calc.Around(tpcOn, 0.5F, 0.15F)) - { - float dd = (float)Math.Abs(tpcOn - 0.5F); - if (dd < minDist) { minDist = dd; bestA = a; bestB = b; } - nGood++; - }*/ - N--; - a -= vdY; b -= vdY; - } - if (nGood != 0) return MeanColsOrRows(bestA, bestB); - return 0F; - } - - //Finds the most common module width between a and b. It is not an average, but - // an average of the most common widths. - // ---- This is quite a dumb algorithm. Here can be errors. ---- - float MeanColsOrRows(MyPoint a, MyPoint b) - { - SortedDictionary bars = new SortedDictionary(); - int length = 0, n = 0, total = 0; - bool current = false, first = false; - Bresenham br = new Bresenham(a, b); - - //skip two pixels - if (!br.End()) br.Next(); - if (!br.End()) br.Next(); - // - if (!br.End()) - { - first = current = scan.isBlack(br.Current.X, br.Current.Y); - while (!br.End()) - { - bool next = scan.isBlack(br.Current.X, br.Current.Y); - if (next != current) //new transition - { - if (next == first) //only record each pair to avoid different saturation of black or white - { - if (bars.ContainsKey(n)) bars[n]++; - else bars.Add(n, 1); - total += 1; //increment total number of pairs - n = 0; - } - current = next; - } - n++; - br.Next(); - length++; - } - } - if (n > 0) - { - if (bars.ContainsKey(n)) bars[n]++; - else bars.Add(n, 1); - total += 1; - } - - //find the most common - int most = -1; - foreach (int i in bars.Keys) - if (most == -1 || bars[i] > bars[most]) most = i; - //count repetitions of most - ArrayList reps = new ArrayList(10); - int nMost = bars[most]; - foreach (int i in bars.Keys) if (bars[i] == nMost) reps.Add(i); - if (reps.Count % 2 != 0) most = (int)reps[reps.Count / 2]; - else most = ((int)reps[reps.Count / 2] + (int)reps[reps.Count / 2 - 1]) / 2; - - //average with neighbourhood depending on the approximated module width. - //The larger is the module with, the more are the neighbours taken into account. - float w0, w1, w2, w3, w4; - w0 = w1 = w2 = w3 = w4 = 0.0F; - w2 = (float)nMost; - if (most > 2) - { - w1 = (float)(bars.ContainsKey(most - 1) ? bars[most - 1] : 0); - w3 = (float)(bars.ContainsKey(most + 1) ? bars[most + 1] : 0); - if (most > 10) - { - w0 = (float)(bars.ContainsKey(most - 2) ? bars[most - 2] : 0); - w4 = (float)(bars.ContainsKey(most + 2) ? bars[most + 2] : 0); - } - } - - //w0 *= w0; w1 *= w1; w2 *= w2; w3 *= w3; w4 *= w4; - float w = (float)((most - 2) * w0 + (float)(most - 1) * w1 + most * w2 + - (most + 1) * w3 + (most + 2) * w4) / (float)(w0 + w1 + w2 + w3 + w4); - - //divide by 2 since we were counting pairs - w = w / 2; - return (float)length / w; //approximated number of cols/rows - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMReader.cs deleted file mode 100644 index fbceb2b7..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMReader.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Collections.Generic; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Datamatrix -{ -#if CORE_DEV - public -#else - internal -#endif - class DMReader : SymbologyReader2D - { - int decodingMaxRetries = int.MaxValue;// 120; //number of edges to process in connectReader. Default=0 -->skip connectReader - - bool checkMirrored = false; //by default doesn't check mirrored barcodes - - //allow connect 10 pixels holes - internal double MaxHoleSizeInsideLines = 11d; - - // max hole size between _ and | lines in relative to the largest side (15% = 0.15d etc) - internal double MaxAllowedHoleBetweenLinesInLPattern = 0.15d; - - //Max dispersion of ends of L pattern (in units) - public int LPatternDispersion { get; set; } = 0; - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.DataMatrix; - } - - protected override FoundBarcode[] DecodeBarcode() - { -#if DEBUG_IMAGE - var temp = BWImage.GetAsBitmap(); - DebugHelper.InitImage(temp); -#endif - ImageScaner scan =new ImageScaner(BWImage); - DMLPatternReader lPatternReader = new DMLPatternReader(scan, Encoding, checkMirrored); - - FoundBarcode[] r = new FoundBarcode[0]; - - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - //===== Skip this phase because it is covered by second phase ====== - //var weNeededMoreBarcodes = false; - //DMSimpleReader simpleReader = new DMSimpleReader() { LPatternDispersion = LPatternDispersion }; - //simpleReader.MaxNumberOfBarcodes = this.ExpectedNumberOfBarcodes; - //simpleReader.MaxAllowedHoleBetweenLinesInLPattern = this.MaxAllowedHoleBetweenLinesInLPattern; - //r = simpleReader.DecodeBarcode(BWImage, lPatternReader, this.MinAllowedBarcodeSideSize, this.MaxAllowedBarcodeSideSize); - //weNeededMoreBarcodes = (r.Length == 0 && simpleReader.NumLPattnerns < 100); - //weNeededMoreBarcodes = weNeededMoreBarcodes || this.ExpectedNumberOfBarcodes > 0 && r.Length < this.ExpectedNumberOfBarcodes; - //if (weNeededMoreBarcodes) - { - DMConnectReader connectReader = new DMConnectReader() { LPatternDispersion = LPatternDispersion }; - connectReader.MaxHoleSizeInsideLines = this.MaxHoleSizeInsideLines; - connectReader.MaxAllowedHoleBetweenLinesInLPattern = this.MaxAllowedHoleBetweenLinesInLPattern; - connectReader.MaxNumberOfBarcodes = this.ExpectedNumberOfBarcodes; - connectReader.decodingMaxRetries = decodingMaxRetries; - connectReader.TimeoutTimeInTicks = TimeoutTimeInTicks; - - FoundBarcode[] r2 = connectReader.DecodeBarcode(BWImage, lPatternReader, - this.MinAllowedBarcodeSideSize, this.MaxAllowedBarcodeSideSize); - - r = Combine(r, r2); - } - - return r; - } - - private FoundBarcode[] Combine(FoundBarcode[] r, FoundBarcode[] r2) - { - //add to result first array - var res = new List(r); - //enumerate second array - foreach(var b2 in r2) - { - //is presented in first array? - var presentedAlready = false; - foreach (var b1 in r) - if(b1.Rect.IntersectsWith(b2.Rect)) - { - presentedAlready = true; - break; - } - //if not presented => add to result - if (!presentedAlready) - res.Add(b2); - } - - return res.ToArray(); - } - - public int DecodingDeep - { - get { return this.decodingMaxRetries; } - set { this.decodingMaxRetries = value; } - } - - public bool CheckMirrored - { - get { return this.checkMirrored; } - set { this.checkMirrored = value; } - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMSimpleReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMSimpleReader.cs deleted file mode 100644 index ecc5a33a..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DMSimpleReader.cs +++ /dev/null @@ -1,631 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Drawing; -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Datamatrix -{ - internal partial class DMSimpleReader - { - //Maximum number of edges to process. The solution is "always" found in the largests edges. Thus, processing only the largests - //ones saves time. - // USED in the Inherited Classes only! - internal int decodingMaxRetries; - - // if we should check barcode inside using diagonal beaming line - // and see if there is some dots inside so this could be a barcode - // or not. ATTENTION - it works for simple barcodes only! But NOT working for damaged ones - internal bool UseQuickCheckForBacodeInside = false; - - // max hole size between _ and | lines in relative to the largest side (15% = 0.15d etc) - internal double MaxAllowedHoleBetweenLinesInLPattern; - //Max dist between corners to find the center of L pattern - internal double MAX_DIST_TO_CONSIDER_LPATTERN = 8d; - - internal int MaxNumberOfBarcodes = -1; - - //Scan image rows main step - protected int scanRowStep = 1; - - //Min Y/X of L pattern (edges of DM barcode) - protected float dmMinAspectRatio = 0.6f; - - //Max Y/X of L pattern (edges of DM barcode) - protected float dmMaxAspectRatio = 6f; - - //Max difference angle of the L pattern (from perpendicular) - protected float dmMaxAngleDifference = 0.15f; - - //Min edge length - protected int MIN_SIZE = 20; - - //Max dispersion of ends of L pattern (in units) - public int LPatternDispersion { get; set; } = 0; - - //Dispersion step (in fractions of edge length) - public float LPatternDispersionStep { get; set; } = 1 / 40f; - - protected int width, height; - protected int minAllowedBarcodeSideSize, maxAllowedBarcodeSideSize; - - protected BlackAndWhiteImage bwSourceImage = null; - protected DMLPatternReader lPatternReader; - int numLPatternsFound = 0; //number of L patterns found - - //class to sort edges in length, from larger to shorter - //class ReverseInt : IComparer { public int Compare(int x, int y) { return y - x; } } - - internal long TimeoutTimeInTicks = 0; - - protected bool IsTimeout() - { - if (TimeoutTimeInTicks == 0) - return false; - - if (TimeoutTimeInTicks < DateTime.Now.Ticks) - return true; - - return false; - } - - public FoundBarcode[] DecodeBarcode(BlackAndWhiteImage bwImage, DMLPatternReader lPatternReader, int minAllowedBarcodeSideSize, int maxAllowedBarcodeSideSize) - { - this.bwSourceImage = bwImage; - this.lPatternReader = lPatternReader; - this.width = bwImage.Width; - this.height = bwImage.Height; - int minSize = this.width > this.height ? this.height : this.width; - this.minAllowedBarcodeSideSize = minSize * minAllowedBarcodeSideSize / 100; - this.maxAllowedBarcodeSideSize = minSize * maxAllowedBarcodeSideSize / 100; - - return Scan(); - } - - //object that holds the bw image + methods to sample, follow vertices,... - protected ImageScaner scan; - //list of edges in construction - internal LinkedList edges; - //list of found codes found and correctly decoded. - internal Dictionary> ledges; - protected LinkedList candidates; - - protected void CreateLists() - { - scan = new ImageScaner(bwSourceImage); - edges = new LinkedList(); - ledges = new Dictionary>(); - candidates = new LinkedList(); - numLPatternsFound = 0; - } - - internal virtual FoundBarcode[] Scan() - { - CreateLists(); -#if DEBUG - var temp = bwSourceImage.GetAsBitmap(); - //temp.Save(@"out.png"); - Utils.SaveSKBitmap(temp, @"out.png"); -#endif - - //main loop to scan horizontal lines - XBitArray rowPrev = bwSourceImage.GetRow(0); - XBitArray row = bwSourceImage.GetRow(1); - for (int y = 2; y < height; y += scanRowStep) - { - XBitArray rowNext = bwSourceImage.GetRow(y); - ScanBits(y - 1, rowPrev, row, rowNext, true); - rowPrev = row; - row = rowNext; - } - - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - //main loop to scan vertical lines - XBitArray colPrev = bwSourceImage.GetColumn(0); - XBitArray col = bwSourceImage.GetColumn(1); - for (int x = 2; x < width; x += scanRowStep) - { - XBitArray colNext = bwSourceImage.GetColumn(x); - ScanBits(x - 1, colPrev, col, colNext, false); - colPrev = col; - col = colNext; - } - - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - return FindDM(ledges, true); - } - - internal virtual FoundBarcode[] FindDM(Dictionary> ledges, bool simpleMode) - { - -#if FIND_EDGES - foreach (LinkedList l in ledges.Values) - foreach(Edge e in l) - candidates.AddLast(new BarCodeRegion(e.start, e.end, e.start, e.end)); -#else - ArrayList result = new ArrayList(); - int[] lengths = new int[ledges.Count]; - ledges.Keys.CopyTo(lengths, 0); - - // sort keys - Array.Sort(lengths); - // reverse to make larger first - Array.Reverse(lengths); - - - //Look to find connected edges (dist < MAX_DIST_TO_CONSIDER_LPATTERN) at 90º with a correct ratio - //This is a O(N^2) algorightm, optimized to check only edges with similar lengths - // (this is relaxed to allow rectangular barcodes, or skewed ones). - int processedEdges = 0; - for (int i = 0; i < lengths.Length; i++) - { - if (MaxNumberOfBarcodes > 0 && candidates.Count >= MaxNumberOfBarcodes) - break; - - int currentLength = lengths[i]; - - if (currentLength >= ledges.Count) - { - continue; // skip too long edges - } - - foreach (Edge A in ledges[currentLength]) - { - processedEdges++; - - // if we have simple mode ON - // or if we have simpleMode=false and need to make iterations - if (simpleMode || (processedEdges < decodingMaxRetries)) - { - for (int j = i; j >= 0; j--) - { - - if (MaxNumberOfBarcodes > 0 && candidates.Count >= MaxNumberOfBarcodes) - break; - - //check lengths ratio - int length = lengths[j]; - float ratio = (float)length / (float)currentLength; - if (ratio < dmMinAspectRatio || ratio > dmMaxAspectRatio || length >= ledges.Count) - { - continue; // break; //from sqare to max rectangular ratio - } - foreach (Edge B in ledges[length]) - { - //check angle around 90º - MyVectorF a = A.start - A.end; - MyVectorF b = B.start - B.end; - float prodEsc = (a * b) / a.Length / b.Length; - - //check if angle is perpendicular enough - if (Calc.Around(prodEsc, 0F, dmMaxAngleDifference)) - { - //check if edges are close enough ( lb ? la * maxDistInPercents : lb * maxDistInPercents; //20% of max length - if (d1 < maxDist || d2 < maxDist || d3 < maxDist || d4 < maxDist) - { - MyPoint center, corner1, corner2; - center = corner1 = corner2 = MyPoint.Empty; - if (d1 < MAX_DIST_TO_CONSIDER_LPATTERN) { center = (A.end + B.end) / 2; corner1 = A.end; corner2 = B.end; } - else if (d2 < MAX_DIST_TO_CONSIDER_LPATTERN) { center = (A.end + B.start) / 2; corner1 = A.end; corner2 = B.start; } - else if (d3 < MAX_DIST_TO_CONSIDER_LPATTERN) { center = (A.start + B.end) / 2; corner1 = A.start; corner2 = B.end; } - else { center = (A.start + B.start) / 2; corner1 = A.start; corner2 = B.start; } - - //check diagonal for 50% of black pixels if this is a sqare DM - bool isCandidate = false; - if (UseQuickCheckForBacodeInside) - { - float l1 = (A.end - A.start).Length; - float l2 = (B.end - B.start).Length; - - if (l1 < l2) { float tmp = l1; l1 = l2; l2 = tmp; } //==> l1>l2 - if (l1 / l2 < 4f) - { - Bresenham bre = new Bresenham(corner1, corner2); - int n = 0, nBlack = 0; - while (!bre.End()) - { - n++; - if (scan.isBlack(bre.Current)) nBlack++; - bre.Next(); - } - float grayLevel = (float)nBlack / (float)n; - if (grayLevel > 0.20f & grayLevel < 0.9f) - isCandidate = true; - } - else isCandidate = true; - } - else - isCandidate = true; // else set to true everytime - - if (isCandidate) - { - -#if !FIND_FINDER - bool processed = false; - foreach (BarCodeRegion br in candidates) - if (br.In(center)) - { - processed = true; - break; - } - - if (!processed) -#endif - // calls either base LPattern reader - // or inherited one (from DMConnectedReader for example) - NewLPattern(A, B); - - if (MaxNumberOfBarcodes > 0 && candidates.Count >= MaxNumberOfBarcodes) - break; - - } - } - } - } - } - } - } - } -#endif - - ArrayList results = new ArrayList(); - if (candidates != null) - { - foreach (BarCodeRegion b in candidates) - { - FoundBarcode foundBarcode = new FoundBarcode(); - foundBarcode.BarcodeFormat = SymbologyType.DataMatrix; - - String data = ""; - if (b.Data != null) - foreach (ABarCodeData d in b.Data) - if (d != null) - data += d.ToString(); - foundBarcode.Value = data; - foundBarcode.Color = SKColors.Blue; - - foundBarcode.Polygon = new SKPointI[5] { b.A, b.B, b.D, b.C, b.A }; - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(foundBarcode.Polygon, pointTypes); - //foundBarcode.Rect = Rectangle.Round(path.GetBounds()); - foundBarcode.Rect = Utils.DrawPath(foundBarcode.Polygon); - - foundBarcode.Confidence = b.Confidence; - - if (b.Confidence < 0.001f && foundBarcode.Value.Length < 2) - continue; - - results.Add(foundBarcode); - } - } - return (FoundBarcode[])results.ToArray(typeof(FoundBarcode)); - } - - - internal virtual void NewLPattern(Edge A, Edge B) - { - numLPatternsFound++; -#if FIND_FINDER - float d1 = (A.start - B.start).Length; - float d2 = (A.start - B.end).Length; - float d3 = (A.end - B.start).Length; - float d4 = (A.end - B.end).Length; - BarCodeRegion reg=null; - if (d1 < MAX_HOLE_SIZE_BETWEEN_LINES_IN_PATTERN) reg = new BarCodeRegion(A.start, A.end, B.end, B.end+(A.end-A.start)); - else if (d2 < MAX_HOLE_SIZE_BETWEEN_LINES_IN_PATTERN) reg = new BarCodeRegion(A.start, A.end, B.start, B.start+(A.end-A.start)); - else if (d3 < MAX_HOLE_SIZE_BETWEEN_LINES_IN_PATTERN) reg = new BarCodeRegion(A.end, A.start, B.end, B.end+(A.start-A.end)); - else reg = new BarCodeRegion(A.end, A.start, B.start, B.start+(A.start-A.end)); - if (reg!=null) candidates.AddLast(reg); -#else - //Track edge crossing the center point of the start/reversed stop pattern - EdgeTrack etA = new EdgeTrack(scan); - MyPoint centerA = A.Center(); - MyVector vdY = A.start - A.end; - MyVector vdX = (vdY.isHorizontal() ? new MyVector(0, A.isBlack ? -1 : 1) : new MyVector(A.isBlack ? -1 : 1, 0)); - etA.Track(centerA, vdX, 3F, true); - MyPointF Aup = etA.Up(0); - MyPointF Adown = etA.Down(0); - Adown = Adown - ((MyVectorF)vdY).Normalized; - if (Aup.IsInfinity || Adown.IsInfinity) return; - RegressionLine lineA = etA.GetLine(); - - EdgeTrack etB = new EdgeTrack(scan); - MyPoint centerB = B.Center(); - vdY = B.start - B.end; - vdX = (vdY.isHorizontal() ? new MyVector(0, B.isBlack ? -1 : 1) : new MyVector(B.isBlack ? -1 : 1, 0)); - etB.Track(centerB, vdX, 3, true); - MyPointF Bup = etB.Up(0); - MyPointF Bdown = etB.Down(0); - if (Bup.IsInfinity || Bdown.IsInfinity) return; - Bdown = Bdown - ((MyVectorF)vdY).Normalized; - RegressionLine lineB = etB.GetLine(); - - - //find intersection - MyPointF cross = lineA.Intersection(lineB); if (cross.IsEmpty) return; - - //detect orientation of A and B edges - /* - float d1 = (Aup - Bup).Length; - float d2 = (Aup - Bdown).Length; - float d3 = (Adown - Bup).Length; - float d4 = (Adown - Bdown).Length; - MyPointF a, b, a2, b2; - if (d1 < MAX_HOLE_SIZE_BETWEEN_LINES_IN_PATTERN) { a = Adown; b = Bdown; a2 = A.end; b2 = B.end; } - else if (d2 < MAX_HOLE_SIZE_BETWEEN_LINES_IN_PATTERN) { a = Adown; b = Bup; a2 = A.end; b2 = B.start; } - else if (d3 < MAX_HOLE_SIZE_BETWEEN_LINES_IN_PATTERN) { a = Aup; b = Bdown; a2 = A.start; b2 = B.end; } - else if (d4 < MAX_HOLE_SIZE_BETWEEN_LINES_IN_PATTERN) { a = Aup; b = Bup; a2 = A.start; b2 = B.start; } - else return;*/ - MyPointF a, b, a2, b2; - float d1 = (Aup - cross).Length; - float d2 = (Adown - cross).Length; - if (d1 > d2) { a = Aup; a2 = A.start; } else { a = Adown; a2 = A.end; } - float d3 = (Bup - cross).Length; - float d4 = (Bdown - cross).Length; - if (d3 > d4) { b = Bup; b2 = B.start; } else { b = Bdown; b2 = B.end; } - - //clockwise? - MyVectorF v1 = a - cross; - MyVectorF v2 = b - cross; - float k = v1.X * v2.Y - v1.Y * v2.X; - if (k > 0) { MyPointF d = a; a = b; b = d; } - MyPointF c = b + (a - cross); - MyPointF c2 = b2 + (a2 - cross); - - ReadBarcode(cross, a, b, c); -#endif - } - - protected virtual bool ReadBarcode(MyPointF A, MyPointF B, MyPointF C, MyPointF D) - { - var step = (B - A).Length * LPatternDispersionStep; - var d1 = (B - A).Normalized * step; - var d2 = (C - A).Normalized * step; - - //enumerate different offsets of points along L pattern - for (int iOffset = 0; iOffset <= LPatternDispersion; iOffset++) - { - var a = A; - var b = B + d1 * iOffset; - var c = C + d2 * iOffset; - var d = D + (d1 + d2) * iOffset; - -#if DEBUG_IMAGE - //Console.WriteLine("A: " + A + " B: " + B); - DebugHelper.DrawArrow(a.X, a.Y, b.X, b.Y, Color.Red); - DebugHelper.DrawArrow(a.X, a.Y, c.X, c.Y, Color.Blue); - DebugHelper.DrawSquare(Color.Red, a, b, c, d); - DebugHelper.SaveImage(); - //DebugHelper.ReInitImage(); -#endif - - //try to recognize - BarCodeRegion reg = lPatternReader.ReadBarcode(a, b, c, d); - if (reg != null) - { - candidates.AddLast(reg); - //break enumerate points if barcode is recognized - return true; - } - } - - return false; - } - - //class to define a found edge during the scanning process. - internal class Edge - { - public MyPoint start, end; - public float sumAngle, angle; - public bool isBlack; //white to black edge - LinkedList points; - LinkedList connections; - int N; - - public Edge(MyPoint a, MyPoint e) - { - this.start = a; - this.end = e; - } - - public Edge(MyPoint p, float angle, bool isBlack) - { - start = end = p; - this.sumAngle = this.angle = angle; - this.N = 1; - this.isBlack = isBlack; - this.points = new LinkedList(); - this.connections = new LinkedList(); - } - - public int Belongs(MyPoint p, float angle, bool isBlack, bool isHorizontal) - { - if (this.isBlack == isBlack) - { - int h = isHorizontal ? p.Y - end.Y : p.X - end.X; - if (h > 0) //join to an edge without added points at this step - { - int dw = isHorizontal ? p.X - end.X : p.Y - end.Y; - if (dw < 0) dw = -dw; - if (dw < 3) { - float dist = this.angle - angle; if (dist < 0) dist = -dist; - if (dist > (float)Math.PI) dist = (float)(2 * Math.PI) - dist; - if (dist<0.8F) return dw; - } - } - } - return -1; - } - - public void Add(MyPoint p, float angle) - { - float dist = this.angle - angle; - if (dist > (float)Math.PI) { angle += (float)(2*Math.PI); } - else if (dist < -(float)Math.PI) { angle -= (float)(2 * Math.PI); } - - this.sumAngle += angle; - this.N++; - this.angle = this.sumAngle / this.N; - this.end = p; - this.points.AddLast(p); - } - - public int Length(bool isHorizontal) { return isHorizontal ? this.end.Y - this.start.Y : this.end.X - this.start.X; } - public float RealLength() { return (this.end - this.start).Length; } - public void AddConnection(Edge e) { this.connections.AddLast(e); } - public LinkedList GetConnections() { return this.connections; } - public void FindConnections(LinkedList l, bool isHorizontal, int minSize, double MaxHoleSizeInside) - { - foreach (Edge b in l) if (this.ContinuesFrom(b, isHorizontal, minSize, MaxHoleSizeInside) != -1) - this.AddConnection(b); - } - public int ContinuesFrom(Edge e, bool isHorizontal, int minSize, double MaxHoleSizeInside) - { - if (e.Length(isHorizontal) < minSize) return -1; - - int totalDist = isHorizontal ? e.start.Y - this.end.Y : e.start.X - this.end.X; - if (totalDist < 0) totalDist = -totalDist; - int dist = isHorizontal ? e.end.X - this.start.X : e.end.Y - this.start.Y; - if (dist < 0) dist = -dist; - int length = this.Length(isHorizontal); - bool connect = dist < MaxHoleSizeInside; - - if (length > 0 && connect) - { - MyVectorF a = this.end - this.start; - MyVectorF b = e.end - e.start; - float prodEsc = a * b / a.Length / b.Length; - if (!Calc.Around(prodEsc, 1f, 0.2f)) connect = false; - } - return connect ? dist : -1; - } - public void ScanConnections(MyPoint end, bool isHorizontal, int minSize, ref LinkedList newEdges, double MaxHoleSizeInside) - { - Edge best = null; - int minD = int.MaxValue; - foreach (Edge f in this.connections) - { - int d = this.ContinuesFrom(f, isHorizontal, minSize, MaxHoleSizeInside); - if (d!=-1 && d < minD) { minD = d; best = f; } - } - - if (minD < int.MaxValue) - { - Edge n = new Edge(best.start, end); - newEdges.AddLast(n); - best.ScanConnections(end, isHorizontal, minSize, ref newEdges, MaxHoleSizeInside); //recursion - } - } - - - public MyPoint Center() - { - if (points != null && points.Count > 0) - { - LinkedList.Enumerator e = points.GetEnumerator(); - for (int i = 0; i < points.Count / 2; i++) e.MoveNext(); - return e.Current; - } - else - { - return (start + end) / 2; - } - } - - public override string ToString() - { - return start.ToString() + " -->" + end.ToString(); - } - } - - - //Scans a horizontal - internal virtual void ScanBits(int id, XBitArray bitsPrev, XBitArray bits, XBitArray bitsNext, bool isHorizontal) - { - bool prevIsBlack = bits[1]; - int i = 2; - while (i < bits.Size - 1) - { - bool currentIsBlack = bits[i]; - if (prevIsBlack ^ currentIsBlack) - { - //sobel angle - if (currentIsBlack) i--; //calculate sobel from a white pixel - int c00 = bitsPrev[i - 1] ? 1 : 0, c01 = bitsPrev[i] ? 1 : 0, c02 = bitsPrev[i + 1] ? 1 : 0; - int c10 = bits[i - 1] ? 1 : 0, c11 = bits[i] ? 1 : 0, c12 = bits[i + 1] ? 1 : 0; - int c20 = bitsNext[i - 1] ? 1 : 0, c21 = bitsNext[i] ? 1 : 0, c22 = bitsNext[i + 1] ? 1 : 0; - if (currentIsBlack) i++; //remove offset from i - else i--; //move to the previous black pixel - - int Gx = c02 + c22 - c00 - c20 + 2 * (c12 - c10); - int Gy = c20 + c22 - c00 - c02 + 2 * (c21 - c01); - int G = (Gx > 0 ? Gx : -Gx) + (Gy > 0 ? Gy : -Gy); - if (G >= 2) //4 - { - //Hough - double Gangle = isHorizontal ? Math.Atan2(Gy, Gx) : Math.Atan2(Gx, Gy); - //if (Gangle < 0) Gangle += Math.PI; - //else while (Gangle + 0.01 > Math.PI) Gangle -= Math.PI; - MyPoint p = isHorizontal ? new MyPoint(i, id) : new MyPoint(id, i); - int best = int.MaxValue, j = 0; - Edge eBest = null; - foreach (Edge e in edges) - { - int d = e.Belongs(p, (float)Gangle, currentIsBlack, isHorizontal); - if (d != -1 && d < best) - { - best = d; - eBest = e; - } - j++; - } - if (eBest != null) - { - eBest.Add(p, (float)Gangle); - } - else - { - edges.AddLast(new Edge(p, (float)Gangle, currentIsBlack)); - } - } - if (!currentIsBlack) i++; - } - prevIsBlack = currentIsBlack; - i++; - } - - //purge terminated edges - LinkedList removed = new LinkedList(); - foreach (Edge e in edges) - if (isHorizontal && (id == scan.Height - 2 || e.end.Y < id - 3) || - !isHorizontal && (id == scan.Width - 2 || e.end.X < id - 3)) - removed.AddLast(e); - foreach (Edge e in removed) - { - edges.Remove(e); - int k = (int)(e.end - e.start).Length; - if (k>MIN_SIZE && k > minAllowedBarcodeSideSize && k20 pixels - { - if (!ledges.ContainsKey(k)) ledges.Add(k, new LinkedList()); - ledges[k].AddLast(e); - } - } - } - - public int NumLPattnerns { get { return numLPatternsFound; } } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DM_DPM_Reader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DM_DPM_Reader.cs deleted file mode 100644 index 978ad767..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/DM_DPM_Reader.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace BarcodeReader.Core.Datamatrix -{ - /// - /// Reads Datamatrix DPM ("dotted") barcodes - /// -#if CORE_DEV - public -#else - internal -#endif - class DM_DPM_Reader : DMReader - { - public DM_DPM_Reader() - { - this.ThresholdFilterMethodToUse = ThresholdFilterMethod.BlockSmoothed; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/ModulePlacement.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/ModulePlacement.cs deleted file mode 100644 index df406d3b..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/ModulePlacement.cs +++ /dev/null @@ -1,274 +0,0 @@ -using System; -using System.IO; -using System.Reflection; - -namespace BarcodeReader.Core.Datamatrix -{ - // stores the placement matrices used in ECC000-140 - class ModulePlacementNonECC200 - { - private const int SmallestGrid = 7; - private const int LargestGrid = 47; - private const int GridCount = (LargestGrid - SmallestGrid) / 2 + 1; - private readonly ushort[][][] placementGrids = new ushort[GridCount][][]; - private static ModulePlacementNonECC200 instance; - - public static void Init(string resourceName) - { - if (instance == null) - { - instance = new ModulePlacementNonECC200(resourceName); - } - } - - private ModulePlacementNonECC200(string resourceName) - { - Assembly assembly = Assembly.GetExecutingAssembly(); - string resourcePath = null; - foreach (string n in assembly.GetManifestResourceNames()) - if (n.EndsWith(resourceName, StringComparison.Ordinal)) - { - resourcePath = n; - break; - } - - var stream = assembly.GetManifestResourceStream(resourcePath); - if (stream == null) - throw new Exception("Could not load embedded resource."); - BinaryReader bw = new BinaryReader(stream); - for (int i = 0; i < GridCount; ++i) - { - int size = bw.ReadUInt16(); - placementGrids[i] = new ushort[size][]; - for (int y = 0; y < size; ++y) - { - placementGrids[i][y] = new ushort[size]; - - for (int x = 0; x < size; ++x) - { - placementGrids[i][y][x] = bw.ReadUInt16(); - } - } - } - } - - public static ushort[][] GetPlacementGrid(int rows, int columns) - { - if (rows != columns || rows % 2 != 1) - { - return null; - } - return instance.placementGrids[(rows-7)/2]; - } - - - // Use this code snippet if you need to convert the module placement txt file to dat format. - // Shouldn't be needed though. - public static void Convert(string from, string to) - { - FileStream fs = new FileStream(from, FileMode.Open); - StreamReader sr = new StreamReader(fs); - short[][][] placementGrids = new short[GridCount][][]; - for (int i = 0; i < GridCount; ++i) - { - int size = int.Parse(sr.ReadLine()); - placementGrids[i] = new short[size][]; - for (int y = 0; y < size; y++) - placementGrids[i][y] = new short[size]; - - for (int y = size - 1; y >= 0; --y) - { - string row = sr.ReadLine(); - string[] idxs = row.Split(' '); - for (int x = 0; x < size; ++x) - { - placementGrids[i][y][x] = short.Parse(idxs[x]); - } - } - } - - FileStream os = new FileStream(to, FileMode.Create); - BinaryWriter bw = new BinaryWriter(os); - for (int i = 0; i < GridCount; ++i) - { - short size = (short) placementGrids[i].Length; - bw.Write(size); - for (int y = 0; y < size; ++y) - { - for (int x = 0; x < size; ++x) - { - bw.Write(placementGrids[i][y][x]); - } - } - } - bw.Flush(); - os.Close(); - } - } - - // implements the module placement matrix generator algorithm introduced in the Datamatrix specification - class ModulePlacementECC200 - { - private int rowCount; - private int columnCount; - private int[] array; - - - /* "Module" places "chr+bit" with appropriate wrapping within array[] */ - private void Module(int row, int col, int chr, int bit) - { - if (row < 0) { row += rowCount; col += 4 - ((rowCount + 4) % 8); } - if (col < 0) { col += columnCount; row += 4 - ((columnCount + 4) % 8); } - array[row * columnCount + col] = 10 * chr + bit; - } - /* "Utah" places the 8 bits of a Utah-shaped symbol character in ECC200 */ - private void Utah(int row, int col, int chr) - { - Module(row - 2, col - 2, chr, 1); - Module(row - 2, col - 1, chr, 2); - Module(row - 1, col - 2, chr, 3); - Module(row - 1, col - 1, chr, 4); - Module(row - 1, col, chr, 5); - Module(row, col - 2, chr, 6); - Module(row, col - 1, chr, 7); - Module(row, col, chr, 8); - } - /* "cornerN" places 8 bits of the four special corner cases in ECC200 */ - private void Corner1(int chr) - { - Module(rowCount - 1, 0, chr, 1); - Module(rowCount - 1, 1, chr, 2); - Module(rowCount - 1, 2, chr, 3); - Module(0, columnCount - 2, chr, 4); - Module(0, columnCount - 1, chr, 5); - Module(1, columnCount - 1, chr, 6); - Module(2, columnCount - 1, chr, 7); - Module(3, columnCount - 1, chr, 8); - } - private void Corner2(int chr) - { - Module(rowCount - 3, 0, chr, 1); - Module(rowCount - 2, 0, chr, 2); - Module(rowCount - 1, 0, chr, 3); - Module(0, columnCount - 4, chr, 4); - Module(0, columnCount - 3, chr, 5); - Module(0, columnCount - 2, chr, 6); - Module(0, columnCount - 1, chr, 7); - Module(1, columnCount - 1, chr, 8); - } - private void Corner3(int chr) - { - Module(rowCount - 3, 0, chr, 1); - Module(rowCount - 2, 0, chr, 2); - Module(rowCount - 1, 0, chr, 3); - Module(0, columnCount - 2, chr, 4); - Module(0, columnCount - 1, chr, 5); - Module(1, columnCount - 1, chr, 6); - Module(2, columnCount - 1, chr, 7); - Module(3, columnCount - 1, chr, 8); - } - private void Corner4(int chr) - { - Module(rowCount - 1, 0, chr, 1); - Module(rowCount - 1, columnCount - 1, chr, 2); - Module(0, columnCount - 3, chr, 3); - Module(0, columnCount - 2, chr, 4); - Module(0, columnCount - 1, chr, 5); - Module(1, columnCount - 3, chr, 6); - Module(1, columnCount - 2, chr, 7); - Module(1, columnCount - 1, chr, 8); - } - /* "ECC200" fills an nrow x ncol array with appropriate values for ECC200 */ - private void ECC200() - { - int row, col; - /* First, fill the array[] with invalid entries */ - for (row = 0; row < rowCount; row++) - { - for (col = 0; col < columnCount; col++) - { - array[row * columnCount + col] = 0; - } - } - /* Starting in the correct location for character #1, bit 8,... */ - int chr = 1; row = 4; col = 0; - do - { - /* repeatedly first check for one of the special corner cases, then... */ - if ((row == rowCount) && (col == 0)) Corner1(chr++); - if ((row == rowCount - 2) && (col == 0) && (columnCount % 4 != 0)) Corner2(chr++); - if ((row == rowCount - 2) && (col == 0) && (columnCount % 8 == 4)) Corner3(chr++); - if ((row == rowCount + 4) && (col == 2) && (!(columnCount % 8 != 0))) Corner4(chr++); - /* sweep upward diagonally, inserting successive characters,... */ - do - { - if ((row < rowCount) && (col >= 0) && (array[row * columnCount + col] == 0)) - Utah(row, col, chr++); - row -= 2; col += 2; - } while ((row >= 0) && (col < columnCount)); - row += 1; col += 3; - /* & then sweep downward diagonally, inserting successive characters,... */ - do - { - if ((row >= 0) && (col < columnCount) && (array[row * columnCount + col] == 0)) - Utah(row, col, chr++); - row += 2; col -= 2; - } while ((row < rowCount) && (col >= 0)); - row += 3; col += 1; - /* ... until the entire array is scanned */ - } while ((row < rowCount) || (col < columnCount)); - /* Lastly, if the lower righthand corner is untouched, fill in fixed pattern */ - if (array[rowCount * columnCount - 1] == 0) //!=0 23/12/2013 - { - array[rowCount * columnCount - 1] = array[rowCount * columnCount - columnCount - 2] = 1; - } - } - - public MyPoint[][] GetPlacementGridP(int rows, int columns) - { - rowCount = rows; - columnCount = columns; - if ((rowCount < 6) || (rowCount % 2 != 0) || (columnCount < 6) || (columnCount % 2 != 0)) return null; - array = new int[rowCount * columnCount]; - ECC200(); - -#if DEBUG - //for (int i = 0; i < rowCount; i++) - //{ - // for (int j = 0; j < columnCount; j++) Console.Write(" " + array[i * columnCount + j]); - // Console.WriteLine(); - //} -#endif - - - MyPoint[][] grid = new MyPoint[rows][]; - for (int y = 0; y < rows; y++) - grid[y] = new MyPoint[columns]; - - for (int y = 0; y < rowCount; y++) - { - for (int x = 0; x < columnCount; x++) - { - int z = array[y * columnCount + x]; - if (z == 0) - { - grid[rowCount - y - 1][x] = MyPoint.Empty; - // "WHITE" - } - else if (z == 1) - { - grid[rowCount - y - 1][x] = MyPoint.Empty; - // "BLACK" - } - else - { - // byte.bit format stored in MyPoint - grid[rowCount - y - 1][x] = new MyPoint(z / 10 - 1, 8 - z % 10); - } - } - } - - return grid; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/SymbolDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/SymbolDecoder.cs deleted file mode 100644 index 6e633cc7..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/SymbolDecoder.cs +++ /dev/null @@ -1,439 +0,0 @@ -using System; -using System.Collections; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Datamatrix -{ - // non-ecc200 dataformat, with enum values corresponding to values found in the data itself - internal enum NonECC200DataFormat - { - Base11 = 0, Base27 = 1, Base41 = 2, Base37 = 3, ASCII = 4, Base256 = 5 - } - - // Used to decode compressed data. Supports ECC200 and ECC000-140 formats as well - internal class SymbolDecoder - { - #region Charsets - public static readonly char[] C40Set = new char[] - { - '¦', '¦', '¦', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', - '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', - 'Z', (char) 0, (char) 1, (char) 2, (char) 3, (char) 4, (char) 5, - (char) 6, (char) 7, (char) 8, (char) 9, (char) 10, (char) 11, - (char) 12, (char) 13, (char) 14, (char) 15, (char) 16, (char) 17, - (char) 18, (char) 19, (char) 20, (char) 21, (char) 22, (char) 23, - (char) 24, (char) 25, (char) 26, (char) 27, (char) 28, (char) 29, - (char) 30, (char) 31, '¦', '¦', '¦', '¦', '¦', '¦', '¦', '¦', '!' - , '“', '#', '$', '%', '&', '‘', '(', ')', '*', '+', ',', '-', '.' - , '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', - '_', '¦', '¦', '¦', '¦', '¦', '¦', '¦', '¦', '¦', '¦', '¦', '¦', - '¦', '‘', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', - 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', - 'y', 'z', '{', '|', '}', '~', (char) 8, '¦', '¦', '¦', '¦', '¦', - '¦', '¦', '¦' - }; - - public static readonly char[] TextSet = new char[] - { - '¦', '¦', '¦', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', - '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', - 'z', (char) 0, (char) 1, (char) 2, (char) 3, (char) 4, (char) 5, - (char) 6, (char) 7, (char) 8, (char) 9, (char) 10, (char) 11, - (char) 12, (char) 13, (char) 14, (char) 15, (char) 16, (char) 17, - (char) 18, (char) 19, (char) 20, (char) 21, (char) 22, (char) 23, - (char) 24, (char) 25, (char) 26, (char) 27, (char) 28, (char) 29, - (char) 30, (char) 31, '¦', '¦', '¦', '¦', '¦', '¦', '¦', '¦', '!', - '“', '#', '$', '%', '&', '‘', '(', ')', '*', '+', ',', '-', '.', - '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', - '¦', '¦', '¦', '¦', '¦', '¦', '¦', '¦', '¦', '¦', '¦', '¦', '¦', - '‘', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', - 'Z', '{', '|', '}', '~', (char) 8, '¦', '¦', '¦', '¦', '¦', '¦', - '¦', '¦' - }; - - public static readonly char[] X12Set = new char[] - { - (char) 13, (char) 42, (char) 62, ' ', '0', '1', '2', '3', '4', - '5', '6', '7', '8', - '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', - 'Z' - }; - - public static readonly byte[] Base27Set = new byte[] - { - 32, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90 - }; - - - public static readonly byte[] Base37Set = new byte[] - { - 32, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 48, 49, 50, 51, 52, - 53, 54, 55, 56, 57 - }; - - public static readonly byte[] Base41Set = new byte[] - { - 32, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 48, 49, 50, 51, 52, - 53, 54, 55, 56, 57, 46, 44, 45, 47 - }; - - public static readonly byte[] Base11Set = new byte[] {32, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57}; - - public const int C40TextPageLength = 40; - - public const byte ASCIIUpperShift = 235; - - public const byte EDIFACTUnlatch = 31; - #endregion - - private static readonly Hashtable SectionLengthBase = new Hashtable(); - - private static readonly Hashtable BaseIdToBase = new Hashtable(); - - private static readonly Hashtable BaseLookupTables = new Hashtable(); - - static SymbolDecoder() - { - BaseIdToBase.Add(NonECC200DataFormat.Base11, 11); - BaseIdToBase.Add(NonECC200DataFormat.Base27, 27); - BaseIdToBase.Add(NonECC200DataFormat.Base37, 37); - BaseIdToBase.Add(NonECC200DataFormat.Base41, 41); - - SectionLengthBase.Add(11, new byte[] {4, 7, 11, 14, 18, 21}); - SectionLengthBase.Add(27, new byte[] {5, 10, 15, 20, 24}); - SectionLengthBase.Add(37, new byte[] {6, 11, 16, 21}); - SectionLengthBase.Add(41, new byte[] {6, 11, 17, 22}); - - BaseLookupTables.Add(11, Base11Set); - BaseLookupTables.Add(27, Base27Set); - BaseLookupTables.Add(37, Base37Set); - BaseLookupTables.Add(41, Base41Set); - } - - public static ABarCodeData[] DecodeASCII(int[] symbols, ref int index) - { - ArrayList resultList = new ArrayList(); - while (index < symbols.Length && (symbols[index] < 230 && symbols[index] != 129 || symbols[index] == ASCIIUpperShift)) - { - StringBuilder chars = new StringBuilder(); - while (index < symbols.Length && (symbols[index] < 129 || symbols[index] == ASCIIUpperShift)) - { - int symb = symbols[index]; - if (symb == ASCIIUpperShift) - { - ++index; - if (index >= symbols.Length) break; - symb = symbols[index]; - chars.Append((char)(symb + 127)); - } - else - { - chars.Append((char)(symb - 1)); - } - ++index; - } - if (chars.Length > 0) - { - resultList.Add(new StringBarCodeData(chars.ToString())); - } - - ArrayList digits = new ArrayList(); - while (index < symbols.Length && symbols[index] > 129 && symbols[index] < 230) - { - digits.Add((byte) ((symbols[index] - 130)/10)); - digits.Add((byte) ((symbols[index] - 130)%10)); - ++index; - } - if (digits.Count > 0) - { - byte[] value = new byte[digits.Count]; - digits.CopyTo(value); - resultList.Add(new NumericBarCodeData(value)); - } - } - if (resultList.Count > 0) - { - ABarCodeData[] data = new ABarCodeData[resultList.Count]; - resultList.CopyTo(data); - return data; - } - - return null; - } - - public static ABarCodeData[] DecodeBase256(int[] symbols, ref int index, System.Text.Encoding encoding) - { - if (index >= symbols.Length) - { - return null; - } - - int sequenceEnd; - byte d1 = UnRandomise256(symbols[index], index + 1); - if (d1 == 0) - { - sequenceEnd = symbols.Length; - ++index; - } - else if (d1 < 250) - { - ++index; - sequenceEnd = index + d1; - - } - else - { - if (index + 1 >= symbols.Length) - { - return null; - } - int d2 = UnRandomise256(symbols[index + 1], index + 2); - index += 2; - sequenceEnd = index + (d1 - 249)*250 + d2; - } - - byte[] data = new byte[sequenceEnd - index]; - int offset = index; - for (; index < sequenceEnd && index> (2-bitIndex)) & 63 /*binary 111111*/; - bitIndex += 6; - if (bitIndex > 7) - { - bitIndex -= 8; - ++index; - } - } - else - { - bitIndex -= 2; - ediByte = (symbols[index++] << bitIndex) & 63; //binary 111111 - if (index >= symbols.Length) break; - ediByte |= (symbols[index] >> (8-bitIndex)); - } - - if (ediByte == EDIFACTUnlatch) - { - if (bitIndex != 0) - { - ++index; - } - break; - } - - edifatData.Add(ediByte); - } while (index < symbols.Length); - - StringBuilder resultBuilder = new StringBuilder(); - foreach (int edd in edifatData) - { - if (edd < 32) - { - resultBuilder.Append((char) (edd + 64)); - } - else - { - resultBuilder.Append((char)edd); - } - } - - return new ABarCodeData[] { new StringBarCodeData(resultBuilder.ToString())}; - } - - // decodes and ECI switch sequence - public static ABarCodeData DecodeECI(int[] symbols, ref int index) - { - if (index >= symbols.Length) - { - return null; - } - - int c1 = symbols[index++]; - if (c1 < 128) - { - return new ECISwitchSymbol(c1 - 1); - } - - if (index >= symbols.Length) - { - return null; - } - - int c2 = symbols[index++]; - if (c1 < 192) - { - return new ECISwitchSymbol((c1 - 128)*254 + c2 + 126); - } - - if (index >= symbols.Length) - { - return null; - } - - int c3 = symbols[index++]; - return new ECISwitchSymbol((c1 - 192)*64516 + (c2 - 1)*254 + c3 + 16382); - } - - // decodes a structured append sequence - public static ABarCodeData DecodeStructuredAppend(int[] symbols, ref int index) - { - if (index + 2 >= symbols.Length) - { - return null; - } - - int val1 = symbols[index]; - int val2 = symbols[index + 1]; - int val3 = symbols[index + 2]; - - index += 3; - return new StructuredAppendSymbol(val1, val2, val3); - } - - // decodes Base coded data. Used in ECC000-140 - public static byte[] DecodeBaseX(BitArray bitData, int dataLength, NonECC200DataFormat dataFormat) - { - int codeBase = (int) BaseIdToBase[dataFormat]; - byte[] set = (byte[]) BaseLookupTables[codeBase]; - byte[] sectionLengths = (byte[]) SectionLengthBase[codeBase]; - - byte[] originalData = new byte[dataLength]; - for (int bitIndex = 0, i = 0; i < dataLength;) - { - int chunksize = Math.Min(sectionLengths.Length, dataLength - i); - int bitCount = sectionLengths[chunksize - 1]; - int value = 0; - for (int b = 0; b < bitCount; ++b, ++bitIndex) - { - if (bitIndex >= bitData.Length) - { - return null; - } - if (bitData[bitIndex]) - { - value |= (1 << b); - } - } - - for (int b = 0; b < chunksize; ++b, ++i) - { - originalData[i] = set[value%codeBase]; - value /= codeBase; - } - } - - return originalData; - } - - // extracts C40, Text and X12 bytes from a compressed datastream - private static int[] UnfoldC40TextX12(int[] symbols, ref int index) - { - int c40Length = 0; - while (index + c40Length +1 < symbols.Length && symbols[index + c40Length] != 254) - c40Length += 2; - - int[] c40Data = new int[c40Length / 2 * 3]; - int originalIndex = index; - for (int i = 0, c = 0; i < c40Length; i += 2) - { - int currentIndex = originalIndex + i; - if (currentIndex + 1 >= symbols.Length) - { - break; - } - - int word = symbols[currentIndex] * 256 + symbols[currentIndex + 1] - 1; - byte c1 = (byte) (word/1600); - word %= 1600; - byte c2 = (byte) (word/40); - byte c3 = (byte) (word%40); - c40Data[c++] = c1; - c40Data[c++] = c2; - c40Data[c++] = c3; - } - index = originalIndex + c40Length; - if (index= 0) - { - return (byte)tmp; - } - - return (byte) (tmp + 256); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/mpnonecc200.dat b/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/mpnonecc200.dat deleted file mode 100644 index 3b1e305f..00000000 Binary files a/MuPDF.NET/Barcode/Decoders/NewDecoders/Datamatrix/mpnonecc200.dat and /dev/null differ diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/EAN13LinearReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/EAN13LinearReader.cs deleted file mode 100644 index f1f695ac..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/EAN13LinearReader.cs +++ /dev/null @@ -1,246 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.EAN -{ - /// - /// EAN13 reader. - /// This calss also can read UPC-A. - /// Also it can read supplemented EAN2 and EAN5. - /// -#if CORE_DEV - public -#else - internal -#endif - class EAN13LinearReader : LinearReader - { - public bool FindSupplementalEAN2 { get; set; } = false; - public bool FindSupplementalEAN5 { get; set; } = false; - - private static int[][] _startStopPatterns = { new int[] { 1, 1, 1 } }; - private static int[][] _middlePattern = { new int[] { 1, 1, 1, 1, 1 } }; - - // Odd parity patterns for left-hand encoding - private static int[][] _leftOddPatterns = - { - new int[] {3, 2, 1, 1}, // 0 - new int[] {2, 2, 2, 1}, // 1 - new int[] {2, 1, 2, 2}, // 2 - new int[] {1, 4, 1, 1}, // 3 - new int[] {1, 1, 3, 2}, // 4 - new int[] {1, 2, 3, 1}, // 5 - new int[] {1, 1, 1, 4}, // 6 - new int[] {1, 3, 1, 2}, // 7 - new int[] {1, 2, 1, 3}, // 8 - new int[] {3, 1, 1, 2} // 9 - }; - - // All left-hand encoding patterns (odd parity and even parity) - private static int[][] _leftPatterns = - { - // odd parity - _leftOddPatterns[0], _leftOddPatterns[1], - _leftOddPatterns[2], _leftOddPatterns[3], - _leftOddPatterns[4], _leftOddPatterns[5], - _leftOddPatterns[6], _leftOddPatterns[7], - _leftOddPatterns[8], _leftOddPatterns[9], - - // even parity - new int[] {1, 1, 2, 3}, // 0 - new int[] {1, 2, 2, 2}, // 1 - new int[] {2, 2, 1, 2}, // 2 - new int[] {1, 1, 4, 1}, // 3 - new int[] {2, 3, 1, 1}, // 4 - new int[] {1, 3, 2, 1}, // 5 - new int[] {4, 1, 1, 1}, // 6 - new int[] {2, 1, 3, 1}, // 7 - new int[] {3, 1, 2, 1}, // 8 - new int[] {2, 1, 1, 3} // 9 - }; - - private static int[][][] _patterns = new int[][][] - { - _startStopPatterns, - _leftPatterns, - _middlePattern, - _leftOddPatterns, - _startStopPatterns - }; - - static int[] nBars = { 3, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 3 }; - static int[] nLength = { 3, 7, 7, 7, 7, 7, 7, 5, 7, 7, 7, 7, 7, 7, 3 }; - static int[] tableIndexes = { 0, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 3, 3, 3, 4 }; - - public EAN13LinearReader() - { - //incerase MaxSkewAngle because EAN can be on curved surface - MaxSkewAngle = 40 *(float)Math.PI / 180; - } - - internal override void GetParams(out int[] startPattern, out int[] stopPattern, out int minModulesPerBarcode, out int maxModulesPerBarcode) - { - startPattern = stopPattern = _startStopPatterns[0]; - maxModulesPerBarcode = minModulesPerBarcode = 89; - - if (Reader == null) - { - Reader = new BarSymbolReader(Scan, nBars, nLength, tableIndexes, false, true, 1, _patterns, UseE, null); - } - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.EAN13; - } - - internal override bool Decode(BarCodeRegion reg, int[] row) - { - if (row.Length < 15) return false; - - //calc 13 digit - var digit13 = Calc13Digit(row); - - if (digit13 < 0) return false; - - //decode - byte[] data = new byte[13]; - data[0] = (byte)digit13; - - for (int i = 1; i < 7; i++) - { - if (row[i] < 0) return false; - data[i] = (byte)(row[i] % 10); - } - - for (int i = 8; i < 14; i++) - { - if (row[i] < 0) return false; - data[i - 1] = (byte)(row[i] % 10); - } - - if (!VerifyCheckSum(data)) return false; - - reg.Data = new ABarCodeData[] { new NumericBarCodeData(data) }; - - return true; - } - - internal virtual bool VerifyCheckSum(byte[] data) - { - int sum = 0; - for (int i = data.Length - 2; i >= 0; i -= 2) - { - sum += data[i]; - } - - sum *= 3; - - for (int i = data.Length - 1; i >= 0; i -= 2) - { - sum += data[i]; - } - - return sum % 10 == 0; - } - - private int Calc13Digit(int[] row) - { - //calc signature - var sign = 0; - for (int i = 1; i < 7; i++) - if (row[i] < 10) - sign = sign * 10 + 0; - else - sign = sign * 10 + 1; - // - switch (sign) - { - case 000000: return 0; - case 001011: return 1; - case 001101: return 2; - case 001110: return 3; - case 010011: return 4; - case 011001: return 5; - case 011100: return 6; - case 010101: return 7; - case 010110: return 8; - case 011010: return 9; - default: return -1; - } - } - - public override FoundBarcode[] Decode(BlackAndWhiteImage bwImage) - { - //call base method - var res = base.Decode(bwImage); - - //try to find supplimental barcodes (EAN2, EAN5) - if (FindSupplementalEAN2 || FindSupplementalEAN5) - { - var list = new List(res); - - for (int i = 0; i < list.Count; i++) - { - var parent = list[i]; - - if (FindSupplementalEAN5) - { - var supp = FindSupplemental(parent, new EAN5LinearReader(), bwImage); - if (supp != null) - { - list.Insert(i + 1, supp); - i++; - continue; - } - } - - if (FindSupplementalEAN2) - { - var supp = FindSupplemental(parent, new EAN2LinearReader(), bwImage); - if (supp != null) - { - list.Insert(i + 1, supp); - i++; - continue; - } - } - } - - res = list.ToArray(); - } - - return res; - } - - private FoundBarcode FindSupplemental(FoundBarcode parent, LinearReader reader, BlackAndWhiteImage bwImage) - { - //copy parameters - CopyTo(reader); - // excepts one barcode - reader.ExpectedNumberOfBarcodes = 1; - //calc ROI - var d = (parent.ParentRegion.B - parent.ParentRegion.A); - var d1 = d; - var d2 = d * 2; - if (parent.ParentRegion.Reversed) - { - //reversed - d1 = -d * 2; - d2 = -d; - } - - var r = new BarCodeRegion(parent.ParentRegion);//clone region - r.SetCorners(r.A + d1, r.B + d2, r.C + d2, r.D + d1);//offset region - reader.ROI = r.GetBounds(); - reader.ROI.Inflate(5, 5); - //find barcode - var found = reader.Decode(bwImage); - - return found.Length > 0 ? found[0] : null; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/EAN2LinearReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/EAN2LinearReader.cs deleted file mode 100644 index 694a1075..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/EAN2LinearReader.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System; -using System.Drawing; -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.EAN -{ - /// - /// EAN2 reader. - /// -#if CORE_DEV - public -#else - internal -#endif - class EAN2LinearReader : LinearReader - { - private static int[][] _startPatterns = { new int[] { 1, 1, 2 } }; - private static int[][] _stopPatterns = { new int[] {} }; - private static int[][] _separator = { new int[] { 1, 1 } }; - - // Odd parity patterns for left-hand encoding - private static int[][] _leftOddPatterns = - { - new int[] {3, 2, 1, 1}, // 0 - new int[] {2, 2, 2, 1}, // 1 - new int[] {2, 1, 2, 2}, // 2 - new int[] {1, 4, 1, 1}, // 3 - new int[] {1, 1, 3, 2}, // 4 - new int[] {1, 2, 3, 1}, // 5 - new int[] {1, 1, 1, 4}, // 6 - new int[] {1, 3, 1, 2}, // 7 - new int[] {1, 2, 1, 3}, // 8 - new int[] {3, 1, 1, 2} // 9 - }; - - // All left-hand encoding patterns (odd parity and even parity) - private static int[][] _leftPatterns = - { - // odd parity - _leftOddPatterns[0], _leftOddPatterns[1], - _leftOddPatterns[2], _leftOddPatterns[3], - _leftOddPatterns[4], _leftOddPatterns[5], - _leftOddPatterns[6], _leftOddPatterns[7], - _leftOddPatterns[8], _leftOddPatterns[9], - - // even parity - new int[] {1, 1, 2, 3}, // 0 - new int[] {1, 2, 2, 2}, // 1 - new int[] {2, 2, 1, 2}, // 2 - new int[] {1, 1, 4, 1}, // 3 - new int[] {2, 3, 1, 1}, // 4 - new int[] {1, 3, 2, 1}, // 5 - new int[] {4, 1, 1, 1}, // 6 - new int[] {2, 1, 3, 1}, // 7 - new int[] {3, 1, 2, 1}, // 8 - new int[] {2, 1, 1, 3} // 9 - }; - - private static int[][][] _patterns = new int[][][] - { - _startPatterns, - _leftPatterns, - _separator - }; - - static int[] nBars = { 3, 4, 2, 4 }; - static int[] nLength = { 4, 7, 2, 7 }; - static int[] tableIndexes = { 0, 1, 2, 1 }; - - public EAN2LinearReader() - { - //incerase MaxSkewAngle because EAN can be on curved surface - MaxSkewAngle = 30 * (float)Math.PI / 180; - } - - internal override void GetParams(out int[] startPattern, out int[] stopPattern, out int minModulesPerBarcode, out int maxModulesPerBarcode) - { - startPattern = _startPatterns[0]; - stopPattern = _stopPatterns[0]; - maxModulesPerBarcode = minModulesPerBarcode = 20; - if (MinQuietZone > 3) - MinQuietZone = 3;//decrease quiet zone because between EAN13 and EAN2 can be small interval - - if (Reader == null) - { - Reader = new BarSymbolReader(Scan, nBars, nLength, tableIndexes, false, true, 1, _patterns, UseE, null); - } - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.EAN2; - } - - internal override bool Decode(BarCodeRegion reg, int[] row) - { - if (row.Length < 4) return false; - - var digit = CalcDigit(row); - if (digit < 0) - return false; - - //decode - byte[] data = new byte[2]; - - for (int i = 1, j = 0; i < 4; i+=2, j++) - { - if (row[i] < 0) return false; - data[j] = (byte)(row[i] % 10); - } - - if (!VerifyCheckSum(data, digit)) return false; - - reg.Data = new ABarCodeData[] { new NumericBarCodeData(data) }; - - return true; - } - - internal virtual bool VerifyCheckSum(byte[] data, int digit) - { - int sum = ((data[0] % 10) * 10 + data[1] % 10) % 4; - - return sum == digit; - } - - private int CalcDigit(int[] row) - { - var res = 0; - if (row[1] >= 10) res += 2; - if (row[3] >= 10) res += 1; - - return res; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/EAN5LinearReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/EAN5LinearReader.cs deleted file mode 100644 index ea6a6363..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/EAN5LinearReader.cs +++ /dev/null @@ -1,158 +0,0 @@ -using System; -using System.Drawing; -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.EAN -{ - /// - /// EAN5 reader. - /// -#if CORE_DEV - public -#else - internal -#endif - class EAN5LinearReader : LinearReader - { - private static int[][] _startPatterns = { new int[] { 1, 1, 2 } }; - private static int[][] _stopPatterns = { new int[] {} }; - private static int[][] _separator = { new int[] { 1, 1 } }; - - // Odd parity patterns for left-hand encoding - private static int[][] _leftOddPatterns = - { - new int[] {3, 2, 1, 1}, // 0 - new int[] {2, 2, 2, 1}, // 1 - new int[] {2, 1, 2, 2}, // 2 - new int[] {1, 4, 1, 1}, // 3 - new int[] {1, 1, 3, 2}, // 4 - new int[] {1, 2, 3, 1}, // 5 - new int[] {1, 1, 1, 4}, // 6 - new int[] {1, 3, 1, 2}, // 7 - new int[] {1, 2, 1, 3}, // 8 - new int[] {3, 1, 1, 2} // 9 - }; - - // All left-hand encoding patterns (odd parity and even parity) - private static int[][] _leftPatterns = - { - // odd parity - _leftOddPatterns[0], _leftOddPatterns[1], - _leftOddPatterns[2], _leftOddPatterns[3], - _leftOddPatterns[4], _leftOddPatterns[5], - _leftOddPatterns[6], _leftOddPatterns[7], - _leftOddPatterns[8], _leftOddPatterns[9], - - // even parity - new int[] {1, 1, 2, 3}, // 0 - new int[] {1, 2, 2, 2}, // 1 - new int[] {2, 2, 1, 2}, // 2 - new int[] {1, 1, 4, 1}, // 3 - new int[] {2, 3, 1, 1}, // 4 - new int[] {1, 3, 2, 1}, // 5 - new int[] {4, 1, 1, 1}, // 6 - new int[] {2, 1, 3, 1}, // 7 - new int[] {3, 1, 2, 1}, // 8 - new int[] {2, 1, 1, 3} // 9 - }; - - private static int[][][] _patterns = new int[][][] - { - _startPatterns, - _leftPatterns, - _separator - }; - - static int[] nBars = { 3, 4, 2, 4, 2, 4, 2, 4, 2, 4 }; - static int[] nLength = { 4, 7, 2, 7, 2, 7, 2, 7, 2, 7 }; - static int[] tableIndexes = { 0, 1, 2, 1, 2, 1, 2, 1, 2, 1 }; - - public EAN5LinearReader() - { - //incerase MaxSkewAngle because EAN can be on curved surface - MaxSkewAngle = 40 * (float)Math.PI / 180; - } - - internal override void GetParams(out int[] startPattern, out int[] stopPattern, out int minModulesPerBarcode, out int maxModulesPerBarcode) - { - startPattern = _startPatterns[0]; - stopPattern = _stopPatterns[0]; - maxModulesPerBarcode = minModulesPerBarcode = 47; - if (MinQuietZone > 3) - MinQuietZone = 3;//decrease quiet zone because between EAN13 and EAN5 can be small interval - if (Reader == null) - { - Reader = new BarSymbolReader(Scan, nBars, nLength, tableIndexes, false, true, 1, _patterns, UseE, null); - } - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.EAN5; - } - - internal override bool Decode(BarCodeRegion reg, int[] row) - { - if (row.Length < 10) return false; - - var digit = CalcDigit(row); - if (digit < 0) - return false; - - //decode - byte[] data = new byte[5]; - - for (int i = 1, j = 0; i < 10; i+=2, j++) - { - if (row[i] < 0) return false; - data[j] = (byte)(row[i] % 10); - } - - if (!VerifyCheckSum(data, digit)) return false; - - reg.Data = new ABarCodeData[] { new NumericBarCodeData(data) }; - - return true; - } - - internal virtual bool VerifyCheckSum(byte[] data, int digit) - { - int sum = 0; - - for (int i = 0; i < 5; i++) - { - sum += data[i] * (i % 2 == 0 ? 3 : 9); - } - - return sum % 10 == digit; - } - - private int CalcDigit(int[] row) - { - //calc signature - var sign = 0; - for (int i = 1; i < 10; i+=2) - if (row[i] < 10) - sign = sign * 10 + 0; - else - sign = sign * 10 + 1; - // - switch (sign) - { - case 11000: return 0; - case 10100: return 1; - case 10010: return 2; - case 10001: return 3; - case 01100: return 4; - case 00110: return 5; - case 00011: return 6; - case 01010: return 7; - case 01001: return 8; - case 00101: return 9; - - default: return -1; - } - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/EAN8LinearReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/EAN8LinearReader.cs deleted file mode 100644 index 3a34177b..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/EAN8LinearReader.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Drawing; -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.EAN -{ - /// - /// EAN8 reader. - /// -#if CORE_DEV - public -#else - internal -#endif - class EAN8LinearReader : LinearReader - { - private static int[][] _startStopPatterns = { new int[] { 1, 1, 1 } }; - private static int[][] _middlePattern = { new int[] { 1, 1, 1, 1, 1 } }; - - private static int[][] _symbols = - { - new int[] {3, 2, 1, 1}, // 0 - new int[] {2, 2, 2, 1}, // 1 - new int[] {2, 1, 2, 2}, // 2 - new int[] {1, 4, 1, 1}, // 3 - new int[] {1, 1, 3, 2}, // 4 - new int[] {1, 2, 3, 1}, // 5 - new int[] {1, 1, 1, 4}, // 6 - new int[] {1, 3, 1, 2}, // 7 - new int[] {1, 2, 1, 3}, // 8 - new int[] {3, 1, 1, 2} // 9 - }; - - private static int[][][] _patterns = new int[][][] - { - _startStopPatterns, - _symbols, - _middlePattern, - _symbols, - _startStopPatterns - }; - - static int[] nBars = { 3, 4, 4, 4, 4, 5, 4, 4, 4, 4, 3 }; - static int[] nLength = { 3, 7, 7, 7, 7, 5, 7, 7, 7, 7, 3 }; - static int[] tableIndexes = { 0, 1, 1, 1, 1, 2, 3, 3, 3, 3, 4 }; - - public EAN8LinearReader() - { - //incerase MaxSkewAngle because EAN can be on curved surface - MaxSkewAngle = 40 * (float)Math.PI / 180; - } - - internal override void GetParams(out int[] startPattern, out int[] stopPattern, out int minModulesPerBarcode, out int maxModulesPerBarcode) - { - startPattern = stopPattern = _startStopPatterns[0]; - maxModulesPerBarcode = minModulesPerBarcode = 67; - - if (Reader == null) - { - Reader = new BarSymbolReader(Scan, nBars, nLength, tableIndexes, false, true, 1, _patterns, UseE, null); - } - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.EAN8; - } - - internal override bool Decode(BarCodeRegion reg, int[] row) - { - if (row.Length < 11) return false; - - //decode - byte[] data = new byte[8]; - - for (int i = 1; i < 5; i++) - { - if (row[i] < 0) return false; - data[i - 1] = (byte)row[i]; - } - - for (int i = 6; i < 10; i++) - { - if (row[i] < 0) return false; - data[i - 2] = (byte)row[i]; - } - - if (!VerifyCheckSum(data)) return false; - - reg.Data = new ABarCodeData[] { new NumericBarCodeData(data) }; - - return true; - } - - internal virtual bool VerifyCheckSum(byte[] data) - { - int sum = 0; - for (int i = data.Length - 2; i >= 0; i -= 2) - { - sum += data[i]; - } - - sum *= 3; - - for (int i = data.Length - 1; i >= 0; i -= 2) - { - sum += data[i]; - } - - return sum % 10 == 0; - } - - private int Calc13Digit(int[] row) - { - //calc signature - var sign = 0; - for (int i = 1; i < 7; i++) - if (row[i] < 10) - sign = sign * 10 + 0; - else - sign = sign * 10 + 1; - // - switch (sign) - { - case 000000: return 0; - case 001011: return 1; - case 001101: return 2; - case 001110: return 3; - case 010011: return 4; - case 011001: return 5; - case 011100: return 6; - case 010101: return 7; - case 010110: return 8; - case 011010: return 9; - default: return -1; - } - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/UPCALinearReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/UPCALinearReader.cs deleted file mode 100644 index 3d68d510..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/UPCALinearReader.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.EAN -{ - /// - /// UPC-A reader. - /// Also it can read supplemented EAN2 and EAN5. - /// -#if CORE_DEV - public -#else - internal -#endif - class UPCALinearReader : EAN13LinearReader - { - public override SymbologyType GetBarCodeType() - { - return SymbologyType.UPCA; - } - - protected override FoundBarcode FindBarcode(Candidate cand, bool reverse) - { - var res = base.FindBarcode(cand, reverse); - - if (res != null) - { - var barData = res.ParentRegion.Data[0] as NumericBarCodeData; - var data = barData.Value; - - // If it's EAN-13 with leading zero then convert it to UPCA. - // Otherwise return null to avoid false positive. - if (data.Length == 13) - { - if (data[0] == 0) - { - for (int i = 0; i < data.Length - 1; i++) - data[i] = data[i + 1]; - Array.Resize(ref data, data.Length - 1); - barData.Value = data; - - res.Value = barData.ToString(); - } - else - return null; - } - } - - return res; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/UPCELinearReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/UPCELinearReader.cs deleted file mode 100644 index ed998374..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/EAN/UPCELinearReader.cs +++ /dev/null @@ -1,274 +0,0 @@ -using System; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.EAN -{ - /// - /// UPC-E reader. - /// -#if CORE_DEV - public -#else - internal -#endif - class UPCELinearReader : EAN13LinearReader - { - private static int[][] _startPattern = { new int[] {1, 1, 1} }; - private static int[][] _stopPattern = { new int[] { 1, 1, 1, 1, 1 } }; - - // Odd parity patterns for left-hand encoding - private static int[][] _leftOddPatterns = - { - new int[] {3, 2, 1, 1}, // 0 - new int[] {2, 2, 2, 1}, // 1 - new int[] {2, 1, 2, 2}, // 2 - new int[] {1, 4, 1, 1}, // 3 - new int[] {1, 1, 3, 2}, // 4 - new int[] {1, 2, 3, 1}, // 5 - new int[] {1, 1, 1, 4}, // 6 - new int[] {1, 3, 1, 2}, // 7 - new int[] {1, 2, 1, 3}, // 8 - new int[] {3, 1, 1, 2} // 9 - }; - - // All left-hand encoding patterns (odd parity and even parity) - private static int[][] _leftPatterns = - { - // odd parity - _leftOddPatterns[0], _leftOddPatterns[1], - _leftOddPatterns[2], _leftOddPatterns[3], - _leftOddPatterns[4], _leftOddPatterns[5], - _leftOddPatterns[6], _leftOddPatterns[7], - _leftOddPatterns[8], _leftOddPatterns[9], - - // even parity - new int[] {1, 1, 2, 3}, // 0 - new int[] {1, 2, 2, 2}, // 1 - new int[] {2, 2, 1, 2}, // 2 - new int[] {1, 1, 4, 1}, // 3 - new int[] {2, 3, 1, 1}, // 4 - new int[] {1, 3, 2, 1}, // 5 - new int[] {4, 1, 1, 1}, // 6 - new int[] {2, 1, 3, 1}, // 7 - new int[] {3, 1, 2, 1}, // 8 - new int[] {2, 1, 1, 3} // 9 - }; - - private static int[][][] _patterns = new int[][][] - { - _startPattern, - _leftPatterns, - _stopPattern - }; - - static int[] nBars = { 3, 4, 4, 4, 4, 4, 4, 5 }; - static int[] nLength = { 3, 7, 7, 7, 7, 7, 7, 5 }; - static int[] tableIndexes = { 0, 1, 1, 1, 1, 1, 1, 2 }; - - internal override void GetParams(out int[] startPattern, out int[] stopPattern, out int minModulesPerBarcode, out int maxModulesPerBarcode) - { - startPattern = _startPattern[0]; - stopPattern = _stopPattern[0]; - maxModulesPerBarcode = minModulesPerBarcode = 50; - - if (Reader == null) - { - Reader = new BarSymbolReader(Scan, nBars, nLength, tableIndexes, false, true, 1, _patterns, UseE, null); - } - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.UPCE; - } - - internal override bool Decode(BarCodeRegion reg, int[] row) - { - if (row.Length < 8) return false; - - //calc 7 digit - var digit7 = Calc7Digit(row); - - if (digit7 < 0) return false; - - //decode - byte[] data = new byte[7]; - data[6] = (byte)digit7; - - for (int i = 1; i < 7; i++) - { - if (row[i] < 0) return false; - data[i - 1] = (byte)(row[i] % 10); - } - - //decode to UPCA and calc checksum - var upca = convertUpceToUpca(data); - if (!VerifyCheckSum(upca)) return false; - - //insert numeric system - var numSyst = CalcNumberSystem(row); - Array.Resize(ref data, data.Length + 1); - for (int i = data.Length - 1; i >= 1; i--) - data[i] = data[i - 1]; - data[0] = (byte)numSyst; - - reg.Data = new ABarCodeData[] { new NumericBarCodeData(data) }; - - return true; - } - - public static byte[] convertUpceToUpca(byte[] upce) - { - byte[] upceCore = null; - - if (upce.Length == 6) - { - upceCore = upce; - } - else if (upce.Length == 7) - { - // truncate last digit, assume it is just check digit - upceCore = new byte[6]; - Array.Copy(upce, upceCore, 6); - } - else if (upce.Length == 8) - { - // truncate first and last digit, - // assume first digit is number system digit - // last digit is check digit - upceCore = new byte[6]; - Array.Copy(upce, 1, upceCore, 0, 6); - } - else - { - return null; - } - - byte[] upca = new byte[12]; - byte lastDigit = upceCore[5]; - switch (lastDigit) - { - case 0: - case 1: - case 2: - upca[1] = upceCore[0]; - upca[2] = upceCore[1]; - upca[3] = lastDigit; - upca[8] = upceCore[2]; - upca[9] = upceCore[3]; - upca[10] = upceCore[4]; - break; - case 3: - upca[1] = upceCore[0]; - upca[2] = upceCore[1]; - upca[3] = upceCore[2]; - upca[9] = upceCore[3]; - upca[10] = upceCore[4]; - break; - case 4: - upca[1] = upceCore[0]; - upca[2] = upceCore[1]; - upca[3] = upceCore[2]; - upca[4] = upceCore[3]; - upca[10] = upceCore[4]; - break; - default: - upca[1] = upceCore[0]; - upca[2] = upceCore[1]; - upca[3] = upceCore[2]; - upca[4] = upceCore[3]; - upca[5] = upceCore[4]; - upca[10] = lastDigit; - break; - } - - int check = calculateUpcaChecksum(upca); - if (check > -1) - { - upca[11] = (byte)check; - } - - return upca; - } - - protected static int calculateUpcaChecksum(byte[] data) - { - int sum = 0; - for (int i = data.Length - 2; i >= 0; i -= 2) - { - sum += data[i]; - } - - sum *= 3; - - for (int i = data.Length - 1; i >= 0; i -= 2) - { - sum += data[i]; - } - - return 10 - sum % 10; - } - - private int Calc7Digit(int[] row) - { - //calc signature - var sign = 0; - for (int i = 1; i < 7; i++) - if (row[i] < 10) - sign = sign * 10 + 0; - else - sign = sign * 10 + 1; - // - switch (sign) - { - case 111000: case 000111: return 0; - case 110100: case 001011: return 1; - case 110010: case 001101: return 2; - case 110001: case 001110: return 3; - case 101100: case 010011: return 4; - case 100110: case 011001: return 5; - case 100011: case 011100: return 6; - case 101010: case 010101: return 7; - case 101001: case 010110: return 8; - case 100101: case 011010: return 9; - default: return -1; - } - } - - private int CalcNumberSystem(int[] row) - { - //calc signature - var sign = 0; - for (int i = 1; i < 7; i++) - if (row[i] < 10) - sign = sign * 10 + 0; - else - sign = sign * 10 + 1; - // - switch (sign) - { - case 111000: - case 110100: - case 110010: - case 110001: - case 101100: - case 100110: - case 100011: - case 101010: - case 101001: - case 100101: return 0; - case 000111: - case 001011: - case 001101: - case 001110: - case 010011: - case 011001: - case 011100: - case 010101: - case 010110: - case 011010: return 1; - default: return -1; - } - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormLines/FieldsToFillDetector.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/FormLines/FieldsToFillDetector.cs deleted file mode 100644 index 3bc5e59d..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormLines/FieldsToFillDetector.cs +++ /dev/null @@ -1,177 +0,0 @@ -using System; -using System.Collections; -using System.Drawing; -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.FormLines -{ - //Class to detect lines in a document with a white area above. - //The area of the box must be between minHeight and maxHeight, otherwise they are discarded. - //In a posproces, boxes distant = 0; i--) - { - FoundBarcode f = (FoundBarcode)result[i]; - MyPoint a = f.Polygon[0]; - MyPoint b = f.Polygon[1]; - MyPoint c = f.Polygon[2]; - int width = (int)Math.Round((b - a).Length); - //int height = (int)Math.Round((c - b).Length); //uncomment to remove boxes with height>maxHeight - if (width < minRad /*|| height >= maxHeight*/) result.RemoveAt(i); - } - return (FoundBarcode[])result.ToArray(typeof(FoundBarcode)); - } - - //Main method to find boxes. Starting from a line from a to b, first searches how many - //white pixels there are on top of each pixel of the line a-b. This is stored in the array height. - //In a second step, loops trough the height array looking for consistent parts, i.e grouped heights - //between minHeight and maxHeight. - void CheckBox(MyPoint a, MyPoint b) - { - //first step: find the height array, and also bottom and top points - MyVectorF vdX = (b - a); vdX = vdX.Normalized; - MyVectorF vdY = vdX.Perpendicular; - Bresenham br = new Bresenham(a, vdY); - Bresenham brX = new Bresenham(a, b); - MyPoint[] bottom = new MyPoint[brX.Steps + 1]; - MyPoint[] top = new MyPoint[brX.Steps + 1]; - int[] height = new int[brX.Steps + 1]; - int x = 0; - while (!brX.End()) - { - int h = 0; - bottom[x] = brX.Current; - br.MoveTo(brX.Current); - br.Next(); - while (scan.In(br.Current) && scan.isBlack(br.Current) && h < minRad) { br.Next(); h++; } //skip line black pixels - while (scan.In(br.Current) && !scan.isBlack(br.Current) && h < maxHeight) { br.Next(); h++; } //scan white pixels - top[x] = br.Current; - height[x] = h; - brX.Next(); - x++; - } - - //second step: loop through the height array to find clusters of heights >minHeight and maxHeight - //with a correct height area. - if (x == height.Length || height[x] < minHeight) //division - { - if (x > x0) //has more than 1 pixel - { - //find segmentMinHeight discarding both extremes (minDist) - segmentMinHeight=maxHeight; - for (int i=x0+minRad; i height[i]) segmentMinHeight = height[i]; - - //join with prev boxes, if close enough - MyVector up = (MyVector)(vdY * (float)segmentMinHeight); //vector height - MyPoint[] polygon = new MyPoint[] { bottom[x0], bottom[x - 1], bottom[x - 1] + up, bottom[x0] + up }; - - for (int i=result.Count-1; i>=0; i--) - { - FoundBarcode f = (FoundBarcode)result[i]; - MyPoint q0 = f.Polygon[0]; //left bottom point - MyPoint q1 = f.Polygon[1]; //right bottom point - MyPoint q2 = f.Polygon[2]; //right top point (to calculate vdY) - if ((int)((polygon[0] - q1).Length) < minRad && //join to a left previous box - isWhite(polygon[0], q1, vdY, segmentMinHeight)) //the area in between is white - { - float prevHeight = (q2 - q1).Length; - float newHeight = (float)segmentMinHeight > prevHeight ? prevHeight : (float)segmentMinHeight; - up = (MyVector)(vdY * newHeight); //vector height - segmentMinHeight = (int)newHeight; - - //update Polygon to join previous box - polygon[0] = f.Polygon[0]; - polygon[2] = polygon[1] + up; - polygon[3] = polygon[0] + up; - //remove previous box - result.RemoveAt(i); - } - else if ((int)((polygon[1] - q0).Length) < minRad && //join to a right previous box - isWhite(polygon[1], q0, vdY, segmentMinHeight)) //the area in between is white - { - float prevHeight = (q2 - q1).Length; - float newHeight = (float)segmentMinHeight > prevHeight ? prevHeight : (float)segmentMinHeight; - up = (MyVector)(vdY * newHeight); //vector height - segmentMinHeight = (int)newHeight; - - //update Polygon to join previous box - polygon[1] = f.Polygon[1]; - polygon[2] = polygon[1] + up; - polygon[3] = polygon[0] + up; - //remove previous box - result.RemoveAt(i); - } - } - - FoundBarcode foundBarcode = new FoundBarcode(); - foundBarcode.BarcodeFormat = SymbologyType.UnderlinedField; - foundBarcode.Value = "box"; - foundBarcode.Polygon = new SKPointI[] { polygon[0], polygon[1], polygon[2], polygon[3], polygon[0] }; - foundBarcode.Color = SKColors.Blue; - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(foundBarcode.Polygon, pointTypes); - //foundBarcode.Rect = Rectangle.Round(path.GetBounds()); - foundBarcode.Rect = Utils.DrawPath(foundBarcode.Polygon); - result.Add(foundBarcode); - } - x0 = x + 1; - } - } - } - - - //scans a region to detect blackpixels, with noise tolerance - bool isWhite(MyPoint a, MyPoint b, MyVectorF vdY, int height) - { - int n = 0; //black pixels counter - Bresenham br = new Bresenham(a, b); - Bresenham up = new Bresenham(br.Current, vdY); - while (!br.End()) - { - int i = 0; - up.MoveTo(br.Current); - while (i < height) - { - if (scan.isBlack(up.Current)) n++; - up.Next(); - i++; - } - br.Next(); - } - return n < height / 2; //allow a level of black pixels - } - - public int MinHeight { get { return minHeight; } set { minHeight = value; } } - public int MaxHeight { get { return maxHeight; } set { maxHeight = value; } } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormLines/HorizontalLinesDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/FormLines/HorizontalLinesDecoder.cs deleted file mode 100644 index 25e02511..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormLines/HorizontalLinesDecoder.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections; -using System.Drawing; -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.FormLines -{ - //Class to detect horizontal lines in a document. It is based on Hough transform to detect - //subsets of aligned edges (transitions from white to black) in the image. - //Then a time consuming algorithm is executed to find continous lines in each point (cell) of the hough transform. - //Hough only detects aligned pixels, not continuous! -#if CORE_DEV - public -#else - internal -#endif - class HorizontalLinesDecoder : LinesDecoder - { - public HorizontalLinesDecoder() - { - scanHorizonal = true; scanVertical = false; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.HorizontalLine; - } - } - -#if CORE_DEV - public -#else - internal -#endif - class VerticalLinesDecoder : LinesDecoder - { - public VerticalLinesDecoder() - { - scanHorizonal = false; scanVertical = true; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.VerticalLine; - } - } - - -#if CORE_DEV - public -#else - internal -#endif - abstract class LinesDecoder : SymbologyReader2D - { - ArrayList result; - protected ImageScaner scan; - protected int width, height; //width and height of the image - protected int minRad = 5; //min long of accepted lines - protected int maxRad = 5000; //max long of accepted lines - protected int distToJoin = 20; //min dist to join segments that are aligned - protected int minLength = 50; //min dist to join pixels during segmentation - protected bool scanHorizonal=true, scanVertical=true; - - protected override FoundBarcode[] DecodeBarcode() - { - scan = new ImageScaner(BWImage); - LineSlicer slicer = new LineSlicer(BWImage); - ArrayList[] angles = slicer.GetLineEdges(minRad, maxRad, distToJoin, minLength, scanHorizonal, scanVertical); - result = new ArrayList(); - foreach(ArrayList angle in angles) - foreach (LineEdge edge in angle) - { - FoundBarcode b = new FoundBarcode(); - b.BarcodeFormat = scanHorizonal ? SymbologyType.HorizontalLine : SymbologyType.VerticalLine; - b.Polygon = new SKPointI[] { edge.a, edge.b, edge.a}; - b.Color = SKColors.Orange; - //byte[] pointTypes = new byte[3] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(b.Polygon, pointTypes); - //b.Rect = Rectangle.Round(path.GetBounds()); - b.Rect = Utils.DrawPath(b.Polygon); - b.Rect = new SKRect(b.Rect.Left, b.Rect.Top, - b.Rect.Width == 0 ? b.Rect.Left+1 : b.Rect.Right, - b.Rect.Height == 0 ? b.Rect.Top+1 : b.Rect.Bottom); - b.Value = "line"; - result.Add(b); - } - - return (FoundBarcode[])result.ToArray(typeof(FoundBarcode)); - } - - - - public int MinRad { get { return minRad; } set { minRad = value; } } - public int MaxRad { get { return maxRad; } set { maxRad = value; } } - public int DistToJoin { get { return distToJoin; } set { distToJoin = value; } } - public int MinLength { get { return minLength; } set { minLength = value; } } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMR.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMR.cs deleted file mode 100644 index 985b2acb..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMR.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Drawing; -using BarcodeReader.Core.Common; -using SkiaSharp; - -namespace BarcodeReader.Core.FormOMR -{ - //Base class used to find shapes (circles, ovals and squares) in an image. - //Uses an slicer to find segments of the image (segments are connected black pixels). For each segment, - //the algorithm finds the outline and subclasses will check if this outline has a concrete shape. -#if CORE_DEV - public -#else - internal -#endif - abstract partial class FormOMR : SymbologyReader2D - { - protected ImageScaner scan; - Slicer slicer; - ArrayList result; - - protected int minRad = 5; - protected int maxRad = 300; - protected int minDist = 2; //default: connect 1 pixel separate segments - protected float minRatio = 0.5f; //min shortSide/largeSide - protected float maxRatio = float.MaxValue; //max shortSide/largeSide - - protected int whiteZone = 5; //default no white zone - protected float PI = (float)Math.PI; - - /// - /// Enables extended mode. - /// - /// This mode finds template in recognized segements. - /// And then finds unrecognized segments that are like the template. - /// This method can find partially filled, strike outed sqaures and circles. - /// - /// This mode is enough long! - /// - public bool ExtendedMode = false; - - protected override FoundBarcode[] DecodeBarcode() - { - scan = new ImageScaner(BWImage); - slicer = new Slicer(minDist, minRad, maxRad, 45000, minRatio, maxRatio, true); - result = new ArrayList(); - recognizedSegments.Clear(); - unrecognizedSegments.Clear(); -#if DEBUG_IMAGE - //bwImage.GetAsBitmap().Save(@"out.png"); -#endif - //Get an array of all segments meeting minDist, minRad, maxRad restrictions - ArrayList segments = slicer.GetParts(BWImage); - - ScanParts(segments); - - if (ExtendedMode) - ExtendedSearch(); - - return (FoundBarcode[])result.ToArray(typeof(FoundBarcode)); - } - - private List recognizedSegments = new List(); - private List unrecognizedSegments = new List(); - - //debug method to show all segments, and matching symbol. - protected const int N=24; - - void ScanParts(ArrayList parts) - { - foreach (Segment p in parts) - if (p != null) - { - if (CheckSegment(p) && checkWhiteZone(p)) - { - recognizedSegments.Add(p); - AddFoundBarcode(p); - }else - { - unrecognizedSegments.Add(p); - } - } - } - - private void AddFoundBarcode(Segment p) - { - FoundBarcode foundBarcode = new FoundBarcode(); - - if (this is RawSlicer) - foundBarcode.BarcodeFormat = SymbologyType.Segment; - else if (this is FormOMRCircle) - foundBarcode.BarcodeFormat = SymbologyType.Circle; - else if (this is FormOMROval) - foundBarcode.BarcodeFormat = SymbologyType.Oval; - else if (this is FormOMRSquare) - foundBarcode.BarcodeFormat = SymbologyType.Checkbox; - - foundBarcode.Color = SKColors.Orange; - foundBarcode.Polygon = p.GetBBox(); - foundBarcode.Rect = p.GetRectangle(); - foundBarcode.Value = IsFilled(p) ? "1" : "0"; - result.Add(foundBarcode); - } - - - //finds a discretized outline of the segment and calls derived classes to check the outline. - //The outline is defined by N points, corresponding to N arcs of 2PI/N radians. - bool CheckSegment(Segment p) - { - MyPointF center=p.CenterF; - float w = (float)p.Width / 2f; - float h = (float)p.Height / 2f; - - //find outline of the segment - float[] outline = new float[N]; - MyPoint[] pOut = new MyPoint[N]; - for (int i=0;i outline[discreteAngle]) { outline[discreteAngle] = d; pOut[discreteAngle] = o; } - } - -#if DEBUG - foreach (var o in p.Points) - DebugHelper.DrawSquare(SKColors.Blue, o); - - DebugHelper.DrawSquare(SKColors.Red, p.Center); -#endif - - return CheckOutline(p, outline); - } - - abstract protected bool CheckOutline(Segment p, float[] outline); - - //checks if the segment is filled or not. This algorithm is an approximation that seems to work. - //It only scans the circle interior to the segment. - virtual protected bool IsFilled(Segment p) - { - MyPointF center = p.CenterF; - float w = (float)p.Width / 2f; - float h = (float)p.Height / 2f; - int R = (int)Math.Truncate((w < h ? w : h)*0.6f); - - int n = 0, black=0; - for (int y = -R; y <= R; y++) - { - int x = (int)Math.Truncate(Math.Sqrt(R * R - y * y)); - Bresenham br = new Bresenham(new MyPointF(center.X - x, center.Y + y), new MyPointF(center.X + x, center.Y + y)); - while (!br.End()) - { - if (scan.isBlack(br.Current)) black++; - n++; - br.Next(); - } - } - return (float)black / (float)n > 0.1f; - } - - //check a white zone around the segment, actually only to the left and right of the segment. - bool checkWhiteZone(Segment p) - { - if (whiteZone == -1) return true; - bool white = true; - Bresenham br = new Bresenham(p.LU, p.LD); - MyPoint q=p.LU; q.X--; - for (int i = 0; i < whiteZone && white; i++) - { - br.MoveTo(q); - while (white && !br.End()) if (scan.isBlack(br.Current)) white = false; else br.Next(); - q.X--; - } - br = new Bresenham(p.RU, p.RD); - q = p.RU; q.X++; - for (int i = 0; i < whiteZone && white; i++) - { - br.MoveTo(q); - while (white && !br.End()) if (scan.isBlack(br.Current)) white = false; else br.Next(); - q.X++; - } - return white; - } - - public int MinRad { get { return minRad; } set { minRad = value; } } - public int MaxRad { get { return maxRad; } set { maxRad = value; } } - public int MinDist { get { return minDist; } set { minDist = value; } } - public int WhiteZone { get { return whiteZone; } set { whiteZone = value; } } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMRCircle.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMRCircle.cs deleted file mode 100644 index bfe820f5..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMRCircle.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Drawing; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.FormOMR -{ -#if CORE_DEV - public -#else - internal -#endif - class FormOMRCircle : FormOMR - { - public FormOMRCircle() - { - this.minRatio = 0.9f; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Circle; - } - - protected override bool CheckOutline(Segment p, float[] outline) - { - MyPointF center=p.CenterF; - float w = (float)p.Width / 2f; - - //check min max - int nFails = 0; - for (int i = 0; i < N; i++) - if (!Calc.Around(outline[i] / w,1f,0.1f)) - if (++nFails > 2) return false; - - return true; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMROval.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMROval.cs deleted file mode 100644 index 7644fa60..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMROval.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.FormOMR -{ -#if CORE_DEV - public -#else - internal -#endif - class FormOMROval : FormOMR - { - public FormOMROval() - { - this.minRatio = 0.3f; - } - - const float maxMargin = 0.1f; - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Oval; - } - - protected override bool CheckOutline(Segment p, float[] outline) - { - MyPointF center=p.CenterF; - float w = (float)p.Width / 2f; - float h = (float)p.Height / 2f; - - //reject circles - if (Calc.Around(w / h, 1f, 0.15f)) return false; - - //check min max - int nFails = 0; - for (int i = 0; i < N; i++) - if (outline[i]!=0f && outline[i] / w < 0.8f && outline[i] / h < 0.8f) - if (++nFails > 2) return false; - - //check if outline is an symetric - float e = 0f; - for (int i = 0; i < N / 4; i++) - { - float o0 = outline[i]; - float o1 = outline[N / 2 - 1 - i]; - float o2 = outline[N / 2 + i]; - float o3 = outline[(N - 1 - i) % N]; - - float e1 = o0 != 0f && o3 != 0f ? o0 - o3 : 0f; - float e2 = o0 != 0f && o2 != 0f ? o0 - o2 : 0f; - float e3 = o0 != 0f && o1 != 0f ? o0 - o1 : 0f; - e += e1 * e1 + e2 * e2 + e3 * e3; - } - - if (e < 15f) - { - //check that at 0º and 180º outline is around w - //check that at 90º and 270º outline is around h - //check that at 45, 135,...are 90% shorter that max(w,h) - float max = (float)Math.Sqrt(w * w + h * h); - if ((Calc.Around(outline[0] / w, 1f, maxMargin) || Calc.Around(outline[N - 1] / w, 1f, maxMargin)) && - (Calc.Around(outline[N / 4] / h, 1f, maxMargin) || Calc.Around(outline[N / 4 - 1] / h, 1f, maxMargin)) && - (Calc.Around(outline[2 * N / 4] / w, 1f, maxMargin) || Calc.Around(outline[2 * N / 4 - 1] / w, 1f, maxMargin)) && - (Calc.Around(outline[3 * N / 4] / h, 1f, maxMargin) || Calc.Around(outline[3 * N / 4 - 1] / h, 1f, maxMargin)) && - outline[N / 8] / max <0.9f && - outline[3 * N / 8] / max <0.9f && - outline[5 * N / 8] / max <0.9f && - outline[7 * N / 8] / max <0.9f - ) - return true; - else - return false; - } - return false; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMRSquare.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMRSquare.cs deleted file mode 100644 index 47ff1bd3..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMRSquare.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.FormOMR -{ -#if CORE_DEV - public -#else - internal -#endif - class FormOMRRectangle : FormOMRSquare - { - public FormOMRRectangle() - { - // allow square and rectangle checkboxes - this.minRatio = 1.1f; - this.maxRatio = 5; - this.minDist = 1; - } - } - -#if CORE_DEV - public -#else - internal -#endif - class FormOMRSquare : FormOMR - { - public FormOMRSquare() - { - // allowing square checkboxes only - this.minRatio = 1.0f; - this.maxRatio = 1.2f; - this.minDist = 1; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Checkbox; - } - - //outline[] holds the distance from the segment outline at a given angle - //to the center of the semgnet. - //N is the size of the outline, and angles are sampled at 360º/N. - protected override bool CheckOutline(Segment p, float[] outline) - { - MyPointF center=p.CenterF; - - //check if outline is an symetric - float e = 0f; //accumulated error - for (int i = 0; i < N / 4; i++) //from 0º to 90º - { - float e1 = outline[i] - outline[(N - 1 - i) % N]; //check 0..90º with 360....270º - float e2 = outline[i] - outline[N / 2 + i]; //check 0..90º with 180..270º - float e3 = outline[i] - outline[N / 2 - 1 - i]; //check 0..90º with 180..90º - e += e1 * e1 + e2 * e2 + e3 * e3; - } - - if (e > 15f) return false; - - //if error is low enough - //check if corners are larger than mid - float minW = (float)p.Width / 2f; - float minH = (float)p.Height / 2f; - float max =(float)Math.Sqrt(minW*minW+minH*minH); - if (!(Calc.Around(outline[0] / minW, 1f, 0.1f) && //check that at 0º outline is around minW - Calc.Around(outline[N / 4] / minH, 1f, 0.1f) && //check that at 90º outline is around minH - Calc.Around(outline[2 * N / 4] / minW, 1f, 0.1f) && //check that at 180º outline is around minW - Calc.Around(outline[3 * N / 4] / minH, 1f, 0.1f) && //check that at 270º outline is around minH - //and now check that corners are larger (max is the diagonal) - Calc.Around(outline[N / 8] / max, 1f, 0.15f) && //check that at 45º outline is around max - Calc.Around(outline[3 * N / 8] / max, 1f, 0.15f) && //check that at 135º outline is around max - Calc.Around(outline[5 * N / 8] / max, 1f, 0.15f) && //check that at 225º outline is around max - Calc.Around(outline[7 * N / 8] / max, 1f, 0.15f) //check that at 315º outline is around max - )) return false; - - //check inner horizontal lines - if (!checkHInnerLines(p)) return false; - - //check inner vertical lines - if (!checkVInnerLines(p)) return false; - - return true; - } - - bool checkHInnerLines(Segment p) - { - int offset=p.Height/10; - int n = 0; - for (int y = p.LU.Y + offset; y < p.LD.Y - offset; y++) - { - int cc = 0; - for (int x = p.LU.X + offset; x < p.RU.X - offset; x++) - if (scan.isBlack(x, y)) cc++; - if (Calc.Around(cc, p.Width-2*offset, p.Width/4)) n++; - } - return n == 0; - } - - bool checkVInnerLines(Segment p) - { - int offset = p.Width / 10; - int n = 0; - for (int x = p.LU.X + offset; x < p.RU.X - offset; x++) - { - int cc = 0; - for (int y = p.LU.Y + offset; y < p.LD.Y - offset; y++) - if (scan.isBlack(x, y)) cc++; - if (Calc.Around(cc, p.Height-2*offset, p.Height/4)) n++; - } - return n == 0; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMRSquareLPattern.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMRSquareLPattern.cs deleted file mode 100644 index c9f7f73c..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMRSquareLPattern.cs +++ /dev/null @@ -1,408 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Drawing; -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.FormOMR -{ -#if CORE_DEV - public -#else - internal -#endif - abstract class FormOMRLPattern : SymbologyReader2D - { - protected LinkedList candidates; - protected ImageScaner scan; - protected int width, height; //width and height of the image - protected int minRad = 5; //min long of accepted lines - protected int maxRad = 5000; //max long of accepted lines - protected int distToJoin = 20; //min dist to join segments that are aligned - - protected virtual bool checkRatio(SKPointI[] pp) { return true; } - public bool QuiteZoneRequired = true; // quite zone requirement - - public FormOMRLPattern() - { - // setting to use .WholeImage as the thershold filter so it provides thinner lines - ThresholdFilterMethodToUse = ThresholdFilterMethod.WholeImage; - } - - const int K = 10; - protected override FoundBarcode[] DecodeBarcode() - { - scan = new ImageScaner(BWImage); - int W = scan.Width / K, H = scan.Height / K; - LineSlicer slicer = new LineSlicer(BWImage); - slicer.MaxVariance = 0.001f; - LinkedList edges = slicer.GetEdges(minRad, maxRad, true, true); - ArrayList aEdges = new ArrayList(edges); - aEdges.Sort(new EdgeLengthComparer()); - - - Grid grid = new Grid(W, H, K); - foreach (EdgeVar e in edges) grid.add(e, slicer); - - candidates = new LinkedList(); - - foreach (EdgeVar edge in aEdges) - { - follow(edge.End, edge, grid); - follow(edge.In, slicer.mirror(edge), grid); - } - - ArrayList result = new ArrayList(); - foreach (BoundingBox c in candidates) result.Add(c.FoundBarcode); - return (FoundBarcode[])result.ToArray(typeof(FoundBarcode)); - } - - void follow(MyPoint p, EdgeVar edge, Grid grid) - { - foreach (EdgeVar e in grid.get(p)) - if (edge != e && edge.Variance + e.Variance < 0.1f && checkRatio(e, edge)) - { - MyPoint vA = e.In; - MyPoint vB = e.End; - float dist = (p - vA).Length; - float dist2 = (p - vB).Length; - if (dist < dist2 && dist < distToJoin) - { - float diff = (edge.Angle - e.Angle); - float sinDiff = (float)Math.Sin(diff); - bool isCorner = Calc.Around(sinDiff, 1f, 0.1f); - if (isCorner) - { - SKPointI[] points = checkShape(edge, e); - if (points != null) - { - //detect if box is empty or checked - FoundBarcode f = new FoundBarcode(); - f.BarcodeFormat = SymbologyType.Checkbox; - f.Polygon = points; - - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(f.Polygon, pointTypes); - //f.Rect = Rectangle.Round(path.GetBounds()); - f.Rect = Utils.DrawPath(f.Polygon); - - BoundingBox bb = new BoundingBox(f); - bool done = false; - foreach (BoundingBox i in candidates) if (bb.contains(i)) { done = true; break; } - if (!done) - { - f.Value = boxEmpty(points) ? "0" : "1"; - candidates.AddLast(bb); - } - } - } - } - } - } - - protected virtual bool checkRatio(EdgeVar a, EdgeVar b) { return false; } - protected virtual SKPointI[] checkShape(EdgeVar a, EdgeVar b) { return null; } - - - //looks for a parallel segment from p to p+vdX (with length l) in the vdY direction, scaning from "from" to "to" - //returns true if found, and the vertex of the twin segment. - protected bool hasTwin(MyPoint p, float l, float from, float to, MyVectorF vdX, MyVectorF vdY, out MyPointF A, out MyPointF B) - { - A = B = MyPointF.Empty; - for (float offset = from; offset <= to; offset += 1f) - { - MyPoint a = p + vdY * offset; - MyPoint b = a + vdX * l; - Bresenham br = new Bresenham(a, b); - int white = 0; - while (!br.End()) - { - if (!scan.isBlack(br.Current)) white++; - br.Next(); - } - if (white < l / 5) - { - A = a; B = b; - return true; - } - } - return false; - } - - protected bool boxEmpty(SKPointI[] points) //MyPoint p, float l, MyVectorF vdX, MyVectorF vdY) - { - MyPoint p = (MyPoint)points[0]; - MyPoint q = (MyPoint)points[3]; - MyPoint pp = (MyPoint)points[1]; - MyVector vdX = pp - p; - int border = Convert.ToInt32(Math.Ceiling(vdX.Length / 10f)); //10% of the width of the square - int nLines = 0, nBlackLines = 0; - Bresenham brY = new Bresenham(p, q); - while (!brY.End()) - { - MyPoint b = brY.Current + vdX; - Bresenham brX = new Bresenham(brY.Current, b); - - //skip white pixels if any, and if they are just a few - int n = 0; - while (!brX.End() && !scan.isBlack(brX.Current)) { brX.Next(); n++; } - if (n < border) //consider white pixels as noise. So, now skip the border of the edge - { - while (!brX.End() && scan.isBlack(brX.Current)) brX.Next(); - } - - //now we are on the first INNER white pixel - bool isBlack = false; - int nBlack = 0, nCurrent = 0, prevBlack = 0; - while (!brX.End()) - { - if (scan.isBlack(brX.Current) != isBlack) //transition - { - if (isBlack) //black to white transition - { nBlack += nCurrent; prevBlack = nCurrent; } - nCurrent = 0; - isBlack = !isBlack; - } - else - { - nCurrent++; - } - brX.Next(); - } - if (!isBlack && nCurrent < border) nBlack -= prevBlack; - if (nBlack > 0) nBlackLines++; - nLines++; - brY.Next(); - } - return nBlackLines <= 2;//;nLines / 4; - } - - protected SKPointI[] box(MyPointF aIn, MyPointF aEnd, MyPointF AIn, MyPointF AEnd, - MyPointF bIn, MyPointF bEnd, MyPointF BIn, MyPointF BEnd) - { - RegressionLine La = new RegressionLine(aIn, aEnd); - RegressionLine LA = new RegressionLine(AIn, AEnd); - RegressionLine Lb = new RegressionLine(bIn, bEnd); - RegressionLine LB = new RegressionLine(BIn, BEnd); - - MyPointF Q1 = La.Intersection(Lb); - MyPointF Q2 = La.Intersection(LB); - MyPointF Q3 = LA.Intersection(LB); - MyPointF Q4 = LA.Intersection(Lb); - - if (QuiteZoneRequired) - { - if (checkQuietZone(new MyPointF[] { Q1, Q2, Q3, Q4 })) - return new SKPointI[] { Q1, Q2, Q3, Q4, Q1 }; - else - return null; - } - else - { - return new SKPointI[] { Q1, Q2, Q3, Q4, Q1 }; - } - } - - protected bool checkQuietZone(MyPointF[] box) - { - Bresenham[] br = new Bresenham[8]; - for (int i = 0; i < 4; i++) - { - MyPointF a = box[i]; - MyPointF b = box[(i + 1) % 4]; - br[2 * i] = new Bresenham(a, a - b); - br[2 * i + 1] = new Bresenham(b, b - a); - } - - int pass = 0; - while (pass < 10) - { - bool allWhite = true; - for (int i = 0; i < 8 && allWhite; i++) - { - if (scan.isBlack(br[i].Current)) allWhite = false; - } - if (allWhite) return true; - for (int i = 0; i < 8; i++) br[i].Next(); - pass++; - } - return false; - } - - - class Grid - { - int W, H, K; - public LinkedList edges; - ArrayList[][] cells; - public Grid(int W, int H, int K) - { - this.W = W; this.H = H; this.K = K; - cells = new ArrayList[W + 1][]; - for (int i = 0; i <= W; i++) - { - cells[i] = new ArrayList[H + 1]; - for (int j = 0; j <= H; j++) cells[i][j] = new ArrayList(); - } - edges = new LinkedList(); - } - - public void add(int x, int y, EdgeVar e) - { - if (x >= 0 && x <= W && y >= 0 && y <= H) cells[x][y].Add(e); - } - - public ArrayList get(SKPointI p) - { - return cells[p.X / K][p.Y / K]; - } - - private void _add(EdgeVar e) - { - MyPoint p = e.In; - int x = p.X / K, y = p.Y / K; - for (int i = -1; i <= 1; i++) - for (int j = -1; j <= 1; j++) - add(x + i, y + j, e); - edges.AddLast(e); - } - - public void add(EdgeVar e, LineSlicer slicer) - { - _add(e); - EdgeVar m = slicer.mirror(e); - _add(m); - } - } - - - public int MinRad { get { return minRad; } set { minRad = value; } } - public int MaxRad { get { return maxRad; } set { maxRad = value; } } - public int DistToJoin { get { return distToJoin; } set { distToJoin = value; } } - } - -#if CORE_DEV - public -#else - internal -#endif - class FormOMRSquareLPattern : FormOMRLPattern - { - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Checkbox; - } - protected override bool checkRatio(EdgeVar a, EdgeVar b) - { - return Calc.Around(a.Length, b.Length, b.Length / 7); - } - - protected override SKPointI[] checkShape(EdgeVar a, EdgeVar b) - { - float la = a.Length; - float lb = b.Length; - float l = la > lb ? la : lb; - - MyVectorF vdA = (MyPoint)a.End - (MyPoint)a.In; - MyVectorF vdB = (MyPoint)b.End - (MyPoint)b.In; - vdA = vdA.Normalized; - vdB = vdB.Normalized; - - MyPointF AIn, AEnd, BIn, BEnd; - if (hasTwin(a.In, l, l * 0.7f, l * 1.3f, vdA, vdB, out AIn, out AEnd) && - hasTwin(b.In, l, l * 0.7f, l * 1.3f, vdB, -vdA, out BIn, out BEnd)) - return box(a.In, a.End, AIn, AEnd, b.In, b.End, BIn, BEnd); - return null; - } - } - -#if CORE_DEV - public -#else - internal -#endif - class FormOMRRectangleLPattern : FormOMRLPattern - { - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Checkbox; - } - - protected override bool checkRatio(EdgeVar A, EdgeVar B) - { - if (Calc.Around((float)Math.Cos(A.Angle), 1f, 0.1f)) return checkRectangleOrientation(A, B); - else if (Calc.Around((float)Math.Cos(B.Angle), 1f, 0.1f)) return checkRectangleOrientation(B, A); - return false; - } - - protected virtual bool checkRectangleOrientation(EdgeVar A, EdgeVar B) { return false; } - - - protected override SKPointI[] checkShape(EdgeVar a, EdgeVar b) - { - float la = a.Length; - float lb = b.Length; - float l = la > lb ? la : lb; - - MyVectorF vdA = (MyPoint)a.End - (MyPoint)a.In; - MyVectorF vdB = (MyPoint)b.End - (MyPoint)b.In; - vdA = vdA.Normalized; - vdB = vdB.Normalized; - - MyPointF AIn, AEnd, BIn, BEnd; - if (hasTwin(a.In, la, lb * 0.8f, lb * 1.2f, vdA, vdB, out AIn, out AEnd) && - hasTwin(b.In, lb, la * 0.8f, la * 1.2f, vdB, -vdA, out BIn, out BEnd)) - return box(a.In, a.End, AIn, AEnd, b.In, b.End, BIn, BEnd); - return null; - } - } - -#if CORE_DEV - public -#else - internal -#endif - class FormOMRRectangleLPatternVert : FormOMRRectangleLPattern - { - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Checkbox; - } - - protected override bool checkRectangleOrientation(EdgeVar A, EdgeVar B) - { - float a = A.Length, b = B.Length; - if (a < b) - { - float r = a / b; - return r > 0.5f && r < 0.8f; - } - return false; - } - } - -#if CORE_DEV - public -#else - internal -#endif - class FormOMRRectangleLPatternHoriz : FormOMRRectangleLPattern - { - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Checkbox; - } - - protected override bool checkRectangleOrientation(EdgeVar A, EdgeVar B) - { - float a = A.Length, b = B.Length; - if (a > b) - { - float r = b / a; - return r > 0.5f && r < 0.8f; - } - return false; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMR_Extended.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMR_Extended.cs deleted file mode 100644 index b7f31b52..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/FormOMR_Extended.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using BarcodeReader.Core.Common; -using System.Collections.Generic; -using System.Drawing; -using SkiaSharp; - -namespace BarcodeReader.Core.FormOMR -{ -#if CORE_DEV - public -#else - internal -#endif - abstract partial class FormOMR - { - /// - /// This method finds template in recognized segements. - /// And then finds unrecognized segments that are like template. - /// This method can find partially filled, strike outed sqaures and circles. - /// - /// This method is enough long! - /// - private void ExtendedSearch() - { - if (recognizedSegments.Count == 0) return; - if (unrecognizedSegments.Count == 0) return; - - var locker = new Object(); - - // find template in recognized segments - // (template with minimum black points) - Segment template = null; - var minPoints = int.MaxValue; - - foreach(var seg in recognizedSegments) - if (seg.BlackPixelCounter < minPoints) - { - template = seg; - minPoints = seg.BlackPixelCounter; - } - - //build template mask - var blackMask = new Dictionary(); - var whiteMask = new Dictionary(); - foreach (var p in template.Points) - { - var pp = new MyPoint(p.X - template.Center.X, p.Y - template.Center.Y); - blackMask[pp] = 1; - blackMask[new MyPoint(pp.X + 1, pp.Y)] = 1; - blackMask[new MyPoint(pp.X - 1, pp.Y)] = 1; - blackMask[new MyPoint(pp.X, pp.Y + 1)] = 1; - blackMask[new MyPoint(pp.X, pp.Y - 1)] = 1; - } - - const float MAX_CENTER_OFFSET_X = 1 / 5f; - const float MAX_CENTER_OFFSET_Y = 1 / 4f; - const float BLACK_COEFF = 0.9f; - const float WHITE_COEFF = 0.8f; - var whiteD = (int)Math.Round(1 + template.Width / 20f); - - foreach (var p in template.Points) - { - var dx = p.X < template.Center.X ? -whiteD : whiteD; - var dy = p.Y < template.Center.Y ? -whiteD : whiteD; - var pp = new MyPoint(p.X - template.Center.X + dx, p.Y - template.Center.Y + dy); - whiteMask[pp] = 1; - } - - //find in unrecognized segments - Parallel.For(0, unrecognizedSegments.Count, (iSeg) => - { - var seg = unrecognizedSegments[iSeg]; - - //filter unrecognized segments - if (seg.BlackPixelCounter < template.BlackPixelCounter * 0.9f) goto next; - if (seg.Width < template.Width * 0.9f) goto next; - if (seg.Height < template.Height * 0.9f) goto next; - if (seg.Width > template.Width * 2.5f) goto next; - if (seg.Height > template.Height * 2.5f) goto next; - - //check different center offsets - var dx = (int) (template.Width * MAX_CENTER_OFFSET_X); - var dy = (int) (template.Height * MAX_CENTER_OFFSET_Y); - var center = seg.Center; - var bestCount = 0; - MyPoint bestOffset = new MyPoint(); - - for (var dX = -dx; dX <= dx; dX++) - for (var dY = -dy; dY <= dy; dY++) - { - //check black pixels - var blackCount = 0; - var whiteCount = template.BlackPixelCounter; - var offset = new MyPoint(dX - center.X, dY - center.Y); - foreach (var p in seg.Points) - { - var pp = new MyPoint(p.X + offset.X, p.Y + offset.Y); - if (blackMask.ContainsKey(pp)) - blackCount++; - if (whiteMask.ContainsKey(pp)) - whiteCount--; - } - - if (blackCount < template.BlackPixelCounter * BLACK_COEFF) continue; //no black pixels - if (whiteCount < template.BlackPixelCounter * WHITE_COEFF) continue; //no white pixels - - if (whiteCount > bestCount) - { - bestCount = whiteCount; - bestOffset = offset; -#if !DEBUG - goto recognized; -#endif - } - } - - recognized: - - if (bestCount == 0) goto next; - -#if DEBUG - DebugHelper.DrawSquare(SKColors.Lime, - new MyPoint(center.X - (center.X + bestOffset.X), center.Y - (center.Y + bestOffset.Y))); -#endif - lock (locker) - AddFoundBarcode(seg); - - next:; - }); - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/RawSlicer.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/RawSlicer.cs deleted file mode 100644 index 259af889..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormOMR/RawSlicer.cs +++ /dev/null @@ -1,33 +0,0 @@ -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.FormOMR -{ -#if CORE_DEV - public -#else - internal -#endif - class RawSlicer : FormOMR - { - public RawSlicer() - { - this.minRatio = 0f; //all ratios - this.whiteZone = -1; //no white zone check - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Segment; - } - - protected override bool CheckOutline(Segment p, float[] outline) - { - return true; //all segments are added to results - } - - protected override bool IsFilled(Segment p) - { - return false; //don't check if it is filled or not - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormTables/LineTablesDetector.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/FormTables/LineTablesDetector.cs deleted file mode 100644 index 1a784306..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/FormTables/LineTablesDetector.cs +++ /dev/null @@ -1,364 +0,0 @@ -using System.Collections; -using System.Drawing; -using SkiaSharp; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.FormTables -{ - //Class to detect tables with borders. - //The algorithm uses horizontal lines returned by LineSlicer. -#if CORE_DEV - public -#else - internal -#endif - class LineTablesDetector : SymbologyReader2D - { - ImageScaner scan; - ArrayList[] angles; - ArrayList result; //array to hold all found results - int minRad = 5; //min long of segments - int maxRad = 5000; //max long of segments - int distanceBetweenSegmentsToSeeIfWeShouldJoinThem = 20; //max dist to join lines - int minLengthOfSegments = 50; //min length of result lines - int scanColumnsHeight = 10; //if a vertical line has >=scanColumnsHeight, consider it as a column - - //when true check for a left vertical line connecting two horizontal segments, - //in order to consider them in the same table - bool checkVerticalLinesToJoinRowsInTables = true; - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.Table; - } - - int OFFSET = 1; - protected override FoundBarcode[] DecodeBarcode() - { - scan = new ImageScaner(BWImage); - LineSlicer slicer = new LineSlicer(BWImage); - angles = slicer.GetLineEdges(minRad, maxRad, distanceBetweenSegmentsToSeeIfWeShouldJoinThem, minLengthOfSegments, true, false); - result = new ArrayList(); - - //find parallel segments of the same length - for (int i = 0; i < angles.Length; i++) - for (int j = 0; j < angles[i].Count; j++) if (angles[i][j] != null) - { - LineEdge a = (LineEdge) angles[i][j]; - //angles[i][j] = null; //remove the current line to not to be used later - - ArrayList parallels = new ArrayList(); - parallels.Add(a); - //Look for parallel lines to a of the same length - for (int o = -OFFSET; o <= OFFSET; o++) - { - int ii = (i + o + angles.Length) % angles.Length; - for (int jj = 0; jj < angles[ii].Count; jj++) if (angles[ii][jj] != null && angles[ii][jj] != a) - { - parallels.Add(angles[ii][jj]); - } - } - - parallels.Sort(); - - - //a = (LineEdge)parallels[0]; - float maxIncX = a.length * 0.01f; - float maxIncLength = a.length * 0.1f; - if (maxIncLength > (float) distanceBetweenSegmentsToSeeIfWeShouldJoinThem) - maxIncLength = (float) distanceBetweenSegmentsToSeeIfWeShouldJoinThem * 0.97f + a.length * 0.03f; //limit to minDist pixels, to avoid joining large lines - - ArrayList segments = new ArrayList(); - ArrayList lostEdges = new ArrayList(); - //segments.Add(a); - //Look for parallel lines to a of the same length - for (int ii = 0; ii < parallels.Count; ii++) - { - LineEdge b = (LineEdge) parallels[ii]; - if (Calc.Around(a.a.X, b.a.X, maxIncLength * 0.6f) && Calc.Around(a.length, b.length, maxIncLength)) - { - segments.Add(b); - //a = b; //use b as the next reference edge --> more flexible than use only the first edge - } - else - { - lostEdges.Add(b); - } - } - //if 2 or more parallel lines of the same length are found process - //each row in order to detect vertical lines and split rows in cells - if (segments.Count > 1) - { - //alignSegments(segments); - ArrayList tables = findConnectedSegments(segments); - - foreach (ArrayList table in tables) if (table.Count > 1) - { - alignSegments(table); - ArrayList tag = new ArrayList(); - for (int k = 1; k < table.Count; k++) - { - //find short edges that falls between k-1 and k - LineEdge prev = (LineEdge) table[k - 1]; - LineEdge next = (LineEdge) table[k]; - int minY = prev.a.Y; - int maxY = next.a.Y; - int minX = prev.a.X; - int maxX = prev.b.X; - ArrayList shortEdges = new ArrayList(); - ArrayList reminderEdges = new ArrayList(); - foreach (LineEdge e in lostEdges) - if (e.a.Y >= minY && e.a.Y <= maxY) shortEdges.Add(e); - else reminderEdges.Add(e); - lostEdges = reminderEdges; - - //split the current row in cells - ArrayList cells = new ArrayList(); - ArrayList usedEdges = new ArrayList(); - SplitColumns(next, prev.b - next.b, shortEdges, cells, usedEdges); - tag.Add(cells); - - //remove edges from angles - removeEdges(angles, table); - removeEdges(angles, usedEdges); //remove only used shortEdges - } - - LineEdge first = (LineEdge) table[0]; - LineEdge last = (LineEdge) table[table.Count - 1]; - - StringBuilder cellCounts = new StringBuilder(); - foreach (ArrayList row in tag) - cellCounts.AppendFormat(" {0}", row.Count); - - FoundBarcode foundBarcode = new FoundBarcode(); - foundBarcode.Value = "table" + cellCounts; - foundBarcode.Polygon = new SKPointI[] { first.a, first.b, last.b, last.a, first.a }; - foundBarcode.Tag = tag; - foundBarcode.Color = SKColors.Orange; - - // get bounding rectangle - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //using (GraphicsPath path = new GraphicsPath(foundBarcode.Polygon, pointTypes)) - // foundBarcode.Rect = Rectangle.Round(path.GetBounds()); - foundBarcode.Rect = Utils.DrawPath(foundBarcode.Polygon); - - result.Add(foundBarcode); - } - } - } - return (FoundBarcode[]) result.ToArray(typeof(FoundBarcode)); - } - - - void removeEdges(ArrayList[] angles, ArrayList edges) - { - for (int i = 0; i < angles.Length; i++) - for (int j = 0; j < angles[i].Count; j++) if (angles[i][j] != null) - foreach (LineEdge e in edges) if (e == angles[i][j]) - angles[i][j] = null; - } - - //x-align start and end of all segments to be aligned with the first and last segments. - void alignSegments(ArrayList segments) - { - LineEdge last = (LineEdge) segments[segments.Count - 1]; - MyVectorF vdY = last.NormalizedVdY(); - for (int i = 0; i < segments.Count; i++) - { - alignSegments(last, (LineEdge) segments[i], vdY); - } - } - - //align start and end of b with a using the given normalized up vector - void alignSegments(LineEdge a, LineEdge b, MyVectorF vdY) - { - float dist = a.Dist(b.a); - b.a = a.a + vdY * dist; - b.b = a.b + vdY * dist; - } - - - //if checkVerticalLinesToJoinRowsInTables is true join segments in tables if - //they have a left vertical line connecting them. - ArrayList findConnectedSegments(ArrayList segments) - { - LineEdge top = (LineEdge) segments[0]; - LineEdge bottom = (LineEdge) segments[segments.Count - 1]; - MyVectorF vdY = top.a - bottom.a; - vdY = vdY.Normalized; - ArrayList tables = new ArrayList(); - if (checkVerticalLinesToJoinRowsInTables) - { - ArrayList currentTable = new ArrayList(); - LineEdge prev = (LineEdge) segments[0]; - currentTable.Add(prev); - for (int i = 1; i < segments.Count; i++) - { - LineEdge current = (LineEdge) segments[i]; - if (connectedSegments(prev, current, vdY)) currentTable.Add(current); - else - { - tables.Add(currentTable); - currentTable = new ArrayList(); - currentTable.Add(current); - } - prev = current; - } - tables.Add(currentTable); - } - else tables.Add(segments); - return tables; - } - - //two segments are connectes if there is a left vertical line connecting them. - bool connectedSegments(LineEdge top, LineEdge bottom, MyVectorF vdY) - { - MyVectorF vdX = (bottom.b - bottom.a); vdX = vdX.Normalized; - Bresenham br = new Bresenham(bottom.a, vdY); - float offset = bottom.length * 0.02f; if (offset < 5f) offset = 5f; //search at least around -5..+5 pixels - Bresenham brX = new Bresenham(bottom.a - vdX * offset, bottom.a + vdX * offset); - - while (!brX.End()) - { - br.MoveTo(brX.Current); - if (hasColumn(br, (int) top.Dist(brX.Current))) return true; - brX.Next(); - } - return false; - } - - //split a row in columns - void SplitColumns(LineEdge bottom, MyVector up, ArrayList shortEdges, ArrayList cells, ArrayList usedEdges) - { - int height = (int) up.Length; - Bresenham br = new Bresenham(bottom.a, up); - Bresenham brX = new Bresenham(bottom.a, bottom.b); - MyPoint start = bottom.a; - - int x = 0; - //scans the line top from left to right. For each pixel, traces a vertical line - //and counts black pixels. If they are >scanColumnsHeight, this is considered as a - //cell separator. - while (!brX.End()) - { - br.MoveTo(brX.Current); - if (x > 10 && hasColumn(br, height)) - { - if (ProcessColumn(start, brX.Current, up, shortEdges, cells, usedEdges)) - { - x = 0; - start = brX.Current; - } - } - brX.Next(); - x++; - } - if (x > 10) - { - ProcessColumn(start, brX.Current, up, shortEdges, cells, usedEdges); - } - } - - - - bool ProcessColumn(MyPoint start, MyPoint end, MyVector up, ArrayList shortEdges, ArrayList cells, ArrayList usedEdges) - { - float xDist = (end - start).Length; - float ratio = up.Length / xDist; - if (ratio > 3f && xDist < 20) return false; - - int xIn = start.X; - int xEnd = end.X; - - //find short edges crossing the vertical line (column) - ArrayList innerEdges = new ArrayList(); - ArrayList rowEdges = new ArrayList(); - bool clean = true; - for (int i = 0; i < shortEdges.Count; i++) if (shortEdges[i] != null) - { - LineEdge e = (LineEdge) shortEdges[i]; - if (e.b.X - 10 < xIn || e.a.X + 10 > xEnd) - { - /*edges at the left or right of the cell --> discard these edges */ - } - else - { - if (e.a.X + 10 < xEnd && xEnd < e.b.X - 10) clean = false; - if (Calc.Around(xEnd, e.b.X, 10)) rowEdges.Add(e); - else innerEdges.Add(e); - } - } - - if (clean) - { - if (rowEdges.Count == 0) cells.Add(new SKPointI[] { start, start + up, end + up, end, start }); - else SplitRows(new LineEdge(start, end, 0f), up, rowEdges, innerEdges, cells, usedEdges); - return true; - } - return false; - } - - - - //split a cell in rows - void SplitRows(LineEdge bottom, MyVector up, ArrayList rowEdges, ArrayList innerEdges, ArrayList cells, ArrayList usedEdges) - { - LineEdge prev = bottom; - MyVectorF vdY = bottom.NormalizedVdY(); - for (int i = rowEdges.Count - 1; i >= 0; i--) - { - LineEdge e = (LineEdge) rowEdges[i]; - alignSegments(prev, e, vdY); - usedEdges.Add(e); - SplitColumns(prev, e.b - prev.b, innerEdges, cells, usedEdges); - prev = e; - } - SplitColumns(prev, up - (prev.b - bottom.b), innerEdges, cells, usedEdges); - } - - //Scans br to check if it follows a black line - bool hasColumn(Bresenham br, int height) - { - int h = 0; - br.Next(); - while (scan.In(br.Current) && scan.isBlack(br.Current) && h < scanColumnsHeight) - { - br.Next(); - h++; - } - if (h >= scanColumnsHeight) - { - int offset = 0; - int offsetLimit = height / 10; // 10% - MyVectorF left = br.Vd.Perpendicular.Normalized; - MyVectorF right = new MyVectorF(-left.X, -left.Y); - while (scan.In(br.Current) && h < height && offset >= -offsetLimit && offset <= offsetLimit) - { - if (scan.isBlack(br.Current)) h++; - else if (scan.isBlack(br.CurrentF + left)) - { - h++; - br.MoveTo(br.CurrentF + left); - offset--; - } - else if (scan.isBlack(br.CurrentF + right)) - { - h++; - br.MoveTo(br.CurrentF + right); - offset++; - } - br.Next(); - } - return h > height * 9 / 10; - } - return false; - } - - public int MinRad { get { return minRad; } set { minRad = value; } } - public int MaxRad { get { return maxRad; } set { maxRad = value; } } - public int DistanceBetweenSegmentsToSeeIfWeShouldJoinThem { get { return distanceBetweenSegmentsToSeeIfWeShouldJoinThem; } set { distanceBetweenSegmentsToSeeIfWeShouldJoinThem = value; } } - public int MinLengthOfSegments { get { return minLengthOfSegments; } set { minLengthOfSegments = value; } } - public bool CheckVerticalLinesToJoinRowsInTables { get { return checkVerticalLinesToJoinRowsInTables; } set { checkVerticalLinesToJoinRowsInTables = value; } } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/Combination.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/Combination.cs deleted file mode 100644 index 5b850710..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/Combination.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections; - -namespace BarcodeReader.Core.GS1DataBar -{ - internal class Combination - { - ArrayList[] values; - int[] indexs, current; - bool end; - - public Combination(ArrayList[] values) - { - this.values = values; - this.indexs = new int[values.Length]; - this.current = new int[values.Length]; - for (int i = 0; i < values.Length; i++) - indexs[i] = 0; - end = false; - } - - public int[] Current() - { - for (int i = 0; i < values.Length; i++) - current[i] = (int)values[i][indexs[i]]; - return current; - } - - //next combination - public void Next() - { - bool done = false; - int i = values.Length - 1; - while (i >= 0 && !done) - { - indexs[i]++; - if (indexs[i] < values[i].Count) done = true; - else indexs[i--] = 0; - } - end = !done; - } - - public bool End() - { - return end; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/Decoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/Decoder.cs deleted file mode 100644 index 52ec7cb0..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/Decoder.cs +++ /dev/null @@ -1,348 +0,0 @@ -using System; -using System.Collections; - -namespace BarcodeReader.Core.GS1DataBar -{ -#if CORE_DEV - public -#else - internal -#endif - class BarcodeChar : IComparable, ICloneable - { - public int[] widths; - public int[] iE; - public int value; - public float error; - public BarcodeChar(int[] widths, int[] iE, int value, float error) - { - this.widths = widths; - this.iE = iE; - this.value = value; - this.error = error; - } - public int CompareTo(object o) - { - BarcodeChar b = (BarcodeChar)o; - return (int)((this.error - b.error) * 100F); - } - public object Clone() - { - return new BarcodeChar((int[])widths.Clone(), (int[])iE.Clone(), value, error); - } - } - -#if CORE_DEV - public -#else - internal -#endif - enum MinBars {Even, Odd, Last, None}; - -#if CORE_DEV - public -#else - internal -#endif - abstract class Decoder - { - Hashtable cache = new Hashtable(); - - //given an array of widths, returns the associated value. - //minBars sets if the odd or even subset requires at least 1 width of 1 module. - //dir especifies the direction of the char. Only used in expanded finders, to allow to calculate its value. - abstract public int DecodeChar(int[] w, int N, MinBars minBars); - - int iDecodeChar(int[] w, int N, MinBars minBars) - { - for (int i = 0; i < w.Length; i++) if (w[i] < 1) return -1; - return DecodeChar(w, N, minBars); - } - - //receives raw bar widths, that correspond to N modules - //and returns an array with possible decoded chars, using minBars - //restriction (different for each databar type: omni, limited or expanded). - public ArrayList getBarcodeChars(int[] w, int N, MinBars restriction, bool recovery) - { - ArrayList chars = new ArrayList(); - //bool isEven = w.Length % 2 == 0; - int[] iE; - float[] fE; - string sW; - if (getEFromW(w, N, out iE, out fE, out sW)) - { - int barSum; - int[] nw; - int ch; - - if (cache.ContainsKey(sW)) return (ArrayList)cache[sW]; - else if (getWFromE(iE, N, restriction, out barSum, out nw) && - (ch = iDecodeChar(nw, N, restriction)) != -1) - { - //ISO fast solution - chars.Add(new BarcodeChar(nw, iE, ch, 0.0F)); - //cache[sE] = chars; - } - else if (recovery) - { - //chars = recovery3powDigits(iE, fE, N, restriction); - chars = recoveryFastProbabilistic(iE, fE, N, restriction); - } - } - cache[sW] = chars; - return chars; - } - - ArrayList recovery3powDigits(int[] iE, float[] fE, int N, MinBars restriction) - { - ArrayList chars = new ArrayList(); - ArrayList[] values = new ArrayList[iE.Length]; - for (int i = 0; i < iE.Length; i++) - { - ArrayList a = values[i] = new ArrayList(3); - int e = (int)Math.Truncate(fE[i]); - a.Add(e - 1); - a.Add(e); - a.Add(e + 1); - } - if (restriction == MinBars.Last) values[iE.Length - 1] = new ArrayList(new int[] { 2 }); - - float maxError = 0.25F * iE.Length; //empirical error limit - int barSum, ch; - int[] nw, jE; - Combination comb = new Combination(values); - while (!comb.End()) - { - jE = comb.Current(); - float error = 0.0F; - for (int i = 0; i < jE.Length; i++) - { - float e = fE[i] - jE[i]; - error += e * e; - } - if (error < maxError && getWFromE(jE, N, restriction, out barSum, out nw) && - (ch = iDecodeChar(nw, N, restriction)) != -1) - { - chars.Add(new BarcodeChar(nw, (int[])jE.Clone(), ch, error)); - } - comb.Next(); - } - chars.Sort(); - return chars; - } - - - ArrayList recoveryFastProbabilistic(int[] iE, float[] fE, int N, MinBars restriction) - { - ArrayList chars = new ArrayList(); - ArrayList[] values = new ArrayList[iE.Length]; - for (int i = 0; i < iE.Length; i++) - { - ArrayList a = values[i] = new ArrayList(3); - float fe = fE[i]; - int ie=(int)Math.Truncate(fe); - a.Add(new ProbabilisticCombination.Candidate(ie, Math.Abs(fe-ie))); - a.Add(new ProbabilisticCombination.Candidate(ie+1, Math.Abs(fe-ie-1))); - a.Add(new ProbabilisticCombination.Candidate(ie-1, Math.Abs(fe-ie+1))); - a.Sort(); - } - if (restriction == MinBars.Last) - { - values[iE.Length - 1] = new ArrayList(); - values[iE.Length - 1].Add(new ProbabilisticCombination.Candidate(2, 0.0F)); - } - - float error=0F; //error of the current combination - int[] jE = new int[values.Length]; //current combination - ProbabilisticCombination comb = new ProbabilisticCombination(values); - int count = 0; - int MAX = iE.Length * 10; - float MAX_ERROR = iE.Length * GS1DataBar.MaxBarError; - while (!comb.End() && count++ 1.0F) { fe=1.5F; ie = 2;} //slight recovery - else if (fe > 12.5F && fe <= 13.0F) {fe=12.5F; ie = 12;} //slight recovery - else if (ie < 2 || ie > 12) return false; - fE[i] = fe; - iE[i] = ie; - //sE += ie+"."; - sW += w[i] + "."; - } - sW += w[w.Length - 1]; - return true; - } - - /* derive element widths from normalized edge-to-similar-edge measurements */ - protected bool getWFromE(int[] E, int N, MinBars restriction, out int barSum, out int[] widths) - { - widths = new int[E.Length + 1]; - if (restriction == MinBars.Last) //only for finders ending in xxx...xx11 - { - widths[E.Length] = widths[E.Length - 1] = 1; - barSum = 2; - for (int i = E.Length - 2; i >= 0; i--) - { - widths[i] = E[i] - widths[i + 1]; - barSum += widths[i]; - } - return (barSum == N); - } - else //chars with even widths - { - barSum = 0; - bool found=false; - for (int first = 1; !found && first < E[0]; first++) - { - barSum = widths[0] = first; - int minEven = 10, minOdd = 10; /* start with a too big minimum */ - if (widths[0] < minOdd) minOdd = widths[0]; - for (int i = 1; i < E.Length + 1; i++) - { - widths[i] = E[i - 1] - widths[i - 1]; - barSum += widths[i]; - if (i % 2 == 1 && widths[i] < minEven) minEven = widths[i]; - if (i % 2 == 0 && widths[i] < minOdd) minOdd = widths[i]; - } - if (barSum != N) return false; //there is no solution because E has an error - else if (minEven >= 1 && minOdd >= 1) - { - if (restriction == MinBars.Even && minEven == 1 || - restriction == MinBars.Odd && minOdd == 1 || - restriction == MinBars.None) - return true; - } - } - return false; - } - } - - //returns odd widths of the w array - protected int[] getOdd(int[] w, out int sum) - { - int[] odd = new int[w.Length / 2]; - sum = 0; - for (int i = 0; i < w.Length; i += 2) sum += odd[i / 2] = w[i]; - return odd; - } - - //returns even widths of the w array - protected int[] getEven(int[] w, out int sum) - { - int[] even = new int[w.Length / 2]; - sum = 0; - for (int i = 1; i < w.Length; i += 2) sum += even[i / 2] = w[i]; - return even; - } - - //================= From ISO ================================================= - /* maxWidth = maximum module width of an element*/ - /* minOneModule = true will skip patterns without a one module wide element*/ - protected int getValue(int[] widths, int elements, int maxWidth, bool minOneModule) - { - int i, n; - int val = 0; - int elmWidth; - int narrowMask = 0; - for (n = 0, i = 0; i < elements; i++) n += widths[i]; - - for (int bar = 0; bar < elements - 1; bar++) - { - for (elmWidth = 1, narrowMask |= (1 << bar); elmWidth < widths[bar]; - elmWidth++, narrowMask &= ~(1 << bar)) - { - /* get all nk combinations */ - int subVal = combins(n - elmWidth - 1, elements - bar - 2); - /* less combinations with no narrow */ - if ((minOneModule) && (narrowMask == 0) && (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) - subVal -= combins(n - elmWidth - (elements - bar), elements - bar - 2); - - /* less combinations with elements > maxVal */ - if (elements - bar - 1 > 1) - { - int lessVal = 0; - for (int mxwElement = n - elmWidth - (elements - bar - 2); mxwElement > maxWidth; mxwElement--) - { - lessVal += combins(n - elmWidth - mxwElement - 1, elements - bar - 3); - } - subVal -= lessVal * (elements - 1 - bar); - } - else if (n - elmWidth > maxWidth) - { - subVal--; - } - val += subVal; - } - n -= elmWidth; - } - return val; - } - - // combins(n,r): returns the number of Combinations of r selected from n: - // Combinations = n! / ((n – r)! * r!) - int combins(int n, int r) - { - int minDenom, maxDenom; - if (n - r > r) - { - minDenom = r; - maxDenom = n - r; - } - else - { - minDenom = n - r; - maxDenom = r; - } - int val = 1; - int j = 1; - for (int i = n; i > maxDenom; i--) - { - val *= i; - if (j <= minDenom) - { - val /= j; - j++; - } - } - for (; j <= minDenom; j++) - { - val /= j; - } - return val; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/Encodation.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/Encodation.cs deleted file mode 100644 index 98b0da48..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/Encodation.cs +++ /dev/null @@ -1,303 +0,0 @@ -using System; - -namespace BarcodeReader.Core.GS1DataBar -{ - internal class Encodation - { - protected string binaryString; - protected bool link2D; - protected string GTIN; - protected string code; - - public Encodation(string s) - { - binaryString = s; - link2D = s.Substring(0, 1) == "1"; //link 2D flag: we don't use it - } - - protected string Decode(int pos, int[] bitW, int[] digitW) - { - string code = ""; - for (int i = 0; i < bitW.Length; i++ ) - { - int w = bitW[i]; - int d=Decode(pos,w); - string dd = Convert.ToString(d).PadLeft(digitW[i], '0'); - code += dd; - pos += w; - } - return code; - } - - class EndException : Exception { } - protected int Decode(int pos, int w) - { - if (pos + w > binaryString.Length) throw new EndException(); - string bin = binaryString.Substring(pos,w); - return Convert.ToInt32(bin, 2); - } - - protected string ZeroPad(int value, int length) - { - string s=Convert.ToString(value); - return s.PadLeft(length,'0'); - } - - //calculates checksum from a 13 digit gtin string - static public int GTIN14CheckSum(string gtin) - { - int sum = 0; - for (int i = 0; i < 13; i++) sum += Convert.ToInt32(gtin.Substring(i, 1)) * (i % 2 == 0 ? 3 : 1); - sum = sum % 10; - if (sum != 0) sum = 10 - sum; - return sum; - } - - private string GetDigit(int Dx) - { - if (Dx == 10) return "FNC1"; - return Convert.ToString(Dx); - } - - private bool Latch(ref int pos, string pattern) - { - if (binaryString.Length >= pos + pattern.Length && - binaryString.Substring(pos, pattern.Length) == pattern) - { - pos += pattern.Length; - return true; - } - return false; - } - - enum EncodationSchema { NUMERIC, ALPHANUMERIC, ISO646 }; - const string ALPHA_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ*,-./"; - const string ISO_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - const string ISO_SYMBOLS = "!\"%&'()*+,-./:;<=>?_ "; - protected string GeneralPourpose(int pos) - { - bool end = false; - string code = ""; - EncodationSchema sch = EncodationSchema.NUMERIC; //default - while (pos < binaryString.Length && !end) - { - switch (sch) - { - case EncodationSchema.NUMERIC: - if (Latch(ref pos, "0000")) - sch = EncodationSchema.ALPHANUMERIC; - else - { - if (binaryString.Length < pos+4) end = true; - else if (binaryString.Length < pos + 7) - { - int D1 = Decode(pos, 4); - if (D1 >= 1) code += GetDigit(D1 - 1); - end = true; - } - else - { - int n = Decode(pos, 7) - 8; - int D1 = n / 11; //0..9 digits - int D2 = n % 11; //10 FNC1 - code += GetDigit(D1) + GetDigit(D2); - pos += 7; - } - } - break; - case EncodationSchema.ALPHANUMERIC: - if (binaryString.Substring(pos, 1) == "0") - { - if (Latch(ref pos, "000")) sch = EncodationSchema.NUMERIC; - else if (Latch(ref pos, "00100")) sch = EncodationSchema.ISO646; - else if (binaryString.Length < pos + 5) end = true; - else - { //Numbers 5 bits 0xxxx x=5..14(0..9) and 15(FNC1) - int n = Decode(pos + 1, 4); - if (n >= 5 && n <= 14) code += ZeroPad(n - 5, 1); - else if (n == 15) code += "FNC1"; - else code += "?"; - pos += 5; - } - } - else - { //Letters + Symbols 6 bits 1xxxxx x=0..31 - if (binaryString.Length < pos + 6) end = true; - else - { - try - { - int n = Decode(pos + 1, 5); - if (n >= 0 && n <= 31) - code += ALPHA_CHARS.Substring(n, 1); - else code += "?"; - pos += 6; - } - catch - { - throw new EndException(); - } - } - } - break; - case EncodationSchema.ISO646: - if (binaryString.Substring(pos, 1) == "0") - { - if (Latch(ref pos, "000")) sch = EncodationSchema.NUMERIC; - else if (Latch(ref pos, "00100")) sch = EncodationSchema.ALPHANUMERIC; - else if (binaryString.Length < pos + 5) end = true; - else - { //Numbers 5 bits 0xxxx x=5..14(0..9) and 15(FNC1) - int n = Decode(pos + 1, 4); - if (n >= 5 && n <= 14) code += ZeroPad(n - 5, 1); - else if (n == 15) code += "FNC1"; - else code += "?"; - pos += 5; - } - } - else - { - if (binaryString.Length < pos + 7) end = true; - else - { //Letters Uppercase/Lowecase 7 bits 1xxxxxx x=0..51 - int n = Decode(pos+1, 6); - if (n >= 0 && n <= 51) - { - code += ISO_CHARS.Substring(n, 1); - pos += 7; - } - else if (binaryString.Length < pos + 8) end = true; - else - { //Symbol 8 bits 111xxxxx x=8..28; - n = Decode(pos + 3, 5); - if (n >= 8 && n <= 28) code += ISO_SYMBOLS.Substring(n - 8, 1); - else code += "?"; - pos += 8; - } - } - } - break; - } - } - return code; - } - - public string Code { get { return code; } } - - static public Encodation Factory(string s) - { - //get encodation type - string encodationType = null; - string[] encodationTypes = { "1", "00", "010", "0110", "0111" }; - foreach (string et in encodationTypes) - if (s.Substring(1, et.Length) == et) { encodationType = et; break; } - try - { - switch (encodationType) - { - case "1": return new Encodation1(s); - case "00": return new Encodation00(s); - case "010": return new Encodation010(s); - case "0110": return new Encodation0110(s); - case "0111": return new Encodation0111(s); - } - } - catch (EndException) { } //if the end has arrived prematurely - return null; - } - } - - //(01)GTIN14 - internal class Encodation1 : Encodation - { - public Encodation1(string s):base(s) - { - GTIN=Decode(4,new int[]{ 4, 10, 10, 10, 10 }, new int[]{1,3,3,3,3}); - string general = GeneralPourpose(48); //44 - code = "(01)" + GTIN + GTIN14CheckSum(GTIN) + general; - } - } - - //[general pourpose data] - internal class Encodation00 : Encodation - { - public Encodation00(string s):base(s) - { - code= GeneralPourpose(5); - } - } - - //(01)GTIN14 Weight in Kg or pounds 010x - internal class Encodation010 : Encodation - { - public Encodation010(string s): base(s) - { - GTIN = "9" + this.Decode(5, new int[] { 10, 10, 10, 10 }, new int[] { 3, 3, 3, 3 }); - code = "(01)" + GTIN + GTIN14CheckSum(GTIN); - - bool isKg = s.Substring(4, 1) == "0"; - int weight = Decode(45, 15); - if (isKg) code += "(3103)" + ZeroPad(weight,6); - else - { - if (weight < 10000) code += "(3202)" + ZeroPad(weight,6); - else code += "(3203)" + ZeroPad(weight-10000,6); - } - } - } - - //(01)GTIN14 + Price + [Currency] + [general pourpose data] 0110x - internal class Encodation0110 : Encodation - { - public Encodation0110(string s): base(s) - { - GTIN = "9" + this.Decode(8, new int[] { 10, 10, 10, 10 }, new int[] { 3, 3, 3, 3 }); - code = "(01)" + GTIN + GTIN14CheckSum(GTIN); - - //has currency? - bool hasCurrency = binaryString.Substring(5, 1) == "1"; - int generalPourposePos = (hasCurrency ? 60 : 50); - - //decimals - int decimals = Decode(48, 2); - - if (!hasCurrency) code += "(392" + ZeroPad(decimals, 1) + ")"; - else code += "(393" + ZeroPad(decimals, 1) + ")" + ZeroPad(Decode(50, 10), 3); - - //general pourpose - code += GeneralPourpose(generalPourposePos); - } - } - - //(01)GTIN14 + Weight + Date 0111xxx - internal class Encodation0111 : Encodation - { - public Encodation0111(string s):base(s) - { - GTIN = "9" + this.Decode(8, new int[] { 10, 10, 10, 10 }, new int[] { 3, 3, 3, 3 }); - code = "(01)" + GTIN + GTIN14CheckSum(GTIN); - - //weight - string weightAI = (binaryString.Substring(7, 1) == "0" ? "310" : "320"); - string weight = ZeroPad(Decode(48, 20), 6); - code += "("+weightAI + weight.Substring(0, 1) + ")0" + weight.Substring(1); - - //date - string AI = "??", ai = binaryString.Substring(5, 2); - switch (ai) - { - case "00": AI = "(11)"; break; - case "01": AI = "(13)"; break; - case "10": AI = "(15)"; break; - case "11": AI = "(17)"; break; - } - int date = Decode(68, 16); - int yy = date / 384; - date = date % 384; - int mm = date / 32 + 1; - int dd = date % 32; - code += AI + ZeroPad(yy, 2) + ZeroPad(mm, 2) + ZeroPad(dd, 2); - } - } - -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBar.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBar.cs deleted file mode 100644 index ed8fd96a..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBar.cs +++ /dev/null @@ -1,274 +0,0 @@ -using System.Collections; - -namespace BarcodeReader.Core.GS1DataBar -{ -#if CORE_DEV - public -#else - internal -#endif - enum Direction { LeftToRight, RightToLeft }; - -#if CORE_DEV - public -#else - internal -#endif - abstract class GS1DataBar : SymbologyReader - { - protected bool reverse; //used in derived classes to add a 2nd reversed pass in the decoder. - protected bool reversed; //used in derived classes to know if the received row is reversed or not - protected Direction finderDirection; - protected bool finderStartsWithBlack; - protected int finderElements; - protected Decoder charDecoder, finderDecoder; - public const float GS1_DATABAR_ALLOWED_CONFIDENCE = 0.9f; - - protected bool xRestriction; - protected int lastStartX, lastEndX; //when part=1, interval where part 0 was found. - protected int lastRowNumber; // row number where the barcode begins - - protected int charElements; - protected bool recovery; - protected static readonly float maxError = 100.0F; - protected static float maxBarError = 0.4F; - - protected abstract ArrayList IsFinder(int[] w, Direction dir); - protected abstract FoundBarcode Decode(int rowIndex, XBitArray row, int offsetIn, int offsetOut, ArrayList leftFinder); - - public GS1DataBar(IBarcodeConsumer consumer) : base(consumer) - { - //recovery = false; - //maxError = 1e10F; - - recovery = true; - } - - //Called for each row in the image, from 1..Height - //Looks for a left finder in the row. When found, try to decode chars around it. - // Checks checksum, and if success, returns the code. - //left finders start with a white bar, and are left to right order. - //right finders start with a black bar, and are right to left order. - public override FoundBarcode[] DecodeRow(int rowNumber, XBitArray xx) - { - FoundBarcode[] forwards, backwards=null; - - initDecodeRow(rowNumber); - XBitArray row=xx; - reversed = false; - forwards = iDecodeRow(rowNumber, row); - if (reverse) - { - row=xx.Reverse(); //needed for expanded stacked in even rows - reversed = true; - backwards = iDecodeRow(rowNumber, row); - } - if (forwards==null) return backwards; - if (backwards==null) return forwards; - FoundBarcode[] merge=new FoundBarcode[forwards.Length+backwards.Length]; - int count=0; - foreach(FoundBarcode b in forwards) merge[count++]=b; - foreach(FoundBarcode b in backwards) merge[count++]=b; - return merge; - } - - /// - /// Gs1 Databar only: checks if found barcode provides required level of the confidence - /// - /// found barcode object - /// true if confidence level is enough - public bool IsAllowedConfidenceForGS1Databar(FoundBarcode foundBarcode) - { - // as GS1 Databar barcodes tends to generate lot of - // results with 0.75 confidnce or even less - // else we check if this confidence is allowed - return foundBarcode.Confidence > GS1_DATABAR_ALLOWED_CONFIDENCE; - } - - private FoundBarcode[] iDecodeRow(int rowNumber, XBitArray row) - { - //set limits of the finder search. If part=0, from 0 to row.Size. If part=1, parevious limits. - int startX = 0, endX = row.Size; - if (xRestriction) - { - int inc = (rowNumber - lastRowNumber) / 10; //10% - startX = lastStartX - inc; - if (startX < 0) startX = 0; - endX = lastEndX + inc; - if (endX > row.Size) endX = row.Size; - } - - //skip black (if finder starts with white) or white(if black) pixels - while (startX < endX && (row[startX] ^ (finderStartsWithBlack))) startX++; - - //look for the finder - int currentElement = 0, n = 0; - int[] elementWidths = new int[finderElements]; - int symbolStart = startX; - bool processingWhite = !finderStartsWithBlack; - for (int x = startX; x < endX; x++) - { - if (row[x] ^ processingWhite) n++; - else - { - elementWidths[currentElement++] = n; - if (currentElement == finderElements) - { - ArrayList finders = IsFinder(elementWidths, finderDirection); - if (finders.Count>0) - { - FoundBarcode result = Decode(rowNumber, row, symbolStart, x, finders); - if (result != null) - { - if (IsAllowedConfidenceForGS1Databar(result)) - { - result.BarcodeFormat = GetBarCodeType(); - return new FoundBarcode[] { result }; - } - } - } - SkipTwoModules(ref symbolStart, elementWidths); - currentElement -= 2; - } - n = 1; - processingWhite = !processingWhite; - } - } - return null; - } - - virtual protected void initDecodeRow(int rowNumber) { } - - //Adds the application identifier (01) and the check digit to a 13 digit GTIN value. - protected string fullGTIN14(string value) - { - value = "00000000000000" + value; - value = value.Substring(value.Length - 13); - int sum = Encodation.GTIN14CheckSum(value); - return "(01)" + value + sum; - } - - //returns a widths array from the row. Starting at offset, look for numElements bars. - //forward=true --> offset is increased. - //forward=false--> offset is decreased. - //leftToRight=false, then elements are mirrored. - protected int[] getElements(XBitArray row, ref int offset, int numElements, bool forward, bool leftToRight) - { - int[] w = new int[numElements]; - int currentElement = 0; - int incOffset = (forward ? 1 : -1); - if (!forward) offset--; - if (offset < 0 || offset >= row.Size) return null; //empty set - bool isWhite = !row[offset]; - int n = 0; - while (offset >= 0 && offset < row.Size) - { - if (row[offset] ^ isWhite) n++; - else - { - if (forward && leftToRight || !forward && !leftToRight) w[currentElement++] = n; - else w[numElements - 1 - currentElement++] = n; - if (currentElement == numElements) break; - isWhite = !isWhite; - n = 1; - } - offset += incOffset; - } - return (currentElement == numElements ? w : null); - } - - - abstract protected bool verifyCheckSum(BarcodeChar[] current, int nChars); - - //find the combination of chars that leads to a valid checksum - //For each char (0..3) we receive an array of possible widths due to statistical recovery - protected int[] verifyCheckSum(ArrayList[] chars, ArrayList[] finders, out float error) - { - return verifyCheckSum(chars, chars.Length, finders, finders.Length, out error); - } - - const int MAX_COMBINATIONS = 1000; - protected int[] verifyCheckSum(ArrayList[] chars, int nChars, ArrayList[] finders, int nFinders, out float error) - { - int N = nChars + nFinders; - int[] currentIndexs = new int[N]; - BarcodeChar[] current = new BarcodeChar[N]; - - //max depth of chars or finders - int max = 0; - for (int i = 0; i < nChars; i++) if (max < chars[i].Count) max = chars[i].Count; - for (int i = 0; i < nFinders; i++) if (max < finders[i].Count) max = finders[i].Count; - - //initial combination - for (int i = 0; i < currentIndexs.Length; i++) currentIndexs[i] = 0; //initial combination - int currentMax = 0; - int nCombinations = 0; - while (currentMax <= max && nCombinations++= 0 && !done) - { - currentIndexs[j]++; - int M = (j < nChars ? chars[j].Count : finders[j-nChars].Count); - if (currentIndexs[j] < M && currentIndexs[j] <= currentMax) done = true; - else currentIndexs[j--] = 0; - } - if (!done) - { - currentMax++; - for (int i = 0; i < currentIndexs.Length; i++) - currentIndexs[i] = 0; - currentIndexs[currentIndexs.Length - 1] = currentMax - 1; //the next look will be increased to currentMax! - } - else - { - //check that at least one currentMax exist (otherwise it is a repeated comb) - done = false; - for (int i = 0; i < currentIndexs.Length; i++) - if (currentIndexs[i] == currentMax) done = true; - } - } - } - error = -1.0F; - return null; - } - - protected int[] getRawData(ArrayList[] chars, int[] indexs) - { - return getRawData(chars, chars.Length, indexs); - } - protected int[] getRawData(ArrayList[] chars, int n, int[] indexs) - { - int[] raw = new int[n]; - for (int i=0;i maxError) return null; - - //get binary string and decode to ascii string - string code = decodeChars(chars, numChar, indexs); - if (code == null) return null; - - FoundBarcode result = new FoundBarcode(); - offsetIn++; - getElements(row, ref offsetIn, 1, false, false); - getElements(row, ref offsetOut, 1, true, true); - result.Rect = new SKRect(offsetIn, rowIndex, offsetOut, rowIndex+1); - result.Value = code; - float tError = (float) (numChar * 7 + numFinder * 4); - result.Confidence = (tError - error) / tError; - result.RawData = getRawData(chars, numChar, indexs); - return result; - } - - } -} - diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarExpandedStacked.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarExpandedStacked.cs deleted file mode 100644 index ec103deb..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarExpandedStacked.cs +++ /dev/null @@ -1,273 +0,0 @@ -using SkiaSharp; -using System; -using System.Collections; -using System.Drawing; - -namespace BarcodeReader.Core.GS1DataBar -{ -#if CORE_DEV - public -#else - internal -#endif - class GS1DataBarExpandedStacked : GS1DataBarGroup3 - { - public GS1DataBarExpandedStacked() : this(null) - { - } - - public GS1DataBarExpandedStacked(IBarcodeConsumer consumer) : base(consumer) - { - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.GS1DataBarExpandedStacked; - } - - public override void Reset() - { - //reset decoder - partialSolutions = null; - firstFinderSize = -1; - charsInFirstRow = -1; - reverse = false; //initially only process rows forwards - xStart = xEnd = -1; //bounding box - yStart = 0; - } - override protected void initDecodeRow(int rowNumber) - { - if (rowNumber == 0) Reset(); - } - - class PartialSolution - { - int nChars; - ArrayList[] chars; - int[] finderSequence; - int length, lastIndex; - - public PartialSolution(int length, ArrayList chars, int charIndex, int[] finderSequence) - { - this.length = length; - this.lastIndex = 0; //index of the last char added - this.chars = new ArrayList[length]; - this.nChars = 0; - foreach (ArrayList ch in chars) this.chars[this.nChars++] = ch; - this.chars[0] = new ArrayList(); - this.chars[0].Add(((ArrayList)chars[0])[charIndex]); - this.finderSequence = finderSequence; - } - - //PRE: finders[0] contains a start finder A1 (value 0) - static public PartialSolution CanStart(ArrayList firstRowchars, int firstCharIndex, ArrayList finders) - { - ArrayList firstChar = (ArrayList)firstRowchars[0]; - int length = ((BarcodeChar)firstChar[firstCharIndex]).value / 211 + 4; - if (length < 4 || length > 22) return null; - //check finders sequence - int[] finderSequence = FINDERS_SEQUENCES[(length - 3) / 2]; - for (int i = 1; i < finders.Count; i++) - { - ArrayList f = (ArrayList)finders[i]; - bool found = false; - foreach (BarcodeChar bc in f) if (bc.value == finderSequence[i]) { found = true; break; } - if (!found) return null; - } - return new PartialSolution(length, firstRowchars, firstCharIndex, finderSequence); - } - - //returns an array of PartialSolutions that can start given a row (chars+finders). - static public ArrayList CanStart(ArrayList chars, ArrayList finders) - { - //search A1 finder (value 0) - if (finders.Count == 0) return null; - ArrayList f = (ArrayList)finders[0]; - bool start = false; - foreach (BarcodeChar bc in f) if (bc.value == 0) { start = true; break; } - if (!start) return null; - - ArrayList sols = new ArrayList(); - ArrayList firstChar = (ArrayList)chars[0]; - Hashtable h = new Hashtable(); - for (int i = 0; i < firstChar.Count; i++) - { - int value=((BarcodeChar)firstChar[i]).value; - if (!h.ContainsKey(value)) - { - PartialSolution ps = PartialSolution.CanStart(chars, i, finders); - if (ps != null) - { - sols.Add(ps); - h.Add(value,value); - } - } - } - return sols; - } - - //if finders follow finder sequence in the partial solution, add chars to the partial solution - //returns true if new chars are added. - public bool AddRow(ArrayList chars, ArrayList finders) - { - //try to add finders to the end - bool right = true; - for (int i = 0; i < finders.Count; i++) - { - int nextFinderInSeq=nChars / 2 + i; - if (nextFinderInSeq>=finderSequence.Length) {right=true; break;} // - ArrayList f = (ArrayList)finders[i]; - bool found = false; - foreach (BarcodeChar bc in f) if (bc.value == finderSequence[nextFinderInSeq]) - { found = true; break; } - if (!found) { right = false; break; } - } - if (right) //if finders are in correct order, add chars to the partial solution - { - int start = nChars; - foreach (ArrayList ch in chars) if (this.nChars= length; - } - - public void RollBack() - { - nChars = lastIndex; - } - - public ArrayList[] Chars { get { return chars; } } - } - - ArrayList partialSolutions = null; - int firstFinderSize = -1; - int charsInFirstRow = -1; - int xStart, xEnd, yStart; - - //Decode char1..char2 around the finder - // char1 | finder| char2 - //offsetIn and offsetOut points to the x coord of the first and last bar of the finder. - override protected FoundBarcode Decode(int rowIndex, XBitArray row, int offsetIn, int offsetOut, ArrayList finder) - { - ArrayList chars = new ArrayList(); - ArrayList finders = new ArrayList(); - int finderSize = offsetOut - offsetIn; //remember finder size - if (this.firstFinderSize != -1) //check finder size - { - int diff = Math.Abs(finderSize - firstFinderSize); - int inc = firstFinderSize / 10; - if (diff > inc) return null; - } - - //first row char - int[] rawWidths = getElements(row, ref offsetIn, charElements, false, true); - if (rawWidths == null) return null; - ArrayList ch= charDecoder.getBarcodeChars(rawWidths, N, MinBars.Odd, recovery); - if (ch.Count== 0) return null; - chars.Add(ch); - int rowEnd=offsetOut, rowStart = offsetIn; //remember where row starts and ends - - //first row finder - finders.Add(finder); - - bool end = false; - while (!end) - { - //read next char - rawWidths = getElements(row, ref offsetOut, charElements, true, chars.Count % 2 == 0); - if (rawWidths == null) end = true; - else - { - //normalized widths for the char - ch = charDecoder.getBarcodeChars(rawWidths, N, MinBars.Odd, recovery); - if (ch.Count == 0) end = true; - else - { - chars.Add(ch); //add char - rowEnd = offsetOut; - - if (chars.Count % 2 == 1) //if next to the char there is a finder... - { - //read finder - bool leftToRight = (finders.Count % 2 == 0); - rawWidths = getElements(row, ref offsetOut, finderElements, true, leftToRight); - if (rawWidths == null) end = true; - else - { - ch = IsFinder(rawWidths, leftToRight ? Direction.LeftToRight : Direction.RightToLeft); - if (ch.Count == 0) end = true; - else finders.Add(ch); - } - } - } - } - } - - - if (partialSolutions == null) //first row - { - if (chars.Count < 4 || chars.Count % 2 != 0) return null; - partialSolutions = PartialSolution.CanStart(chars, finders); - this.firstFinderSize = finderSize; - this.charsInFirstRow = chars.Count; - - UpdateMinMax(row, rowStart, rowEnd, finders.Count); - yStart = rowIndex; - reverse = true; //enable row reverse process. - } - else - { - foreach (PartialSolution ps in partialSolutions) - if (ps.AddRow(chars, finders)) - { - UpdateMinMax(row, rowStart, rowEnd, finders.Count); - if (ps.Completed()) - { - //validate checksum in finders - float error; - int[] indexs = verifyCheckSum(ps.Chars, ps.Chars.Length, null, 0, out error); - if (indexs == null || error > maxError) return null; - - //rollback last update, so next rows also finish the partialSolution - ps.RollBack(); - - //get binary string and decode to ascii string - string code = decodeChars(ps.Chars, ps.Chars.Length, indexs); - if (code == null) return null; - - FoundBarcode result = new FoundBarcode(); - result.Rect = new SKRect(xStart, yStart, xEnd, rowIndex); - result.Value = code; - float tError = (float) (ps.Chars.Length * 7 + ps.Chars.Length / 2 * 4); - result.Confidence = (tError - error) / tError; - result.RawData = getRawData(ps.Chars, indexs); - yStart = rowIndex; - return result; - } - } - } - return null; //no complete solution found - } - - void UpdateMinMax(XBitArray row, int offsetIn, int offsetOut, int nFinders) - { - offsetIn++; - getElements(row, ref offsetIn,1, false, false); - getElements(row, ref offsetOut, nFinders%2==0?2:1, true, true); - if (reversed) { - int tmp = offsetOut; - offsetOut = row.Size - offsetIn; - offsetIn = row.Size - tmp; - } - if (xStart == -1 || offsetIn < xStart) xStart = offsetIn; - if (xEnd == -1 || offsetOut > xEnd) xEnd = offsetOut; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarGroup1.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarGroup1.cs deleted file mode 100644 index 38bdefed..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarGroup1.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Collections; - -namespace BarcodeReader.Core.GS1DataBar -{ -#if CORE_DEV - public -#else - internal -#endif - abstract class GS1DataBarGroup1: GS1DataBar - { - public GS1DataBarGroup1(IBarcodeConsumer consumer) : base(consumer) - { - this.reverse = false; - this.finderElements = 5; - this.charElements = 8; - this.charDecoder = new CharDecoder(); - this.finderDecoder = new FinderDecoder(); - } - - //Checks if the widths array correspond to a finder. - //Widths are always left to right (so right finders must be previously mirrored) - //Returns the finder index: 0..9 - //Finder in Group1 are 5/15 (5 bars and 15 modules) (module=1 narrow bar width). - //4th and 5th bars are always 1 module length. Therefore, they are not in tFinders. - const float MIN_RATIO = 0.79166666666666666666666666666667F; //9.5/12; - const float MAX_RATIO = 0.89285714285714285714285714285714F; //12.5/14; - - override protected ArrayList IsFinder(int[] w, Direction dir) - { - //w1,2 -- w2,3 ratio check - int leftPair, rightPair; - if (dir == Direction.LeftToRight) { leftPair = w[1] + w[2]; rightPair = w[3] + w[4]; } - else { leftPair = w[3] + w[2]; rightPair = w[1] + w[0]; } - int sum = leftPair + rightPair; - if (sum == 0) return new ArrayList(0); - float ratio1 = (float)leftPair / sum; - if ((MIN_RATIO <= ratio1 && ratio1 <= MAX_RATIO)) - { - if (dir==Direction.RightToLeft) { int tmp; tmp=w[0]; w[0]=w[4]; w[4]=tmp; tmp=w[1]; w[1]=w[3]; w[3]=tmp;} - ArrayList nw = finderDecoder.getBarcodeChars(w, 15, MinBars.Last, recovery); - if (nw.Count > 0) return nw; - } - return new ArrayList(0); - } - - //Calculates the checksum of a complete barcode widths and checks if it correspond to the - //checksum stored in both finders. - int[][] WEIGHT_CHAR ={new int[]{1, 3, 9, 27, 2, 6, 18, 54},new int[]{4, 12, 36, 29, 8, 24, 72, 58}, - new int[]{16, 48, 65, 37, 32, 17, 51, 74}, new int[]{64, 34, 23, 69, 49, 68, 46, 59}}; - const int LEFT_FINDER = 4; - const int RIGHT_FINDER = 5; - override protected bool verifyCheckSum(BarcodeChar[] current, int nChars) - { - int sum = 0; - for (int i = 0; i < 4; i++) for (int j = 0; j < 8; j++) sum += current[i].widths[j] * WEIGHT_CHAR[i][j]; - sum = sum % 79; - int tmp = sum; - if (tmp >= 8) tmp++; - if (tmp >= 72) tmp++; - int leftF = tmp / 9; - int rightF = tmp % 9; - return (leftF == current[LEFT_FINDER].value) && (rightF == current[RIGHT_FINDER].value); - } - - - - - //Finder Grup1 bar decoder - class FinderDecoder : Decoder - { - static readonly string[] tFinders = { "382", "355", "337", "319", "274", "256", "238", "157", "139" }; - override public int DecodeChar(int[] w, int N, MinBars minBars) - { - string code = "" + w[0] + w[1] + w[2]; - int pos = Array.IndexOf(tFinders, code); - return pos; - } - } - - - //Group1 char bar decoder - class CharDecoder : Decoder - { - //Given a widths array corresponding to a char of N modules and K white bars + K black bars - //returns the encoded value. - override public int DecodeChar(int[] w, int N, MinBars minBars) - { - int sumOdd, sumEven; - int[] oddW = getOdd(w, out sumOdd); - int[] evenW = getEven(w, out sumEven); - int maxWidthOdd, maxWidthEven, gSum, tOdd, tEven; - if (!GetCharCharacteristics(sumOdd, N, out maxWidthOdd, out maxWidthEven, out gSum, out tOdd, out tEven)) return -1; - int oddValue = getValue(oddW, 4, maxWidthOdd, minBars==MinBars.Odd); - int evenValue = getValue(evenW, 4, maxWidthEven, minBars==MinBars.Even); - int value = -1; - if (N == 16) value = oddValue * tEven + evenValue + gSum; - else if (N == 15) value = evenValue * tOdd + oddValue + gSum; - return value; - } - - - //Returns encoding characteristics for the given sumOdd, N and K. - bool GetCharCharacteristics(int sumOdd, int N, out int maxWidthOdd, out int maxWidthEven, - out int gSum, out int tOdd, out int tEven) - { - maxWidthEven = maxWidthOdd = gSum = tOdd = tEven = -1; - if (N == 16) - { - switch (sumOdd) - { - case 12: - maxWidthOdd = 8; maxWidthEven = 1; - gSum = 0; tOdd = 161; tEven = 1; - break; - case 10: - maxWidthOdd = 6; maxWidthEven = 3; - gSum = 161; tOdd = 80; tEven = 10; - break; - case 8: - maxWidthOdd = 4; maxWidthEven = 5; - gSum = 961; tOdd = 31; tEven = 34; - break; - case 6: - maxWidthOdd = 3; maxWidthEven = 6; - gSum = 2015; tOdd = 10; tEven = 70; - break; - case 4: - maxWidthOdd = 1; maxWidthEven = 8; - gSum = 2715; tOdd = 1; tEven = 126; - break; - } - } - else if (N == 15) - { - switch (sumOdd) - { - case 5: - maxWidthOdd = 2; maxWidthEven = 7; - gSum = 0; tOdd = 4; tEven = 84; - break; - case 7: - maxWidthOdd = 4; maxWidthEven = 5; - gSum = 336; tOdd = 20; tEven = 35; - break; - case 9: - maxWidthOdd = 6; maxWidthEven = 3; - gSum = 1036; tOdd = 48; tEven = 10; - break; - case 11: - maxWidthOdd = 8; maxWidthEven = 1; - gSum = 1516; tOdd = 81; tEven = 1; - break; - } - } - return gSum != -1; - } - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarGroup3.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarGroup3.cs deleted file mode 100644 index 51d83b8b..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarGroup3.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System; -using System.Collections; - -namespace BarcodeReader.Core.GS1DataBar -{ -#if CORE_DEV - public -#else - internal -#endif - abstract class GS1DataBarGroup3: GS1DataBar - { - public GS1DataBarGroup3(IBarcodeConsumer consumer) : base(consumer) - { - this.reverse = false; - this.finderElements = 5; //15 elements - this.charElements = 8; - this.finderDirection = Direction.LeftToRight; - this.finderStartsWithBlack = false; - this.xRestriction = false; - this.charDecoder = new CharDecoder(); - this.finderDecoder = new FinderDecoder(); - } - - //Checks if the widths array correspond to a finder. - //Widths are always left to right (so even finders must be previously mirrored) - //Returns the finder index: 0..6 - //Finder in Limites are 18/7 (14 bars and 18 modules) (module=1 narrow bar width). - //6th and 7th bars are always 1 module length. Therefore, they are not in tFinders. - const float MIN_RATIO = 0.79166666666666666666666666666667F; // 9.5/12 - const float MAX_RATIO = 0.89285714285714285714285714285714F; // 12.5/14 - //direction in w always left to right. Used to calculate the value 2*index+1 if rightToLeft - override protected ArrayList IsFinder(int[] w, Direction dir) - { - //w1,2 -- w2,3 ratio check - int sumLeft = w[1] + w[2]; - int sum = sumLeft + w[3] + w[4]; - float ratio = (float)sumLeft / (float)sum; - if ((MIN_RATIO <= ratio && ratio <= MAX_RATIO)) - { - ArrayList nw = finderDecoder.getBarcodeChars(w, 15, MinBars.Last, recovery); //finder 15/2 - if (nw.Count > 0) - { - ArrayList a = new ArrayList(); - foreach (BarcodeChar b in nw) - { - BarcodeChar c = (BarcodeChar)b.Clone(); - c.value = c.value * 2 + (dir == Direction.LeftToRight ? 0 : 1); - a.Add(c); - } - return a; - } - } - return new ArrayList(0); - } - - - protected const int N = 17;//number of modules for chars - const int K = 4; //number of white and black bars. Total bars=2*K - protected const int MAX_CHARS = 22; //maximun number of chars of a GS1 databar expanded barcode - - protected string decodeChars(ArrayList[] chars, int nChars, int[] indexs) - { - //calculate barcode binary string - string s = ""; - for (int i = 1; i < nChars; i++) - s += Convert.ToString(((BarcodeChar)chars[i][indexs[i]]).value, 2).PadLeft(12, '0'); - - //decode - Encodation enc = Encodation.Factory(s); - if (enc == null) return null; - return enc.Code; - } - - - - - - //Calculates the checksum of a complete barcode widths and checks if it correspond to the - //checksum stored in both finders. - override protected bool verifyCheckSum(BarcodeChar[] current, int nChars) - { - //extract length and checksum from first char - int length = current[0].value / 211 + 4; - if (length < 4 || length > 22) return false; - if (length != nChars) return false; - int checkSum = current[0].value % 211; - - //check finder sequence associated to the given length if needed - //Expanded Stacked checks finders during scanning - int[] finderSequence = FINDERS_SEQUENCES[(length - 3) / 2]; - if (nChars > current.Length) - { - for (int i = 0; i < finderSequence.Length; i++) - if (finderSequence[i] != current[nChars + i].value) return false; - } - - //calculate checksum - int sum = 0; - for (int i = 1; i < length; i++) - for (int j = 0; j < 8; j++) - { - int weight = WEIGHT_CHAR[2 * finderSequence[i / 2] + i % 2][j]; - sum += current[i].widths[j] * weight; - } - sum = sum % 211; - return (sum == checkSum); - } - - const int A1 = 0, A2 = 1, B1 = 2, B2 = 3, C1 = 4, C2 = 5, D1 = 6, D2 = 7, E1 = 8, E2 = 9, F1 = 10, F2 = 11; - protected static readonly int[][] FINDERS_SEQUENCES = new int[][]{ - new int[]{A1,A2}, - new int[]{A1,B2,B1}, - new int[]{A1,C2,B1,D2}, - new int[]{A1,E2,B1,D2,C1}, - new int[]{A1,E2,B1,D2,D1,F2}, - new int[]{A1,E2,B1,D2,E1,F2,F1}, - new int[]{A1,A2,B1,B2,C1,C2,D1,D2}, - new int[]{A1,A2,B1,B2,C1,C2,D1,E2,E1}, - new int[]{A1,A2,B1,B2,C1,C2,D1,E2,F1,F2}, - new int[]{A1,A2,B1,B2,C1,D2,D1,E2,E1,F2,F1} - }; - static readonly int[][] WEIGHT_CHAR = new int[][]{ - new int[]{0,0,0,0,0,0,0,0}, - new int[]{1,3,9,27,81,32,96,77}, - new int[]{20,60,180,118,143,7,21,63}, - new int[]{189,145,13,39,117,140,209,205}, - new int[]{193,157,49,147,19,57,171,91}, - new int[]{62,186,136,197,169,85,44,132}, - new int[]{185,133,188,142,4,12,36,108}, - new int[]{113,128,173,97,80,29,87,50}, - new int[]{150,28,84,41,123,158,52,156}, - new int[]{46,138,203,187,139,206,196,166}, - new int[]{76,17,51,153,37,111,122,155}, - new int[]{43,129,176,106,107,110,119,146}, - new int[]{16,48,144,10,30,90,59,177}, - new int[]{109,116,137,200,178,112,125,164}, - new int[]{70,210,208,202,184,130,179,115}, - new int[]{134,191,151,31,93,68,204,190}, - new int[]{148,22,66,198,172,94,71,2}, - new int[]{6,18,54,162,64,192,154,40}, - new int[]{120,149,25,75,14,42,126,167}, - new int[]{79,26,78,23,69,207,199,175}, - new int[]{103,98,83,38,114,131,182,124}, - new int[]{161,61,183,127,170,88,53,159}, - new int[]{55,165,73,8,24,72,5,15}, - new int[]{45,135,194,160,58,174,100,89} - }; - - } - - - //Finder Grup3 bar decoder (expanded and stacked expanded - class FinderDecoder : Decoder - { - static readonly string[] tFinders = new string[] { "184", "364", "346", "328", "265", "229" }; - override public int DecodeChar(int[] w, int N, MinBars minBars) - { - string code = "" + w[0] + w[1] + w[2]; - int pos = Array.IndexOf(tFinders, code); - return pos; - } - } - - - //Group1 char bar decoder (expanded and stacked expanded - class CharDecoder : Decoder - { - //Given a widths array corresponding to a char of N modules and K white bars + K black bars - //returns the encoded value. - override public int DecodeChar(int[] w, int N, MinBars minBars) - { - int sumOdd, sumEven; - int[] oddW = getOdd(w, out sumOdd); - int[] evenW = getEven(w, out sumEven); - int maxWidthOdd, maxWidthEven, gSum, tOdd, tEven; - if (!GetCharCharacteristics(sumOdd, N, out maxWidthOdd, out maxWidthEven, out gSum, out tOdd, out tEven)) return -1; - int oddValue = getValue(oddW, 4, maxWidthOdd, minBars == MinBars.Odd); - int evenValue = getValue(evenW, 4, maxWidthEven, minBars == MinBars.Even); - int value = oddValue * tEven + evenValue + gSum; - return value; - } - - //Returns encoding characteristics for the given sumOdd, N and K. - bool GetCharCharacteristics(int sumOdd, int N, out int maxWidthOdd, out int maxWidthEven, - out int gSum, out int tOdd, out int tEven) - { - maxWidthEven = maxWidthOdd = gSum = tOdd = tEven = -1; - switch (sumOdd) - { - case 12: - maxWidthOdd = 7; maxWidthEven = 2; - gSum = 0; tOdd = 87; tEven = 4; - break; - case 10: - maxWidthOdd = 5; maxWidthEven = 4; - gSum = 348; tOdd = 52; tEven = 20; - break; - case 8: - maxWidthOdd = 4; maxWidthEven = 5; - gSum = 1388; tOdd = 30; tEven = 52; - break; - case 6: - maxWidthOdd = 3; maxWidthEven = 6; - gSum = 2948; tOdd = 10; tEven = 104; - break; - case 4: - maxWidthOdd = 1; maxWidthEven = 8; - gSum = 3988; tOdd = 1; tEven = 204; - break; - } - return gSum != -1; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarLimited.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarLimited.cs deleted file mode 100644 index 3e585820..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarLimited.cs +++ /dev/null @@ -1,208 +0,0 @@ -using SkiaSharp; -using System; -using System.Collections; -using System.Drawing; - -namespace BarcodeReader.Core.GS1DataBar -{ -#if CORE_DEV - public -#else - internal -#endif - class GS1DataBarLimited : GS1DataBar - { - public GS1DataBarLimited() : this(null) - { - } - - public GS1DataBarLimited(IBarcodeConsumer consumer) : base(consumer) - { - reverse = false; - finderElements = 14 * 3; //14 elements + finder + 14 elements = 42 elements - charElements = 14; - finderDirection = Direction.LeftToRight; - finderStartsWithBlack = false; - xRestriction = false; - this.charDecoder = new CharDecoder(); - this.finderDecoder = new FinderDecoder(); - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.GS1DataBarLimited; - } - - int[] N = { 26, 26 };//number of modules for char1...char2 - //Decode char1..char2 around the finder - // char1 | finder| char2 - //offsetIn and offsetOut points to the x coord of the first and last bar of the finder. - override protected FoundBarcode Decode(int rowIndex, XBitArray row, int offsetIn, int offsetOut, ArrayList finders) - { - //raw widths - int origin = offsetIn, end = offsetOut; - int[][] charWidths = new int[2][]; - charWidths[0] = getElements(row, ref offsetIn, charElements, true, true); // 16/4 - charWidths[1] = getElements(row, ref offsetOut, charElements, false, true); // 15/4 - if (charWidths[0] == null || charWidths[1] == null) - return null; - - //get normalized widths from raw widths - ArrayList[] chars = new ArrayList[2]; - for (int i = 0; i < 2; i++) - { - chars[i] = charDecoder.getBarcodeChars(charWidths[i], N[i], MinBars.Even, recovery); - if (chars[i].Count == 0) return null; - } - - //validate checksum in finders - float error; - int[] indexs = verifyCheckSum(chars, new ArrayList[] {finders}, out error); - if (indexs == null || error > maxError) return null; - - //calculate barcode value - long symbol = 2013571L * ((BarcodeChar)chars[0][indexs[0]]).value + ((BarcodeChar)chars[1][indexs[1]]).value; - FoundBarcode result = new FoundBarcode(); - origin++; - getElements(row, ref origin, 1, false, false); - getElements(row, ref end, 2, true, true); - result.Rect = new SKRect(origin, rowIndex, end, rowIndex+1); - result.Value = fullGTIN14(Convert.ToString(symbol)); - result.Confidence = (39.0F - error) / 39.0F; - result.RawData = getRawData(chars, indexs); - return result; - } - - //Checks if the widths array correspond to a finder. - //Widths are always left to right (so right finders must be previously mirrored) - //Returns the finder index: 0..89 - //Finder in Limites are 18/7 (14 bars and 18 modules) (module=1 narrow bar width). - //6th and 7th bars are always 1 module length. Therefore, they are not in tFinders. - const float MIN_RATIO = 1.3611111111111111111111111111111F; // (26-1.5)/18 - const float MAX_RATIO = 1.5277777777777777777777777777778F; // (26+1.5)/18 - //Limited finder is always left to right - override protected ArrayList IsFinder(int[] w, Direction dir) - { - int[] sum = new int[3]; - for (int i = 0; i < 42; i++) sum[i / 14] += w[i]; - - float ratio1 = (float)sum[0] / (float)sum[1]; - float ratio2 = (float)sum[2] / (float)sum[1]; - if ((MIN_RATIO <= ratio1 && ratio1 <= MAX_RATIO) && - (MIN_RATIO <= ratio2 && ratio2 <= MAX_RATIO)) - { - //extract finder - int[] finderW = new int[14]; - for (int i = 14, j = 0; i < 28; i++) finderW[j++] = w[i]; - - ArrayList nw = finderDecoder.getBarcodeChars(finderW, 18, MinBars.Last, recovery);//finder (18/7) - if (nw.Count > 0) return nw; - } - return new ArrayList(0); - } - - - //Calculates the checksum of a complete barcode widths and checks if it correspond to the - //checksum stored in both finders. - int[][] WEIGHT_CHAR ={new int[]{1, 3, 9, 27, 81, 65, 17, 51, 64, 14, 42, 37, 22, 66}, - new int[]{20, 60, 2, 6, 18, 54, 73, 41, 34, 13, 39, 28, 84, 74}}; - const int FINDER = 2; - //nChars always 3 - override protected bool verifyCheckSum(BarcodeChar[] current, int nChars) - { - int sum = 0; - for (int i = 0; i < 2; i++) for (int j = 0; j < 14; j++) sum += current[i].widths[j] * WEIGHT_CHAR[i][j]; - sum = sum % 89; - return (sum == current[FINDER].value); - } - - - - - //Finder Limited bar decoder - class FinderDecoder : Decoder - { - static readonly string[] tFinders = new string[]{"111111111133","111111111232","111111111331","111111121132" - ,"111111121231","111111131131","111112111132","111112111231","111112121131","111113111131","111211111132" - ,"111211111231","111211121131","111212111131","111311111131","121111111132","121111111231","121111121131" - ,"121112111131","121211111131","131111111131","111111112123","111111112222","111111112321","111111122122" - ,"111111122221","111111132121","111112112122","111112112221","111112122121","111113112121","111211112122" - ,"111211112221","111211122121","111212112121","111311112121","121111112122","121111112221","121111122121" - ,"121112112121","121211112121","131111112121","111111113113","111111113212","111111123112","111211113112" - ,"121111113112","111111211123","111111211222","111111211321","111111221122","111211211122","111211211221" - ,"111211221121","111212211121","111311211121","121111211122","121111211221","121211211121","111121111123" - ,"111121111222","111121111321","111121121122","111121121221","111122111122","121121111122","121121111221" - ,"121121121121","121122111121","121221111121","131121111121","112111111123","112111111222","112111111321" - ,"112111121122","112111121221","112111131121","112112111122","112112111221","112211111122","211111111222" - ,"211111111321","211111121122","211111121221","211111131121","211112111221","211112121121","211211111221" - ,"211111112212"}; - - override public int DecodeChar(int[] w, int N, MinBars minBars) - { - string code = ""; - for (int i = 0; i < 12; i++) code += w[i]; - int pos = Array.IndexOf(tFinders, code); - return pos; - } - } - - - //Limited char bar decoder - class CharDecoder : Decoder - { - //Given a widths array corresponding to a char of N modules and K white bars + K black bars - //returns the encoded value. - override public int DecodeChar(int[] w, int N, MinBars minBars) - { - int sumOdd, sumEven; - int[] oddW = getOdd(w, out sumOdd); - int[] evenW = getEven(w, out sumEven); - int maxWidthOdd, maxWidthEven, gSum, tOdd, tEven; - if (!GetCharCharacteristics(sumOdd, N, out maxWidthOdd, out maxWidthEven, out gSum, out tOdd, out tEven)) return -1; - int oddValue = getValue(oddW, 7, maxWidthOdd, minBars == MinBars.Odd); - int evenValue = getValue(evenW, 7, maxWidthEven, minBars == MinBars.Even); - int value = oddValue * tEven + evenValue + gSum; - return value; - } - - //Returns encoding characteristics for the given sumOdd, N. - bool GetCharCharacteristics(int sumOdd, int N, out int maxWidthOdd, out int maxWidthEven, - out int gSum, out int tOdd, out int tEven) - { - maxWidthEven = maxWidthOdd = gSum = tOdd = tEven = -1; - switch (sumOdd) - { - case 17: - maxWidthOdd = 6; maxWidthEven = 3; - gSum = 0; tOdd = 6538; tEven = 28; - break; - case 13: - maxWidthOdd = 5; maxWidthEven = 4; - gSum = 183064; tOdd = 875; tEven = 728; - break; - case 9: - maxWidthOdd = 3; maxWidthEven = 6; - gSum = 820064; tOdd = 28; tEven = 6454; - break; - case 15: - maxWidthOdd = 5; maxWidthEven = 4; - gSum = 1000776; tOdd = 2415; tEven = 203; - break; - case 11: - maxWidthOdd = 4; maxWidthEven = 5; - gSum = 1491021; tOdd = 203; tEven = 2408; - break; - case 19: - maxWidthOdd = 8; maxWidthEven = 1; - gSum = 1979845; tOdd = 17094; tEven = 1; - break; - case 7: - maxWidthOdd = 1; maxWidthEven = 8; - gSum = 1996939; tOdd = 1; tEven = 16632; - break; - } - return gSum != -1; - } - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarOmnidirectional.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarOmnidirectional.cs deleted file mode 100644 index 56cf4cd2..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarOmnidirectional.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Drawing; -using System.Collections; -using System; -using SkiaSharp; - -namespace BarcodeReader.Core.GS1DataBar -{ -#if CORE_DEV - public -#else - internal -#endif - class GS1DataBarOmnidirectional : GS1DataBarGroup1 - { - public GS1DataBarOmnidirectional() : this(null) - { - } - - public GS1DataBarOmnidirectional(IBarcodeConsumer consumer) : base(consumer) - { - finderDirection = Direction.LeftToRight; - finderStartsWithBlack = false; - xRestriction = false; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.GS1DataBarOmnidirectional; - } - - int[] N = { 16, 15, 16, 15 };//number of modules for char1...char4 - //Decode char1..char2 around the finder, and next char4 rightFinder char3. - // char1 | left finder| char2 | char4 | right finder| char 3 - //offsetIn and offsetOut points to the x coord of the first and last bar of the finder. - override protected FoundBarcode Decode(int rowIndex, XBitArray row, int offsetIn, int offsetOut, ArrayList leftFinders) - { - //raw widths - int[][] charWidths = new int[4][]; - charWidths[0] = getElements(row, ref offsetIn, charElements, false, true); // 16/4 - charWidths[1] = getElements(row, ref offsetOut, charElements, true, false); // 15/4 - charWidths[3] = getElements(row, ref offsetOut, charElements, true, true); // 15/4 - int[] rightFinderWidths = getElements(row, ref offsetOut, finderElements, true, false); // right finder - charWidths[2] = getElements(row, ref offsetOut, charElements, true, false); // 16/4 - if (charWidths[0] == null || charWidths[1] == null || charWidths[2] == null || charWidths[3] == null || rightFinderWidths == null) - return null; - - //get normalized widths from raw widths - ArrayList[] chars = new ArrayList[4]; - for (int i= 0; i < 4; i++) - { - chars[i] = charDecoder.getBarcodeChars(charWidths[i], N[i], i%2==0?MinBars.Even:MinBars.Odd, recovery); - if (chars[i].Count == 0) return null; - } - - //decode finder - ArrayList rightFinders = IsFinder(rightFinderWidths, Direction.LeftToRight); - if (rightFinders.Count == 0) return null; - ArrayList[] finders = new ArrayList[2]; - finders[0] = leftFinders; - finders[1] = rightFinders; - - //validate checksum in finders - float error; - int[] indexs=verifyCheckSum(chars, finders, out error); - if (indexs==null || error>maxError) return null; - - //calculate barcode value - int leftPair = 1597 * ((BarcodeChar)chars[0][indexs[0]]).value + ((BarcodeChar)chars[1][indexs[1]]).value; - int rightPair = 1597 * ((BarcodeChar)chars[2][indexs[2]]).value + ((BarcodeChar)chars[3][indexs[3]]).value; - long symbol = 4537077 * (long)leftPair + (long)rightPair; - FoundBarcode result=new FoundBarcode(); - offsetIn++; - getElements(row, ref offsetIn, 1, false, false); - getElements(row, ref offsetOut, 2, true, true); - result.Rect = new SKRect(offsetIn, rowIndex, offsetOut, rowIndex+1); - result.Value = fullGTIN14(Convert.ToString(symbol)); - result.Confidence = (36.0F - error) / 36.0F; - result.RawData = getRawData(chars, indexs); - return result; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarStacked.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarStacked.cs deleted file mode 100644 index 03883d20..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/GS1DataBarStacked.cs +++ /dev/null @@ -1,108 +0,0 @@ -using SkiaSharp; -using System; -using System.Collections; -using System.Drawing; - -namespace BarcodeReader.Core.GS1DataBar -{ -#if CORE_DEV - public -#else - internal -#endif - class GS1DataBarStacked : GS1DataBarGroup1 - { - int part = 0; //the current part of the stacked barcode. Can be 0..1 - bool isFirst; //true if is looking for the first row of the second part of a stacked barcode. - ArrayList[] chars = new ArrayList[4]; - ArrayList[] finders = new ArrayList[2]; - - public GS1DataBarStacked() : this(null) - { - } - - public GS1DataBarStacked(IBarcodeConsumer consumer) : base(consumer) - { - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.GS1DataBarStacked; - } - - public override void Reset() - { - //reset decoder - part = 0; - } - - override protected void initDecodeRow(int rowNumber) - { - if (rowNumber == 0) Reset(); - finderDirection = (part == 0 ? Direction.LeftToRight : Direction.RightToLeft); - finderStartsWithBlack = (part==1); - xRestriction = (part == 1); - } - - - //Decode left and right chars around the finder. If part=0 decodes chars around left finder. - //If part=1 decodes chars around right finder. - // char1 | left finder| char2 | char4 | right finder| char 3 - //For right finder, chars must be swapped! - //offsetIn and offsetOut points to the x coord of the first and last bar of the finder. - int[] N = { 16, 15 }; //number of modules for right and left char - override protected FoundBarcode Decode(int rowIndex, XBitArray row, int offsetIn, int offsetOut, ArrayList finder) - { - //raw widths. - int[][] charWidths = new int[2][]; - charWidths[part] = getElements(row, ref offsetIn, charElements, false, true); - charWidths[1-part] = getElements(row, ref offsetOut, charElements, true, false); - if (charWidths[0] == null || charWidths[1] == null) return null; - - //get normalized widths from raw widths - for (int i = 0; i < 2; i++) - { - chars[2*part+i] = charDecoder.getBarcodeChars(charWidths[i], N[i], i % 2 == 0 ? MinBars.Even : MinBars.Odd, recovery); - if (chars[2 * part + i].Count == 0) return null; - } - - if (part == 0) - { - finders[0] = finder; - part = 1; - lastStartX = offsetIn; - lastEndX = offsetOut; - lastRowNumber = rowIndex; - isFirst = true; - return null; - } - else - { - finders[1] = finder; - - //validate checksum in finders - float error; - int[] indexs = verifyCheckSum(chars, finders, out error); - if (indexs == null || error>maxError) return null; - - //calculate barcode value - int leftPair = 1597 * ((BarcodeChar)chars[0][indexs[0]]).value + ((BarcodeChar)chars[1][indexs[1]]).value; - int rightPair = 1597 * ((BarcodeChar)chars[2][indexs[2]]).value + ((BarcodeChar)chars[3][indexs[3]]).value; - long symbol = 4537077 * (long)leftPair + (long)rightPair; - FoundBarcode result = new FoundBarcode(); - int height = (isFirst ? rowIndex - lastRowNumber : 1); - int y = (isFirst ? lastRowNumber : rowIndex); - offsetIn++; - getElements(row, ref offsetIn, 1, false, false); - getElements(row, ref offsetOut, 2, true, true); - result.Rect = new SKRect(offsetIn, y, offsetOut, y+height); - result.Value = fullGTIN14(Convert.ToString(symbol)); - result.Confidence = (36.0F - error) / 36.0F; - result.RawData = getRawData(chars, indexs); - isFirst = false; - return result; - } - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/ProbabilisticCombination.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/ProbabilisticCombination.cs deleted file mode 100644 index 8eb4b288..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/GS1DataBar/ProbabilisticCombination.cs +++ /dev/null @@ -1,254 +0,0 @@ -using System; -using System.Collections; - -namespace BarcodeReader.Core.GS1DataBar -{ - internal class ProbabilisticCombination - { - internal class Candidate : IComparable - { - public int value; - public float error; - public Candidate(int value, float error) - { - this.value = value; - this.error = error; - } - public int CompareTo(object o) - { - Candidate b = (Candidate)o; - return (int)((this.error - b.error) * 100F); - } - } - - ArrayList[] incs; - int[] indexs; - float error; - bool end; - Hashtable h=new Hashtable(); - - //set of possible values for each digit, sorted from smaller to greater error. - public ProbabilisticCombination(ArrayList[] values) - { - //init delta table - error = 0.0F; - incs = new ArrayList[values.Length]; - for (int i = 0; i < values.Length; i++) - { - incs[i] = new ArrayList(values[i].Count); - float next, initial, prev; - initial = prev = ((Candidate)values[i][0]).error; - error += initial; - for (int j = 0; j < values[i].Count - 1; j++) - { - next = ((Candidate)values[i][j + 1]).error; - incs[i].Add(next - prev); - prev = next; - } - } - - //initial combination - indexs = new int[values.Length]; //initially all to 0 - string s = toString(indexs); - int pos = findMinInc(indexs, null); - h.Add(s, new CombItem(error, pos, error+(float)incs[pos][0], incs.Length)); - end = false; - - } - - public int[] Current(out float error) - { - error = this.error; - return indexs; - } - - class CombItem - { - public float baseError, nextError; - public int nextPos; - public bool[] visited; - public CombItem(float baseError, int pos, float nextError, int N) - { - this.baseError = baseError; - this.visited = new bool[N]; - this.nextPos = pos; - this.nextError = nextError; - } - } - public void Next() - { - CombItem minError = null; - string minStr = null; - IDictionaryEnumerator i = h.GetEnumerator(); - while (i.MoveNext()) - { - CombItem e = (CombItem)i.Value; - if (e != null && (minError == null || e.nextError < minError.nextError)) - { - minError = e; - minStr = (string)i.Key; - } - } - if (minError == null) end = true; - else - { - error = minError.nextError; - toIndexs(minStr, indexs); //string to int[] - - //update current comb - int currentPos = minError.nextPos; - minError.visited[minError.nextPos] = true; - int nextPos = minError.nextPos = findMinInc(indexs, minError.visited); //next candidate - if (nextPos == -1) h[minStr] = null; //comb has no more moviments. - else minError.nextError = minError.baseError + (float)incs[nextPos][indexs[nextPos]]; - - //add new comb - indexs[currentPos]++; - string newStr = toString(indexs); - if (!h.ContainsKey(newStr)) - { - nextPos = findMinInc(indexs, null); - if (nextPos != -1) - { - h.Add(newStr, new CombItem(error, nextPos, error + (float)incs[nextPos][0], incs.Length)); - } - } - } - } - - protected string toString(int[] indexs) - { - string s = ""; - for (int i = 0; i < indexs.Length; i++) s += indexs[i]; - return s; - } - - protected void toIndexs(string s, int[] indexs) - { - char[] a = s.ToCharArray(); - for (int i = 0; i < a.Length; i++) indexs[i] = a[i] - '0'; - } - - protected int findMinInc(int[] indexs, bool[] visited) - { - float min = 1e10F; - int pos = -1; - for (int i = 0; i < indexs.Length; i++) if (indexs[i] 0) - { - //try backwards - indexs[i]--; - float e=(float)incs[i][indexs[i]]; - error -= e; - if (error >= 0) //try this branch - { - searchMinInc(forward, indexs, error, ref minError, ref minIndexs); - } - - //restore - indexs[i]++; - error += e; - } - }*/ - - /* - //next combination - public void Next() - { - float f; - bool[] visited = new bool[incs.Length]; //to false; - int minPos = nextDelta(visited, out f); - - //move to next combination - end = (minPos==-1); - bool found = false; - while (!found && !end) - { - error += (float)incs[minPos][indexs[minPos]]; //increment or decrement error - indexs[minPos]++; - if (indexs[minPos] >= incs[minPos].Count) - { - indexs[minPos] = 0; - visited[minPos] = true; - minPos = nextDelta(visited, out f); - if (minPos == -1) end = true; - } - else - { - found = true; - } - } - } - - int nextDelta(bool[] visited, out float min) - { - //search min delta - int minPos = -1; - min = 1e10F; - for (int i = 0; i < incs.Length; i++) if (!visited[i]) - { - float m2, m = (float)incs[i][indexs[i]]; - if (m < 0) - { - visited[i] = true; - nextDelta(visited, out m2); - visited[i] = false; - m += m2; - } - if (m >= 0 && m < min) { min = m; minPos = i; } - } - return minPos; - } - * */ - - public bool End() - { - return end; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/I2of5LinearReader/GTIN14LinearReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/I2of5LinearReader/GTIN14LinearReader.cs deleted file mode 100644 index 1a8b1c49..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/I2of5LinearReader/GTIN14LinearReader.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace BarcodeReader.Core.I2of5LinearReader -{ - /// - /// GTIN14 reader. - /// -#if CORE_DEV - public -#else - internal -#endif - class GTIN14LinearReader : I2of5LinearReader - { - public GTIN14LinearReader() - { - SubMode = I2OF5SubMode.GTIN14; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/I2of5LinearReader/I2of5LinearReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/I2of5LinearReader/I2of5LinearReader.cs deleted file mode 100644 index 12db0ce1..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/I2of5LinearReader/I2of5LinearReader.cs +++ /dev/null @@ -1,319 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.I2of5LinearReader -{ - /// - /// I2of5 reader. - /// - [Obsolete("This class does not pass all tests.")] -#if CORE_DEV - public -#else - internal -#endif - class I2of5LinearReader : LinearReader - { - private bool HighPrecision = false; - protected I2OF5SubMode SubMode = I2OF5SubMode.Interleaved2of5; - - #region Patterns - - private static int[][] template = new [] - { - new int[]{1,1,2,2,1}, - new int[]{2,1,1,1,2}, - new int[]{1,2,1,1,2}, - new int[]{2,2,1,1,1}, - new int[]{1,1,2,1,2}, - new int[]{2,1,2,1,1}, - new int[]{1,2,2,1,1}, - new int[]{1,1,1,2,2}, - new int[]{2,1,1,2,1}, - new int[]{1,2,1,2,1}, - }; - - private static int[][] _patterns = new int[100][]; - - #endregion - - static I2of5LinearReader() - { - //generate pattern - for (int i = 0; i < 10; i++) - for (int j = 0; j < 10; j++) - { - var blacks = template[i]; - var whites = template[j]; - var pattern = _patterns[j * 10 + i] = new int[10]; - - for (int k = 0; k < 10; k+=2) - { - pattern[k] = blacks[k / 2]; - pattern[k + 1] = whites[k / 2]; - } - } - } - - public I2of5LinearReader() - { - UseE = true; - MinConfidence = 0.4f;//0.4 - MaxReadError = 5; - MaxClusterDistanceY = 7; - - MaxPatternSymbolDifference = 0.9f; - MaxPatternAverageSymbolDifference = 0.6f; - } - - public override SymbologyType GetBarCodeType() - { - switch (SubMode) - { - case I2OF5SubMode.Interleaved2of5: return SymbologyType.I2of5; - case I2OF5SubMode.ITF14: return SymbologyType.ITF14; - case I2OF5SubMode.GTIN14: return SymbologyType.GTIN14; - case I2OF5SubMode.Circular: return SymbologyType.CircularI2of5; - } - return SymbologyType.I2of5; - } - - private BarSymbolReaderLooseProjection reader2; - private BarSymbolReaderNaive ReaderNaive; - - internal override void GetParams(out int[] startPattern, out int[] stopPattern, out int minModulesPerBarcode, out int maxModulesPerBarcode) - { - startPattern = new int[] { 1, 1, 1 }; - stopPattern = new int[] { 2, 1, 1 }; - - switch (SubMode) - { - default: - minModulesPerBarcode = 21; - maxModulesPerBarcode = int.MaxValue; - break; - case I2OF5SubMode.ITF14: - case I2OF5SubMode.GTIN14: - maxModulesPerBarcode = minModulesPerBarcode = 4 + 14 * 14 + 4; - break; - } - - if (Reader == null) - { - Reader = new BarSymbolReader(Scan, 10, 14, false, true, 1, _patterns, -1, UseE, null); - //reader2 = new BarSymbolReaderLooseProjection(Scan, 6, 11, true, _patterns, 106, true, UseE); - ReaderNaive = new BarSymbolReaderNaive(10, 14, _patterns); - } - } - - internal override bool ReadSymbols(BarCodeRegion region, Pattern startPattern, Pattern stopPattern, MyPoint from, MyPoint to, float module, bool firstPass) - { - float error, maxError, confidence; - - var from0 = from; - var to0 = to; - - //go 3 bars back from end of line - to = GotoBar(to, from, 3); - //go 4 bars from start of line - from = GotoBar(from, to, 4); - - int[] row = ReadSymbols(startPattern, stopPattern, from, to, module, out error, out maxError, out confidence); - -#if DEBUG - var line = Scan.GetPixels(from, to); - DebugHelper.AddDebugItem(region.A.ToString() + " err" + error, line, from, to); -#endif - - if (error < MaxReadError) - { - if (confidence >= MinConfidence) - if (confidence > region.Confidence) - { - if (Decode(region, row)) //try to decode - { - region.Confidence = confidence; - return true; - } - } - } - - //try naive reader - row = ReaderNaive.Read(Scan.GetPixels(from, to), out error, out maxError, out confidence); - //var row = ReaderNaive.Read(Scan.GetPixels(from, to, 1.4f), out error, out maxError, out confidence); - //row = ReaderNaive.Read(Scan.GetPixels3Lines(from, to, 1.4f, 3.5f), out error, out maxError, out confidence); - - if (confidence >= MinConfidence && maxError < 0.5f) - if (confidence > region.Confidence) - if (Decode(region, row)) //try to decode - { - region.Confidence = confidence; - return true; - } - - //try BarSymbolReaderLooseProjection - if (HighPrecision) - if (confidence > 0.6f) - { - float[] widths = new float[] { 0.5f, 0.9f }; - foreach (float w in widths) - { - row = reader2.Read(region, module, w); - if (row != null && row.Length > 0) - { - if (Decode(region, row)) - { - region.Confidence = confidence; - return true; - } - } - } - } - - return false; - } - - private MyPoint GotoBar(MyPoint from, MyPoint to, int barsCount) - { - var br = new Bresenham(from, to); - //go to first black bar - while (!br.End()) - { - if (Scan.isBlack(br.Current)) - break; - br.Next(); - } - - //skip barsCount - var isBlack = true; - while (!br.End() && barsCount > 0) - { - if (Scan.isBlack(br.Current) ^ isBlack) - { - barsCount--; - isBlack = !isBlack; - if (barsCount == 0) - break; - } - br.Next(); - } - - return br.Current; - } - - #region Decoders - - //Method to decode bytecodes into the final string. - internal override bool Decode(BarCodeRegion r, int[] row) - { - var data = new byte[row.Length * 2]; - for (int i = 0; i < row.Length; i++) - { - data[i * 2] = (byte)(row[i] % 10); - data[i * 2 + 1] = (byte)(row[i] / 10); - } - - switch (SubMode) - { - case I2OF5SubMode.ITF14: - case I2OF5SubMode.GTIN14: - if (data.Length != 14) - return false; - break; - } - - var conf = VerifyCheckSumAndGetConfidence(data); - - if (conf < 0.5f) - return false; - - r.Data = new ABarCodeData[] { new NumericBarCodeData(data) }; - - return true; - } - - private float VerifyCheckSumAndGetConfidence(byte[] data) - { - int datalength = data.Length; // length of data without the very last one - // output result flag - bool checksumMatched = false; - // 2 cases: - // 1) EVEN number of digits with checksum: - // - should contain ODD number of digits + single checksum digit - // if checksum matches then return confidence 1.00 - // 2) EVEN number of digits without checksum: - // return 0.5 as confidence - int iRetries = 0; - for (iRetries = 0; iRetries < 2; iRetries++) - { - int sum = 0; - for (int i = 0; i < datalength - 1; i++) - sum += (int)(data[i]) * (i % 2 == 0 ? 3 : 1); - sum = sum % 10; - if (sum != 0) - sum = 10 - sum; - - // getting the result - checksumMatched = sum == (int)data[datalength - 1]; - - if (checksumMatched) - break; - - // when trying for the first time then we may try to remove the very last symbol and try again - // if checksum is not matching then we should check if we have odd number of symbols - // like 13 symbols or 15 - // but in I2of5 we should have even number - // for example we may have: 16444118888780 (14 digits) and this is not correct with checksum (checksum is not zero) - // but 1644411888878 is correct (checksum = 8 and with 13 digits) - - if (iRetries == 0) - { - // try to decrease the data length the very last digit and try again - if (data.Length > 2 && (int)data[data.Length - 1] == 0) - datalength--; - else - break; - } - else - break; - } - - if (checksumMatched) - { - return 1.00f; - } - else - { - if (data.Length % 2 > 0) // if ODD number of digits - return 0.0f; - else - return 0.7f; - - } - } - - #endregion - } - - -#if CORE_DEV - public -#else - internal -#endif - enum I2OF5SubMode - { - /// - /// interleaved 2 of 5 - /// - Interleaved2of5, - /// - /// itf-14 ( - /// - ITF14, - GTIN14, - Circular - }; -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/I2of5LinearReader/ITF14LinearReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/I2of5LinearReader/ITF14LinearReader.cs deleted file mode 100644 index fa9bf3c0..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/I2of5LinearReader/ITF14LinearReader.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace BarcodeReader.Core.I2of5LinearReader -{ - /// - /// ITF14 reader. - /// -#if CORE_DEV - public -#else - internal -#endif - class ITF14LinearReader : I2of5LinearReader - { - public ITF14LinearReader() - { - SubMode = I2OF5SubMode.ITF14; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/IntelligentMail/IMDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/IntelligentMail/IMDecoder.cs deleted file mode 100644 index 51fcbf56..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/IntelligentMail/IMDecoder.cs +++ /dev/null @@ -1,247 +0,0 @@ -using System; -using System.Globalization; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.IntelligentMail -{ - //Class to decode IM barcodes. We recieve an boolean array of ascendent and descendent bars and - //returns the decoded message - internal class IMDecoder: IDecoderAscDescBars - { - static int[] codeWordToChar = null; - - //each bar correspond to a bit of the byte string, but they are interleaved. Here are the indexs: - static readonly int[] ascendantCharIndex = new int[] { 4, 0, 2, 6, 3, 5, 1, 9, 8, 7, 1, 2, 0, 6, 4, 8, 2, 9, 5, 3, 0, 1, 3, 7, 4, 6, 8, 9, 2, 0, 5, 1, 9, 4, 3, 8, 6, 7, 1, 2, 4, 3, 9, 5, 7, 8, 3, 0, 2, 1, 4, 0, 9, 1, 7, 0, 2, 4, 6, 3, 7, 1, 9, 5, 8 }; - static readonly int[] ascendantCharBit = new int[] { 3, 0, 8, 11, 1, 12, 8, 11, 10, 6, 4, 12, 2, 7, 9, 6, 7, 9, 2, 8, 4, 0, 12, 7, 10, 9, 0, 7, 10, 5, 7, 9, 6, 8, 2, 12, 1, 4, 2, 0, 1, 5, 4, 6, 12, 1, 0, 9, 4, 7, 5, 10, 2, 6, 9, 11, 2, 12, 6, 7, 5, 11, 0, 3, 2 }; - static readonly int[] descendantCharIndex = new int[] { 7, 1, 9, 5, 8, 0, 2, 4, 6, 3, 5, 8, 9, 7, 3, 0, 6, 1, 7, 4, 6, 8, 9, 2, 5, 1, 7, 5, 4, 3, 8, 7, 6, 0, 2, 5, 4, 9, 3, 0, 1, 6, 8, 2, 0, 4, 5, 9, 6, 7, 5, 2, 6, 3, 8, 5, 1, 9, 8, 7, 4, 0, 2, 6, 3 }; - static readonly int[] descendantCharBit = new int[] { 2, 10, 12, 5, 9, 1, 5, 4, 3, 9, 11, 5, 10, 1, 6, 3, 4, 1, 10, 0, 2, 11, 8, 6, 1, 12, 3, 8, 6, 4, 4, 11, 0, 6, 1, 9, 11, 5, 3, 7, 3, 10, 7, 11, 8, 2, 10, 3, 5, 8, 0, 3, 12, 11, 8, 4, 5, 1, 3, 0, 7, 12, 9, 8, 10 }; - - public IMDecoder() - { - if (codeWordToChar == null) - { - codeWordToChar = new int[1365]; - int[] t5of13 = InitializeNof13Table(5, 1287); - int[] t2of13 = InitializeNof13Table(2, 78); - t5of13.CopyTo(codeWordToChar, 0); - t2of13.CopyTo(codeWordToChar, 1287); - } - } - - - //convert samples to code words. Not all combinations are allowed, thus FindChar can return null. - //Some CRC bits are encoded in the code words, one bit per word. If crc bit is 1 then code word is reversed. - int[] BarsToCodeWords(bool[][] samples, bool leftToRight, out int crc) - { - //Bars to chars - int[] chars = new int[10]; //A(0), B(1),...J(9) - for (int i = 0; i < 65; i++) - if (leftToRight) - { - if (samples[i][0]) chars[ascendantCharIndex[i]] |= 1 << ascendantCharBit[i]; - if (samples[i][1]) chars[descendantCharIndex[i]] |= 1 << descendantCharBit[i]; - } - else - { - if (samples[64-i][1]) chars[ascendantCharIndex[i]] |= 1 << ascendantCharBit[i]; - if (samples[64-i][0]) chars[descendantCharIndex[i]] |= 1 << descendantCharBit[i]; - } - - //Chars to CodeWords - int[] codeWords = new int[10]; - crc = 0; - int p2 = 1; - for (int i = 0; i < 10; i++) - { - int pos = FindChar(chars[i]); - if (pos >= 0) codeWords[i] = pos; - else - { //if fails try in the reversed direction and set crc bit - int inverted = ~chars[i] & 8191; - pos = FindChar(inverted); - if (pos >= 0) codeWords[i] = pos; - else return null; //wrong code - crc += p2; - } - p2 *= 2; - } - return codeWords; - } - - //Decode two boolean array (for ascendent and descendent bars) and check crc. - public string Decode(bool[][] samples, out float confidence) - { - confidence=1.0f; - int crc; - int[] codeWords = BarsToCodeWords(samples, true, out crc); - if (codeWords == null) codeWords = BarsToCodeWords(samples, false, out crc); - if (codeWords == null) return null; - - //check orientation - if ((codeWords[9] & 1) == 1) - { - for (int i = 0; i < 5; i++) { int c = codeWords[i]; codeWords[i] = codeWords[9 - i]; codeWords[9 - i] = c; } - if ((codeWords[9] & 1) == 1) return null; //wrong code - } - codeWords[9] /= 2; - - //remove FCS of A - if (codeWords[0] >= 659) - { - codeWords[0] -= 659; - crc += (1 << 10); //p2 - } - - //To binary - BigInt binary = new BigInt(140, 0); - for (int i = 0; i < 10; i++) - { - binary = binary * new BigInt(i < 9 ? 1365 : 636); - binary = binary + new BigInt(codeWords[i]); - } - - //Check CRC - int crc2 = this.USPS_MSB_Math_CRC11GenerateFrameCheckSequence(binary); - if (crc != crc2) confidence = 0f; - - //binary to string message - BigInt serviceTypeMailerIdSerial, id1, id0; - Decimal routingCode=0; - binary.Divide(new BigInt((decimal)1e18), out binary, out serviceTypeMailerIdSerial); - binary.Divide(new BigInt(5), out binary, out id1); - binary.Divide(new BigInt(10), out binary, out id0); - Decimal d = binary.ToDecimal(); - int digits = 0; - if (d >= (decimal) (1e9 + 1e5 + 1)) { routingCode = d - (decimal) (1e9 + 1e5 + 1); digits = 11; } - else if (d >= (decimal) (1e5 + 1)) { routingCode = d - (decimal) (1e5 + 1); digits = 9; } - else if (d >= (decimal) (1)) { routingCode = d - (decimal) (1); digits = 5; } - - // pad serviceTypeMailerIdSerial to 18 digits - string serviceTypeMailerIdSerialStr = serviceTypeMailerIdSerial.ToDecimal().ToString(CultureInfo.InvariantCulture).PadLeft(18, '0'); - // pad routine code to 0, 5, 9, or 11 digits - string routingCodeStr = digits == 0 ? "" : "-" + routingCode.ToString(CultureInfo.InvariantCulture).PadLeft(digits, '0'); - - return "" + id0.ToInt() + id1.ToInt() + "-" + serviceTypeMailerIdSerialStr + routingCodeStr; - } - - - private int FindChar(int ch) - { - for (int i = 0; i < codeWordToChar.Length; i++) - if (codeWordToChar[i] == ch) return i; - return -1; - } - - - - - - /****************************************************************************** - ** InitializeNof13Table - ** - ** Inputs: - ** N is the type of table (i.e. 5 for 5of13 table, 2 for 2of13 table - ** TableLength is the length of the table requested (i.e. 78 for 2of13 table) - ** Output: - ** TableNof13 is a pointer to the resulting table - ******************************************************************************/ - private int[] InitializeNof13Table(int N, int TableLength) - { - int LUT_LowerIndex, LUT_UpperIndex; - int[] TableNof13 = new int[TableLength]; - - /* Count up to 2^13 - 1 and find all those values that have N bits on */ - LUT_LowerIndex = 0; - LUT_UpperIndex = TableLength - 1; - for (int Count = 0; Count < 8192; Count++) - { - int BitCount = 0; - for (int BitIndex = 0; BitIndex < 13; BitIndex++) - BitCount += ((Count & (1 << BitIndex)) != 0 ? 1 : 0); - - /* If we don't have the right number of bits on, go on to the next value */ - if (BitCount != N) continue; - /* If the reverse is less than count, we have already visited this pair before */ - int Reverse = ReverseUnsignedShort(Count) >> 3; - if (Reverse < Count) continue; - /* If Count is symmetric, place it at the first free slot from the end of the */ - /* list. Otherwise, place it at the first free slot from the beginning of the */ - /* list AND place Reverse at the next free slot from the beginning of the list.*/ - if (Count == Reverse) - { - TableNof13[LUT_UpperIndex] = Count; - LUT_UpperIndex -= 1; - } - else - { - TableNof13[LUT_LowerIndex] = Count; - LUT_LowerIndex += 1; - TableNof13[LUT_LowerIndex] = Reverse; - LUT_LowerIndex += 1; - } - } - - /* Make sure the lower and upper parts of the table meet properly */ - if (LUT_LowerIndex != (LUT_UpperIndex + 1)) return null; - return TableNof13; - } - - private int ReverseUnsignedShort(int Input) - { - int Reverse = 0; - for (int i = 0; i < 16; i++) - { - Reverse <<= 1; - Reverse |= Input & 1; - Input >>= 1; - } - return Reverse; - } - - /*************************************************************************** - ** USPS_MSB_Math_CRC11GenerateFrameCheckSequence - ** - ** Inputs: - ** ByteArrayPtr is the address of a 13 byte array holding 102 bits which - ** are right justified - ie: the leftmost 2 bits of the first byte do not - ** hold data and must be set to zero. - ** - ** Outputs: - ** return unsigned short - 11 bit Frame Check Sequence (right justified) - ***************************************************************************/ - int USPS_MSB_Math_CRC11GenerateFrameCheckSequence(BigInt n) - { - int GeneratorPolynomial = 0x0F35; - int FrameCheckSequence = 0x07FF; - - /* Do most significant byte skipping the 2 most significant bits */ - int Data = n.GetByte(12); - Data <<= 5; - for (int Bit = 2; Bit < 8; Bit++) - { - if (((FrameCheckSequence ^ Data) & 0x0400) != 0) - FrameCheckSequence = (FrameCheckSequence << 1) ^ GeneratorPolynomial; - else - FrameCheckSequence = (FrameCheckSequence << 1); - FrameCheckSequence &= 0x7FF; - Data <<= 1; - } - /* Do rest of the bytes */ - for (int ByteIndex = 1; ByteIndex < 13; ByteIndex++) - { - Data = n.GetByte(12 - ByteIndex); - Data <<= 3; - for (int Bit = 0; Bit < 8; Bit++) - { - if (((FrameCheckSequence ^ Data) & 0x0400) != 0) - FrameCheckSequence = (FrameCheckSequence << 1) ^ GeneratorPolynomial; - else - FrameCheckSequence = (FrameCheckSequence << 1); - FrameCheckSequence &= 0x7FF; - Data <<= 1; - } - } - return FrameCheckSequence; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/IntelligentMail/IMReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/IntelligentMail/IMReader.cs deleted file mode 100644 index cf0125f3..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/IntelligentMail/IMReader.cs +++ /dev/null @@ -1,48 +0,0 @@ -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.IntelligentMail -{ - //Class to read Intelligent Mail Barcode - -#if CORE_DEV - public -#else - internal -#endif - class IMReader : TwoFourStateBarcodesReader - { - //Min aspect ratio Y/X for IM barcodes - protected float imMinAspectRatio = 6f; - - //Max aspect ratio Y/X for IM barcodes - protected float imMaxAspectRatio = 25; - - //Number of bars of this barcode - protected int NBars = 65; - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.IntelligentMail; - } - - protected override bool IsFourState() - { - return true; - } - - protected override bool CheckNBars(int nBars) - { - return nBars == NBars || nBars==NBars+1; //IM always has 65 bars, but read if one more (due to noise) - } - - protected override bool CheckRatio(float ratio) - { - return ratio > imMinAspectRatio && ratio < imMaxAspectRatio; //IM has width>>height - } - - //returns the decoder to decode the barcode region. Overwritten in RM, KIX and postCode - protected override IDecoderAscDescBars GetDecoder() - { - return new IMDecoder(); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/BarSymbolReaderNaive.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/BarSymbolReaderNaive.cs deleted file mode 100644 index f75fc66b..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/BarSymbolReaderNaive.cs +++ /dev/null @@ -1,371 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.Common -{ - internal class BarSymbolReaderNaive - { - struct SymbolInfo - { - public int[] Pattern; - public float AvgWhite; - public float StdDevWhite; - public float AvgBlack; - public float StdDevBlack; - } - - private readonly int[] _nBars; - private readonly int[] _nLength; - private readonly int[] _tableIndexes; - private readonly SymbolInfo[][] _patterns; - private readonly int maxBarSize = 0;//max width of bar in modules - - private readonly int _fixedBars; - private readonly int _fixedLength; - private readonly bool noStartStop = false; - - public BarSymbolReaderNaive(int[] nBars, int[] nLength, int[] tableIndexes, int[][][] patterns) - { - _nBars = nBars; - _nLength = nLength; - _tableIndexes = tableIndexes; - PreparePatterns(patterns, out _patterns, out maxBarSize); - } - - public BarSymbolReaderNaive(int nBars, int nLength, int[][] patterns) - { - _fixedBars = nBars; - _fixedLength = nLength; - var p = new int[1][][]; - p[0] = patterns; - PreparePatterns(p, out _patterns, out maxBarSize); - noStartStop = true; - } - - private void PreparePatterns(int[][][] patterns, out SymbolInfo[][] prepared, out int maxBarSize) - { - prepared = new SymbolInfo[patterns.Length][]; - maxBarSize = 0; - - for (int iTable = 0; iTable < patterns.Length; iTable++) - { - var table = patterns[iTable]; - var preapredTable = prepared[iTable] = new SymbolInfo[table.Length]; - - for (var iSymbol = 0; iSymbol < table.Length; iSymbol++) - { - //calc avg of white and black symbols - var symbol = table[iSymbol]; - var sumBlack = 0f; - var sumWhite = 0f; - var sumBlackSq = 0f; - var sumWhiteSq = 0f; - var blackCount = 0; - var whiteCount = 0; - var preparedSym = new SymbolInfo(); - preparedSym.Pattern = symbol; - var symLen = symbol.Length % 2 == 0 ? symbol.Length - 1 : symbol.Length; - - for (int i = 0; i < symLen; i++) - { - if (i % 2 == 0) - { - sumBlack += symbol[i]; - sumBlackSq += symbol[i] * symbol[i]; - blackCount++; - } - else - { - sumWhite += symbol[i]; - sumWhiteSq += symbol[i] * symbol[i]; - whiteCount++; - } - - if (symbol[i] > maxBarSize) - maxBarSize = symbol[i]; - } - - preparedSym.AvgBlack = sumBlack / blackCount; - preparedSym.StdDevBlack = 0.001f + (float)Math.Sqrt(sumBlackSq / blackCount - (sumBlack / blackCount) * (sumBlack / blackCount)); - - preparedSym.AvgWhite = sumWhite / whiteCount; - preparedSym.StdDevWhite = 0.001f + (float)Math.Sqrt(sumWhiteSq / whiteCount - (sumWhite / whiteCount) * (sumWhite / whiteCount)); - - preapredTable[iSymbol] = preparedSym; - } - } - } - - public int[] Read(float[] pixels, out float error, out float maxError, out float confidence) - { - if (noStartStop) - return ReadNoStartStop(pixels, out error, out maxError, out confidence); - - confidence = 1;//temp - error = 0; - maxError = 0; - - var res = new int[_nBars.Length]; - int iPixel = 0; - GotoBlack(pixels, ref iPixel); - var isBlack = true; - var first = true; - int iSymbol; - for (iSymbol = 0; iSymbol < _nBars.Length; iSymbol++) - { - int iStart = iPixel; - //read n bars - var n = _nBars[iSymbol]; - for (int i = 0; i < n; i++) - { - if (iPixel >= pixels.Length) - break; //no more bars - GotoNextBar(pixels, ref iPixel); - isBlack = !isBlack; - } - // - var iStop = iPixel; - //find best match - var iTable = _tableIndexes[iSymbol]; - //FindBestMatch(pixels, iStart, iStop, _patterns[iTable], isBlack ^ first ? 1 : -1); - first = false; - } - - //fill -1 - for (; iSymbol < res.Length; iSymbol++) - res[iSymbol] = -1; - - return res; - } - - private int[] ReadNoStartStop(float[] pixels, out float error, out float maxError, out float confidence) - { - var nSymbols = 0; - //prepare and check bars - CheckAndPrepare(pixels, out confidence, out nSymbols); - if (confidence < 0.01f) - { - error = 10; - maxError = 10; - return new int[0]; - } -#if DEBUG - DebugHelper.AddDebugItem("sym" + nSymbols, pixels); -#endif - var res = new int[nSymbols]; - var offset = _fixedBars % 2 == 0 ? _fixedBars - 1 : _fixedBars; - var patterns = _patterns[0]; - var sumCorr = 0f; - var worstCorr = 1f; - - //get start and stop position for each symbol - for (int iSymbol = 0; iSymbol < nSymbols; iSymbol++) - { - var i = iSymbol * _fixedBars; - - //find best match - int sym; - float corr; - FindBestMatch(i, i + offset, patterns, out sym , out corr); - res[iSymbol] = sym; - sumCorr += corr; - if (corr < worstCorr) - worstCorr = corr; - } - - //confidence = sumCorr / nSymbols; - confidence = sumCorr / nSymbols; - error = 1 - confidence; - maxError = 1 - worstCorr; - - return res; - } - - private List barWidth = new List(200); - private int barCount; - - private void CheckAndPrepare(float[] pixels, out float confidence, out int nSymbols) - { - barWidth.Clear(); - - confidence = 0; - nSymbols = 0; - - //go first black - var iPixel = 0; - GotoBlack(pixels, ref iPixel); - if (iPixel >= pixels.Length) return; - - var minBlack = int.MaxValue; - var maxBlack = 0; - var minWhite = int.MaxValue; - var maxWhite = 0; - - //calc bar positions, bars count , min/max bar length - var prevIndex = iPixel; - bool isBlack = true; - for (; iPixel < pixels.Length; iPixel++) - { - var v = pixels[iPixel]; - var changed = false; - - if (v < -0.3f) - changed = !isBlack; - else if (v < 0.3f) - changed = true; - else - changed = isBlack; - - if (changed) - { - var len = iPixel - prevIndex; - if (isBlack) - { - if (len > maxBlack) maxBlack = len; - if (len < minBlack) minBlack = len; - } - else - { - if (len > maxWhite) maxWhite = len; - if (len < minWhite) minWhite = len; - } - barWidth.Add(len); - prevIndex = iPixel; - isBlack = !isBlack; - } - } - - //add last - { - var len = iPixel - prevIndex; - if (isBlack) - { - if (len > maxBlack) maxBlack = len; - if (len < minBlack) minBlack = len; - } - else - { - if (len > maxWhite) maxWhite = len; - if (len < minWhite) minWhite = len; - } - barWidth.Add(len); //add ficitve white bar - } - - //check total bar count - var barCount = barWidth.Count; - if (barCount % 2 == 1) //add last fictive white bar - barCount++; - - nSymbols = barCount / _fixedBars;//symbols count - - - if (nSymbols * _fixedBars != barCount) //ups... is not barcode of my type - { -#if DEBUG - DebugHelper.AddDebugItem(""+ barCount, pixels); -#endif - return; - } - - //check difference between max and min length of bar - if (minBlack < 2) minBlack = 2; - if (maxBlack / (float)minBlack > maxBarSize * 3f) - return;//too wide or short bars - - if (minWhite < 2) minWhite = 2; - if (maxWhite / (float)minWhite > maxBarSize * 3f) - return;//too wide or short bars - - //all right - confidence = 1; - } - - private void FindBestMatch(int iStart, int iStop, SymbolInfo[] table, out int bestSymbolIndex, out float bestCorr) - { - //calc avg and stdDev for pixels - var sumBlackX = 0f; - var nBlack = 0; - var sumBlackXsq = 0f; - var sumWhiteX = 0f; - var nWhite = 0; - var sumWhiteXsq = 0f; - var isBlack = true; - for (int i = iStart; i < iStop; i++) - { - var v = barWidth[i]; - - if (isBlack) - { - sumBlackX += v; - sumBlackXsq += v * v; - nBlack++; - } - else - { - sumWhiteX += v; - sumWhiteXsq += v * v; - nWhite++; - } - isBlack = !isBlack; - } - - var avgBlack = sumBlackX / nBlack; - var stdDevBlack = 0.001f + (float)Math.Sqrt(sumBlackXsq / nBlack - avgBlack * avgBlack); - var avgWhite = sumWhiteX / nWhite; - var stdDevWhite = 0.001f + (float)Math.Sqrt(sumWhiteXsq / nWhite - avgWhite * avgWhite); - - //// - bestSymbolIndex = -1; - bestCorr = -2; - - for (int iSymbol = 0; iSymbol < table.Length; iSymbol++) - { - var symbol = table[iSymbol]; - var pattern = symbol.Pattern; - var count = nBlack + nWhite; - //calc black correlation with symbol - var sumXY = 0f; - for (int i = 0; i < count; i += 2) - sumXY += barWidth[iStart + i] * pattern[i]; - - var corrBlack = (sumXY / nBlack - avgBlack * symbol.AvgBlack + 0.001f * 0.001f) / (stdDevBlack * symbol.StdDevBlack); - - //calc white corr - sumXY = 0f; - for (int i = 1; i < count; i += 2) - sumXY += barWidth[iStart + i] * pattern[i]; - - var corrWhite = (sumXY / nWhite - avgWhite * symbol.AvgWhite + 0.001f * 0.001f) / (stdDevWhite * symbol.StdDevWhite); - - //total corr - var corr = Math.Min(corrBlack , corrWhite) - iSymbol / 1000f; - - if (corr > bestCorr) - { - bestCorr = corr; - bestSymbolIndex = iSymbol; - } - } - } - - - private void GotoBlack(float[] pixels, ref int iPixel) - { - for(;iPixel < pixels.Length;iPixel++) - if (pixels[iPixel] < 0) - break; - } - - void GotoNextBar(float[] pixels, ref int iPixel) - { - var isBlack = pixels[iPixel] < 0; - for (; iPixel < pixels.Length; iPixel++) - { - if ((pixels[iPixel] < 0) ^ isBlack) - return; - } - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/Candidate.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/Candidate.cs deleted file mode 100644 index d13a1a0c..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/Candidate.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace BarcodeReader.Core.Common -{ -#if CORE_DEV - public -#else - internal -#endif - class Candidate - { - public float ModuleEstimate; - public int BarsCount; - public int SumWhiteLength; - public int SumBlackLength; - public int LineLength; - public int MinBarLength; - public int MaxBarLength; - - public PatternCluster From; - public PatternCluster To; - - public BarCodeRegion Region; - - public Candidate(PatternCluster from , PatternCluster to) - { - this.From = from; - this.To = to; - } - } - - -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/LineScanner.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/LineScanner.cs deleted file mode 100644 index 0a7d0a16..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/LineScanner.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; - -namespace BarcodeReader.Core.Common -{ - /// - /// Scans image horizontally, finds bar's positions and lengthes - /// Even (0, 2, 4, ...) - white bars - /// Odd (1, 3, 5, ...) - black bars - /// -#if CORE_DEV - public -#else - internal -#endif - class LineScanner - { - // Even indexes (0, 2, 4, ...) - white bars - // Odd indexes (1, 3, 5, ...) - black bars - public int[] BarLength; - public int[] BarPos; - public int BarsCount = 0; - public int Y; - public bool Reverse; - - private int fromX; - private int toX; - - /// Inclusive start X - /// Exclusive end X - public LineScanner(int fromX, int toX) - { - BarPos = new int[toX + 4]; - BarLength = new int[toX + 4]; - this.fromX = fromX; - this.toX = toX; - } - - /// - /// Finds bar's lengthes and positions. - /// - /// - public void FindBars(XBitArray row, int y) - { - this.Y = y; - int iBar = -1; - - var prevBarX = -10000; - bool isBlack = false; - - for (int x = fromX; x < toX; x++) - { - if (row[x] ^ isBlack)//change color - { - iBar++; - BarLength[iBar] = x - prevBarX; - BarPos[iBar] = prevBarX; - prevBarX = x; - isBlack = !isBlack; - } - } - - //close last black bar - if (isBlack) - { - iBar++; - BarLength[iBar] = toX - prevBarX; - BarPos[iBar] = prevBarX; - prevBarX = toX; - } - - //add long right white bar - iBar++; - BarLength[iBar] = 20000; - BarPos[iBar] = prevBarX; - - BarsCount = iBar + 1; - } - - /// - /// !!!!! - /// Finds bars, allowing noise removing. Noise level definies the number of pixels considered as noise. - /// noiseLevel=1: accept modules > 1 pixel wide. - /// noiseLevel=2: accept modules > 2 pixel wide. - /// - public void FindBars(int y, int noiseLevel) - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/LinearPatternFinder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/LinearPatternFinder.cs deleted file mode 100644 index edfb4ce6..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/LinearPatternFinder.cs +++ /dev/null @@ -1,326 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace BarcodeReader.Core.Common -{ - /// - /// Base class for pattern finders - /// - abstract class LinearPatternFinder - { - protected float maxModuleSize; - protected float minQuietZone; - protected float maxPatternSymbolDifference; - protected float maxPatternAverageSymbolDifference; - protected int[] pattern; - - protected int[] moduleWidths; - protected float sumPatternModules; - - public LinearPatternFinder(LinearReader reader, int[] pattern) - { - this.minQuietZone = reader.MinQuietZone; - this.maxModuleSize = reader.maxModuleSize; - this.maxPatternSymbolDifference = reader.MaxPatternSymbolDifference; - this.maxPatternAverageSymbolDifference = reader.MaxPatternAverageSymbolDifference; - this.pattern = pattern; - - //calc sum of bar modules - sumPatternModules = Utils.Sum(pattern); - - if (minQuietZone <= 0) - throw new Exception("QuietZone must be more 0"); - - moduleWidths = new int[pattern.Length]; - } - - public LinearPatternFinder(int[] pattern) - { - this.minQuietZone = 5; - this.maxModuleSize = 200; - this.maxPatternSymbolDifference = 0.85f;//0.83f; - this.maxPatternAverageSymbolDifference = 0.55f;//0.55f; - this.pattern = pattern; - - //calc sum of bar modules - sumPatternModules = Utils.Sum(pattern); - - if (minQuietZone <= 0) - throw new Exception("QuietZone must be more 0"); - - moduleWidths = new int[pattern.Length]; - } - - public abstract IEnumerable FindPattern(LineScanner line); - } - - /// - /// Finds start patterns with Quiet Zone - /// - class StartPatternFinder : LinearPatternFinder - { - public StartPatternFinder(LinearReader reader, int[] pattern) : base(reader, pattern) - { - } - - public StartPatternFinder(int[] pattern) : base(pattern) - { - } - - public override IEnumerable FindPattern(LineScanner line) - { - var barPos = line.BarPos; - var barLength = line.BarLength; - var barCount = line.BarsCount; - int nBar = pattern.Length; - int addWhiteBar = 0; - if (pattern.Length % 2 == 0) - { - nBar--; - addWhiteBar = 1; - } - - //enumerate black bars - for (int i = nBar; i < barCount - 5; i += 2) - { - //get quietZone white bar - var iQuietZone = i - nBar; - var iPatternStart = iQuietZone + 1; - var lengthQuietZone = barLength[iQuietZone]; - var patternStartPos = barPos[iPatternStart]; - var patternEndPos = barPos[i + 1 + addWhiteBar]; - var lengthPattern = patternEndPos - patternStartPos; - var moduleLength = 1f * lengthPattern / sumPatternModules; - if (moduleLength > maxModuleSize) - continue; - - //quietZone must be more MinQuietZone modules - if (lengthQuietZone >= minQuietZone * moduleLength) - { - //var Next3BarsLength = barLength[i + 1] + barLength[i + 2] + barLength[i + 3]; - //if (Next3BarsLength > 9 * moduleLength) - // continue; - - var sumBars = 0; - for (int j = 0; j < moduleWidths.Length; j++) - moduleWidths[j] = barLength[iPatternStart + j]; - - if (SymbologyReader.calcDifference(moduleWidths, pattern, maxPatternSymbolDifference, false) < maxPatternAverageSymbolDifference) - { - yield return new Pattern(new FoundPattern(0, 0, 0, moduleLength), patternStartPos, patternEndPos, line.Y); - } - } - } - } - } - - - /// - /// Finds start patterns with Quiet Zone - /// - class StartPatternFinderSimple : LinearPatternFinder - { - public StartPatternFinderSimple(LinearReader reader, int[] pattern) : base(reader, pattern) - { - } - - public StartPatternFinderSimple(int[] pattern) : base(pattern) - { - } - - public override IEnumerable FindPattern(LineScanner line) - { - var barPos = line.BarPos; - var barLength = line.BarLength; - var barCount = line.BarsCount; - int nBar = pattern.Length; - int addWhiteBar = 0; - if (pattern.Length % 2 == 0) - { - nBar--; - addWhiteBar = 1; - } - - //enumerate black bars - for (int i = nBar; i < barCount - 5; i += 2) - { - //get quietZone white bar - var iQuietZone = i - nBar; - var iPatternStart = iQuietZone + 1; - var lengthQuietZone = barLength[iQuietZone]; - var patternStartPos = barPos[iPatternStart]; - var patternEndPos = barPos[i + 1 + addWhiteBar]; - var lengthPattern = patternEndPos - patternStartPos; - var moduleLength = 1f * lengthPattern / sumPatternModules; - if (moduleLength > maxModuleSize) - continue; - - //quietZone must be more MinQuietZone modules - if (lengthQuietZone >= minQuietZone * moduleLength) - { - yield return new Pattern(new FoundPattern(0, 0, 0, moduleLength), patternStartPos, patternEndPos, line.Y); - } - } - } - } - - /// - /// Finds stop patterns with Quiet Zone - /// - class StopPatternFinder : LinearPatternFinder - { - public StopPatternFinder(LinearReader reader, int[] pattern) : base(reader, pattern) - { - if (pattern.Length % 2 == 0) - throw new Exception("Pattern length must be Odd (1, 3, 5, ...)"); - } - - public override IEnumerable FindPattern(LineScanner line) - { - var barPos = line.BarPos; - var barLength = line.BarLength; - var barCount = line.BarsCount; - int nBar = pattern.Length; - - //enumerate white bars - for (int i = nBar + 7; i < barCount; i += 2) - { - //get quietZone white bar - var iQuietZone = i; - var lengthQuietZone = barLength[iQuietZone]; - if (lengthQuietZone < minQuietZone) continue;//too small white bar - var iPatternStart = iQuietZone - nBar; - var patternStartPos = barPos[iPatternStart]; - var patternEndPos = barPos[iQuietZone]; - var lengthPattern = patternEndPos - patternStartPos; - var moduleLength = 1f * lengthPattern / sumPatternModules; - if (moduleLength > maxModuleSize) continue;//too small module - //quietZone must be more MinQuietZone modules - if (lengthQuietZone >= minQuietZone * moduleLength) - { - //var Prev3BarsLength = barLength[iPatternStart - 1] + barLength[iPatternStart - 2] + barLength[iPatternStart - 3]; - //if (Prev3BarsLength > 9 * moduleLength) - // continue; - - for (int j = 0; j < moduleWidths.Length; j++) - moduleWidths[j] = barLength[iPatternStart + j]; - - var maxDiff = maxPatternSymbolDifference; - var maxAvgDiff = maxPatternAverageSymbolDifference; - if (moduleLength < 1.6f) - { - maxDiff *= 1.5f; - maxAvgDiff *= 1.3f; - } - - if (SymbologyReader.calcDifference(moduleWidths, pattern, maxDiff, false) < maxAvgDiff) - { - yield return new Pattern(new FoundPattern(1, 0, 0, moduleLength), patternStartPos, patternEndPos, line.Y); - } - } - } - } - } - - /// - /// Finds empty stop pattern with Quiet Zone - /// In fact it finds black line with right hand Quiet Zone - /// - class StopEmptyPatternFinder : LinearPatternFinder - { - public StopEmptyPatternFinder(LinearReader reader) : base(reader, new int[0]) - { - } - - public override IEnumerable FindPattern(LineScanner line) - { - var barPos = line.BarPos; - var barLength = line.BarLength; - var barCount = line.BarsCount; - const int nBar = 1; - - //enumerate white bars - for (int i = nBar + 7; i < barCount; i += 2) - { - //get quietZone white bar - var iQuietZone = i; - var lengthQuietZone = barLength[iQuietZone]; - var lengthPattern = barLength[iQuietZone - 1]; - if (lengthQuietZone < 6) continue;//too small white bar - if (lengthPattern > 4 * maxModuleSize) continue;//too big black bar - var module = lengthPattern / 1.5f; - if (module < 2) module = 2; - //quietZone must be more MinQuietZone modules - if (lengthQuietZone >= minQuietZone * module) - { - //check size diapason of previous bars - var low = lengthPattern / 5; - var high = lengthPattern * 5; - - var b = barLength[i - 2]; - if (b < low || b > high) continue; - - b = barLength[i - 3]; - if (b < low || b > high) continue; - - b = barLength[i - 4]; - if (b < low || b > high) continue; - - var pos = barPos[i]; - yield return new Pattern(new FoundPattern(-1, 0, 0, 0), pos, pos, line.Y); - } - } - } - } - - /// - /// Finds empty start pattern with Quiet Zone - /// In fact it finds black line with left hand Quiet Zone - /// - class StartEmptyPatternFinder : LinearPatternFinder - { - public StartEmptyPatternFinder(LinearReader reader) : base(reader, new int[0]) - { - } - - public override IEnumerable FindPattern(LineScanner line) - { - var barPos = line.BarPos; - var barLength = line.BarLength; - var barCount = line.BarsCount; - - //enumerate white bars - for (int i = 0; i < barCount - 5; i += 2) - { - //get quietZone white bar - var iQuietZone = i; - var lengthQuietZone = barLength[iQuietZone]; - var lengthPattern = barLength[iQuietZone + 1]; - if (lengthQuietZone < 6) continue;//too small white bar - if (lengthPattern > 4 * maxModuleSize) continue;//too big black bar - var module = lengthPattern / 1.5f; - if (module < 2) module = 2; - //quietZone must be more MinQuietZone modules - if (lengthQuietZone >= minQuietZone * module) - { - //check size diapason of next bars - var low = lengthPattern / 5; - var high = lengthPattern * 5; - - var b = barLength[i + 2]; - if (b < low || b > high) continue; - - b = barLength[i + 3]; - if (b < low || b > high) continue; - - b = barLength[i + 4]; - if (b < low || b > high) continue; - - var pos = barPos[i + 1]; - yield return new Pattern(new FoundPattern(-1, 0, 0, 0), pos, pos, line.Y); - } - } - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/LinearReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/LinearReader.cs deleted file mode 100644 index 3eb51636..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/LinearReader.cs +++ /dev/null @@ -1,540 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using SkiaSharp; -using System.IO; -using BarcodeReader.Core.Code39; -using BarcodeReader.Core.Code93; - -namespace BarcodeReader.Core.Common -{ - /// - /// Base class to read linear 1D barcodes. - /// Scans image, finds start and stop patterns. - /// Finds barcode regions with different angles (from -45 to 45 degrees). - /// Calls abstract methods GetBarSymbolReader and Decode to decode found barcode area. - /// -#if CORE_DEV - public -#else - internal -#endif - abstract partial class LinearReader : SymbologyReader2D - { - internal float maxModuleSize; - protected int minModulesPerBarcode; - protected int maxModulesPerBarcode; - protected bool stopAndStartPatternAreMirrored; - - protected ImageScaner Scan { get; private set; } - - /// - /// To scan barcode in both forward and reverse order - /// - public bool ReverseEnabled { get; set; } = false; - - /// - /// Region Of Interest. - /// Reader finds barcodes only inside ROI. - /// If empty - reads whole image. - /// - public SKRect ROI { get; set; } = SKRect.Empty; - - #region Parameters that affect on quality and speed of recognition - - //min QuietZone size (in mudules) - public float MinQuietZone { get; set; } = 5; //5 - //Max distance Y between patterns to create new cluster - public int MaxClusterDistanceY { get; set; } = 8; //8 - //Max distance X between patterns to create new cluster - public int MaxClusterDistanceX { get; set; } = 5; //5 - //Min count of patterns in cluster to start recognition process for the barcode - public int MinClusterSize { get; set; } = 13; // default = 15, for barcodes with small height = 6 - //Scan different lines inside barcode region - public bool TryDifferentLinesOfBarcodeRegion { get; set; } = true;//enable for bad(crumpled, noised, skewed) barcodes - //Different lines to scan region - public float[] MidPoints { get; set; } = new float[] { 0.5f, 0.3f, 0.7f, 0.4f, 0.6f, 0.1f, 0.9f, 0.97f/*, 0.2f, 0.8f*/ }; - public float[] ModuleKoeffs { get; set; } = new float[] { 1f }; - //public float[] ModuleKoeffs { get; set; } = new float[] { 1f, 1.2f, 1.3f, 1.5f, 0.9f, 0.7f }; - //public float[] midPoints { get; set; } = new float[] { 0.5f, 0.3f, 0.7f, 0.4f, 0.6f/*, 0.1f, 0.9f, 0.2f, 0.8f*/}; - public float MaxPatternSymbolDifference { get; set; } = 0.83f; // 0.65f, increse this value if black and white bars have too diff width - public float MaxPatternAverageSymbolDifference { get; set; } = 0.55f; // 0.45f, increse this value if black and white bars have too diff width - public float MaxBarcodeAngle { get; set; } = 60 * (float)Math.PI / 180; - public float MaxSkewAngle = 20 * (float)Math.PI / 180; - public float MaxSkew = 5f; - public bool UseE { get; set; } = false; - public float MaxReadError = 1f; // 1 - public float MinConfidence { get; set; } = 0.6f;//0.6 - public float MinRobustConfidence { get; set; } = 0.8f; - public float MaxLeftAndRightModulesDifference = 1.5f; - public float MaxBarLengthDifference = 5f;//for barcodes with max length of bar = 2, if more - you need to increse this value - - #endregion - - #region Barcode specific methods. Must be overrided in inherited class - - // Reader of symbols (must be assigned in inherited classes) - internal BarSymbolReader Reader { get; set; } - //minModulesPerBarcode - min modules per barcode (for example for EAN13 it is 95) - internal abstract void GetParams(out int[] startPattern, out int[] stopPattern, out int minModulesPerBarcode, out int maxModulesPerBarcode); - //decode specific barcode symbols - internal abstract bool Decode(BarCodeRegion r, int[] row); - - #endregion - - #region ctors - - public LinearReader() - { - MinAllowedBarcodeSideSize = 20; - MaxAllowedBarcodeSideSize = 6000; - } - - public void CopyTo(LinearReader other) - { - other.MinAllowedBarcodeSideSize = MinAllowedBarcodeSideSize; - other.MaxAllowedBarcodeSideSize = MaxAllowedBarcodeSideSize; - other.Scan = Scan; - other.ReverseEnabled = ReverseEnabled; - other.ROI = ROI; - - other.MinQuietZone = MinQuietZone; - other.MaxClusterDistanceY = MaxClusterDistanceY; - other.MaxClusterDistanceX = MaxClusterDistanceX; - other.MinClusterSize = MinClusterSize; - other.TryDifferentLinesOfBarcodeRegion = TryDifferentLinesOfBarcodeRegion; - other.MidPoints = MidPoints; - other.MaxPatternSymbolDifference = MaxPatternSymbolDifference; - other.MaxPatternAverageSymbolDifference = MaxPatternAverageSymbolDifference; - other.MaxBarcodeAngle = MaxBarcodeAngle; - other.UseE = UseE; - - - other.ThresholdFilterMethodToUse = ThresholdFilterMethodToUse; - other.ScanStep = ScanStep; - other.MinAllowedBarcodeSideSize = MinAllowedBarcodeSideSize; - other.MaxAllowedBarcodeSideSize = MaxAllowedBarcodeSideSize; - other.ExpectedNumberOfBarcodes = ExpectedNumberOfBarcodes; - other.StopOnFirstFoundBarcodeInTheRow = StopOnFirstFoundBarcodeInTheRow; - other.Encoding = Encoding; - other.TimeoutTimeInTicks = TimeoutTimeInTicks; - } - #endregion - - public override FoundBarcode[] Decode(BlackAndWhiteImage bwImage) - { - Scan = new ImageScaner(bwImage); - return base.Decode(bwImage); - } - - protected override FoundBarcode[] DecodeBarcode() - { - var result = new List(); - - //get specific parameters - int[] startPattern; - int[] stopPattern; - - GetParams(out startPattern, out stopPattern, out minModulesPerBarcode, out maxModulesPerBarcode); - stopAndStartPatternAreMirrored = Utils.IsMirrored(startPattern, stopPattern); - - //pass in forward direction - DecodeBarcode(result, startPattern, stopPattern, false); - - //pass in reverse direction - if (ReverseEnabled && !stopAndStartPatternAreMirrored) - { - DecodeBarcode(result, Utils.Reverse(stopPattern), Utils.Reverse(startPattern), true); - } - - // Sort results by Y, then X - result.Sort((x, y) => - { - float r = x.Rect.Top - y.Rect.Top; - - if (Math.Abs(r) < 50) - r = x.Rect.Left - y.Rect.Left; - - return (int)r; - }); - - return result.ToArray(); - } - - /// - /// Main method to find and recognize barcodes - /// - protected virtual void DecodeBarcode(List result, int[] startPattern, int[] stopPattern, bool reverseReading) - { - //define area of scanning - var fromX = 0; - var toX = Scan.Width; - var fromY = 0; - var toY = Scan.Height; - - if (ROI != SKRect.Empty) - { - fromX = (int)Math.Max(0, ROI.Left); - fromY = (int)Math.Max(0, ROI.Top); - toX = (int)Math.Min(toX, ROI.Right); - toY = (int)Math.Min(toY, ROI.Bottom); - } - - //line scanner - LineScanner line = new LineScanner(fromX, toX); - - //calc max module size - var imgSize = Math.Max(BWImage.Width, BWImage.Height); - maxModuleSize = 1.4f * imgSize / (float)minModulesPerBarcode; - - //create clusters stuffs - var startClustersMap = new PatternCluster[BWImage.Width + 20]; - var startClusters = new LinkedList(); - var stopClustersMap = new PatternCluster[BWImage.Width + 20]; - var stopClusters = new LinkedList(); - - //create pattern finders - LinearPatternFinder startFinder; - if (startPattern.Length > 0) - startFinder = new StartPatternFinder(this, startPattern); - else - startFinder = new StartEmptyPatternFinder(this); - - LinearPatternFinder stopFinder; - if (stopPattern.Length > 0) - stopFinder = new StopPatternFinder(this, stopPattern); - else - //if stop pattern is empty, create special finder - stopFinder = new StopEmptyPatternFinder(this); - - - //scan lines, find patterns, create clusters - for (int y = fromY; y < toY; y += ScanStep) - { - //calc bars - line.FindBars(BWImage.GetRow(y), y); - - //find start patterns - foreach (var p in startFinder.FindPattern(line)) - { - CreateClusterForPattern(startClustersMap, startClusters, p); - #if DEBUG - DebugHelper.DrawSquare(p.xIn, p.y, SKColors.MistyRose); - #endif - } - - //find stop patterns - foreach (var p in stopFinder.FindPattern(line)) - { - CreateClusterForPattern(stopClustersMap, stopClusters, p); - #if DEBUG - DebugHelper.DrawSquare(p.xIn, p.y, SKColors.LightBlue); - #endif - } - } - - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - //filter small clusters, generate common list of clusters - var clusters = new List(); - PrepareClusters(clusters, startClusters, false); - PrepareClusters(clusters, stopClusters, true); - - //sort clusters by X - //clusters.Sort((c1, c2) => c1.A.X.CompareTo(c2.A.X)); - - //sort clusters by Size - clusters.Sort((c1, c2) => -c1.Count.CompareTo(c2.Count)); - - //enumerate start clusters, find stop cluster for them, try to recognize - for (int iStart=0; iStart < clusters.Count;iStart++) - if (!clusters[iStart].IsStopPattern)//is it start pattern ? - { - var start = clusters[iStart]; - //choose opposite stop cluster - for (int iStop = 0; iStop < clusters.Count; iStop++) - if (clusters[iStop].IsStopPattern)//is it stop pattern ? - if (clusters[iStop].OppositeCluster == null)//is not used ? - { - var stop = clusters[iStop]; - - if (start.A.X > stop.A.X) - continue;//stop pattern con not be left from start pattern - - //create candidate - var cand = new Candidate(start, stop); - - //check and prepare candidate - if (!CheckAndPrepare(cand)) - continue;//do not match - - //try to recognize - var res = FindBarcode(cand, reverseReading); - - //try reverse direction - if ((res == null || res.Confidence < 0.7f) && ReverseEnabled && stopAndStartPatternAreMirrored) - { - //make reverse pass - var reverseRes = FindBarcode(cand, !reverseReading); - if (res == null || res.Confidence < reverseRes.Confidence) - res = reverseRes; - } - - //recognized? - if (res != null) - { - //check - is it already exists in result list? - //add to result list - AddIfNotExists(result, res); - - if (res.Confidence > MinRobustConfidence) - { - //stop searching other barcodes with these clusters - clusters[iStart].OppositeCluster = clusters[iStop]; - clusters[iStop].OppositeCluster = clusters[iStart]; - break; - } - } - } - - if (ExpectedNumberOfBarcodes > 0 && result.Count >= ExpectedNumberOfBarcodes) - return; - - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - } - } - - private void AddIfNotExists(List result, FoundBarcode res) - { - var temp = new List(result); - - foreach (var r in temp) - { - if (r.ParentRegion.IntersectsWith(res.ParentRegion, 5)) - { - if (r.Value == res.Value) - return;//do not add - exists - - if (res.Confidence < r.Confidence * 0.9f) - return;//small confidence - - //inside ? - var size1 = (r.ParentRegion.A - r.ParentRegion.B).Length; - var size2 = (res.ParentRegion.A - res.ParentRegion.B).Length; - if (size1 > size2) - return;//small size - - //remove old - result.Remove(r); - } - } - - result.Add(res); - } - - /// - /// Try to read and recognize barcode in found region - /// - protected virtual FoundBarcode FindBarcode(Candidate cand, bool reverse) - { -#if DEBUG - //DebugHelper.DrawRegion(System.Drawing.Color.Red, region); - //DebugHelper.Counter0++; -#endif - var start = cand.From; - var stop = cand.To; - var region = cand.Region; - var m = cand.ModuleEstimate; - var iStart = start.Count / 2; - var iStop = stop.Count / 2; - - var firstPass = true; - -#if DEBUG - //!!!!! - var midFrom = start.GetMiddlePoint(); - var midTo = stop.GetMiddlePoint(); - DebugHelper.DrawArrow(midFrom.X, midFrom.Y, midTo.X, midTo.Y, SKColors.MistyRose); -#endif - - //scan different lines of region - foreach (var mid in MidPoints) - { - //calc points - iStart = (int)(start.Count * mid); - iStop = (int)(stop.Count * mid); - - var from = start.GetPoint(iStart); - var to = stop.GetPoint(iStop); - - //swap if reverse - if (reverse) - Utils.Swap(ref from, ref to); - - //var from = MyPointF.Lerp(region.A, region.D, mid); - //var to = MyPointF.Lerp(region.B, region.C, mid); - - var startPattern = start[iStart]; - var stopPattern = stop[iStop]; - - //try to read and decode symbols between From and To - if (ReadSymbols(region, startPattern, stopPattern, from, to, m, firstPass)) - break; //successfully recognized - - if (!TryDifferentLinesOfBarcodeRegion) - break; //do not try other lines - - firstPass = false; - } - - if (region.Confidence <= float.Epsilon) - return null;//is not recognized - - //expand region - Track(start, m, ref region.A, ref region.D); - Track(stop, m, ref region.B, ref region.C); - - region.Reversed = reverse; - - return new FoundBarcode(GetBarCodeType(), region); - } - - //try to read and decode symbols between From and To - internal virtual bool ReadSymbols(BarCodeRegion region, Pattern startPattern, Pattern stopPattern, MyPoint from, MyPoint to, float module, bool firstPass) - { - float error, maxError, confidence; - int[] row = ReadSymbols(startPattern, stopPattern, from, to, module, out error, out maxError, out confidence); - - if (error < MaxReadError) - { - if (confidence >= MinConfidence) - if (confidence > region.Confidence) - if (Decode(region, row)) //try to decode - { - region.Confidence = confidence; - return true; - } - } - return false; - } - - //try to read symbols between From and To - internal virtual int[] ReadSymbols(Pattern leftPattern, Pattern rightPattern, MyPointF from, MyPointF to, float module, out float error, out float maxError, out float confidence) - { - return Reader.Read(Scan, module, from, to, out error, out maxError, out confidence); - } - - private void PrepareClusters(List result, LinkedList clusters, bool isStopPattern) - { - var minClusterSize = Math.Max(4, this.MinClusterSize / ScanStep); - foreach (var cl in clusters) - { - //Calc adaptive cluster size - //for thin lines (module < 1.5px) - reduce cluster size by 2 times - var minSize = cl.AvgModule <= 1.5f ? minClusterSize / 2 : minClusterSize; - if (minSize < 5) minSize = 5; - if (cl.Count >= minSize) - { - cl.CalcParams(isStopPattern); - result.Add(cl); - -#if DEBUG - //!!!! - foreach (var p in cl) - DebugHelper.DrawSquare(isStopPattern ? p.xEnd : p.xIn, p.y, - isStopPattern ? SKColors.Blue : SKColors.Red); - //DebugHelper.DrawArrow(cl.A.X, cl.A.Y, cl.B.X, cl.B.Y, isStopPattern ? Color.Blue : Color.Red); -#endif - } - } - } - - private void CreateClusterForPattern(PatternCluster[] clustersMap, LinkedList clusters, Pattern pattern) - { - var w = clustersMap.Length; - // - var myY = pattern.y; - var myX = Math.Min(pattern.xIn + MaxClusterDistanceX / 2, w - 1); - - //find my cluster above - PatternCluster foundCluster = null; - var cl = clustersMap[myX]; - - //check cluster and module size - if (cl != null) - { - var last = cl.Last.Value; - if (myY - last.y < MaxClusterDistanceY - && Math.Abs(pattern.xIn - last.xIn) < MaxClusterDistanceX - && Calc.Around(pattern.foundPattern.moduleLength, last.foundPattern.moduleLength, 1f) - ) - { - //join to cluster - cl.AddLast(pattern); - foundCluster = cl; - } - } - - //create new cluster - if(foundCluster == null) - { - foundCluster = new PatternCluster(); - foundCluster.AddLast(pattern); - clusters.AddLast(foundCluster); - } - - foundCluster.SumModule += pattern.foundPattern.moduleLength; - - //set my cluster to map - var toX = Math.Min(w, pattern.xIn + MaxClusterDistanceX); - for (int x = pattern.xIn; x < toX; x++) - { - clustersMap[x] = foundCluster; - } - } - - void Track(PatternCluster cluster, float module, ref MyPointF A, ref MyPointF B) - { - var i = (int)(cluster.Count / 2); - var p = cluster[i]; - var x1 = p.xIn; - var x2 = p.xEnd; - var patternLengthInModuls = 0; - if (x1 >= x2) - { - x1 = x2 - 1; - patternLengthInModuls = 1; - } - else - { - patternLengthInModuls = (int)Math.Round((x2 - x1) / p.foundPattern.moduleLength); - } - - MyPointF up, down; - EdgeTrack et = new EdgeTrack(Scan); - - //first try using the first bar (thin bar) - et.Track(new MyPoint(x1, p.y), new MyVector(-1, 0), module, true); - - //find the top and bottom points of the edge - up = et.Up(); - down = et.Down(); - - if (cluster.IsStopPattern) - { - MyVectorF n = (up - down).Normalized; - n = new MyVectorF(-n.Y, n.X); - up += n * module * patternLengthInModuls; - down += n * module * patternLengthInModuls; - } - - if ((up - down).LengthSq > (A - B).LengthSq) - { - A = up; - B = down; - } - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/LinearReader_CandFilter.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/LinearReader_CandFilter.cs deleted file mode 100644 index 5c1ac424..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/LinearReader_CandFilter.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System; -using System.Drawing; - -namespace BarcodeReader.Core.Common -{ - /// - /// Filter of candidates - /// - partial class LinearReader - { - private bool CheckAndPrepare(Candidate cand) - { - var start = cand.From; - var stop = cand.To; - - //check angle - var p1 = start.GetMiddlePoint(); - var p2 = stop.GetMiddlePoint(); - var axis = p2 - p1; - var angle = axis.Angle; - if (angle > MaxBarcodeAngle || angle < -MaxBarcodeAngle) - return false; - - var m1 = start.AvgModule; - var m2 = stop.AvgModule; - if (m2 <= float.Epsilon) m2 = m1;//empty stop pattern - if (m1 <= float.Epsilon) m1 = m2;//empty start pattern - - - //check difference of modules of stop and start pattern - if (m1 > 0 & m2 > 0) - if (m1 / (m2 + 1) > MaxLeftAndRightModulesDifference || - m2 / (m1 + 1) > MaxLeftAndRightModulesDifference) - return false; - - //calc module - var m = (m1 + m2) / 2; - m = m * (float)Math.Cos(angle);//correct average module - cand.ModuleEstimate = m; - - //check distance between start and stop points - var len = axis.Length; - var modulesCount = len / m; - if (modulesCount < minModulesPerBarcode * 0.7f || - modulesCount > maxModulesPerBarcode * 1.7f) - return false; - - //check width of barcode - if (len < MinAllowedBarcodeSideSize || - len > MaxAllowedBarcodeSideSize) - return false; - - //check skewing - if (!CheckSkewing(start, stop, angle)) - return false; - - //estimate module - if (!CheckAndPrepareBars(cand)) - return false; - - //create region - cand.Region = new BarCodeRegion(cand.From.A, cand.To.A, cand.To.B, cand.From.B); - -#if DEBUG - var from = cand.From.GetMiddlePoint(); - var to = cand.To.GetMiddlePoint(); - //DebugHelper.AddDebugItem(from.ToString(), line, from, to); -#endif - - return true; - } - - private bool CheckSkewing(PatternCluster cl1, PatternCluster cl2, float axisAngle) - { - if (cl1.Count < cl2.Count) - return CheckSkewing(cl2, cl1, axisAngle); - - var maxSkewAngle = MaxSkewAngle; - - //clac skewing - //max distance - var maxDist = cl1.Count * MaxSkew / 2; - if (cl1.Length <= 15) - { - maxDist *= 2.5f; - maxSkewAngle *= 1.2f; - } - - //calc dist from ends of cl2 to axis - var O = cl1.GetMiddlePoint(); - var A = new MyVectorF(cl2.A.X - O.X, cl2.A.Y - O.Y); - var dA = A.NormalFromLine(cl1.Normal).Length;//distance from A to axis - - var B = new MyVectorF(cl2.B.X - O.X, cl2.B.Y - O.Y); - var dB = B.NormalFromLine(cl1.Normal).Length;//distance from B to axis - - if (Math.Min(dA, dB) > maxDist) return false; - - //check skew angle - if (Math.Abs(cl1.Normal.Angle - axisAngle) > maxSkewAngle) - return false; - if (Math.Abs(cl2.Normal.Angle - axisAngle) > maxSkewAngle) - return false; - - return true; - } - - private bool CheckAndPrepareBars(Candidate cand) - { - return true; - - //get middle points - var from = cand.From.GetMiddlePoint(); - var to = cand.To.GetMiddlePoint(); - - //calc bars - int BarsCount = 0; - int SumWhiteLength = 0; - int SumBlackLength = 0; - int MinBarLength = int.MaxValue; - int MaxBarLength = 0; - - var br = new Bresenham(from, to); - var isBlack = Scan.isBlack(br.Current); - var count = 0; - var prevBar = 0; - - while (!br.End()) - { - if (isBlack ^ Scan.isBlack(br.Current)) - { - var len = count - prevBar; - if (isBlack) - SumBlackLength += len; - else - SumWhiteLength += len; - - if (len > MaxBarLength) MaxBarLength = len; - if (len < MinBarLength) MinBarLength = len; - - BarsCount++; - prevBar = count; - isBlack = !isBlack; - } - count++; - br.Next(); - } - - //check difference between min and max bars - var min = MinBarLength + 2f; - var max = MaxBarLength + 2f; - if (max / min > MaxBarLengthDifference) - return false; - - cand.LineLength = count; - cand.BarsCount = BarsCount; - cand.MinBarLength = MinBarLength; - cand.MaxBarLength = MaxBarLength; - cand.SumWhiteLength = SumWhiteLength; - cand.SumBlackLength = SumBlackLength; - - //cand.ModuleEstimate = cand.MaxBarLength / 2;//!!!!!! temp - - return true; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/PatternCluster.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/PatternCluster.cs deleted file mode 100644 index d8068d8c..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/LinearReader/PatternCluster.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System.Collections.Generic; - -namespace BarcodeReader.Core.Common -{ - /// - /// Contains patterns joined vertically. - /// Analog of StackedPattern. - /// Used for LinearReader. - /// -#if CORE_DEV - public -#else - internal -#endif - class PatternCluster : LinkedList - { - public MyPoint A; - public MyPoint B; - public bool IsStopPattern; - public PatternCluster OppositeCluster; - public float SumModule = 0f; - public float AvgModule { get { return SumModule > float.Epsilon ? SumModule / Count : 0; }} - - public MyVectorF Normal;//perpendicular normalized vector to the cluster cloud - public MyVectorF Dir;//vector along cluster point cloud - public int RobustIndexA; - public int RobustIndexB; - public MyPoint RobustA; - public MyPoint RobustB; - public float Length; - - private List _list; - - public Pattern this[int index] - { - get { return _list[index]; } - } - - public MyPoint GetPoint(int index) - { - var p = _list[index]; - return new MyPoint(IsStopPattern ? p.xEnd : p.xIn, p.y); - } - - public MyPoint GetMiddlePoint() - { - var p = _list[Count / 2]; - return new MyPoint(IsStopPattern ? p.xEnd : p.xIn, p.y); - } - - public void CalcParams(bool isStopPattern) - { - this.IsStopPattern = isStopPattern; - A = new MyPoint(isStopPattern ? First.Value.xEnd : First.Value.xIn, First.Value.y); - B = new MyPoint(isStopPattern ? Last.Value.xEnd : Last.Value.xIn, Last.Value.y); - - _list = new List(this); - - //calc robust ends - if (Count < 8) - { - RobustIndexA = 0; - RobustIndexB = Count - 1; - RobustA = A; - RobustB = B; - } - else - { - //cut points 30% - 70% - RobustIndexA = (int)(Count * 0.3f); - RobustIndexB = (int)(Count * 0.7f); - RobustA = GetPoint(RobustIndexA); - RobustB = GetPoint(RobustIndexB); - } - - //Calc normal to the border - Normal = new MyVectorF(RobustB.Y - RobustA.Y, RobustA.X - RobustB.X).Normalized; - - //calc direction and length - Dir = new MyVectorF(RobustB.X - RobustA.X, RobustB.Y - RobustA.Y).Normalized; - Length = (A - B).Length; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/MICR/MICR.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/MICR/MICR.cs deleted file mode 100644 index 3003fd57..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/MICR/MICR.cs +++ /dev/null @@ -1,371 +0,0 @@ -using SkiaSharp; -using System; -using System.Drawing; - -namespace BarcodeReader.Core.MICR -{ - internal class MICR - { - //if the matched char has an error proceed - protected float maxMatchCharError = 0.40F; - - BlackAndWhiteImage img; - - //Initialize borders gray level for each MICR symbol. This is used to adjust - //image borders before matching is done. For example, 0 has a left, right, top - //and bottom borders black. Then, when we call GetChar, we crop the image to fit - //with border gray levels. This is usefull to remove noise around a symbol. - public MICR() - { - if (borders == null) - { - borders = new Borders[chars.Length]; - lock (borders) - for (int j = 0; j < chars.Length; j++) - { - int[][] c = chars[j]; - int l = 0, r = 0, t = 0, b = 0; - int cols = c[0].Length, cols1 = cols - 1; - int rows = c.Length, rows1 = rows - 1; - for (int i = 0; i < rows; i++) l += c[i][0]; - for (int i = 0; i < rows; i++) r += c[i][cols1]; - for (int i = 0; i < cols; i++) t += c[0][i]; - for (int i = 0; i < cols; i++) b += c[rows1][i]; - borders[j] = new Borders((float)l / (float)(2 * rows), (float)r / (float)(2 * rows), - (float)t / (float)(2 * cols), (float)b / (float)(2 * cols)); - } - } - } - - const float EPS = 0.2F; - //To crop the image in the rectangle r, crop the 4 borders to fit the precalculated - //level of gray. - SKRect Crop(Borders b, SKRect r) - { - int h = (int)r.Height; - int w2 = (int)Math.Floor(r.Width / 4f) ; if (w2 < 2) w2 = 2; - int h2 = (int)Math.Floor(h / 4f); if (h2 < 2) h2 = 2; - int xIn = MaxColFit((int)r.Left, (int)r.Top, (int)r.Height, 1, w2, b.left); - int xEnd = MaxColFit((int)r.Right - 1, (int)r.Top, (int)r.Height, -1, w2, b.right); - int w = xEnd - xIn + 1; - int yIn = MaxRowFit(xIn, (int)r.Top, w, 1, h2, b.top); - int yEnd = MaxRowFit(xIn, (int)r.Bottom - 1, w, -1, h2, b.bottom); - return new SKRect(xIn, yIn, xEnd - xIn + 1, yEnd - yIn + 1); - } - - //Moves a vertical boder until the best gray level fit. - int MaxColFit(int x, int y, int h, int inc, int end, float b) - { - int min = x; - float minD = 1.0F; - float n = 0.0F; - for (int i = 0; i < end; i++, x += inc) - { - float gray = GrayCol(x, y, h); - float d = (float)Math.Abs(b - gray) + n; - if ((minD - d) > EPSILON) - { - minD = d; min = x; - } - n += 0.1F; - } - return min; - } - - //Moves an horizontal boder until the best gray level fit. - int MaxRowFit(int x, int y, int w, int inc, int end, float b) - { - int min = y; - float minD = 1.0F; - float n = 0.0F; - for (int i = 0; i < end; i++, y += inc) - { - float gray = GrayRow(x, y, w); - float d = (float)Math.Abs(b - gray) + n; - if ((minD - d) > EPSILON) - { - minD = d; min = y; - } - n += 0.1F; - } - return min; - } - - //calculates the gray level of a given row. - float GrayRow(int x, int y, int w) - { - if (y >= 0 && y < img.Height) - { - int c = 0; - XBitArray row = img.GetRow(y); - for (int i = 0; i < w; i++, x++) if (row[x]) c++; - return (float)c / (float)w; - } - return 0.0F; - } - - //calculates the gray level of a given column. - float GrayCol(int x, int y, int h) - { - if (x >= 0 && x < img.Width) - { - int c = 0; - XBitArray col = img.GetColumn(x); - for (int i = 0; i < h; i++, y++) if (col[y]) c++; - return (float)c / (float)h; - } - return 0.0F; - } - - //Main method to find the matching MICR symbol. Find the matching symbol - //for a region r of the image img. - public string GetChar(BlackAndWhiteImage img, ref SKRect r) - { - this.img = img; - float min = float.MaxValue; - string minChar = "-"; - SKRect minRect = r; - for (int i = 0; i < chars.Length; i++) - { - SKRect rect = Crop(borders[i], r); - float e = GetError(rect, chars[i]); - if (e < min) - { - min = e; - minChar = Convert.ToString(Chars[i]); - minRect = rect; - } - } - if (min < maxMatchCharError) //if the best fit is under 0.4 we have found the matching symbol. - { - r = minRect; - return minChar; // +" E:" + min; - } - return null; - } - - const float EPSILON = 5e-4F; - //Key method to calculate the difference between a region of the image and a pattern. - //The region can be bigger or smaller than the pattern, so a few maths are involved. - float GetError(SKRect rect, int[][] c) - { - int rows = c.Length; - int cols = c[0].Length; - float[][] count = new float[rows][]; - for (int i = 0; i < rows; i++) count[i] = new float[cols]; - - float w = (float)cols / (float)rect.Width; - float h = (float)rows / (float)rect.Height; - - float Y = 0.0F; - for (int j = 0; j < rect.Height; j++, Y += h) - { - float X = 0.0F; - XBitArray row = img.GetRow((int)(rect.Top + j)); - for (int i = 0; i < rect.Width; i++, X += w) - { - bool isBlack = row[(int)rect.Left + i]; - if (isBlack) - { - float y = Y; - float pendH = h, currentH = 0.0F; - int nY = (int)Math.Floor(y + h); - while (pendH > EPSILON) - { - int iY = (int)Math.Floor(y); - if (iY == nY) currentH = pendH; - else currentH = (float)Math.Floor(y + 1F) - y; - - float x = X; - float pendW = w, currentW = 0.0F; - int nX = (int)Math.Floor(x + w); - while (pendW > EPSILON) - { - int iX = (int)Math.Floor(x); - if (iX == nX) currentW = pendW; - else currentW = (float)Math.Floor(x + 1F) - x; - - add(count, iY, iX, currentH * currentW); - - pendW -= currentW; - x += currentW; - } - pendH -= currentH; - y += currentH; - } - } - } - } - - float error = 0.0F; - for (int j = 0; j < rows; j++) - for (int i = 0; i < cols; i++) - { - float e = ((float)c[j][i]) / 2F - count[j][i]; - if (e < 0.0f) e = -e; - error += e * e; - } - return (float)Math.Sqrt(error / (float)(rows * cols)); - } - - void add(float[][] count, int row, int col, float inc) - { - if (row < count.Length && col < count[0].Length) - count[row][col] += inc; - } - - //MICR patterns: 0(white), 1(gray), 2(black) - static readonly int[][][] chars = new int[][][] { - new int[][]{ //0 - new int[]{1,2,2,2,2,2,1}, - new int[]{2,1,0,0,0,1,2}, - new int[]{2,0,0,0,0,0,2}, - new int[]{2,0,0,0,0,0,2}, - new int[]{2,0,0,0,0,0,2}, - new int[]{2,0,0,0,0,0,2}, - new int[]{2,0,0,0,0,0,2}, - new int[]{2,1,0,0,0,1,2}, - new int[]{1,2,2,2,2,2,1} - }, - new int[][]{ //1 --> 4x9 - new int[]{2,2,0,0}, - new int[]{2,2,0,0}, - new int[]{0,2,0,0}, - new int[]{0,2,0,0}, - new int[]{0,2,0,0}, - new int[]{2,2,2,2}, - new int[]{2,2,2,2}, - new int[]{2,2,2,2}, - new int[]{2,2,2,2} - }, - new int[][]{ //2 --> 4x9 - new int[]{2,2,2,2}, - new int[]{0,0,0,2}, - new int[]{0,0,0,2}, - new int[]{0,0,0,2}, - new int[]{2,2,2,2}, - new int[]{2,0,0,0}, - new int[]{2,0,0,0}, - new int[]{2,0,0,0}, - new int[]{2,2,2,2} - }, - new int[][]{ //3 --> 5x9 - new int[]{2,2,2,2,0}, - new int[]{0,0,0,2,0}, - new int[]{0,0,0,2,0}, - new int[]{0,0,0,2,0}, - new int[]{2,2,2,2,1}, - new int[]{0,0,0,2,2}, - new int[]{0,0,0,2,2}, - new int[]{0,0,0,2,2}, - new int[]{2,2,2,2,2} - }, - new int[][]{ //4 --> 6x9 - new int[]{2,2,0,0,0,0}, - new int[]{2,2,0,0,0,0}, - new int[]{2,2,0,0,0,0}, - new int[]{2,2,0,0,0,0}, - new int[]{2,2,0,0,0,0}, - new int[]{2,2,0,0,2,2}, - new int[]{2,2,2,2,2,2}, - new int[]{0,0,0,0,2,2}, - new int[]{0,0,0,0,2,2} - }, - new int[][]{ //5 --> 5x9 - new int[]{2,2,2,2,2}, - new int[]{2,0,0,0,0}, - new int[]{2,0,0,0,0}, - new int[]{2,0,0,0,0}, - new int[]{2,2,2,2,2}, - new int[]{0,0,0,0,2}, - new int[]{0,0,0,0,2}, - new int[]{0,0,0,0,2}, - new int[]{2,2,2,2,2} - }, - new int[][]{ //6 --> 6x9 - new int[]{2,2,2,2,0,0}, - new int[]{2,0,0,2,0,0}, - new int[]{2,0,0,1,0,0}, - new int[]{2,0,0,0,0,0}, - new int[]{2,0,0,0,0,0}, - new int[]{2,2,2,2,2,2}, - new int[]{2,0,0,0,0,2}, - new int[]{2,0,0,0,0,2}, - new int[]{2,2,2,2,2,2} - }, - new int[][]{ //7 --> 5x9 - new int[]{2,2,2,2,2}, - new int[]{2,0,0,0,2}, - new int[]{2,0,0,0,2}, - new int[]{0,0,0,1,2}, - new int[]{0,0,2,1,0}, - new int[]{0,0,2,0,0}, - new int[]{0,0,2,0,0}, - new int[]{0,0,2,0,0}, - new int[]{0,0,2,0,0} - }, - new int[][]{ //8 --> 7x9 - new int[]{0,2,2,2,2,2,0}, - new int[]{0,2,0,0,0,2,0}, - new int[]{0,2,0,0,0,2,0}, - new int[]{0,2,0,0,0,2,0}, - new int[]{1,2,2,2,2,2,1}, - new int[]{2,2,0,0,0,2,2}, - new int[]{2,2,0,0,0,2,2}, - new int[]{2,2,0,0,0,2,2}, - new int[]{2,2,2,2,2,2,2} - }, - new int[][]{ //9 --> 6x9 - new int[]{2,2,2,2,2,2}, - new int[]{2,0,0,0,0,2}, - new int[]{2,0,0,0,0,2}, - new int[]{2,0,0,0,0,2}, - new int[]{2,2,2,2,2,2}, - new int[]{0,0,0,0,2,2}, - new int[]{0,0,0,0,2,2}, - new int[]{0,0,0,0,2,2}, - new int[]{0,0,0,0,2,2} - }, - new int[][]{ //100 --> 7x9 - new int[]{0,0,0,0,2,2,2}, - new int[]{0,0,0,0,2,2,2}, - new int[]{2,2,0,0,2,2,2}, - new int[]{2,2,0,0,0,0,0}, - new int[]{2,2,0,0,0,0,0}, - new int[]{2,2,0,0,0,0,0}, - new int[]{2,2,0,0,2,2,2}, - new int[]{0,0,0,0,2,2,2}, - new int[]{0,0,0,0,2,2,2} - }, - new int[][]{ //110 --> 7x7 - new int[]{0,0,0,0,2,2,2}, - new int[]{2,0,2,0,2,2,2}, - new int[]{2,0,2,0,2,2,2}, - new int[]{2,0,2,0,2,2,2}, - new int[]{2,0,2,0,0,0,0}, - new int[]{2,0,2,0,0,0,0}, - new int[]{2,0,2,0,0,0,0} - }, - new int[][]{ //010 --> 7x9 - new int[]{0,0,0,0,0,2,2}, - new int[]{0,0,0,0,0,2,2}, - new int[]{0,0,0,1,0,2,2}, - new int[]{0,0,0,2,0,2,2}, - new int[]{0,0,0,2,0,0,0}, - new int[]{2,2,0,2,0,0,0}, - new int[]{2,2,0,1,0,0,0}, - new int[]{2,2,0,0,0,0,0}, - new int[]{2,2,0,0,0,0,0} - }, - new int[][]{ //001 --> 7x4 - new int[]{2,2,0,2,2,0,2}, - new int[]{2,2,0,2,2,0,2}, - new int[]{2,2,0,2,2,0,2}, - new int[]{2,2,0,2,2,0,2} - } - }; - class Borders { public float left, right, top, bottom; public Borders(float l, float r, float t, float b) { left = l; right = r; top = t; bottom = b; } } - static readonly string Chars = "0123456789abcd"; - static Borders[] borders; - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/MICR/MICRReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/MICR/MICRReader.cs deleted file mode 100644 index 3506f1a4..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/MICR/MICRReader.cs +++ /dev/null @@ -1,378 +0,0 @@ -using System; -using System.Collections; -using System.Drawing; -using SkiaSharp; -using System.Runtime.InteropServices; -using BarcodeReader.Core.Common; -using MuPDF.NET; - -namespace BarcodeReader.Core.MICR -{ -#if CORE_DEV - public -#else - internal -#endif - class MICRReader : SymbologyReader2D - { - //Max accepted differencte between the height of symbols in a MICR code - protected float maxHeightDifferenceBetweenSymbols = 0.3F; - - //Max accepted differencte between the width of symbols in a MICR code - protected float maxWidthDifferenceBetweenSymbols = 0.3F; - - ImageScaner scan; - Slicer slicer; - MICR micrOcr; - MICRSimpleReader simpleReader; - ArrayList result; - - int width, height; - int minRad = 5, //minRad = 15, // changed to 5 on 19 Feb 2014 by Eugene - maxRad = 100, - minDist = 1, - minDigitCount = 15; - bool ocr = true, - hough = true, - restrictHorizontal = false; //restrictHorizontal=true; // changed to false on 19 Feb 2014 by Eugene - string spaceCharacter = " ", unknownCharacter = "?"; - - SKBitmap imgHough; - public SKBitmap GetImage() { return imgHough; } - - int mainAngle; - - public MICRReader() : base() - { - // change the default block filte to .BlockOld filter type - base.ThresholdFilterMethodToUse = ThresholdFilterMethod.BlockOld; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.MICR; - } - - protected override FoundBarcode[] DecodeBarcode() - { - this.width = BWImage.Width; - this.height = BWImage.Height; - scan = new ImageScaner(BWImage); - // calling Slicer with AspectMax = 0f (that means no need to check for max aspect ratio as we need it in OMR) - slicer = new Slicer(minDist, minRad, maxRad, 450, 3F / 9F, 0f , false); - micrOcr = new MICR(); - simpleReader = new MICRSimpleReader(slicer, micrOcr, minDigitCount, spaceCharacter, unknownCharacter); - result = new ArrayList(); -#if DEBUG_IMAGE - //BWImage.GetAsBitmap().Save(@"out.png"); -#endif - //Get an array of all segments meeting minDist, minRad, maxRad restrictions - ArrayList segments = slicer.GetParts(BWImage); - - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - // Process all segments - FoundBarcode[] res = hough ? HoughScan(segments) : ScanParts(segments); - - // Calculate confidence - foreach (FoundBarcode barcode in res) - { - int countOfUnknown = barcode.Value.Split(UnknownCharacter.ToCharArray()).Length - 1; - if (countOfUnknown > 0) - barcode.Confidence = (barcode.Value.Length - countOfUnknown) / (float) barcode.Value.Length; - } - - return res; - } - - - FoundBarcode[] HoughScan(ArrayList segments) - { - //Add all segments and get a list of hough cells sorted by the number of segments. - //A hough cell defines a line based on its angle and distance, and contains - //all segments that are near that line. - Hough h = new Hough(width, height, 100, height / 4); - foreach (Segment p in segments) - h.Add(p.Center, 1, p); // weight could be p.Width * p.Height to sort by size - imgHough = h.GetImage(); - HoughCell[] cells = h.Sort(); - - //for each aligned subset of segments - //mainAngle: once a MICR code has been found, the rest must be +/- in the same direction. - // So, initially mainAngle=-1 and when the first code is found is assigned to its angle - //MIN_DIGITS: minimun number of digits of an acceptable MICR code. - mainAngle = restrictHorizontal ? 50 : -1; - foreach (HoughCell c in cells) - if (c.Objects.Count > minDigitCount && (mainAngle == -1 || Calc.Around(c.Angle, mainAngle, 3))) - { - //If any of the segments of this cell belongs to a previously read code, skip it. - //This is important because there are several hough cells crossing the same code. - bool scaned = false; - foreach (Segment s in c.Objects) if (s.Scaned) { scaned = true; break; } - - if (!scaned) - { - //Sort all parts from left to right - Segment[] sortedsegments = new Segment[c.Objects.Count]; - int ii = 0; - foreach (Segment s in c.Objects) { s.X = h.XDist(c, s.Center); sortedsegments[ii++] = s; } - Array.Sort(sortedsegments); - - FindClusters(sortedsegments, c.Angle); - - if (this.ExpectedNumberOfBarcodes>0 && result.Count >= this.ExpectedNumberOfBarcodes) break; - } - - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - } - return (FoundBarcode[])result.ToArray(typeof(FoundBarcode)); - } - - //Find subsets of segments that are close enough to be a possible code, and - //have a similiar height, i.e. find clusters of segments. - //It also calculates the mean distance between symbols, needed to scan - //intermediate non numeric symbols. - void FindClusters(Segment[] sortedsegments, int angle) - { - float D = 0F; - int iIn = 0, iEnd = 0, count = 1, dCount = 0; - Segment prev = sortedsegments[0]; - int meanHeight = sortedsegments[1].Height; //meanHeight is used to check height similarity - for (int i = 1; i < sortedsegments.Length && (this.ExpectedNumberOfBarcodes <= 0 || result.Count < this.ExpectedNumberOfBarcodes); i++) - { - Segment segment = sortedsegments[i]; - float mH = (float)meanHeight / (float)(count); - //if the current segment has a similar height from previous ones - if (Calc.Around(mH / (float)segment.Height, 1F, maxHeightDifferenceBetweenSymbols) || - Calc.Around((float)segment.Height / mH, 1F, maxHeightDifferenceBetweenSymbols)) - { - float d = prev.X - segment.X; - float tmpD = (dCount > 0 ? D / (float)dCount : d); - //if the current segment is close enough from the previous one - if (d < 15F * tmpD) - { - iEnd = i; - meanHeight += segment.Height; - count++; - //if there is no white space between the current segment and the previous - //used their distance to calculate the average distance between symbols - if (Calc.Around(d / tmpD, 1F, maxWidthDifferenceBetweenSymbols) || - Calc.Around(tmpD / d, 1F, maxWidthDifferenceBetweenSymbols)) - { - dCount++; - D += d; - } - } - //if the current segment if far away from the previous one, finalize the - //cluster and start a new one. - else - { - DecodeCluster(sortedsegments, iIn, iEnd, tmpD, angle); - iIn = iEnd = i; - count = 1; meanHeight = sortedsegments[i].Height; - dCount = 0; D = 0F; - } - prev = segment; - } - //if the current segment's height is too different from the previous one - else if (segment.Height > mH) //stop if bigger - { - DecodeCluster(sortedsegments, iIn, iEnd, D / (float)dCount, angle); - iIn = iEnd = i; - count = 1; meanHeight = sortedsegments[i].Height; - dCount = 0; D = 0F; - prev = segment; - } - //else consider as noise, just skip - } - DecodeCluster(sortedsegments, iIn, iEnd, D / (float)dCount, angle); - } - - //Given a set of segments (with similar height and close enough from each other) - //the estimated distance between symbols, and the direction of the segments (angle) - //find the matching symbol for each segment. - //Since non-numeric symbols usually lead to a more than one segment, they are - //rarely matched. Now, we will scan empty areas to find those non-numeric symbols. - void DecodeCluster(Segment[] group, int iIn, int iEnd, float d, int angle) - { - if (iEnd - iIn > minDigitCount) //decode - { - string micr = null; - SKPointI[] rect = null; - Segment a = group[iIn]; - Segment b = group[iEnd]; - - if (restrictHorizontal) - { - Segment[] parts = new Segment[iEnd - iIn + 1]; - for (int i = iIn, j = 0; i <= iEnd; i++, j++) parts[j] = group[i]; - if (ocr) micr = simpleReader.Read(BWImage, parts, d); - else micr = ""; - rect = new SKPointI[] { a.LU, a.LD, b.RD, b.RU, a.LU}; - } - else - { - //find bounding box aligned in the segments direction. - //We also check that all segments are vertically aligned. We calculate - //the mean height and then compare this mean height with the height of - //the bounding box. - BBox bbox = new BBox((MyPoint)a.Center, (MyPoint)b.Center - (MyPoint)a.Center, d); - int blackPixelCount = 0; - float sumHeight = 0; int countH = 0; - for (int i = iIn; i <= iEnd; i++) - { - Segment p = group[i]; - - float maxY = float.MinValue, minY = float.MaxValue; - bbox.Update(p.LU, ref maxY, ref minY); - bbox.Update(p.LD, ref maxY, ref minY); - bbox.Update(p.RU, ref maxY, ref minY); - bbox.Update(p.RD, ref maxY, ref minY); - - float hh = maxY - minY; - if (countH == 0) { sumHeight = hh; countH = 1; } - else - { - float tmpH = sumHeight / (float)countH; - if (Calc.Around(tmpH, hh, tmpH * 0.25F)) - { - sumHeight += hh; - countH++; - } - else if (hh > tmpH) { sumHeight = hh; countH = 1; } - } - blackPixelCount += p.BlackPixelCounter; - } - float meanH = sumHeight / (float)countH; - - //if mean height and bounding box are not similar exit. - if (!Calc.Around(bbox.GetHeight(), meanH, meanH * 0.5F)) - return; - rect = bbox.GetBBox(); - if (ocr) - { - //resample the bounding box to a horizontal rectangle - SKRect r = bbox.GetRectangle(); - if (r.Height < 8) r.Bottom = r.Top+8; - - using (SKBitmap rotated = new SKBitmap((int)r.Width, (int)r.Height, SKColorType.Rgb888x, SKAlphaType.Opaque)) - { - /* - BitmapData data = rotated.LockBits(new Rectangle(0, 0, r.Width, r.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); - int stride = data.Stride; - byte[] scanBytes = new byte[stride]; - IntPtr ptr = data.Scan0; - for (int y = 0; y < r.Height; y++) - { - for (int x = 0, xx = 0; x < r.Width; x++) - { - MyPointF p = bbox.GetPoint(x, y); - int gray = scan.isBlack(p) ? 0 : 255; //sample a point using 1 pixel. Seems to be enough (and faster). - //int gray = (int)(scan.getSample(p) * 255F); //sample a point using 4 pixel neighbours - if (gray > 255) gray = 255; - scanBytes[xx++] = scanBytes[xx++] = scanBytes[xx++] = (byte)gray; - } - Marshal.Copy(scanBytes, 0, ptr, stride); - ptr = new IntPtr(ptr.ToInt64() + stride); - } - rotated.UnlockBits(data); - */ - for (int y = 0; y < (int)r.Height; y++) - { - for (int x = 0; x < (int)r.Width; x++) - { - MyPointF p = bbox.GetPoint(x, y); - int gray = scan.isBlack(p) ? 0 : 255; // or use scan.getSample(p) * 255F for interpolated sample - if (gray > 255) gray = 255; - - // Write RGB triplet - rotated.SetPixel(x, y, new SKColor((byte)gray, (byte)gray, (byte)gray)); - } - } -#if DEBUG_IMAGE - //rotated.Save(@"d:\part.png"); -#endif - - //rescan horizontal image - using (BlackAndWhiteImage bwr = new BlackAndWhiteImage(rotated, 1, ThresholdFilterMethodToUse, BWImage.ThresholdLevelAdjustment)) - { -#if DEBUG_IMAGE - //bwr.GetAsBitmap().Save(@"d:\partBW.png"); -#endif - micr = simpleReader.Read(bwr, d); - } - } - } - else micr = ""; - } - - if (micr != null) - { - if (ocr) - { - //set the main angle to avoid scanning hough cells of different angles. - if (result.Count == 0) { mainAngle = angle; } - //mark segments as used, to avoid read the same code more than once. - for (int i = iIn; i <= iEnd; i++) group[i].Scaned = true; - } - FoundBarcode foundBarcode = new FoundBarcode(); - foundBarcode.BarcodeFormat = SymbologyType.MICR; - foundBarcode.Value = micr; - - foundBarcode.Polygon = rect; - foundBarcode.Color = SKColors.Blue; - - foundBarcode.Rect = new SKRect(rect[0].X, rect[0].Y, rect[3].X, rect[1].Y); - - result.Add(foundBarcode); - } - } - } - - //debug method to show all segments, and matching symbol. - FoundBarcode[] ScanParts(ArrayList parts) - { - foreach (Segment p in parts) - if (p != null && (p.Width > minRad || p.Height > minRad) && - (p.Width < maxRad && p.Height < maxRad)) - { - String data = ""; - SKRect r = p.GetRectangle(); - if (ocr) data = micrOcr.GetChar(BWImage, ref r); - FoundBarcode foundBarcode = new FoundBarcode(); - foundBarcode.BarcodeFormat = SymbologyType.MICR; - if (data != null) - { - p.SetRectangle(r); - foundBarcode.Color = SKColors.Blue; - } - else - { - data = "?"; - foundBarcode.Color = SKColors.Orange; - } - foundBarcode.Value = data; - foundBarcode.Polygon = p.GetBBox(); - SKPointI[] bBox = p.GetBBox(); - foundBarcode.Rect = new SKRect(bBox[0].X, bBox[0].Y, bBox[3].X, bBox[1].Y); - result.Add(foundBarcode); - } - return (FoundBarcode[])result.ToArray(typeof(FoundBarcode)); - } - - public int MinRad { get { return minRad; } set { minRad = value; } } - public int MaxRad { get { return maxRad; } set { maxRad = value; } } - public int MinDist { get { return minDist; } set { minDist = value; } } - public int MinDigitCount { get { return minDigitCount; } set { minDigitCount = value; } } - public bool OCR { get { return ocr; } set { ocr = value; } } - public bool Hough { get { return hough; } set { hough = value; } } - public bool RestrictHorizontal { get { return restrictHorizontal; } set { restrictHorizontal = value; } } - public string SpaceCharacter { get { return spaceCharacter; } set { spaceCharacter = value; } } - public string UnknownCharacter { get { return unknownCharacter; } set { unknownCharacter = value; } } - } - -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/MICR/MICRSimpleReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/MICR/MICRSimpleReader.cs deleted file mode 100644 index 082b7f11..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/MICR/MICRSimpleReader.cs +++ /dev/null @@ -1,162 +0,0 @@ -using System; -using System.Collections; -using System.Drawing; -using BarcodeReader.Core.Common; -using SkiaSharp; - -namespace BarcodeReader.Core.MICR -{ - //This class receives a rotated region of the original image (i.e a small horizontal image) - //and slices its parts again to find the right bounding boxes for each segment (Part) (since - //bounding boxes of rotated segments are oversized!). Then it finds a match for each segment - //and, for too separated segments, try to read symbols in the middle. It also reads the first - //and last non-numeric symbols of a MICR code, since they are splitted in different segments - //and never matched. - internal class MICRSimpleReader - { - int minDigitCount; - string spaceCharacter, unknownCharacter; - Slicer slicer; - MICR micrOcr; - BlackAndWhiteImage img; - - public MICRSimpleReader(Slicer slicer, MICR micrOcr, int minDigitCount, string spaceCharacter, string unknownCharacter) - { - this.slicer = slicer; - this.micrOcr = micrOcr; - this.minDigitCount=minDigitCount; - this.spaceCharacter=spaceCharacter; - this.unknownCharacter=unknownCharacter; - } - - public string Read(BlackAndWhiteImage img, float d) - { - ArrayList parts = slicer.GetParts(img); - Segment[] sortedparts = new Segment[parts.Count]; - int ii = 0; - foreach (Segment p in parts) { p.X = -p.XIn; sortedparts[ii++] = p; } - Array.Sort(sortedparts); - return Read(img, sortedparts, d); - } - - public string Read(BlackAndWhiteImage img, Segment[] sortedparts, float d) - { - this.img = img; - string[] chars = new string[sortedparts.Length]; - ArrayList decoded = new ArrayList(); - for (int i = 0; i < sortedparts.Length; i++) - { - SKRect rr = sortedparts[i].GetRectangle(); - string ch = micrOcr.GetChar(img, ref rr); - chars[decoded.Count] = ch; - if (ch != null) decoded.Add(sortedparts[i]); - } - string micr = null; - if (decoded.Count > minDigitCount) - { - bool isWhite; - Segment first = (Segment)decoded[0]; - micr = SorroundingChar(first, -d * 1.5F, d, out isWhite); - - Segment prev = null; - int cont = 1, maxCont = 1; - for (int i = 0; i < decoded.Count; i++) - { - Segment p = (Segment)decoded[i]; - if (prev != null) - { - float dist = p.Dist(prev); - if (dist > d * 1.2F) - { - float dd = d * 0.5F; - while (dd < dist - d) - { - string ch = SorroundingChar(prev, dd, d, out isWhite); - if (ch == null) ch = isWhite ? spaceCharacter : unknownCharacter; - micr += ch; - dd += d; - } - cont = 1; - } - else - { - cont++; - if (maxCont < cont) maxCont = cont; - } - } - else cont = 1; - micr += chars[i]; - prev = p; - } - if (maxCont < cont) maxCont = cont; - if (maxCont < 3) micr = null; - else micr += SorroundingChar(prev, d * 0.5F, d, out isWhite); - } - return micr; - } - - - //If to matched segments are too distant, tries to find intermediate symbols - //based on the estimated symbols width (MICR are fixed pitch). Uses a crop - //method to adjust the bounding box before matching it. - static readonly int[] offsets = new int[] { 0, -2, +2 }; - public string SorroundingChar(Segment p, float d, float w, out bool isWhite) - { - isWhite = true; - SKPointI center = p.Center; - foreach (int off in offsets) - { - int xIn = center.X + off + (int)d; - SKRect r = new SKRect(xIn, p.LU.Y, (int)(w-1+xIn), p.Height+p.LU.Y); - Crop(ref r); - string ch = micrOcr.GetChar(img, ref r); - if (ch != null) return ch; - else if (r.Width > (int)w/2) isWhite = false; - } - return null; - } - - public void Crop(ref SKRect r) - { - //return; - int width = (int)r.Width; - int height = (int)r.Height; - int xIn = (int)r.Left, yIn = (int)r.Top, xEnd = (int)r.Right - 1, yEnd = (int)r.Bottom - 1; - for (int i = 0; i < width; i++, xIn++) if (!isWhiteV(xIn, yIn, height)) break; - for (int i = 0; i < width; i++, xEnd--) if (!isWhiteV(xEnd, yIn, height)) break; - width = xEnd - xIn + 1; - for (int i = 0; i < height; i++, yIn++) if (!isWhiteH(xIn, yIn, width)) break; - for (int i = 0; i < height; i++, yEnd--) if (!isWhiteH(xIn, yEnd, width)) break; - - r.Left = xIn; - r.Top = yIn; - r.Right = xEnd + 1; - r.Bottom = yEnd + 1; - } - - bool isWhiteV(int x, int y, int h) - { - if (x < 0 || x >= img.Width) return true; - int max = 2; - int count = max; - XBitArray col = img.GetColumn(x); - for (int i = 0; i < h; i++, y++) - if (col[y]) { if (--count == 0) return false; } - else count = max; - return true; - } - - bool isWhiteH(int x, int y, int w) - { - if (y < 0 || y >= img.Height) return true; - int max = 2; - int count = max; - XBitArray row = img.GetRow(y); - for (int i = 0; i < w; i++, x++) - if (row[x]) { if (--count == 0) return false; } - else count = max; - return true; - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/MICR/Part.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/MICR/Part.cs deleted file mode 100644 index 7ee891e8..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/MICR/Part.cs +++ /dev/null @@ -1,103 +0,0 @@ -using SkiaSharp; -using System; -using System.Drawing; - -namespace BarcodeReader.Core.MICR -{ - internal class Part - { - bool scaned = false; - string ch; - int xIn, xEnd, yIn, yEnd; - public Part(Part p) - { - this.xIn = p.xIn; - this.xEnd = p.xEnd; - this.yIn = p.yIn; - this.yEnd = p.yEnd; - this.ch = p.ch; - this.scaned = p.scaned; - } - - public Part(MyPoint p) - { - xIn = xEnd = p.X; - yIn = yEnd = p.Y; - } - - public void Add(int x, int y) - { - if (xIn > x) xIn = x; - else if (xEnd < x) xEnd = x; - - if (yEnd < y) yEnd = y; - } - - public void Join(Part p) - { - if (xIn > p.xIn) xIn = p.xIn; - if (xEnd < p.xEnd) xEnd = p.xEnd; - if (yIn > p.yIn) yIn = p.yIn; - if (yEnd < p.yEnd) yEnd = p.yEnd; - } - - public SKPointI[] GetBBox() - { - return new SKPointI[] { new SKPointI(xIn - 1, yIn - 1), new SKPointI(xEnd + 1, yIn - 1), new SKPointI(xEnd + 1, yEnd + 1), new SKPointI(xIn - 1, yEnd + 1), new SKPointI(xIn - 1, yIn - 1) }; - } - - public SKRect GetRectangle() - { - return new SKRect(xIn, yIn, xEnd + 1, yEnd + 1); - } - - public void SetRectangle(SKRect r) - { - this.xIn = (int)r.Left; - this.yIn = (int)r.Top; - this.xEnd = (int)r.Right - 1; - this.YEnd = (int)r.Bottom - 1; - } - - public int XIn { get { return xIn; } set { xIn = value; } } - public int XEnd { get { return xEnd; } set { xEnd = value; } } - public int YIn { get { return yIn; } set { yIn = value; } } - public int YEnd { get { return yEnd; } set { yEnd = value; } } - - public int Width { get { return xEnd - xIn + 1; } } - public int Height { get { return yEnd - yIn + 1; } } - public string Char { get { return ch; } set { ch = value; scaned = true; } } - public bool Scaned { get { return scaned; } } - - public SKPointI Center { get { return new SKPointI((int)((xIn + xEnd) / 2), (int)((yIn + yEnd) / 2)); } } - public SKPointI LU { get { return new SKPointI((int)xIn, (int)yIn); } } - public SKPointI LD { get { return new SKPointI((int)xIn, (int)yEnd); } } - public SKPointI RU { get { return new SKPointI((int)xEnd, (int)yIn); } } - public SKPointI RD { get { return new SKPointI((int)xEnd, (int)yEnd); } } - - public float Dist(Part p) - { - SKPointI b = p.Center; - SKPointI a = this.Center; - return (float)(Math.Sqrt((a.X - b.X) * (a.X - b.X) + (a.Y - b.Y) * (a.Y - b.Y))); - } - } - - internal class SortablePart : Part, IComparable - { - float x; - public SortablePart(Part p, float x) - : base(p) - { - this.x = x; - } - - public int CompareTo(Object o) - { - SortablePart p = (SortablePart)o; - return (int)((p.x - this.x) * 10); - } - - public float X { get { return x; } } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/MSI/MSI.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/MSI/MSI.cs deleted file mode 100644 index bd7b8182..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/MSI/MSI.cs +++ /dev/null @@ -1,197 +0,0 @@ -using System; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.MSI -{ - //MSI have 6 different checksums algorithms. By default, ALL are tried. -#if CORE_DEV - public -#else - internal -#endif - enum MSIChecksum { ALL, MOD10, MOD11_IBM, MOD11_NCR, MOD1010, MOD1110_IBM, MOD1110_NCR }; - - //Main class to scan an image looking for code39 barcodes. It inherits from Reader2DNoise, - //responsible to find start and stop patterns in the image, and calling FindBarcode methods - //to read the barcode in between. -#if CORE_DEV - public -#else - internal -#endif - class MSIReader : Reader2DNoise - { - MSIChecksum mode = MSIChecksum.ALL; - public MSIChecksum Mode { get { return mode; } set { mode = value; } } - - public MSIReader() - { - //Define start and stop patterns. - startPatterns = _largeStartPatterns; - stopPatterns = _stopPatterns; - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.MSI; - } - - //Method to scan a single row - protected override BarCodeRegion FindBarcode(ImageScaner scan, int startPattern, MyPoint start, MyPoint end, FoundPattern foundPattern) - { - BarSymbolReader reader = new BarSymbolReader(scan, 6, 9, true, true, foundPattern.moduleLength, _patterns, -1, false, null); - float error, maxError, confidence; - - int[] row = reader.Read(start, end, out error, out maxError, out confidence); - if (error < 1f) - { - BarCodeRegion r = new BarCodeRegion(start, reader.Current, reader.Current + new MyPoint(0, 5), start + new MyPoint(0, 5)); - r.Confidence = confidence; - if (Decode(r, row)) return r; - } - return null; - } - - //Method to scan a region. It starts with a scanning the line in the middle of the region - //to be able to read quickly good quality barcodes. If it fails, then uses the slower - //loose projection reader. - int[] nBars = new int[] {2, 8}; - int[] nModules = new int[] { 3, 12 }; - int[] tIndexs = new int[] { 0, 1 }; - int[][][] tables = new int[][][] { _startPatterns, _patterns }; - override protected BarCodeRegion FindBarcode(ImageScaner scan, int startPattern, BarCodeRegion r, FoundPattern foundPattern) - { - BarSymbolReader reader = new BarSymbolReader(scan, nBars, nModules, tIndexs, true, true, foundPattern.moduleLength, tables, false, null); - MyPoint a = r.A * 0.5f + r.D * 0.5f; - MyPoint b = r.B * 0.5f + r.C * 0.5f; - float error, maxError, confidence; - - int[] row = reader.Read(a, b, out error, out maxError, out confidence); r.Confidence = confidence; - if (Decode(r, row)) - return r; //if sampling is good enough, then use simple row scanning - - BarSymbolReaderLooseProjection reader2 = new BarSymbolReaderLooseProjection(scan, nBars, nModules, true, tables, -1, true, useE); - float[] widths = new float[] { 0.5f, 0.9f }; - - foreach (float w in widths) - { - row = reader2.Read(r, foundPattern.moduleLength, w); - if (Decode(r, row)) - return r; - } - - return null; - } - - //Method to decode bytecodes into the final string. - protected bool Decode(BarCodeRegion r, int[] rawData) - { - if (rawData == null) return false; - - int end = rawData.Length-1; while (end>0 && rawData[end] == -1) end--; - int n = 0; for (int i = end; i >= 0; i--) if (rawData[i] == -1) n++; - if (n==0 && end > 1) //minimun 1checkdigit - { - int[] row = new int[end]; - Array.Copy(rawData, 1, row, 0, end); - - if (row != null && row.Length > 1) //1 checksum - { - bool valid = false; - if (mode==MSIChecksum.ALL || mode==MSIChecksum.MOD10) valid|=checkChecksum10(row, row.Length); - if (mode==MSIChecksum.ALL || mode==MSIChecksum.MOD11_IBM) valid|=checkChecksum11(row, row.Length,7); - if (mode==MSIChecksum.ALL || mode==MSIChecksum.MOD11_NCR) valid|=checkChecksum11(row, row.Length,9); - if (mode==MSIChecksum.ALL || mode==MSIChecksum.MOD1010) valid|=checkChecksum1010(row); - if (mode==MSIChecksum.ALL || mode==MSIChecksum.MOD1110_IBM) valid|=checkChecksum1110(row, 7); - if (mode==MSIChecksum.ALL || mode==MSIChecksum.MOD1110_NCR) valid|=checkChecksum1110(row, 9); - if (valid) - { - string s = ""; - for (int i = 0; i < row.Length - 1; i++) s += row[i]; - r.Data = new ABarCodeData[] { new StringBarCodeData(s) }; - return true; - } - else if (row.Length > 4) // trying to recover if the value is larger than 4 symbols - { - // if check digit is not valid but we still will try to provide the output value - // but lowering the confidence by 0.3 - // see TestCase/msi/ - r.Confidence *= 0.3f; - string s = ""; - for (int i = 0; i < row.Length - 1; i++) s += row[i]; - // adding the check digit - //s += "(" + row[row.Length - 1] + ")"; - r.Data = new ABarCodeData[] { new StringBarCodeData(s) }; - return true; - - } - } - } - return false; - } - - private static bool checkChecksum10(int[] row, int N) - { - int sum = 0; - for (int i = N-2; i >= 0; i--) - { - int d = row[i]; - if ((N - i) % 2 == 0) - { - d *= 2; - if (d > 9) d = 1 + d % 10; - } - sum += d; - } - sum *= 9; - int checkDigit = sum % 10; - return checkDigit == row[N-1]; - } - - //limit can be 7 for IBM modulo11, or 9 for NCR modulo 11. - private static bool checkChecksum11(int[] row, int N, int limit) - { - int weight = 2; - int sum = 0; - for (int i = N - 2; i >= 0; i--) { - sum += row[i] * weight; - if (++weight > limit) weight = 2; - } - int checkDigit = (11 - (sum % 11)) % 11; - return checkDigit==row[N-1]; - } - - private static bool checkChecksum1010(int[] row) - { - return checkChecksum10(row, row.Length-1) && checkChecksum10(row, row.Length); - } - - private static bool checkChecksum1110(int[] row, int limit) - { - return checkChecksum11(row, row.Length - 1, limit) && checkChecksum10(row, row.Length); - } - - static readonly int[][] _largeStartPatterns = new int[][] - { - new int[]{2,1,1,2,1,2}, //0 - new int[]{2,1,1,2,2,1}, //4 - new int[]{2,1,2,1,1,2} //8 - }; - static readonly int[][] _startPatterns = new int[][] { new int[] { 2, 1 } }; - static readonly int[][] _stopPatterns = new int[][] { new int[] { 1, 2, 1 } }; - - static readonly int[][] _patterns = new int[][] { - new int[]{1,2,1,2,1,2,1,2}, //0 - new int[]{1,2,1,2,1,2,2,1}, //1 - new int[]{1,2,1,2,2,1,1,2}, //2 - new int[]{1,2,1,2,2,1,2,1}, //3 - new int[]{1,2,2,1,1,2,1,2}, //4 - new int[]{1,2,2,1,1,2,2,1}, //5 - new int[]{1,2,2,1,2,1,1,2}, //6 - new int[]{1,2,2,1,2,1,2,1}, //7 - new int[]{2,1,1,2,1,2,1,2}, //8 - new int[]{2,1,1,2,1,2,2,1}, //9 - }; - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeAlignedPatterns.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeAlignedPatterns.cs deleted file mode 100644 index 2f2a55f2..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeAlignedPatterns.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.MaxiCode -{ - //Class that represents a set of 6 alignment patterns (pixel coordinates + total quality) - class MaxiCodeAlignedPatterns - { - public float quality; - public MyPointF[] points; - int n = 0; - - public MaxiCodeAlignedPatterns() { quality = 0F; points = new MyPointF[6]; n = 0; } - - public void Add(float q, MyPointF p) - { - quality += q; - points[n++] = p; - } - - //This is the more important method! Alignment patterns are not completely centered, - //barcodes are skewed, sometimes the finder is not centered,... These things make - //really difficult to find the 4 corners of the barcode. - //The solution has been to track modules from the "center" to the corners. Actually, - //4 tracks are traced, 2 starting at the alignment pattern 0, and 2 more at the alignment - //pattern 3. - public BarCodeRegion ScanRegion(ImageScaner scan, float trackCoef) - { - MyVectorF vdX = (points[0] - points[3]) / 12F; - MyVectorF vdY = vdX.Perpendicular * (2F / (float)Math.Sqrt(3)); // H = 2W/sqrt(3) - MyVectorF vd60 = (points[1] - points[4]) / 12F; - MyVectorF vd120 = (points[2] - points[5]) / 12F; - - float W = vdX.Length; - - MyPointF ru = points[0]; - MyPointF lu = points[3]; - MyPointF rd = points[0]; - MyPointF ld = points[3]; - - //Track 16 modules to find the corners - if (!Track(scan, ref ru, vd60, vdX, vd60, vd120, W, 16, trackCoef)) ru = points[0] + vd60 * 16F; - if (!Track(scan, ref lu, vd120, vdX, vd60, vd120, W, 16, trackCoef)) lu = points[3] + vd120 * 16F; - if (!Track(scan, ref rd, -vd120, vdX, vd60, vd120, W, 16, trackCoef)) rd = points[0] - vd120 * 16F; - if (!Track(scan, ref ld, -vd60, vdX, vd60, vd120, W, 16, trackCoef)) ld = points[3] - vd60 * 16F; -#if DEBUG_IMAGE - scan.Save(@"d:\outTrack.png"); -#endif - //Left down and left up corners are 1 module (1W) left more! - //And corners are 0.5W from the center of the module! - return new BarCodeRegion(ld - vdX * 0.5F - vdY * 0.5F, rd + vdX * 1.5F - vdY * 0.5F, - lu - vdX * 0.5F + vdY * 0.5F, ru + vdX * 1.5F + vdY * 0.5F); - } - - static readonly MyVectorF[] offsets = new MyVectorF[] { new MyVectorF(0, 0), new MyVectorF(1, 0), - new MyVectorF(1, 1), new MyVectorF(0, 1), new MyVectorF(-1, 1), new MyVectorF(-1, 0), - new MyVectorF(-1, -1), new MyVectorF(0, -1), new MyVectorF(1, -1)}; - - //At the end, a very short method that is able to move through the barcode modules - //even for skewed ones. The idea is to move to the theoric position and then calculate - //the quality of this position and 6 positions around (at k distance from the theoric position). - //The best quality will be the adjusted next position. An so on, nModules times. - public bool Track(ImageScaner scan, ref MyPointF p, MyVectorF vd, MyVectorF vd0, MyVectorF vd60, MyVectorF vd120, float W, int nModules, float k) - { -#if DEBUG_IMAGE - //if (trace) - scan.Reset(); - scan.setPixel(p, System.Drawing.Color.Yellow); -#endif - MyVectorF vdX = vd.Normalized; - MyVectorF vdY = vdX.Perpendicular; - for (int i = 0; i < nModules; i++) - { - p += vd; - float max = 0F; - MyVectorF offsetMax = offsets[0]; - float[] qualities = new float[offsets.Length * 5]; - int n = 0; - foreach (MyVectorF offset in offsets) - { - float q = GetQuality(scan, p + offset * (W * k), W, vd0, vd60, vd120); - qualities[n++] = q; - if (q > max) { max = q; offsetMax = offset * (W * k); } - } -#if DEBUG_IMAGE - scan.setPixel(p, System.Drawing.Color.Orange); -#endif - //if (max < 4.5F) return false; //get lost - p += offsetMax; -#if DEBUG_IMAGE - scan.setPixel(p, System.Drawing.Color.Blue); - //if (trace) - scan.Save(@"d:\outTrack.png"); -#endif - } - return true; - } - - //The quality of a position is the quality of its module and 6 modules around (at 0, 60, 120, 180, 240 and 300º). - //Adding the quality of modules around, gives better results that only taking into account the main position. - float GetQuality(ImageScaner scan, MyPointF p, float W, MyVectorF vd0, MyVectorF vd60, MyVectorF vd120) - { - float q0 = scan.getModuleGrayLevel(p, W); if (q0 < 0.5F) q0 = 1F - q0; - float q1 = scan.getModuleGrayLevel(p + vd0, W); if (q1 < 0.5F) q1 = 1F - q0; - float q2 = scan.getModuleGrayLevel(p + vd60, W); if (q2 < 0.5F) q2 = 1F - q2; - float q3 = scan.getModuleGrayLevel(p + vd120, W); if (q3 < 0.5F) q3 = 1F - q3; - float q4 = scan.getModuleGrayLevel(p - vd0, W); if (q4 < 0.5F) q4 = 1F - q4; - float q5 = scan.getModuleGrayLevel(p - vd60, W); if (q5 < 0.5F) q5 = 1F - q5; - float q6 = scan.getModuleGrayLevel(p - vd120, W); if (q6 < 0.5F) q6 = 1F - q6; - return (q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4 + q5 * q5 + q6 * q6); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeCharDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeCharDecoder.cs deleted file mode 100644 index 53c291ac..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeCharDecoder.cs +++ /dev/null @@ -1,258 +0,0 @@ -using System; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.MaxiCode -{ - class MaxiCodeCharDecoder - { - const char SHIFTA = '\uFFF0'; - const char SHIFTB = '\uFFF1'; - const char SHIFTC = '\uFFF2'; - const char SHIFTD = '\uFFF3'; - const char SHIFTE = '\uFFF4'; - const char TWOSHIFTA = '\uFFF5'; - const char THREESHIFTA = '\uFFF6'; - const char LATCHA = '\uFFF7'; - const char LATCHB = '\uFFF8'; - const char LOCK = '\uFFF9'; - const char ECI = '\uFFFA'; - const char NS = '\uFFFB'; - const char PAD = '\uFFFC'; - const char FS = '\u001C'; - const char GS = '\u001D'; - const char RS = '\u001E'; - const string NINE_DIGITS = "000000000"; - const string THREE_DIGITS = "000"; - - static readonly String[] charSets = { - "\nABCDEFGHIJKLMNOPQRSTUVWXYZ"+ECI+FS+GS+RS+NS+' '+PAD+"\"#$%&'()*+,-./0123456789:"+SHIFTB+SHIFTC+SHIFTD+SHIFTE+LATCHB, - "`abcdefghijklmnopqrstuvwxyz"+ECI+FS+GS+RS+NS+'{'+PAD+"}~\u007F;<=>?[\\]^_ ,./:@!|"+PAD+TWOSHIFTA+THREESHIFTA+PAD+SHIFTA+SHIFTC+SHIFTD+SHIFTE+LATCHA, - "\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA"+ECI+FS+GS+RS+NS+"\u00DB\u00DC\u00DD\u00DE\u00DF\u00AA\u00AC\u00B1\u00B2\u00B3\u00B5\u00B9\u00BA\u00BC\u00BD\u00BE\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089"+LATCHA+' '+LOCK+SHIFTD+SHIFTE+LATCHB, - "\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA"+ECI+FS+GS+RS+NS+"\u00FB\u00FC\u00FD\u00FE\u00FF\u00A1\u00A8\u00AB\u00AF\u00B0\u00B4\u00B7\u00B8\u00BB\u00BF\u008A\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0092\u0093\u0094"+LATCHA+' '+SHIFTC+LOCK+SHIFTE+LATCHB, - "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\n\u000B\u000C\r\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A"+ECI+PAD+PAD+'\u001B'+NS+FS+GS+RS+"\u001F\u009F\u00A0\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A9\u00AD\u00AE\u00B6\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E"+LATCHA+' '+SHIFTC+SHIFTD+LOCK+LATCHB, -// "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\n\u000B\u000C\r\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\u0020\u0021\"\u0023\u0024\u0025\u0026\u0027\u0028\u0029\u002A\u002B\u002C\u002D\u002E\u002F\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039\u003A\u003B\u003C\u003D\u003E\u003F" - }; - - static readonly int[] dataLengthForModes = new int[] { 0, 0, 84, 84, 77, 93, 0 }; - - //This method receives a byte array from the sampling process and returns the decoded string - //performing error correction. - //byte array is divided into 2 parts: - //- primary : 0..19 --> 0..9 data, 10..19 ecc - //- secondary: 20..143 --> - // EEC: 68 + 56 ecc - // SEC: 84 + 40 ecc - public static string Decode(byte[] bytes, out float confidence) - { - byte[] primary = new byte[20]; - byte[] secondary = new byte[124]; - Array.Copy(bytes, primary, 20); - Array.Copy(bytes, 20, secondary, 0, 124); - - //-------------- ERROR CORRECTION ------------------- - //correct primary always EEC - confidence = 0F; - float confPrimary, confOdd, confEven; - if (!Correct(primary, 10, out confPrimary)) return null; - - - int mode = primary[0] & 15; //001111 modules 3..6 - //EEC or SEC(mode=5) secondary - byte[] secOdd=Extract(secondary, 1); - byte[] secEven=Extract(secondary, 0); - int errorCodeWords = (mode != 5 ? 20 : 28); //SEC for all except for mode 5 that uses EEC - if (!Correct(secOdd, errorCodeWords, out confOdd)) return null; - if (!Correct(secEven, errorCodeWords, out confEven)) return null; - Join(secOdd, secEven, secondary); - confidence = (confPrimary + confEven + confOdd) / 3F; - - //-------------- DECODE DATA BYTES ----------------- - String msg = ""; -#if DEBUG - //msg = "mode: " + mode + " -->"; -#endif - byte[] data = mode <= 6 ? new byte[dataLengthForModes[mode]] : null; - switch (mode) - { - case 0: - case 1: - msg += "obsolete mode"; - break; - case 2: - case 3: - //primary(0..9 Structured Carrier Message) + secondary) - string structuredCarrier= DecodeStructuredCarrier(primary, mode); - Array.Copy(secondary, 0, data, 0, data.Length); - string message= DecodeBytes(data); - - string comp = "[)>" + RS + "01" + GS; - if (message.StartsWith(comp)) - msg += message.Substring(0, comp.Length + 2) + structuredCarrier + GS + message.Substring(comp.Length + 2); - else - msg += structuredCarrier + message; - break; - case 4: - case 5: - Array.Copy(primary, 1, data, 0, 9); - Array.Copy(secondary, 0, data, 9, data.Length - 9); - msg += DecodeBytes(data); - break; - case 6: - msg += "silence mode"; - break; - default: msg += "bad mode"; - break; - } - return msg; - } - - static byte[] Extract(byte[] data, int offset) - { - byte[] r = new byte[data.Length / 2]; - for (int i = 0; i < r.Length; i++) r[i] = data[i*2+offset]; - return r; - } - - static void Join(byte[] odd, byte[] even, byte[] data) - { - for (int i = 0; i < odd.Length; i++) - { - data[i * 2] = even[i]; - data[i * 2 + 1] = odd[i]; - } - } - - static bool Correct(byte[] data, int errorCodeWords, out float confidence) - { - int[] tmp = new int[data.Length]; - for (int i = 0; i < data.Length; i++) tmp[i] = (int)data[i]; - ReedSolomon rs = new ReedSolomon(tmp, errorCodeWords, 6, 67, 1); - rs.Correct(out confidence); - if (!rs.CorrectionSucceeded) return false; - for (int i = 0; i < data.Length; i++) data[i] = (byte)rs.CorrectedData[i]; - return true; - } - - //PRYMARY has 10 codewords of data --> 60 bits - //mode 4 bits - //postal code 36 bits - //country code 10 bits - //service class 10 bits - public static string DecodeStructuredCarrier(byte[] data, int mode) - { - int[] modes = DecodeBits(data, 6, new int[] {2, 5 }, 4); - int serviceClass = DecodeBits(data, 6, new int[] { 54, 59, 48, 51}, 10)[0]; //10 bits - int countryCode = DecodeBits(data, 6, new int[] { 52, 53, 42, 47, 36, 37}, 10)[0]; //10 bits - string sPostalCode = "---------"; - if (mode == 2) - { - int postalCodeLength = DecodeBits(data, 6, new int[] { 38, 41, 30, 31}, 6)[0]; //6 bits - int postalCode = DecodeBits(data, 6, new int[] { 32, 35, 24, 29, 18, 23, 12, 17, 6, 11, 0, 1}, 30)[0]; //30 bits - sPostalCode = ToZeroInt(postalCode, postalCodeLength); - } - else //mode==3 - { - int[] postalCode = DecodeBits(data, 6, new int[] { 38, 41, 30, 35, 24, 29, 18, 23, 12, 17, 6, 11, 0, 1}, 6); //36 bits --> 6 codewords - byte[] bPostalCode = new byte[postalCode.Length]; - for (int i = 0; i < postalCode.Length; i++) bPostalCode[i] = (byte)postalCode[i]; - sPostalCode = DecodeBytes(bPostalCode); - } -//#if DEBUG -// return "[" + sPostalCode + "-" + ToZeroInt(countryCode, 3) + "-" + ToZeroInt(serviceClass, 3) + "]"; -//#else - return sPostalCode + RS + ToZeroInt(countryCode, 3) + RS + ToZeroInt(serviceClass, 3); -//#endif - } - - //indexes from MSB to LSB - static int[] DecodeBits(byte[] data, int codewordLength, int[] indexes, int nBitsPerCodeword) - { - bool[] bits = ReadBits(data, codewordLength, indexes); - if (bits.Length % nBitsPerCodeword != 0) throw new Exception("ERROR in readBits"); - - int[] a = new int[bits.Length / nBitsPerCodeword]; - int codeword = 0, j = 0, i=0; - while (i < bits.Length) - { - int k = (bits[i] ? (1 << (i % nBitsPerCodeword)) : 0); - codeword += k; - i++; - if ((i) % nBitsPerCodeword == 0) { a[j] = codeword; j++; codeword = 0; } - } - return a; - } - - //indexes from MSB to LSB - static bool[] ReadBits(byte[] data, int codewordLength, int[] indexes) - { - int l = 0; - for (int i=0;i> (codewordLength -1 - j % codewordLength)) & 1 )== 1; - return a; - } - - static string ToZeroInt(int n, int length) - { - string s = Convert.ToString(n); - return s.PadLeft(length, '0'); - } - - public static string DecodeBytes(byte[] data) - { - int pos = 0, count=-1, charSet=0, prevCharSet=-1; - string s = ""; - while (pos < data.Length) - { - char ch = charSets[charSet][data[pos++]]; - switch (ch) - { - case LATCHA: charSet = 0; count = -1; break; - case LATCHB: charSet = 1; count = -1; break; - case SHIFTA: prevCharSet = charSet; charSet = 0; count = 1; break; - case TWOSHIFTA: prevCharSet = charSet; charSet = 0; count = 2; break; - case THREESHIFTA: prevCharSet = charSet; charSet = 0; count = 3; break; - case SHIFTB: prevCharSet = charSet; charSet = 1; count = 1; break; - case SHIFTC: prevCharSet = charSet; charSet = 2; count = 1; break; - case SHIFTD: prevCharSet = charSet; charSet = 3; count = 1; break; - case SHIFTE: prevCharSet = charSet; charSet = 4; count = 1; break; - case LOCK: count = -1; break; - case NS: - //5 codewords --> numeric - long n = 0; - for (int i = 0; i < 5; i++) n = (n << 6) + (pos[] result = new LinkedList[radius.Length]; - LinkedList[] tpoints = new LinkedList[radius.Length]; - MyPoint[] prevPoints = new MyPoint[radius.Length]; - MyPoint[] startPoints = new MyPoint[radius.Length]; - for (int i = 0; i < result.Length; i++) - { - result[i] = new LinkedList(); - tpoints[i] = new LinkedList(); - prevPoints[i] = MyPoint.Empty; - startPoints[i] = MyPoint.Empty; - } - - float inc = 360F / samples[0].dist; - for (float angle = 0; angle < 360F; angle += inc) - { - float d, v, w, h; - getRadius(angle, out d, out v, out w, out h); - float cosA = w * (float)Math.Cos(angle * (float)Math.PI / 180F); - float sinA = -w * (float)Math.Sin(angle * (float)Math.PI / 180F); - for(int i=0;i=360F) traceLine(result[i], p, startPoints[i], tpoints[i]); //join with the start point -#if DEBUG_IMAGE - scan.setPixel(p, System.Drawing.Color.Blue); -#endif - } - } - - SampledCircle[] circles = new SampledCircle[radius.Length]; - for (int i = 0; i < radius.Length; i++) circles[i] = new SampledCircle(center, result[i], tpoints[i]); - return circles; - } - - void traceLine(LinkedList l, MyPoint start, MyPoint end, LinkedList points) - { - Bresenham br = new Bresenham(start, end); - br.Next(); - while (!br.End()) - { - points.AddLast(br.Current); - l.AddLast(scan.isBlack(br.Current)); -#if DEBUG_IMAGE - scan.setPixel(br.Current, System.Drawing.Color.Orange); -#endif - br.Next(); - } - } - } - - - internal class Sample{ - public MyPoint point; - public float angle; - public int rect; - public float dist; - - public Sample(MyPoint p, float angle, int rect) - { - this.point = p; - this.angle = angle; - this.rect=rect; - } - - } - - //Class that stores sampled pixels from a circle around the finder. - internal class SampledCircle - { - public XBitArray samples; - public float[] angles; - public MyPoint[] points; - - public SampledCircle(MyPoint center, LinkedList lSamples, LinkedList lPoints) - { - samples = new XBitArray(lSamples.Count - 1); - int j = 0; - foreach (bool isBlack in lSamples) - if (j == lSamples.Count - 1) break; - else samples[j++] = isBlack; - - points = new MyPoint[lPoints.Count - 1]; - angles = new float[lPoints.Count - 1]; - j = 0; - foreach (MyPoint p in lPoints) - if (j == lPoints.Count - 1) break; - else - { - MyVector vd = p - center; - float angle = -vd.Angle; - if (angle<0) angle+=PI2; - angles[j] = angle; - points[j++] = p; - } - } - - public static readonly float PI2 = (float)Math.PI * 2F; - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeReader.cs deleted file mode 100644 index d28d20b7..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeReader.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Drawing; -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.MaxiCode -{ -#if CORE_DEV - public -#else - internal -#endif - class MaxiCodeReader : SymbologyReader2D - { - //Scan image rows main step - protected int scanRowStep = 1; - - //Max difference between the center of the finder and the center of the mid segment of the finder - protected float finderMaxCentersDifference = 0.1f; - - //Max distance in pixels between the center of the finder and the center of the mid segment of the finder - protected int finderMaxCentersDistanceInPixels = 2; - - int width, height; - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.MaxiCode; - } - - protected override FoundBarcode[] DecodeBarcode() - { - this.width = BWImage.Width; - this.height = BWImage.Height; - - return Scan(); - } - - - //object that holds the bw image + methods to sample, follow vertices,... - ImageScaner scan; - //list of found codes found and correctly decoded. - LinkedList candidates; - //list of found and rejected finders, and codes to avoid exploring twice the same barcode - LinkedList exclusion; - - //patternFinder is used in the main scan loop (scanning horizontal lines). - //crossFinder is used for each horizontal pattern found, to check if it is also - //a pattern verically - IPatternFinderNoiseRow pf0, pf90; - PatternFinderNoise pf45, pf135; - - //Patterns detected are not processed immediately. They are stored in foundPatters and - //joined with patterns in next rows. Once a pattern has no continuity (it is added to the - //removedPatterns list), then it is processed. This way we have a better measure of the - //center of the pattern. - LinkedList foundPatterns; - LinkedList removedPatterns; - - //Object to find barcodes from a detected horizontal scan line pattern. - MaxiCodeScaner maxiCodeFinder; - - FoundBarcode[] Scan() - { - scan = new ImageScaner(BWImage); - pf0 = new PatternFinderNoiseRow(MaxiCodeScaner.finder, true, true, 2); - pf90 = new PatternFinderNoiseRow(MaxiCodeScaner.finder, true, true, 2); - pf45 = new PatternFinderNoise(BWImage, MaxiCodeScaner.finder[0], true, 2); - pf135 = new PatternFinderNoise(BWImage, MaxiCodeScaner.finder[0], true, 2); - candidates = new LinkedList(); - exclusion = new LinkedList(); - foundPatterns = new LinkedList(); - maxiCodeFinder = new MaxiCodeScaner(scan, pf90, pf45, pf135); - - //main loop to scan horizontal lines - for (int y = 0; y < height && (ExpectedNumberOfBarcodes <= 0 || candidates.Count < ExpectedNumberOfBarcodes); y += scanRowStep) - { - ScanRow(y, BWImage.GetRow(y)); - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - } - - ArrayList result = new ArrayList(); - foreach (BarCodeRegion c in candidates) - { - FoundBarcode foundBarcode = new FoundBarcode(); - foundBarcode.BarcodeFormat = SymbologyType.MaxiCode; - - String data = ""; - if (c.Data != null) foreach (ABarCodeData d in c.Data) data += d.ToString(); - - foundBarcode.Value = data; - - foundBarcode.Polygon = new SKPointI[5] { c.A, c.B, c.D, c.C, c.A }; - foundBarcode.Color = SKColors.Blue; - - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(foundBarcode.Polygon, pointTypes); - //foundBarcode.Rect = Rectangle.Round(path.GetBounds()); - foundBarcode.Rect = Utils.DrawPath(foundBarcode.Polygon); - - foundBarcode.Confidence = c.Confidence; - result.Add(foundBarcode); - } - return (FoundBarcode[])result.ToArray(typeof(FoundBarcode)); - } - - //Scans a horizontal line looking for finder patterns (1010101010101). - //Patterns detected are not processed immediately. They are stored in foundPatters and - //joined with patterns in next rows. Once a pattern has no continuity (it is added to the - //removedPatterns list), then it is processed. This way we have a better measure of the - //center of the pattern. - private void ScanRow(int y, XBitArray row) - { - //look for the finder - pf0.NewSearch(row); - while (pf0.NextPattern() != null) - { - MyPoint a = new MyPoint(pf0.First, y); - MyPoint b = new MyPoint(pf0.Last, y); - //MyPoint center = new MyPoint(pf0.Center, a.Y); - MyPoint center = new MyPoint((a.X + b.X) / 2, y); - int xCenter = (a.X + b.X) / 2; - int d = xCenter - center.X; - if (d < 0) d = -d; - if (Calc.Around(d, 0F, (float)(b.X - a.X) * finderMaxCentersDifference ) || d < finderMaxCentersDistanceInPixels) - { - Pattern p = new Pattern(null, a.X, b.X, y); - LinkedListNode prev = foundPatterns.Find(p); - if (prev != null) - { //pattern already processed the last row - if (prev.Value.y != y) - { - StackedPattern sp = (StackedPattern)prev.Value; - sp.NewRow(a.X, b.X, y); - } - } - else - { //new - StackedPattern sp = new StackedPattern(null, a.X, b.X, y); - foundPatterns.AddLast(sp); - } - } - } - - //clean old patterns and process them - removedPatterns = Pattern.RemoveOldPatterns(foundPatterns, y); - foreach (Pattern p in removedPatterns) - { - StackedPattern sp = (StackedPattern)p; - ProcessPattern(sp); - } - } - - private bool ProcessPattern(StackedPattern p) - { - MyPoint a, b; - p.MidPoints(out a, out b); - MyPoint center = (a + b) / 2; - - //If the finder pattern is found in a place where another finder was found previously, it is skipped. - foreach (BarCodeRegion c in exclusion) if (c.In(center)) return false; - -#if FIND_PATTERN - MyPoint Y = new MyPoint(0, 1); - SquareFinder f = new SquareFinder(a + Y,b + Y, a, b, 7); - candidates.AddLast(f); -#else - //checks if a vertical pattern cross the horizontal pattern in the middle - BarCodeRegion[] barcodes = maxiCodeFinder.ScanFinder(a, b, center); - if (barcodes != null) foreach (BarCodeRegion r in barcodes) - { -#if FIND_FINDER - candidates.AddLast(r); -#else - byte[] bytes=MaxiCodeSampler.ScanMatrix(scan, r); - float confidence; - string msg = MaxiCodeCharDecoder.Decode(bytes, out confidence); - if (msg != null) - { - r.Data = new ABarCodeData[] { new StringBarCodeData(msg) }; - r.Confidence = confidence; - exclusion.AddFirst(r); - candidates.AddLast(r); - return true; - } -#endif - } -#endif - return false; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeSampler.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeSampler.cs deleted file mode 100644 index ab852a54..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeSampler.cs +++ /dev/null @@ -1,84 +0,0 @@ -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.MaxiCode -{ - class MaxiCodeSampler - { - //array of bit positions for each module in a maxicode barcode - static readonly int[][] Matrix = new int[][] { - new int[] {121,120,127,126,133,132,139,138,145,144,151,150,157,156,163,162,169,168,175,174,181,180,187,186,193,192,199,198, -1, -1}, - new int[] {123,122,129,128,135,134,141,140,147,146,153,152,159,158,165,164,171,170,177,176,183,182,189,188,195,194,201,200,816}, - new int[] {125,124,131,130,137,136,143,142,149,148,155,154,161,160,167,166,173,172,179,178,185,184,191,190,197,196,203,202,818,817}, - new int[] {283,282,277,276,271,270,265,264,259,258,253,252,247,246,241,240,235,234,229,228,223,222,217,216,211,210,205,204,819}, - new int[] {285,284,279,278,273,272,267,266,261,260,255,254,249,248,243,242,237,236,231,230,225,224,219,218,213,212,207,206,821,820}, - new int[] {287,286,281,280,275,274,269,268,263,262,257,256,251,250,245,244,239,238,233,232,227,226,221,220,215,214,209,208,822}, - new int[] {289,288,295,294,301,300,307,306,313,312,319,318,325,324,331,330,337,336,343,342,349,348,355,354,361,360,367,366,824,823}, - new int[] {291,290,297,296,303,302,309,308,315,314,321,320,327,326,333,332,339,338,345,344,351,350,357,356,363,362,369,368,825}, - new int[] {293,292,299,298,305,304,311,310,317,316,323,322,329,328,335,334,341,340,347,346,353,352,359,358,365,364,371,370,827,826}, - new int[] {409,408,403,402,397,396,391,390, 79, 78, -1, -1, 13, 12, 37, 36, 2, -1, 44, 43,109,108,385,384,379,378,373,372,828}, - new int[] {411,410,405,404,399,398,393,392, 81, 80, 40, -1, 15, 14, 39, 38, 3, -1, -1, 45,111,110,387,386,381,380,375,374,830,829}, - new int[] {413,412,407,406,401,400,395,394, 83, 82, 41, -1, -1, -1, -1, -1, 5, 4, 47, 46,113,112,389,388,383,382,377,376,831}, - new int[] {415,414,421,420,427,426,103,102, 55, 54, 16, -1, -1, -1, -1, -1, -1, -1, 20, 19, 85, 84,433,432,439,438,445,444,833,832}, - new int[] {417,416,423,422,429,428,105,104, 57, 56, -1, -1, -1, -1, -1, -1, -1, -1, 22, 21, 87, 86,435,434,441,440,447,446,834}, - new int[] {419,418,425,424,431,430,107,106, 59, 58, -1, -1, -1, -1, -1, -1, -1, -1, -1, 23, 89, 88,437,436,443,442,449,448,836,835}, - new int[] {481,480,475,474,469,468, 48, -1, 30, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 53, 52,463,462,457,456,451,450,837}, - new int[] {483,482,477,476,471,470, 49, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,465,464,459,458,453,452,839,838}, - new int[] {485,484,479,478,473,472, 51, 50, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 42,467,466,461,460,455,454,840}, - new int[] {487,486,493,492,499,498, 97, 96, 61, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 91, 90,505,504,511,510,517,516,842,841}, - new int[] {489,488,495,494,501,500, 99, 98, 63, 62, -1, -1, -1, -1, -1, -1, -1, -1, 28, 27, 93, 92,507,506,513,512,519,518,843}, - new int[] {491,490,497,496,503,502,101,100, 65, 64, 17, -1, -1, -1, -1, -1, -1, -1, 18, 29, 95, 94,509,508,515,514,521,520,845,844}, - new int[] {559,558,553,552,547,546,541,540, 73, 72, 32, -1, -1, -1, -1, -1, -1, 10, 67, 66,115,114,535,534,529,528,523,522,846}, - new int[] {561,560,555,554,549,548,543,542, 75, 74, -1, -1, 7, 6, 35, 34, 11, -1, 69, 68,117,116,537,536,531,530,525,524,848,847}, - new int[] {563,562,557,556,551,550,545,544, 77, 76, -1, 33, 9, 8, 25, 24, -1, -1, 71, 70,119,118,539,538,533,532,527,526,849}, - new int[] {565,564,571,570,577,576,583,582,589,588,595,594,601,600,607,606,613,612,619,618,625,624,631,630,637,636,643,642,851,850}, - new int[] {567,566,573,572,579,578,585,584,591,590,597,596,603,602,609,608,615,614,621,620,627,626,633,632,639,638,645,644,852}, - new int[] {569,568,575,574,581,580,587,586,593,592,599,598,605,604,611,610,617,616,623,622,629,628,635,634,641,640,647,646,854,853}, - new int[] {727,726,721,720,715,714,709,708,703,702,697,696,691,690,685,684,679,678,673,672,667,666,661,660,655,654,649,648,855}, - new int[] {729,728,723,722,717,716,711,710,705,704,699,698,693,692,687,686,681,680,675,674,669,668,663,662,657,656,651,650,857,856}, - new int[] {731,730,725,724,719,718,713,712,707,706,701,700,695,694,689,688,683,682,677,676,671,670,665,664,659,658,653,652,858}, - new int[] {733,732,739,738,745,744,751,750,757,756,763,762,769,768,775,774,781,780,787,786,793,792,799,798,805,804,811,810,860,859}, - new int[] {735,734,741,740,747,746,753,752,759,758,765,764,771,770,777,776,783,782,789,788,795,794,801,800,807,806,813,812,861}, - new int[] {737,736,743,742,749,748,755,754,761,760,767,766,773,772,779,778,785,784,791,790,797,796,803,802,809,808,815,814,863,862} - }; - - //This method scans the image region defined by r, sampling each module and joining - //all bits into a byte array (values from 0..63, since words are 6 bit length). - public static byte[] ScanMatrix(ImageScaner scan, BarCodeRegion r) - { -#if DEBUG_IMAGE - scan.Reset(); -#endif - //33 rows - byte[] bytes = new byte[144]; - for (int y = 0; y < 33; y++) - { - float fy=((float)y+0.5F)/33F; - MyPointF left = r.C * (1F - fy) + r.A * fy; - MyPointF right = r.D * (1F - fy) + r.B * fy; - - //alternating 30 - 29 cols - for (int x = 0; x < 29 + (y % 2 == 0 ? 1 : 0); x++) - { - float fx = ((float)x + (y % 2 == 0 ? 0.5F : 1F)) / 30F; - MyPointF p = left * (1F - fx) + right * fx; - bool isBlack = scan.isBlackSample(p,0f); - if (isBlack) - { - int nBit = Matrix[y][x]; - if (nBit>=0) - bytes[nBit/6]+=(byte)(1<<(5-nBit%6)); - } -#if DEBUG_IMAGE - scan.setPixel(p, System.Drawing.Color.Yellow); - scan.setPixel(p+new MyPointF(0.2F,0), isBlack?System.Drawing.Color.Black:System.Drawing.Color.White); -#endif - } - - } -#if DEBUG_IMAGE - scan.Save(@"d:\outSamples.png"); -#endif - return bytes; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeScaner.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeScaner.cs deleted file mode 100644 index 95ca8114..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/MaxiCode/MaxiCodeScaner.cs +++ /dev/null @@ -1,311 +0,0 @@ -using System; -using System.Collections.Generic; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.MaxiCode -{ - //Class to find maxicode barcodes from a found horizontal pattern - class MaxiCodeScaner - { - ImageScaner scan; - IPatternFinderNoiseRow pf90; - PatternFinderNoise pf45, pf135; - - //patterns to match: there are 2 patterns to allow different diameters. - public static readonly int[][] finder = new int[][] { new int[] { 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1 }, new int[] { 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2 } }; //BWBWBWWBWBWB - - - public MaxiCodeScaner(ImageScaner scan, IPatternFinderNoiseRow pf90, PatternFinderNoise pf45, PatternFinderNoise pf135) - { - this.scan = scan; - this.pf90 = pf90; - this.pf45 = pf45; - this.pf135 = pf135; - } - - //Main method to see if a found horizontal pattern with borders at (a-b) is a valid - //maxicode barcode. center is the middle of the central white segment (not necessary - //the center of a-b). - //First checks if a-b is a valid finder, then turns around the finder looking for - //alignment patterns. Once all 360º are scaned, then tries to detect all possible - //sets of 6-alignment patterns, ordered by its quality. Finally takes the best quality - //set and find the border of the barcode region, using 2 different rectification coefficient. - public BarCodeRegion[] ScanFinder(MyPoint a, MyPoint b, MyPoint center) - { - BarCodeRegion[] barcodes = null; - MaxiCodeFinder finder = IsFinder(a, b, center); - if (finder != null) - { - PatternFound[][] patterns = ScanAlignmentPatterns(finder); - LinkedList matches = FindAlignedPatterns(patterns); - barcodes = new BarCodeRegion[matches.Count > 0 ? 3 : 0]; - int n = 0; - foreach (MaxiCodeAlignedPatterns m in matches) - { - //find barcode corners (region) using different coeficients for tracking modules. - barcodes[n++] = m.ScanRegion(scan, 0.2F); - barcodes[n++] = m.ScanRegion(scan, 0.1F); - barcodes[n++] = m.ScanRegion(scan, 0.05F); - break; //only process the best quality aligned patterns (seems to be enough!) - } - } - return barcodes; - } - - //A finder is valid if has another crossing pattern, vertical or diagonal - public MaxiCodeFinder IsFinder(MyPoint a, MyPoint b, MyPoint center) - { - MyPoint centerV; - MyPoint pUp90, pDown90, pUp45, pDown45, pUp135, pDown135; - - bool is90 = CheckCrossPattern(a, b, center, pf90, out pUp90, out pDown90, out centerV); - bool is45 = CheckCrossPattern(a, b, centerV, Math.PI / 4, pf45, out pUp45, out pDown45); - bool is135 = CheckCrossPattern(a, b, centerV, 3 * Math.PI / 4, pf135, out pUp135, out pDown135); - int count = (is90 ? 1 : 0) + (is45 ? 1 : 0) + (is135 ? 1 : 0); - if (count > 0) - { - scan.setBWThreshold(a, b); -#if DEBUG_IMAGE - scan.Reset(); -#endif - Sample[] samples = null; - if (is90) samples = new Sample[] { new Sample(b, 0F, -1), new Sample(pUp90, 90F, 0), new Sample(a, 180F, 0), new Sample(pDown90, 270F, -1) }; - else if (is45) samples = new Sample[] { new Sample(b, 0F, -1), new Sample(pUp45, 45F, 0), new Sample(a, 180F, 0), new Sample(pDown45, 225F, -1) }; - else if (is135) samples = new Sample[] { new Sample(b, 0F, -1), new Sample(pUp135, 135F, 0), new Sample(a, 180F, 0), new Sample(pDown135, 315F, -1) }; - else return null; -#if DEBUG_IMAGE - foreach (Sample s in samples) scan.setPixel(new MyPointF(0.5F, 0.5F) + (MyPointF)s.point, System.Drawing.Color.Orange); -#endif - //Creates a new maxicode finder based on the borders of the found patterns. - //This allows to trace skewed circles around the finder. - MaxiCodeFinder circle = new MaxiCodeFinder(scan, centerV, samples); - - //checks external black circle: radius 4*W - SampledCircle[] circles = circle.traceCircle(new float[] { 4F }); - int black = 0; - foreach (MyPoint p in circles[0].points) if (scan.isBlack(p)) black++; - float allBlack = (float)black / (float)circles[0].points.Length; - if (allBlack < 0.6F) return null; - - //checks external white circle: radius 3.3*W (adjusted empirically) - circles = circle.traceCircle(new float[] { 3.3F }); - int white = 0; - foreach (MyPoint p in circles[0].points) if (!scan.isBlack(p)) white++; - float allWhite = (float)white / (float)circles[0].points.Length; - if (allWhite < 0.5F) return null; - -#if DEBUG_IMAGE - scan.Save(@"d:\out.png"); -#endif - return circle; - } - return null; - } - - - class PatternFound - { - public float quality; - public MyPointF p; - public PatternFound(float q, MyPointF pp) { quality = q; p = pp; } - } - - enum Color { Black, White, Unknown }; - static readonly Color[][] patterns = new Color[][] { - new Color[]{Color.Black, Color.White, Color.Black, Color.Black, Color.White, Color.Black}, //5F - new Color[]{Color.Black, Color.White, Color.Black, Color.Black, Color.Black, Color.White}, //5.5F - new Color[]{Color.White, Color.White, Color.Black, Color.White, Color.Black, Color.Black}}; //6F - - static float same(float f, Color color) - { - if (color == Color.White) { if (f < 0.4F) return 1F - f; } - else if (color == Color.Black) { if (f > 0.6F) return f; } - return 0F; - } - - //Turns 360º around the finder looking for alignment patterns. Angles are discretized - //at pixel scale. For each angle, tries to find the alignment pattern at different - //distances (to detect them even for skewed barcodes!). - PatternFound[][] ScanAlignmentPatterns(MaxiCodeFinder finder) - { - SampledCircle[] circles = finder.traceCircle(new float[] { 7F }); - PatternFound[][] patternsFound = new PatternFound[circles[0].points.Length][]; - for (int i = 0; i < circles[0].points.Length; i++) - { - MyPointF p = circles[0].points[i]; - MyVectorF vdX = (p - finder.center).Normalized; - MyVectorF vdY = vdX.Perpendicular; - - float angle = circles[0].angles[i]; - float d, v, w, h; - finder.getRadius(angle, out d, out v, out w, out h); - MyPointF pEnd = p - vdX * (w * 2F); //scans alignment patterns from distance 5W to 7W - float qualityThreshold = 0.6F; - - MyVectorF W = vdX * w; - MyVectorF W2H = vdX * (0.5F * w) - vdY * h; - - patternsFound[i] = new PatternFound[6]; - Bresenham br = new Bresenham(pEnd, p); - while (!br.End()) - { - MyPointF p1 = br.CurrentF; - MyPointF p0 = p1 - W; - MyPointF p2 = p1 + W; - MyPointF p3 = p1 + W2H; - - float s0 = scan.getModuleGrayLevel(p0, w); - float s1 = scan.getModuleGrayLevel(p1, w); - float s2 = scan.getModuleGrayLevel(p2, w); - float s3 = scan.getModuleGrayLevel(p3, w); - - for (int j = 0; j < 6; j++) - { - float q0 = (j != 1 ? same(s0, Color.White) : 1F); //do not check for pattern 0 (WWW) - float q1 = same(s1, patterns[0][j]); - float q2 = same(s2, patterns[2][j]); - float q3 = same(s3, patterns[1][j]); - if (q0 > qualityThreshold && q1 > qualityThreshold && q2 > qualityThreshold && q3 > qualityThreshold) - { - float q = (float)Math.Sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); - if (patternsFound[i][j] == null || patternsFound[i][j].quality < q) - patternsFound[i][j] = new PatternFound(q, p1); - } - } -#if DEBUG_IMAGE - /*if (false) - { - scan.Save(@"d:\out.png"); - scan.Reset(); - }*/ -#endif - br.Next(); - } - } -#if DEBUG_IMAGE - scan.Save(@"d:\out.png"); -#endif - return patternsFound; - } - - //Once alignment patterns 360º around the finder are scanned, this method find all - //sets of 6 alignment patterns and order them based on its quality. - LinkedList FindAlignedPatterns(PatternFound[][] patternsFound) - { - int offset = (patternsFound.Length) / 100; - if (offset < 1) offset = 1; - - LinkedList matches = new LinkedList(); - for (int i = 0; i < patternsFound.Length; i++) - { - MaxiCodeAlignedPatterns m = new MaxiCodeAlignedPatterns(); - for (int j = 0; j < 6; j++) - { - int pos = i + (j * patternsFound.Length) / 6; - float best = 0; - int iBest = -1; - MyPointF pBest = MyPointF.Empty; - for (int k = -offset; k <= offset; k++) - { - int pk = pos + k; - if (pk < 0) pk += patternsFound.Length; - if (pk >= patternsFound.Length) pk -= patternsFound.Length; //loop - if (patternsFound[pk][j] != null && patternsFound[pk][j].quality > best) - { - iBest = j; - best = patternsFound[pk][j].quality; - pBest = patternsFound[pk][j].p; - } - } - if (iBest == -1) { m = null; break; } - m.Add(best, pBest); - } - if (m != null) - { //add sorted by quality - LinkedListNode j = matches.First; - while (j != null) if (j.Value.quality < m.quality) break; else j = j.Next; - if (j == null) matches.AddLast(m); - else matches.AddBefore(j, m); - } - } - return matches; - } - - - //Fast check to see if a and b (start and end points of a horizontal pattern) - //has a VERTICAL pattern crossing the center. - //Used to fast accept/reject horizontal patterns found in the scan line process. - public bool CheckCrossPattern(MyPoint a, MyPoint b, MyPoint center, IPatternFinderNoiseRow patternFinder, out MyPoint minpUp, out MyPoint minpDown, out MyPoint centerV) - { - XBitArray col = scan.GetColumn(center.X); - - //cross pattern search - MyPoint pUp, pDown; - pUp = pDown = center; - minpUp = minpDown = MyPoint.Empty; - centerV = center; - - int d = (int)((float)(b.X - a.X) * 0.75F) + 1; - int start = center.Y - d; if (start < 0) start = 0; - int end = center.Y + d; if (end > col.Size) end = col.Size; - patternFinder.NewSearch(col, start, end, 1, -1); - int minDist = int.MaxValue; - while (patternFinder.NextPattern() != null) - { - pUp.Y = patternFinder.First; - pDown.Y = patternFinder.Last; - float dh = (float)(b.X - a.X); - float dv = (float)(pDown.Y - pUp.Y); - if (Calc.Around(dh / dv, 1.0F, 0.3F)) //valid ratio - { - int dist = patternFinder.Center - a.Y; - if (dist < 0) dist = -dist; - if (dist < minDist) - { - minDist = dist; - minpUp = pUp; - minpDown = pDown; - centerV.Y = patternFinder.Center; - } - } - } - if ((float)minDist <= (float)(b.X - a.X) / 12F) return true; - return false; - } - - //Fast check to see if a and b (start and end points of a horizontal pattern) - //has a DIAGONAL pattern crossing the center. - //Used to fast accept/reject horizontal patterns found in the scan line process. - public bool CheckCrossPattern(MyPoint a, MyPoint b, MyPoint center, double angle, PatternFinderNoise patternFinder, out MyPoint minpUp, out MyPoint minpDown) - { - //cross pattern search - MyPoint pUp, pDown; - pUp = pDown = center; - minpUp = minpDown = MyPoint.Empty; - - double d = (double)(b.X - a.X) * 0.75 + 1; - MyVector dd = new MyVector((int)(d * Math.Cos(angle)), -(int)(d * Math.Sin(angle))); - MyPoint start = center + dd; - MyPoint end = center - dd; - Bresenham br = new Bresenham(start, end); - patternFinder.NewSearch(br, true, 0); - float minDist = float.MaxValue; - float hDist = (a - b).Length; - while (patternFinder.NextPattern()>-1) - { - pUp = patternFinder.First; - pDown = patternFinder.Last; - float angleDist = (pUp - pDown).Length; - if (Calc.Around(hDist / angleDist, 1.0F, 0.1F)) //valid ratio - { - float dist = ((pUp + pDown) / 2 - center).Length; - if (dist < 0) dist = -dist; - if (dist < minDist) { minDist = dist; minpUp = pUp; minpDown = pDown; } - } - } - if (minDist <= (float)(b.X - a.X) / 12F) return true; - return false; - } - } -} - diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/MicroPDF/MicroPDFReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/MicroPDF/MicroPDFReader.cs deleted file mode 100644 index 5078893a..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/MicroPDF/MicroPDFReader.cs +++ /dev/null @@ -1,354 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Drawing; -using SkiaSharp; -using BarcodeReader.Core.Common; -using BarcodeReader.Core.PDF417; - -namespace BarcodeReader.Core.MicroPDF -{ - //Class to detect microPDF barcodes. Uses the edge slicer to find all edge candidates and, one - //by one, tries to find the barcode starting at the edge. -#if CORE_DEV - public -#else - internal -#endif - class MicroPDFReader : SymbologyReader2D - { - //Foreach found edge, look for the first LPattern. If it's found far away, it's rejected. - protected float maxDistanceFromEdgeToLPattern= 10f; - - //If the first LPattern has a matching error too hight, it's rejected - protected float maxLPatternError = 0.75f; - protected float maxRPatternError = 0.6f; //a little bit more relaxed - - //When check for start quiet zone, check 4 * modules with white pixels - protected float startPatternQuietZone = 10f; - - //Max width difference between the left and right pattern - protected float maxWidthDifferenceBetweenLRPatterns = 0.1f; - - //Max barcode module length. - protected float maxBarcodeLength = 110f; - - //Reject start/stop patterns that have not the same length. Set to 0 for strict same length, or bigger - //to process skewed barcodes. - protected float maxRatioStartStopLength = 0.1F; - - LinkedList candidates; - PatternFinderNoise LRowFinder; - ImageScaner scan; - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.MicroPDF; - } - - protected override FoundBarcode[] DecodeBarcode() - { - EdgeSlicer slicer = new EdgeSlicer(20, 500, false); - LinkedList edges=slicer.GetEdges(BWImage); - candidates=new LinkedList(); - scan = new ImageScaner(BWImage); - LRowFinder = new PatternFinderNoise(BWImage, LRpatterns, true, 2); - - foreach (Edge e in edges) ProcessEdge(e); - - ArrayList results = new ArrayList(); - foreach (BarCodeRegion b in candidates) - { - FoundBarcode foundBarcode = new FoundBarcode(); - foundBarcode.BarcodeFormat = SymbologyType.MicroPDF; - foundBarcode.Polygon = new SKPointI[5] { b.A, b.B, b.D, b.C, b.A }; - foundBarcode.Color = SKColors.Blue; - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(foundBarcode.Polygon, pointTypes); - //foundBarcode.Rect = Rectangle.Round(path.GetBounds()); - foundBarcode.Rect = Utils.DrawPath(foundBarcode.Polygon); - String data = ""; - if (b.Data != null) foreach (ABarCodeData d in b.Data) data += d.ToString(); - foundBarcode.Value = data; - foundBarcode.Confidence = b.Confidence; - results.Add(foundBarcode); - } - return (FoundBarcode[])results.ToArray(typeof(FoundBarcode)); - } - - int[] nModules = new int[] { 37, 54, 81, 98}; //number of modules for 1, 2, 3 or 4 cols microPDF - - class Candidate { public MyPoint p; public int nCols; public Candidate(MyPoint p, int nCols) { this.p = p; this.nCols = nCols; } } - - //For each found edge, traces a line crossing the center of the edge. Then looks for the - //left row pattern (LRowFinder object). If the pattern is no far away from the edge then - //checks for the quiet zone at the left of the pattern. If quiet zone exists, then looks for - //the right row pattern. Usually, we get a lot of false right patterns that fall inside the - //barcode. All them are stored in the candidates container (var h) and then processed, from the - //larger to the shorter. - bool ProcessEdge(Edge e) - { - //Calculate main directions - MyVectorF vdY = new MyPoint((int)e.In.X, (int)e.In.Y) - new MyPoint((int)e.End.X, (int)e.End.Y); - vdY = vdY.Normalized; - MyVectorF vdX = new MyVectorF(-vdY.Y, vdY.X); - - MyPoint mid = new MyPoint((int)(e.Center.X + (vdX * 5).X), (int)(e.Center.Y + (vdX * 5).Y)); - foreach (BarCodeRegion c in candidates) - if (c.In(mid)) return false; - - MyPoint center = new MyPoint((int)e.Center.X, (int)e.Center.Y); - Bresenham brR = new Bresenham(center, vdX); - MyPoint start=brR.Current; - LRowFinder.NewSearch(brR, false, -1); - int lRow,rRow; - while ((lRow=LRowFinder.NextPattern())!=-1) - { - float l = (start - LRowFinder.First).Length; - if (l > maxDistanceFromEdgeToLPattern) return false; //if finder is 10 pixels far, reject - if (LRowFinder.Error > maxLPatternError) continue; - - //estimate moduleLength - float LFinderLength=(LRowFinder.Last - LRowFinder.First).Length; - float moduleLength = LFinderLength / 10f; - - //check 10 modules length quiet zone - Bresenham br = new Bresenham(center, -vdX); - while (scan.In(br.Current) && scan.isBlack(br.Current)) br.Next(); - bool quietZone = true; - while (quietZone && scan.In(br.Current) && (br.Current - center).Length < moduleLength * startPatternQuietZone) - if (scan.isBlack(br.Current)) quietZone = false; - else br.Next(); - - if (quietZone) - { - //find Right row pattern candidates - MyPoint prev = MyPoint.Empty; - SortedList> h = new SortedList>(); - while ((rRow = LRowFinder.NextPattern()) != -1) - { - if (LRowFinder.Last == prev) continue; - //check if left finder has +- the same length than the left one - float RFinderLength = (LRowFinder.Last - LRowFinder.First).Length; - if (!Calc.Around(LFinderLength / RFinderLength, 1.0f, maxWidthDifferenceBetweenLRPatterns)) continue; - - prev = LRowFinder.Last; - l = (start - LRowFinder.Last).Length; - if (l > moduleLength * maxBarcodeLength) break; //98+10% - if (LRowFinder.Error > maxRPatternError) continue; - float m = l / moduleLength; - int nCols = -1; - float minL = float.MaxValue; - for (int n = 0; n < nModules.Length; n++) - { - float d = (float)Math.Abs(m - nModules[n]); - if (d < minL) { nCols = n; minL = d; } - } - if (nCols != -1 && Calc.Around((float)nModules[nCols], m, (float)nModules[nCols] * 0.07f)) - { //7% - float f = minL * 4 + nCols; - if (!h.ContainsKey(f)) h[f] = new LinkedList(); - h[f].AddLast(new Candidate(LRowFinder.Last, nCols)); - } - } - - foreach (float d in h.Keys) foreach(Candidate c in h[d]) - { - //Candidate c=(Candidate)h[d]; - - //Track edge - EdgeTrack et = new EdgeTrack(scan); - Bresenham brEnd = new Bresenham(c.p, vdX); - while (scan.In(brEnd.Current) && scan.isBlack(brEnd.Current)) brEnd.Next(); - et.Track(brEnd.Current, new MyVector(-1, 0), moduleLength, false); - - //find the top and bottom points of the edge and checks if the length is - //similar to the left side. If left and right edges have no the same length, - //uses the left length to do a last try to read the barcode. - float lIn = e.Length; - float lEnd = (et.Up() - et.Down()).Length; - if (Calc.Around(lIn, lEnd, lIn * maxRatioStartStopLength)) - if (ScanBarcode(new MyPointF(e.In.X, e.In.Y), new MyPointF(e.End.X, e.End.Y), et.Up(), et.Down(), c.nCols)) return true; - MyPointF tempInF = new MyPointF(e.In.X + (vdX * l).X, e.In.Y + (vdX * l).Y); - MyPointF tempEndF = new MyPointF(e.In.X + (vdX * l).X, e.In.Y + (vdX * l).Y); - if (ScanBarcode(new MyPointF(e.In.X, e.In.Y), new MyPointF(e.End.X, e.End.Y), tempInF, tempEndF, c.nCols)) return true; - } - - } - return false; - } - return false; - } - - //Scans all rows of the barcode doing oversampling (each row is samples many times). The most - //common codewords are used as final data. The first time, only left row patterns are decoded, - //just to find the size of the microPDF barcode. If a valid size is found, then all - //codewords are sampled to read all data. - bool ScanBarcode(MyPointF lu, MyPointF ld, MyPointF ru, MyPointF rd, int nCols) - { - int[][] sizes = sizesAndLCRIndexs[nCols]; - int numModules = nModules[nCols]; - float moduleLength = (lu - ru).Length / (float)numModules; - moduleLength *= (float)Math.Cos((ru - lu).Normalized.Angle); - RowSamples rows = new RowSamples(); - BarSymbolReader sr = new BarSymbolReader(scan, NBars[nCols], NModules[nCols], tableIndexs[nCols], true, true, moduleLength, tables, true, new PDF417BestMatch()); - for (float d = 0.01f; d < 1f; d += 0.01f) //samples 100 rows - { - MyPointF l = lu * (1f - d) + ld * d; - MyPointF r = ru * (1f - d) + rd * d; - float error, maxError, confidence; - int[] s=sr.Read(l, r, 1, out error, out maxError, out confidence); - if (s.Length>0 && s[0]!=-1) rows.AddRow(s[0], s); - } - - bool scanned = false; - int[] rowIndexs = rows.GetRowIndexs(); - Array.Sort(rowIndexs); - int i = 0; - while (i blanks = new LinkedList(); - for (int l = 0; l < codewords.Length; l++) - if (codewords[l] == -1) { codewords[l] = 0; blanks.AddLast(l); } - int[] aBlanks = new int[blanks.Count]; - blanks.CopyTo(aBlanks, 0); - if (aBlanks.Length <= 2 * nEC && aBlanks.Length <= 2 * (codewords.Length - aBlanks.Length) && - rs.Correct(codewords, aBlanks, nEC, out confidence)) - { - - // if confidence is zero then stop - if (confidence < float.Epsilon) - return false; - - BarCodeRegion r = new BarCodeRegion(lu, ru, ld, rd); - int[] data = new int[codewords.Length - nEC]; - Array.Copy(rs.correcteddata, data, data.Length); - r.Data = PDF417Decoder.Decode(data, Encoding, 900); - r.Confidence = confidence; - candidates.AddLast(r); - return true; - } - } - i++; - } - return false; - } - - //Adds a codeword in its position if it is a valid codeword. - void add(int[] codewords, int[] row, int i, ref int n) - { - codewords[n++] = (row!=null && row.Length>i?row[i]:-1); - } - - //Class used in the BarSymbolReader sr object to find the closest codeword of a pattern. - class PDF417BestMatch : IBestMatch - { - ModuleDecoder moduleDecoder; - - public PDF417BestMatch() - { - moduleDecoder = new ModuleDecoder("symbolmap.txt", 17); //PDF417 symbols are 17 modules length - } - - public int bestMatch(float[] e, LinkedList symbols) - { - if (e == null) return -1; - int cluster = (symbols.First.Value % 3) * 3; - int codeword=moduleDecoder.GetCodeword(cluster, e); - return codeword; - } - } - - - //sizes for cols=1, 2, 3 or 4 --> (nrows, num EC words, num Data words, firstL, [firstC], firstR) - static readonly int[][][] sizesAndLCRIndexs = new int[][][]{ - new int[][]{new int[]{11,7,4,1,9},new int[]{14,7,7,8,8},new int[]{17,7,10,36,36},new int[]{20,8,12,19,19},new int[]{24,8,16,9,17},new int[]{28,8,20,25,33}}, - new int[][]{new int[]{8,8,8,1,1},new int[]{11,9,13,1,9},new int[]{14,9,19,8,8},new int[]{17,10,24,36,36},new int[]{20,11,29,19,19},new int[]{23,13,33,9,17},new int[]{26,15,37,27,35}}, - new int[][]{new int[]{6,12,6,1,1,1}, new int[]{8,14,10,7,7,7},new int[]{10,16,14,15,15,15},new int[]{12,18,18,25,25,25},new int[]{15,21,24,37,37,37},new int[]{20,26,34,1,17,33},new int[]{26,32,46,1,9,17},new int[]{32,38,58,21,29,37},new int[]{38,44,70,15,31,47}, new int[]{44,50,82,1,25,49}}, - new int[][]{new int[]{4,8,8,47,19,43},new int[]{6,12,12,1,1,1},new int[]{8,14,18,7,7,7},new int[]{10,16,24,15,15,15},new int[]{12,18,30,25,25,25},new int[]{15,21,39,37,37,37},new int[]{20,26,54,1,17,33},new int[]{26,32,72,1,9,17},new int[]{32,38,72,21,29,37},new int[]{38,44,106,15,31,47},new int[]{44,50,126,1,25,49}} - }; - - //Left and Right row patterns - static readonly int[][] LRpatterns = new int[][] { - new int[] { 2,2,1,3,1,1}, new int[] {3,1,1,3,1,1 }, new int[] { 3,1,2,2,1,1}, new int[] { 2,2,2,2,1,1}, new int[] { 2,1,3,2,1,1}, - new int[]{2,1,4,1,1,1},new int[]{2,2,3,1,1,1},new int[]{3,1,3,1,1,1},new int[]{3,2,2,1,1,1},new int[]{4,1,2,1,1,1}, - new int[] {4,2,1,1,1,1 }, new int[] {3,3,1,1,1,1 }, new int[] {2,4,1,1,1,1 }, new int[] { 2,3,2,1,1,1}, new int[] {2,3,1,2,1,1 }, - new int[]{3,2,1,2,1,1},new int[]{4,1,1,2,1,1},new int[]{4,1,1,1,2,1},new int[]{4,1,1,1,1,2},new int[]{3,2,1,1,1,2}, - new int[] { 3,1,2,1,1,2}, new int[] {3,1,1,2,1,2 }, new int[] {3,1,1,2,2,1 }, new int[] {3,1,1,1,3,1 }, new int[] {3,1,1,1,2,2 }, - new int[]{3,1,1,1,1,3},new int[]{2,2,1,1,1,3},new int[]{2,2,1,1,2,2},new int[]{2,2,1,1,3,1},new int[]{2,2,1,2,2,1}, - /*31*/ new int[] {2,2,2,1,2,1 }, new int[] { 3,1,2,1,2,1}, new int[] { 3,2,1,1,2,1}, new int[] { 2,3,1,1,2,1}, new int[] {2,3,1,1,1,2 }, - /*36*/ new int[]{2,2,2,1,1,2},new int[]{2,1,3,1,1,2},new int[]{2,1,2,2,1,2},new int[]{2,1,2,2,2,1},new int[]{2,1,2,1,3,1}, - /*41*/ new int[] {2,1,2,1,2,2 }, new int[] { 2,1,2,1,1,3}, new int[] { 2,1,1,2,1,3}, new int[] {2,1,1,1,2,3 }, new int[] {2,1,1,1,3,2 }, - /*46*/ new int[]{2,1,1,1,4,1},new int[]{2,1,1,2,3,1},new int[]{2,1,1,2,2,2},new int[]{2,1,1,3,1,2},new int[]{2,1,1,3,2,1}, - /*51*/ new int[] {2,1,1,4,1,1 }, new int[] { 2,1,2,3,1,1} - }; - //Center row patterns - static readonly int[][] Cpatterns = new int[][] { - /*1*/ new int[] {1,1,2,2,3,1 }, new int[] { 1,2,1,2,3,1}, new int[] { 1,2,2,1,3,1}, new int[] {1,3,1,1,3,1 }, new int[] {1,3,1,2,2,1 }, - /*6*/ new int[]{1,3,2,1,2,1},new int[]{1,4,1,1,2,1},new int[]{1,4,1,2,1,1},new int[]{1,4,2,1,1,1},new int[]{1,3,3,1,1,1}, - /*11*/ new int[] {1,3,2,2,1,1 }, new int[] { 1,3,1,3,1,1}, new int[] {1,2,2,3,1,1 }, new int[] {1,2,3,2,1,1 }, new int[] {1,2,3,1,1,1 }, - /*16*/ new int[]{1,1,5,1,1,1},new int[]{1,1,4,2,1,1},new int[]{1,1,4,1,2,1},new int[]{1,2,3,1,2,1},new int[]{1,2,3,1,1,2}, - /*21*/ new int[] {1,2,2,2,1,2 }, new int[] {1,2,2,2,2,1 }, new int[] {1,2,1,3,2,1 }, new int[] {1,2,1,4,1,1 }, new int[] {1,1,2,4,1,1 }, - /*26*/ new int[]{1,1,3,3,1,1},new int[]{1,1,3,2,1,2},new int[]{1,1,3,2,1,2},new int[]{1,1,3,1,2,2},new int[]{1,2,2,1,2,2}, - /*31*/ new int[] { 1,3,1,1,2,2}, new int[] { 1,3,1,1,1,3}, new int[] {1,2,2,1,1,3 }, new int[] { 1,1,3,1,1,3}, new int[] {1,1,2,2,1,3 }, - /*36*/ new int[]{1,1,2,2,2,2},new int[]{1,1,2,3,1,2},new int[]{1,1,2,3,2,1},new int[]{1,1,1,4,2,1},new int[]{1,1,1,3,3,1}, - /*41*/ new int[] { 1,1,1,3,2,2}, new int[] {1,1,1,2,3,2 }, new int[] {1,1,1,2,2,3 }, new int[] { 1,1,1,1,3,3}, new int[] {1,1,1,1,2,4 }, - /*46*/ new int[]{1,1,1,2,1,4},new int[]{1,1,2,1,1,4},new int[]{1,2,1,1,1,4},new int[]{1,2,1,1,2,3},new int[]{1,2,1,1,3,2}, - /*51*/ new int[] {1,1,2,1,3,2 }, new int[] {1,1,2,1,4,1 } - }; - - static readonly int[][] NBars = new int[][] { new int[] { 6, 8, 6 }, new int[] { 6, 8, 8, 6 }, - new int[] { 6, 8, 6, 8, 8, 6 }, new int[] { 6, 8, 8,6,8,8,6 }}; - static readonly int[][] NModules = new int[][] { new int[] { 10, 17, 10 }, new int[] { 10, 17, 17, 10 }, - new int[] { 10, 17, 10, 17, 17, 10 }, new int[] { 10, 17, 17, 10, 17, 17, 10 }}; - static readonly int[][] tableIndexs = new int[][] { new int[] { 0, -1, 0 }, new int[] {0, -1, -1, 0 }, - new int[] { 0, -1, 1, -1, -1, 0}, new int[] {0, -1, -1, 1, -1, -1, 0 } }; - static readonly int[][][] tables = new int[][][] { LRpatterns, Cpatterns }; - } - -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/ModuleDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/ModuleDecoder.cs deleted file mode 100644 index 324f8c1c..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/ModuleDecoder.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.IO; -using System.Reflection; - -namespace BarcodeReader.Core.PDF417 -{ - // holds all the possible symbols, and their patterns - class ModuleDecoder - { - private readonly float[][][] mappings = new float[3][][]; - - private int nModules; - - // returns the codeword associated with the given pattern in the given cluster - public int GetCodeword(int cluster, float[] patternData) - { - float dist; - int codeword= GetCodeword(cluster, patternData, out dist); - if (dist < 0.5f) return codeword; - return -1; - } - - public int GetCodeword(int cluster, float[] patternData, out float dist) - { - return GetCodewordX(cluster, patternData, out dist); - } - - public ModuleDecoder(string resourceName, int nModules) - { - this.nModules = nModules; - Assembly assembly = Assembly.GetExecutingAssembly(); - string resourcePath = null; - foreach (string n in assembly.GetManifestResourceNames()) - if (n.EndsWith(resourceName, StringComparison.Ordinal)) - { - resourcePath = n; - break; - } - - var stream = assembly.GetManifestResourceStream(resourcePath); - if (stream == null) - throw new Exception("Could not load embedded resource."); - StreamReader reader = new StreamReader(stream); - string data = reader.ReadToEnd(); - string[] mappingsData = data.Split(new char[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries); - mappings[0] = new float[mappingsData.Length][]; - mappings[1] = new float[mappingsData.Length][]; - mappings[2] = new float[mappingsData.Length][]; - int i=0; - foreach (string mp in mappingsData) - { - string[] parts = mp.Split(' '); - int codeWord = int.Parse(parts[0]); - int cluster0 = int.Parse(parts[1]); - int cluster3 = int.Parse(parts[2]); - int cluster6 = int.Parse(parts[3]); - - mappings[0][i]=toE(cluster0); - mappings[1][i]=toE(cluster3); - mappings[2][i]=toE(cluster6); - i++; - } - - stream.Close(); - } - - float[] toE(int w) - { - int[] ws = new int[8]; //PDF417 has 8 segments BWBWBWBW - int i = 7; - while (i >= 0) { ws[i--] = w % 10; w /= 10; } - float[] e = new float[8]; - for (int j = 0; j < 8; j++) e[j] = (float)( ws[j] + ws[(j + 1) % 8]); - return e; - } - - //Min dist search - private int GetCodewordX(int cluster, float[] patternData, out float minD) - { - int min = -1; - minD = float.MaxValue; - - if (cluster % 3 != 0 || cluster > 6 || cluster < 0) return -1; - - float[][] tE = mappings[cluster/3]; - - for(int k=0;k 0) decodedData.AddRange(plusData); - break; - case 901: - ABarCodeData[] chunkBase256Data = symbolDecoder.DecodeBase256(encodedData, ref index, false, encoding); - if (chunkBase256Data != null && chunkBase256Data.Length > 0) decodedData.AddRange(chunkBase256Data); - break; - } - } - else - { - rememberTextCompactionSubMode = false; - switch (symbol) - { - case 900: // ASCII encodation or pad - ++index; - ABarCodeData[] plusData = symbolDecoder.DecodeText(encodedData, ref index, rememberTextCompactionSubMode); - if (plusData != null && plusData.Length > 0) decodedData.AddRange(plusData); - break; - case 913: // single byte mode - ++index; - rememberTextCompactionSubMode = true; - decodedData.Add(new Base256BarCodeData(new byte[] { (byte)encodedData[index++] }, encoding)); - break; - case 924: // full length multibyte mode - ++index; - ABarCodeData[] fullBase256Data = symbolDecoder.DecodeBase256(encodedData, ref index, true, encoding); - if (fullBase256Data != null && fullBase256Data.Length > 0) decodedData.AddRange(fullBase256Data); - break; - case 901: // incomplete multibyte mode - ++index; - ABarCodeData[] chunkBase256Data = symbolDecoder.DecodeBase256(encodedData, ref index, false, encoding); - if (chunkBase256Data != null && chunkBase256Data.Length > 0) decodedData.AddRange(chunkBase256Data); - break; - case 902: // numeric mode - ++index; - ABarCodeData[] numericData = symbolDecoder.DecodeNumeric(encodedData, ref index, -1); - if (numericData != null && numericData.Length > 0) decodedData.AddRange(numericData); - break; - case 926: //ECI - index += 3; - break; - case 928: //Macro PDF control block - index++; - decodedData.Add(new StringBarCodeData("\\928")); - decodedData.AddRange(symbolDecoder.DecodeNumeric(encodedData, ref index, 2)); - //decodedData.Add(new StringBarCodeData(" id:")); - decodedData.AddRange(symbolDecoder.DecodeBase900(encodedData, ref index, -1)); - while (index < encodedData.Length && encodedData[index] == 923) - { - index++; - decodedData.Add(new StringBarCodeData("\\923")); - int optionType = encodedData[index++]; - decodedData.Add(new StringBarCodeData("\\" + optionType.ToString().PadLeft(3, '0'))); - switch (optionType) - { - case 0: //file name - //decodedData.Add(new StringBarCodeData(" fileName:")); - decodedData.AddRange(symbolDecoder.DecodeText(encodedData, ref index, false)); - break; - case 1: //segment count - //decodedData.Add(new StringBarCodeData(" segmentCount:")); - decodedData.AddRange(symbolDecoder.DecodeNumeric(encodedData, ref index, 4)); - break; - case 2: //time stamp - //decodedData.Add(new StringBarCodeData(" timeStamp:")); - decodedData.AddRange(symbolDecoder.DecodeNumeric(encodedData, ref index, 6)); - break; - case 3: //sender - //decodedData.Add(new StringBarCodeData(" sender:")); - decodedData.AddRange(symbolDecoder.DecodeText(encodedData, ref index, false)); - break; - case 4: //address - //decodedData.Add(new StringBarCodeData(" address:")); - decodedData.AddRange(symbolDecoder.DecodeText(encodedData, ref index, false)); - break; - case 5: //file size - //decodedData.Add(new StringBarCodeData(" fileSize:")); - decodedData.AddRange(symbolDecoder.DecodeNumeric(encodedData, ref index, -1)); - break; - case 6: //checksum - //decodedData.Add(new StringBarCodeData(" checksum:")); - decodedData.AddRange(symbolDecoder.DecodeNumeric(encodedData, ref index, 4)); - break; - default: //decodedData.Add(new StringBarCodeData(" optional:")); - break; - } - decodedData.AddRange(symbolDecoder.DecodeBase900(encodedData, ref index, 1)); - } - break; - case 922: //Macro PDF terminator - index++; - decodedData.Add(new StringBarCodeData("\\922")); - break; - default: - ++index; - //Error - break; - } - } - } - ABarCodeData[] result = new ABarCodeData[decodedData.Count]; - decodedData.CopyTo(result); - return result; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/PDF417Finder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/PDF417Finder.cs deleted file mode 100644 index 04d7b350..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/PDF417Finder.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace BarcodeReader.Core.PDF417 -{ - class PDF417Finder - { - //Pattern of PDF417 finder - //start and reversed stop patterns: used in the main horizontal scan method. - public static readonly int[][] start = new int[][] { new int[] { 8, 1, 1, 1, 1, 1, 1, 3 }, new int[] { 1, 2, 1, 1, 1, 3, 1, 1, 7 } }; - - //stop pattern: used in the secondary scan method, once start pattern has been found - public static readonly int[] stop = { 7, 1, 1, 3, 1, 1, 1, 2, 1 }; - //reversed start pattern: used in the secondary scan method, once reversed stop pattern has been found - public static readonly int[] startReverse = { 3, 1, 1, 1, 1, 1, 1, 8 }; - - //start pattern in E format (adding 2 consecutive modules). Used to scan a given barcode region. - public static readonly int[] startE = { 9, 2, 2, 2, 2, 2, 4 }; - - public static float ModuleLength(float d) - { - return d / 17F; - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/PDF417Reader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/PDF417Reader.cs deleted file mode 100644 index 1670de49..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/PDF417Reader.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace BarcodeReader.Core.PDF417 -{ -#if CORE_DEV - public -#else - internal -#endif - partial class PDF417Reader : SymbologyReader2D - { - int width, height; - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.PDF417; - } - - protected override FoundBarcode[] DecodeBarcode() - { - this.width = BWImage.Width; - this.height = BWImage.Height; - - return Scan(); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/PDF417Scaner.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/PDF417Scaner.cs deleted file mode 100644 index a9cca133..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/PDF417Scaner.cs +++ /dev/null @@ -1,1309 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Drawing; -using SkiaSharp; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.PDF417 -{ -#if CORE_DEV - public -#else - internal -#endif - partial class PDF417Reader - { - //Scan image rows main step - protected int scanRowStep = 1; - - //Process start patterns of mínim 2 pixels height - protected int stackedPatternMinHeight = 2; - - //Reject start/stop patterns that have not the same length. Set to 0 for strict same length, or bigger - //to process skewed barcodes. - protected float maxRatioStartStopLength = 0.2F; - - - //object that holds the bw image + methods to sample, track vertices,... - ImageScaner scan; - //list of found codes found and correctly decoded. - LinkedList candidates; - //list of found and rejected finders, and codes to avoid exploring twice the same barcode - LinkedList exclusion; - - //The main idea is to find start patterns, then track the edge at de beginnig of - //the start pattern, and then trace a perpendicular scan line, looking for the stop pattern. - //(It also applies finding a reversed stop pattern and then lookind for a reverse start pattern.) - - //Edge track works better if it starts in the middle of the edge. So, once the first scan line - //detects a start pattern we don't process it. We wait until the last scan line detects this - //start pattern. For this reason, we have a list of previously found start patterns (foundPatterns) - //and this list is updated at each scan line. - - //We consider a start pattern as a new pattern, if in the foundPatterns list there isn't another - //patterns starting and ending 1 pixel left or right. - //A partern is processed when has no coincident patterns for 2 rows. - - //At the end of each scan line, foundPatterns are purged and those that have not continuity - //are added to the removedPatterns and then processed. - - //List of patterns found but not finished. They will be processed when - LinkedList foundPatterns; - LinkedList removedPatterns; - - - //patternFinder is used in the main scan loop (scanning horizontal lines). - IPatternFinderNoiseRow startFinder; - //stopFinder or startFinderReverse are used for each horizontal pattern found, to check if it is also - //it has a stop or reversed start pattern - PatternFinderNoise stopFinder, startFinderReverse; - - //flag to allow vertical scan process - bool verticalProcess = true; - - public bool VerticalProcess { get { return verticalProcess; } set { verticalProcess = value; } } - - ModuleDecoder moduleDecoder; - - FoundBarcode[] Scan() - { - scan = new ImageScaner(BWImage); - startFinder = new PatternFinderNoiseRow(PDF417Finder.start, true, true, 2); - stopFinder = new PatternFinderNoise(BWImage, PDF417Finder.stop, true, 2); - startFinderReverse = new PatternFinderNoise(BWImage, PDF417Finder.startReverse, false, 2); - candidates = new LinkedList(); - exclusion = new LinkedList(); - foundPatterns = new LinkedList(); - removedPatterns = new LinkedList(); - - moduleDecoder = new ModuleDecoder("symbolmap.txt", 17); //PDF417 symbols are 17 modules length - -#if DEBUG_IMAGE - BWImage.GetAsBitmap().Save(@"out.png"); -#endif - //main loop to scan horizontal lines - for (int y = 0; y < height && (ExpectedNumberOfBarcodes <= 0 || candidates.Count < ExpectedNumberOfBarcodes); y += scanRowStep) - { - ScanRow(y, BWImage.GetRow(y)); - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - } - //process all pending patterns - foreach (StackedPattern sp in foundPatterns) - { - ProcessPattern(sp, true); - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - } - - - if (verticalProcess) - { - foundPatterns.Clear(); - //main loop to scan vertical lines - for (int x = 0; x < width && (ExpectedNumberOfBarcodes <= 0 || candidates.Count < ExpectedNumberOfBarcodes); x += scanRowStep) - { - ScanCol(x, BWImage.GetColumn(x)); - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - } - - //process all vertical pending patterns - foreach (StackedPattern sp in foundPatterns) - { - ProcessPattern(sp, false); - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - } - } - ArrayList result = new ArrayList(); - foreach (BarCodeRegion c in candidates) - { - FoundBarcode foundBarcode = new FoundBarcode(); - foundBarcode.BarcodeFormat = SymbologyType.PDF417; - String data = ""; - - if (c.Data!=null) foreach (ABarCodeData d in c.Data) data += d.ToString(); - - foundBarcode.Value = data; - - foundBarcode.Polygon = new SKPointI[5] { c.A, c.B, c.D, c.C, c.A }; - foundBarcode.Color = SKColors.Blue; - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(foundBarcode.Polygon, pointTypes); - //foundBarcode.Rect = Rectangle.Round(path.GetBounds()); - foundBarcode.Rect = Utils.DrawPath(foundBarcode.Polygon); - foundBarcode.Confidence = c.Confidence; - result.Add(foundBarcode); - - } - return (FoundBarcode[])result.ToArray(typeof(FoundBarcode)); - } - - - - //Scans a horizontal line looking for start or reversed stop patterns. - //For each finder pattern found tracks the edge and follows its perpendicular - // line looking for the stop or revesed start pattern. - private void ScanRow(int y, XBitArray row) - { - //look for the finder - startFinder.NewSearch(row); - FoundPattern foundPattern; - while ((foundPattern = startFinder.NextPattern()) != null) - { - //foundPattern is the index of the found pattern: 0 start, 1 reversed stop - MyPoint a = new MyPoint(startFinder.First, y); - MyPoint b = new MyPoint(startFinder.Last, y); - //Check if the same pattern was processed in the last row. - Pattern p = new Pattern(foundPattern, a.X, b.X, y); - LinkedListNode prev = foundPatterns.Find(p); - if (prev != null) - { //pattern already processed the last row - if (prev.Value.y != y) - { - StackedPattern sp = (StackedPattern)prev.Value; - sp.NewRow(a.X, b.X, y); - } - } - else - { //new - StackedPattern sp = new StackedPattern(foundPattern, a.X, b.X, y); - foundPatterns.AddLast(sp); - } - } - - //clean old patterns and process them - removedPatterns=Pattern.RemoveOldPatterns(foundPatterns, y); - foreach (Pattern p in removedPatterns) - { - StackedPattern sp = (StackedPattern)p; - if (sp.y - sp.startY > stackedPatternMinHeight) ProcessPattern(sp, true); - } - } - - //Equivalent to the previous method but for vertical scans. - //Atention!!! for vertical scans, we use the same data structure than for - //horizontal scans but exchanging x and y coordinates. - private void ScanCol(int x, XBitArray col) - { - //look for the finder - startFinder.NewSearch(col); - FoundPattern foundPattern; - while ((foundPattern = startFinder.NextPattern()) != null) - { - MyPoint a = new MyPoint(x, startFinder.First); - MyPoint b = new MyPoint(x, startFinder.Last); - //Check if the same pattern was processed in the last row. - Pattern p = new Pattern(foundPattern, a.Y, b.Y, x); - LinkedListNode prev = foundPatterns.Find(p); - if (prev != null) - { //pattern already processed the last row - if (prev.Value.y != x) - { - StackedPattern sp = (StackedPattern)prev.Value; - sp.NewRow(a.Y, b.Y, x); - } - } - else - { //new - StackedPattern sp = new StackedPattern(foundPattern, a.Y, b.Y, x); - foundPatterns.AddLast(sp); - } - } - - //clean old patterns - removedPatterns = Pattern.RemoveOldPatterns(foundPatterns, x); - foreach (Pattern p in removedPatterns) - { - StackedPattern sp = (StackedPattern)p; - if (sp.y - sp.startY > stackedPatternMinHeight) ProcessPattern(sp, false); - } - } - - void ProcessPattern(StackedPattern p, bool isHorizontal) - { - //Console.WriteLine("start: " + p.startXIn + " " + p.startY); - MyPoint center, rCenter; p.Center(out center, out rCenter); - if (!isHorizontal) center = new MyPoint(center.Y, center.X); - MyPoint midCenter = center; - - //for vertical scans, we receive x and y coordinates exchanged - if (isHorizontal) midCenter.X += (int)p.MeanWidth(); - else midCenter.Y += (int)p.MeanWidth(); - - //If the finder pattern is found in a place where another finder was found previously, it is skipped. - bool done = false; - foreach (BarCodeRegion c in exclusion) - if (c.In(midCenter)) { done = true; break; } - - if (!done) - { -#if FIND_PATTERN - MyPoint a, b, c, d; - if (isHorizontal) - { - a = new MyPoint(p.startXIn, p.y); - b = new MyPoint(p.startXEnd, p.y); - c = new MyPoint(p.startXIn, p.startY); - d = new MyPoint(p.startXEnd, p.startY); - } - else - { - a = new MyPoint(p.y, p.startXIn); - b = new MyPoint(p.y, p.startXEnd); - c = new MyPoint(p.startY, p.startXIn); - d = new MyPoint(p.startY, p.startXEnd); - } - BarCodeRegion br = new BarCodeRegion(a, b, c, d); - candidates.AddLast(br); -#else - //first approach of module length (it is the horizontal or vertical - //projection of the real length) - float moduleLength=PDF417Finder.ModuleLength(p.MeanWidth()); - - //Track edge crossing the center point of the start/reversed stop pattern - EdgeTrack et = new EdgeTrack(scan); - et.Track(center, (isHorizontal? new MyVector(-1, 0): new MyVector(0,-1)), moduleLength, true); - - //find the top and bottom points of the edge - MyPointF up = et.Up(); - MyPointF down = et.Down(); - - if (!up.IsEmpty && !down.IsEmpty) - { - //Calculate main directions - MyVectorF vdY = (up - down); - vdY = vdY.Normalized; - MyVectorF vdX = new MyVectorF(-vdY.Y, vdY.X); - - //throw 3 perpendicular scan lines to detect the stop / reversed start pattern - //Normally scanning 1 perpendicular line will be enough. The other is needed for - //damaged or noisy images - MyPointF[] crossPoints = new MyPointF[] { up * 0.5F + down * 0.5F}; //, up * 0.25F + down * 0.75F, up * 0.75F + down * 0.25F }; - foreach (MyPointF c in crossPoints) - { - Bresenham br = new Bresenham(c, vdX); - PatternFinderNoise finder = null; - //if main scanning has found a start pattern, now look for stop pattern - if (p.nPattern == 0) finder = stopFinder; - //if main scanning has found a reversed stop pattern, now look for reversed start pattern - else finder = startFinderReverse; - - finder.NewSearch(br, false, -1); - while (finder.NextPattern() != -1) - for (int trackMethod = 0; trackMethod < 2;trackMethod++ ) - { - //Track edge, find pattern top and bottom, and check if length - //is similar to the first pattern length - MyPointF endUp, endDown; - if (trackMethod==0) - { - MyPoint end = (p.nPattern == 0 ? finder.First : finder.Last); - Console.WriteLine("end: " + end); - et.Track(end, (isHorizontal ? new MyVector(-1, 0) : new MyVector(0, -1)), moduleLength, p.nPattern == 0 ? true : false); - endUp = et.Up(); - endDown = et.Down(); - if (p.nPattern == 0) - { - float d = (finder.Last - finder.First).Length; - endUp += (endUp - up).Normalized * d; - endDown += (endDown - down).Normalized * d; - } - } - else - { - MyPointF mid = (p.nPattern == 0 ? (MyPointF)finder.First + vdX * (moduleLength * 3.5f) : (MyPointF)finder.Last - vdX * (moduleLength * 4f)); - MyPointF elu, eld, eru, erd; - et.TrackBar(mid, (isHorizontal ? new MyVector(-1, 0) : new MyVector(0, -1)), moduleLength, true, out elu, out eld, out eru, out erd); - if (p.nPattern == 0) //stop pattern - { - float d = (finder.Last - finder.First).Length; - endUp = elu + (elu - up).Normalized * d; - endDown = eld + (eld - down).Normalized * d; - } - else //reversed start pattern - { - endUp = eru; - endDown = erd; - } - } - - - float dstart = (up - down).Length; - float dstop = (endUp - endDown).Length; - if (Calc.Around(dstart / dstop, 1F, maxRatioStartStopLength)) - { - if (p.nPattern == 0) - { - Console.WriteLine("up {0}, down{1}, endUp{2}, endDown{3}", up, down, endUp, endDown); - if (ReadBarcode(up, down, endUp, endDown, moduleLength, true)) - return; - } - else - { //reversed - vdX = (endUp - up).Normalized; - up -= vdX; down -= vdX; - //REMOVE end -= vdX; endDown -= vdX; - Console.WriteLine("up {0}, down{1}, endUp{2}, endDown{3}", up, down, endUp, endDown); - if (ReadBarcode(endDown, endUp, down, up, moduleLength, true)) - return; - } - } - } - } - //try to read without stop pattern - if (p.nPattern == 0) - ReadBarcode(up, down, up + vdX * 100F, down + vdX * 100F, moduleLength, false); - } -#endif - } - } - - - //up, down, endUp and endDown are the vertices of a candidate barcode. - //module length is a first aprox. of the module length - //hasEnd is true if both, start and stop patterns, where found. If hasEnd is false, - //endUp and endDown only give the direction where the stop pattern should be. - - //This methods has 2 parts: - //1.- scans the firsts 2 columns to read the number of rows, columnes, and error level. It - // does oversampling, to avoid erroneous scans. Then take the most common value for each of them. - //2.- If rows, columns and error level are detected, scans the rest of columns for codewords. - bool ReadBarcode(MyPointF up, MyPointF down, MyPointF endUp, MyPointF endDown, float moduleLength, bool hasEnd) - { - MyVectorF vdY = (up - down); - float startLength = vdY.Length; - vdY = vdY.Normalized; - MyVectorF vdX = new MyVectorF(-vdY.Y, vdY.X); - - MyVectorF endVdY = (endUp - endDown).Normalized; - float d = moduleLength/2f; - bool rowsColsErrRead = false; - Hashtable rowsDiv3 = new Hashtable(); - Hashtable rowsMod3 = new Hashtable(); - Hashtable cols = new Hashtable(); - Hashtable errorCorrectionLevel = new Hashtable(); - int crd3 = 0, crm3 = 0, cc = 0, ce = 0; - SortedList mLenghts=new SortedList(); - int nMLengths = 0; - int minSamplesCount = Math.Max(3, (int)(((down - up).Length / moduleLength) / 6)); - - //first part: scan the 2nd column to extract row, columns and error level info. - //For each start pattern correctly read, we store its length to obtain a good mean - //module length - while (!rowsColsErrRead && d < startLength) - { - MyPointF a = up - vdY * d; - MyPointF b = endUp - endVdY * d; - SymbolReader sr = new SymbolReader(scan, a, b, 8,true, hasEnd, moduleLength); - MyPoint p0 = sr.Current; - float[] start = sr.NextSymbol(); //skip start - if (start != null && ArrayEquals(start, PDF417Finder.startE)) - { - //update the hash of module lengths - float mLength = (sr.Current - p0).Length / 17F; - if (mLenghts.ContainsKey(mLength)) mLenghts[mLength]++; - else mLenghts.Add(mLength, 1); - nMLengths++; - - //if the 2nd row can read extract info - float[] lRow = sr.NextSymbol(); - if (lRow != null) - { - int cluster1 = (int)(Math.Round(lRow[0]) - Math.Round(lRow[1]) + Math.Round(lRow[4]) - Math.Round(lRow[5]) + 9) % 9; - int cluster2 = ((int)Math.Round(lRow[0] - lRow[2] + lRow[4] - lRow[6] + 9)) % 9; - int cluster = cluster1 % 3 == 0 ? cluster1 : cluster2; - - int codeword = moduleDecoder.GetCodeword(cluster, lRow); - if (codeword != -1) - { - if (cluster == 0) - { - add(rowsDiv3, codeword % 30); - crd3++; - } - else if (cluster == 3) - { - add(rowsMod3, (codeword % 30) % 3); - add(errorCorrectionLevel, (codeword % 30) / 3); - crm3++; - ce++; - - } - else if (cluster == 6) - { - add(cols, codeword % 30); - cc++; - } - - //stop when we have enough samples of each parameter - rowsColsErrRead = crd3 > minSamplesCount && crm3 > minSamplesCount && cc > minSamplesCount && ce > minSamplesCount; - } - } - } - d += moduleLength/2f; - } - - //If parameters are read (even if they don't have enough samples) - if (crd3>0 && crm3>0 && cc>0 && ce>0) - { - //extract 2 most probably parameters: - int nCols1 = most(cols); - int nCols2 = mostExcept(cols, nCols1); - int[] nColsArray = new int[] { nCols1 + 1, nCols2 + 1}; - - - int nRows = most(rowsDiv3) * 3 + most(rowsMod3) % 3 + 1; - int nError = most(errorCorrectionLevel); - float mLength = Median(mLenghts, nMLengths); - - foreach (var nCols in nColsArray) - { - if (nCols <= 0) - continue; - - //Define the barcode region. - BarCodeRegion bc; - if (hasEnd) bc = new BarCodeRegion(down, endDown, up, endUp); - else - { //if stop pattern is not found, use an estimate - MyVectorF l = vdX * 17F * (4F + (float)nCols) * moduleLength; - bc = new BarCodeRegion(down, down + l, up, up + l); - } - - //read code words and perform error correction - float confidence = 0f; - int[] data = null; - - if (data == null) data = ReadSymbol(bc, mLength, nRows, nCols, nError, hasEnd, out confidence); - if (data == null) data = ReadSymbol3(bc, mLength, nRows, nCols, nError, out confidence); // improved version of ReadSymbol2 - if (data == null) data = ReadSymbol2(bc, mLength, nRows, nCols, nError, out confidence); - if (data == null) data = ReadSymbolSimple(bc, mLength, nRows, nCols, nError, hasEnd, out confidence); - - if (data != null) - { - //Decode bytes to data - ABarCodeData[] result = DecodeData(data); - if (result != null) - { - bc.Data = result; - bc.Confidence = confidence; - candidates.AddLast(bc); - exclusion.AddLast(bc); - return true; - } - } - } - } - return false; - } - - bool ArrayEquals(float[] a, int[] b) - { - float e = 0f; - for (int i = 0; i < b.Length; i++) - { - float ee = a[i] - b[i]; - e += ee * ee; - } - return e < 2f; - - /*for (int i = 0; i < b.Length; i++) - if ((int)Math.Round(a[i]) != b[i]) return false; - return true;*/ - } - - float Median(SortedList l, int n) - { - int c = 0; - int mid = n / 2; - foreach (float m in l.Keys) - { - c += l[m]; - if (c > mid) return m; - } - return 0F; - } - - void add(Hashtable h, int v) - { - if (h.ContainsKey(v)) h[v]=(int)h[v] + 1; - else h[v] = 1; - } - - int most(Hashtable h) - { - if (h.Count == 0) return -1; - int max = 0, val = -1; - foreach (int i in h.Keys) - if ((int)h[i] > max) - { - max = (int)h[i]; - val = i; - } - return val; - } - - - int mostExcept(Hashtable h, int exceptedValue) - { - if (h.Count == 0) return -1; - int max = 0, val = -1; - foreach (int i in h.Keys) - if ((int)h[i] > max && i != exceptedValue) - { - max = (int)h[i]; - val = i; - } - return val; - } - - int mostNotRepeat(Hashtable h) - { - int m = most(h); - if (m != -1) - { - int max = (int)h[m]; - foreach (int i in h.Keys) - if (i != m && (int)h[i] == max) return -1; //there are 2 values with the same number of repetitions - } - return m; - } - - - // Now that we know how the barcode looks like, try to read the symbols (or at - //least enough of them to attempt error correction). - int[] ReadSymbol(BarCodeRegion r, float moduleLength, int rows, int cols, int errorCorrectionLevel, bool hasEnd, out float confidence) - { - confidence = 0F; - - int errorCodeWordCount = 2 << errorCorrectionLevel; - if (errorCodeWordCount > rows * cols) return null; - -#if DEBUG_IMAGE - scan.Reset(); -#endif - ReedSolomon rs = new ReedSolomon(); - - float[] exposure = new float[] { 0.6f, 0.4f, 0.5f }; - for (int iExposure = 0; iExposure < exposure.Length; iExposure++) - { - int[] data = new int[rows * cols]; - - Grid g = new Grid((cols + 4) * 17 + 2, rows, r.C, r.A, r.D, r.B, true); - MyVectorF vdYStart = (r.A - r.C) / rows; - MyVectorF vdYStop = (r.B - r.D) / rows; - - - float[] offsets = new float[] { 0.5f, 0.3F, 0.7F }; - for (int n = 0; n < offsets.Length; n++) - { - // intitialize data before the next try - // IMPORTANT: refs #150 Decoders: we should re-initialize data before each iteration - // i.e. we must NOT mix data with other iterations - // as in some RARE cases it may cause RS correction to pass the incorrect data made mixed from some INTERSECTED rows - // the root of the issue lays in rare cases when 2 rows are using the data from the actualy same physical points (due to offset) - // see PDF417\2014\pdf417-1\04.png - // initialize data array with -1 (means error or erasure) that will contain data's codewords - for (int i = 0; i < data.Length; i++) data[i] = -1; //initialize - - float offset = offsets[n]; - for (int row = 0; row < rows; row++) - { - //Console.WriteLine("-------------ROW:" + row); - MyPointF a = r.C + vdYStart * ((float)row + offset); - MyPointF b = r.D + vdYStop * ((float)row + offset); - SymbolReader sr = new SymbolReader(scan, a, b, 8, true, false, moduleLength); - - //find the start point: calculate theoric and adjust (if possible) - MyPointF theoric = a; - MyPoint previous = sr.Adjust(theoric); - if (previous == MyPoint.Empty) previous = theoric; - - //scan row: rows 0 and 1 are processed just to adjust the start point of each symbol - MyVectorF vdX = (b - a).Normalized; - float ml = moduleLength * 17F; //initial value. Will be updated at each step. - for (int col = 0; col < cols + 2; col++) - { - //Calculates the end point. First estimates theoric point, then adjust it. - theoric = (hasEnd ? g.GetSamplePoint((float)((col + 1) * 17)+0.5f, (float)row+offset) : previous + vdX * ml); - MyPoint adjusted = sr.Adjust(theoric); - if (adjusted == MyPoint.Empty) adjusted = theoric; - -#if DEBUG_IMAGE - scan.setPixel(theoric, Color.Blue); - scan.setPixel(adjusted, Color.Orange); -#endif - - if (col > 1) - { - //obtain a list of possible symbols (module lenghts in E format) - //Since we give the start and end point of the symbol, we can - //solve noise pixels. The result is a list of possible symbols, since - //gray modules lead to *2 solutions. - LinkedList ES = sr.NextSymbol(previous, adjusted, exposure[iExposure]); - - //for each possible symbol, tries to get the codeword - int codeword = -1; - float minD = float.MaxValue; - foreach (int[] E in ES) - { - float[] fE = new float[8]; - for (int i = 0; i < 8; i++) fE[i] = (float)E[i]; - float dist; - int c = moduleDecoder.GetCodeword((row % 3) * 3, fE, out dist); - //if (c != -1) { data[row * cols + col - 2].AddLast(new Codeword(c,dist)); codeword = c; } - if (c != -1 && dist < minD) { minD = dist; codeword = c; break; } - } - - //if symbol is correctly converted to codeword then add to the data array - if (codeword != -1) - { - data[row * cols + col - 2] = codeword; - if (!hasEnd) ml = (adjusted - previous).Length; //update module length - } - } - - previous = adjusted; - } - } -#if DEBUG_IMAGE - scan.Save(@"outScan.png"); -#endif - List blanks = new List(); - for (int i = 0; i < data.Length; i++) - if (data[i] == -1) - { - blanks.Add(i); - } - int[] aBlanks = new int[blanks.Count]; - blanks.CopyTo(aBlanks, 0); - - if (rs.Correct(data, aBlanks, errorCodeWordCount, out confidence)) - { - return rs.correcteddata; - } - - } // for offsets - - } - - return null; - } - - //another approach to read PDF417 barcodes. This version finds out the starting edge of - //each column, and then scans all rows of that column. - int[] ReadSymbol2(BarCodeRegion r, float moduleLength, int rows, int cols, int errorCorrectionLevel, out float confidence) - { - confidence = 0F; - - int errorCodeWordCount = 2 << errorCorrectionLevel; - if (errorCodeWordCount > rows * cols) return null; - -#if DEBUG_IMAGE - scan.Reset(); -#endif - ReedSolomon rs = new ReedSolomon(); - MyPoint[][] colPoints = FindCols(r, moduleLength, rows, cols); - MyVectorF vdY = (r.A - r.C) / rows; - SymbolReader sr = new SymbolReader(scan, MyPoint.Empty, MyPoint.Empty, 8, true, false, moduleLength); - - Hashtable[] data = new Hashtable[rows * cols]; - for (int i = 0; i < data.Length; i++) data[i] = new Hashtable(); //initialize - - float[] exposure = new float[] { 0.6f, 0.4f, 0.5f }; - for (int iExposure = 0; iExposure < exposure.Length; iExposure++) - { - //scan row: rows 0 and 1 are processed just to adjust the start point of each symbol - int[] realRow = new int[rows * 3]; //due to oversampling x3 - for (int i = 0; i < realRow.Length; i++) realRow[i] = i / 3; //initial guess of row numbers - - for (int col = 1; col < cols + 2; col++) - { - for (int row = 0; row < rows; row++) - { - float[] offsets = new float[] { 0f, 0.3f, -0.3f }; - for (int n = 0; n < offsets.Length; n++) - { - float offset = offsets[n]; - MyPoint aa = findOffset(colPoints[row][col], vdY, offset); - MyPoint bb = findOffset(colPoints[row][col + 1], vdY, offset); - int index = row * 3 + n; //to index realRow; - -#if DEBUG_IMAGE - scan.setPixel(aa, Color.Blue); -#endif - - //obtain a list of possible symbols (module lenghts in E format) - //Since we give the start and end point of the symbol, we can - //solve noise pixels. The result is a list of possible symbols, since - //gray modules lead to *2 solutions. - LinkedList ES = sr.NextSymbol(aa, bb, exposure[iExposure]); - - //for each possible symbol, tries to get the codeword - int codeword = -1, cluster = -1; - foreach (int[] E in ES) - { - float[] fE = new float[8]; - for (int i = 0; i < 8; i++) fE[i] = (float)E[i]; - float dist; - cluster = (realRow[index] % 3); - int c = moduleDecoder.GetCodeword(cluster * 3, fE, out dist); - if (c == -1 && realRow[index] < rows - 1) - { - cluster = (cluster + 1) % 3; - c = moduleDecoder.GetCodeword(cluster * 3, fE, out dist); - if (c != -1) realRow[index] += 1; - } - if (c == -1 && realRow[index] > 0) - { - cluster = (cluster + 1) % 3; - c = moduleDecoder.GetCodeword(cluster * 3, fE, out dist); - if (c != -1) realRow[index] -= 1; - } - if (c != -1) { codeword = c; break; } - } - - //if symbol is correctly converted to codeword then add to the data array - if (codeword != -1) - { - if (col == 1) - { - realRow[index] = (codeword / 30) * 3 + cluster; - } - else - { - int i = realRow[index] * cols + col - 2; - if (i < data.Length) - { - Hashtable h = data[realRow[index] * cols + col - 2]; - if (!h.ContainsKey(codeword)) h.Add(codeword, 1); - else h[codeword] = (int)h[codeword] + 1; - } - } - } - } - } - } -#if DEBUG_IMAGE - scan.Save(@"outScan.png"); -#endif - - - - LinkedList blanks = new LinkedList(); - int[] bytes = new int[data.Length]; - for (int i = 0; i < data.Length; i++) - { - int m = mostNotRepeat(data[i]); - if (m==-1) { blanks.AddLast(i); bytes[i] = -1; } - bytes[i] = m; - } - int[] aBlanks = new int[blanks.Count]; - blanks.CopyTo(aBlanks, 0); - if (rs.Correct(bytes, aBlanks, errorCodeWordCount, out confidence)) - { - return rs.correcteddata; - } - - /*ZXing.PDF417.Internal.EC.ErrorCorrection ec = new ZXing.PDF417.Internal.EC.ErrorCorrection(); - int errorLocationsCount; - for (int i = 0; i < bytes.Length; i++) if (bytes[i] == -1) bytes[i] = 0; - blanks.CopyTo(aBlanks, 0); - if (ec.decode(bytes, errorCodeWordCount, aBlanks, out errorLocationsCount)) - return bytes; - */ - } - return null; - - } - - //another approach to read PDF417 barcodes. This version finds out the starting edge of - //each column, and then scans all rows of that column. - //this is improved version of ReadSymbol2 - int[] ReadSymbol3(BarCodeRegion r, float moduleLength, int rows, int cols, int errorCorrectionLevel, out float confidence) - { - confidence = 0F; - - int errorCodeWordCount = 2 << errorCorrectionLevel; - if (errorCodeWordCount > rows * cols) return null; - -#if DEBUG_IMAGE - scan.Reset(); -#endif - ReedSolomon rs = new ReedSolomon(); - MyPoint[][] colPoints = FindCols2(r, ref moduleLength, rows, cols); - MyVectorF vdY = (r.A - r.C) / rows; - SymbolReader sr = new SymbolReader(scan, MyPoint.Empty, MyPoint.Empty, 8, true, false, moduleLength); - - Hashtable[] data = new Hashtable[rows * cols]; - for (int i = 0; i < data.Length; i++) data[i] = new Hashtable(); //initialize - - //scan row: rows 0 and 1 are processed just to adjust the start point of each symbol - int[] realRow = new int[rows * 3]; //due to oversampling x3 - for (int i = 0; i < realRow.Length; i++) realRow[i] = i / 3; //initial guess of row numbers - - for (int col = 1; col < cols + 2; col++) - { - for (int row = 0; row < rows; row++) - { - MyPoint aa = colPoints[row][col]; - MyPoint bb = colPoints[row][col + 1]; - int index = row * 3; //to index realRow; - - MyPoint adjusted = sr.Adjust(aa); - if (adjusted != MyPoint.Empty) aa = adjusted; - - adjusted = sr.Adjust(bb); - if (adjusted != MyPoint.Empty) bb = adjusted; - -#if DEBUG_IMAGE - scan.setPixel(aa, Color.Lime); - scan.setPixel(bb, Color.Red); -#endif - - //obtain a list of possible symbols (module lenghts in E format) - //Since we give the start and end point of the symbol, we can - //solve noise pixels. The result is a list of possible symbols, since - //gray modules lead to *2 solutions. - LinkedList ES = sr.NextSymbol(aa, bb, 0.5f); - //LinkedList ES = sr.NextSymbolSimple(aa, bb); - - //for each possible symbol, tries to get the codeword - int codeword = -1, cluster = -1; - foreach (int[] E in ES) - { - float[] fE = new float[8]; - for (int i = 0; i < 8; i++) fE[i] = (float)E[i]; - float dist; - cluster = (realRow[index] % 3); - int c = moduleDecoder.GetCodeword(cluster * 3, fE, out dist); - if (c == -1 && realRow[index] < rows - 1) - { - cluster = (cluster + 1) % 3; - c = moduleDecoder.GetCodeword(cluster * 3, fE, out dist); - if (c != -1) realRow[index] += 1; - } - if (c == -1 && realRow[index] > 0) - { - cluster = (cluster + 1) % 3; - c = moduleDecoder.GetCodeword(cluster * 3, fE, out dist); - if (c != -1) realRow[index] -= 1; - } - if (c != -1) { codeword = c; break; } - } - - //if symbol is correctly converted to codeword then add to the data array - if (codeword != -1) - { - if (col == 1) - { - realRow[index] = (codeword / 30) * 3 + cluster; - } - else - { - int i = realRow[index] * cols + col - 2; - if (i < data.Length) - { - Hashtable h = data[realRow[index] * cols + col - 2]; - if (!h.ContainsKey(codeword)) h.Add(codeword, 1); - else h[codeword] = (int)h[codeword] + 1; - } - } - } - } - } - -#if DEBUG_IMAGE - scan.Save(@"outScan.png"); -#endif - - LinkedList blanks = new LinkedList(); - int[] bytes = new int[data.Length]; - for (int i = 0; i < data.Length; i++) - { - int m = mostNotRepeat(data[i]); - if (m == -1) { blanks.AddLast(i); bytes[i] = -1; } - bytes[i] = m; - } - int[] aBlanks = new int[blanks.Count]; - blanks.CopyTo(aBlanks, 0); - //Console.WriteLine("aBlanks: " + aBlanks.Length); - if (rs.Correct(bytes, aBlanks, errorCodeWordCount, out confidence)) - { - return rs.correcteddata; - } - - /*ZXing.PDF417.Internal.EC.ErrorCorrection ec = new ZXing.PDF417.Internal.EC.ErrorCorrection(); - int errorLocationsCount; - for (int i = 0; i < bytes.Length; i++) if (bytes[i] == -1) bytes[i] = 0; - blanks.CopyTo(aBlanks, 0); - if (ec.decode(bytes, errorCodeWordCount, aBlanks, out errorLocationsCount)) - return bytes; - */ - - return null; - - } - - // Simplified version of the read symbol algorithm - int[] ReadSymbolSimple(BarCodeRegion r, float moduleLength, int rows, int cols, int errorCorrectionLevel, bool hasEnd, out float confidence) - { - confidence = 0F; - - int errorCodeWordCount = 2 << errorCorrectionLevel; - if (errorCodeWordCount > rows * cols) return null; - ReedSolomon rs = new ReedSolomon(); - - int[] data = new int[rows * cols]; - - Grid g = new Grid((cols + 4) * 17 + 2, rows, r.C, r.A, r.D, r.B, true); - MyVectorF vdYStart = (r.A - r.C) / rows; - MyVectorF vdYStop = (r.B - r.D) / rows; - - // initialize data array with -1 (means error or erasure) that will contain data's codewords - for (int i = 0; i < data.Length; i++) data[i] = -1; //initialize - - float offset =0.5f; - for (int row = 0; row < rows; row++) - { - //Console.WriteLine("-------------ROW:" + row); - MyPointF a = r.C + vdYStart * ((float)row + offset); - MyPointF b = r.D + vdYStop * ((float)row + offset); - SymbolReader sr = new SymbolReader(scan, a, b, 8, true, false, moduleLength); - - //find the start point: calculate theoric and adjust (if possible) - MyPointF theoric = a; - MyPoint previous = sr.Adjust(theoric); - if (previous == MyPoint.Empty) previous = theoric; - - //scan row: rows 0 and 1 are processed just to adjust the start point of each symbol - MyVectorF vdX = (b - a).Normalized; - float ml = moduleLength * 17F; //initial value. Will be updated at each step. - for (int col = 0; col < cols + 2; col++) - { - //Calculates the end point. First estimates theoric point, then adjust it. - theoric = (hasEnd ? g.GetSamplePoint((float)((col + 1) * 17) + 0.5f, (float)row + offset) : previous + vdX * ml); - MyPoint adjusted = sr.Adjust(theoric); - if (adjusted == MyPoint.Empty) adjusted = theoric; - - if (col > 1) - { - LinkedList ES = sr.NextSymbolSimple(previous, adjusted); - - //for each possible symbol, tries to get the codeword - int codeword = -1; - float minD = float.MaxValue; - foreach (int[] E in ES) - { - float[] fE = new float[8]; - for (int i = 0; i < 8; i++) fE[i] = (float)E[i]; - float dist; - int c = moduleDecoder.GetCodeword((row % 3) * 3, fE, out dist); - if (c != -1 && dist < minD) { minD = dist; codeword = c; break; } - } - - //if symbol is correctly converted to codeword then add to the data array - if (codeword != -1) - { - data[row * cols + col - 2] = codeword; - if (!hasEnd) ml = (adjusted - previous).Length; //update module length - } - } - - previous = adjusted; - } - } - - List blanks = new List(); - for (int i = 0; i < data.Length; i++) - if (data[i] == -1) - { - blanks.Add(i); - } - int[] aBlanks = new int[blanks.Count]; - blanks.CopyTo(aBlanks, 0); - - if (rs.Correct(data, aBlanks, errorCodeWordCount, out confidence)) - { - return rs.correcteddata; - } - - return null; - } - - MyPoint findOffset(MyPoint p, MyVectorF vd, float offset) - { - MyPoint q = p; - if (offset != 0f) - { - int k = 1; - while (q.Equals(q) && k<10) - { - q = p + vd * (offset * (float)k); - k++; - } - } - return q; - } - - MyPoint[][] FindCols(BarCodeRegion r, float moduleLength, int rows, int cols) - { - MyPoint[][] colPoints = new MyPoint[rows][]; - MyVectorF vdYStart = (r.A - r.C) / rows; - MyVectorF vdYEnd = (r.B - r.D) / rows; - SymbolReader[] readers = new SymbolReader[rows]; - float[][] E = new float[rows][]; - for (int i = 0; i < rows; i++) - { - MyPointF a = r.C + vdYStart * ((float)i + 0.5f); - MyPointF b = r.D + vdYEnd * ((float)i +0.5f); - readers[i] = new SymbolReader(scan, a, b, 8, true, false, moduleLength); - colPoints[i] = new MyPoint[cols+1+4]; - colPoints[i][0] = a; - } - - Hashtable dists = new Hashtable(); - float lastDist=0f; - float resolution = moduleLength; - for (int col = 0; col < cols+4; col++) - { - dists.Clear(); - for (int row = 0; row < rows; row++) - { - E[row] = readers[row].NextSymbol(); - int d = (int)Math.Round(readers[row].Offset / resolution, MidpointRounding.AwayFromZero); - if (d < lastDist + (17+7)) - if (!dists.ContainsKey(d)) dists.Add(d, 1); - else dists[d] = (int)dists[d] + 1; - } - int dist = most(dists); - - for (int row = 0; row < rows; row++) - { - int d = (int)Math.Round(readers[row].Offset / resolution, MidpointRounding.AwayFromZero); - if (d == dist) colPoints[row][col + 1] = readers[row].Current; - else - { - colPoints[row][col + 1] = colPoints[row][0] + readers[row].Vd * (resolution * (float)dist); - } - } - lastDist=dist; - } - - return colPoints; - } - - MyPoint[][] FindCols2(BarCodeRegion r, ref float moduleLength, int rows, int cols) - { - MyPoint[][] colPoints = new MyPoint[rows][]; - - var vdYStart = (r.A - r.C); - var vdYEnd = (r.B - r.D); - - //scan barcode horizontally - //build horizontal histogramm - float[] maximums = FindColumns(r, cols); - - for (int iRow = 0; iRow < rows; iRow++) - colPoints[iRow] = new MyPoint[cols + 1 + 4]; - - var sumModules = 0f; - var countModules = 0; - - for (int iCol = 0; iCol < maximums.Length; iCol++) - { - //calc column width - var columnWidth = 0f; - if (iCol < maximums.Length - 1) - columnWidth = maximums[iCol + 1] - maximums[iCol]; - else - columnWidth = maximums[iCol] - maximums[iCol - 1]; - - //find rows - var calculatedRows = FindRows(r, maximums[iCol], columnWidth, rows); - - //copy to output array - for (int iRow = 0; iRow < rows; iRow++) - { - //var k = (iRow + 0.5f) / rows; - var k = calculatedRows[iRow]; - var from = r.C + vdYStart * k; - var to = r.D + vdYEnd * k; - - colPoints[iRow][iCol] = MyPointF.Lerp(from, to, maximums[iCol]); - - if (iCol > 1) - { - sumModules += (colPoints[iRow][iCol] - colPoints[iRow][iCol - 1]).Length / 17f; - countModules++; - } - } - } - - //calc avg moduleLength - if (countModules > 0) - { - moduleLength = sumModules / countModules; - } - - return colPoints; - } - - private float[] FindRows(BarCodeRegion r, float startX, float columnWidthNorm, int rows) - { - var res = new float[rows]; - var vdYStart = (r.A - r.C); - var vdYEnd = (r.B - r.D); - //calc abs coordinates of Column rect - var c = MyPointF.Lerp(r.C, r.D, startX); - var d = MyPointF.Lerp(r.C, r.D, startX + columnWidthNorm); - var a = MyPointF.Lerp(r.A, r.B, startX); - var b = MyPointF.Lerp(r.A, r.B, startX + columnWidthNorm); - var vdY = a - c; - var vdXStart = d - c; - var vdXEnd = b - a; - - //calc vert histogramm - var histLength = (int)Math.Round(vdY.Length); - var histogramm = new float[histLength]; - var linesCount = (int)Math.Round(vdXStart.Length); - - for (int iLine = 0; iLine < linesCount; iLine++) - { - var from = c + vdXStart * (iLine + 1) / (linesCount + 1); - var to = a + vdXEnd * (iLine + 1) / (linesCount + 1); - - var br = new Bresenham(from, to); - var steps = br.Steps; - var prev = false; - while (!br.End()) - { - var pixel = scan.isBlack(br.Current.X, br.Current.Y); - if (prev != pixel) - { - var index = histLength * (steps - br.Steps) / steps; - for (int i = -2; i <= 2; i++) - { - var ii = index + i; - if (ii >= 0 && ii < histLength) - histogramm[ii] += 1 - Math.Abs(i) / 3f; - } - } - - prev = pixel; - br.Next(); - } - } - - //find best row - var delta = (int)Math.Round((1f * histLength / rows) / 6f); - for (int iRow = 0; iRow < rows; iRow++) - { - var bestRowPos = (iRow + 0.5f) / rows; - var minDisp = float.MaxValue; - var index = (int)Math.Round(histLength * bestRowPos); - //find minimal dispersion of histogramm - if (delta > 0) - { - for (int i = index - delta; i <= index + delta; i++) - if (i >= 0 && i < histLength) - { - if (histogramm[i] < minDisp) - { - minDisp = histogramm[i]; - bestRowPos = 1f * i / histLength; - } - } - } - - // - res[iRow] = bestRowPos; - } - - return res; - } - - private float[] FindColumns(BarCodeRegion r, int cols) - { - var vdYStart = (r.A - r.C); - var vdYEnd = (r.B - r.D); - - var rowsCount = (int)((r.A - r.C).Length); - var colsCount = (int)((r.D - r.C).Length); - var histogramm = new int[colsCount * 2]; - var histLength = histogramm.Length; - for (int iRow = 0; iRow < rowsCount; iRow += 2) - { - var k = (float)iRow / rowsCount; - var from = r.C + vdYStart * k; - var to = r.D + vdYEnd * k; - - var br = new Bresenham(from, to); - var steps = br.Steps; - var prev = false; - while (!br.End()) - { - var pixel = scan.isBlack(br.Current.X, br.Current.Y); - if (prev == false && pixel == true) - { - var index = histLength * (steps - br.Steps) / steps; - for (int i = index - 1; i <= index + 1; i++) - if (i >= 0 && i < histLength) - histogramm[i]++; - } - - prev = pixel; - br.Next(); - } - } - - //find maximum for each column - var maximums = new float[cols + 5]; - var d = histLength / (cols + 4) / 7; - for (int iCol = 0; iCol <= cols + 4; iCol++) - { - var expectedX = histLength * iCol / (cols + 4); - var best = 0; - var bestX = 0f; - for (var x = expectedX - d; x <= expectedX + d; x++) - if (x >= 0 && x < histLength) - if (histogramm[x] > best) - { - best = histogramm[x]; - bestX = x; - } - //normalized point x for maximum - maximums[iCol] = bestX / histLength; - } - - return maximums; - } - - void ArrToConsole(int[] arr) - { - var sb = new StringBuilder(); - for (int i = 0; i < arr.Length; i++) - sb.Append(arr[i].ToString() + "\t"); - - Console.WriteLine(sb.ToString()); - } - - // decode the data, according to the indicated encodation type - ABarCodeData[] DecodeData(int[] data) - { - int dataLength = data[0] - 1; - if (dataLength > data.Length || dataLength<1) return null; - int[] encodedData = new int[dataLength]; - Array.Copy(data, 1, encodedData, 0, dataLength); - return PDF417Decoder.Decode(encodedData, Encoding,900); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/ReedSolomon.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/ReedSolomon.cs deleted file mode 100644 index 36b6741a..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/ReedSolomon.cs +++ /dev/null @@ -1,552 +0,0 @@ -using System; - -namespace BarcodeReader.Core.PDF417 -{ - // Reed-solomon decoder for PDF417, base3 code - // http://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction - // http://en.wikiversity.org/wiki/Reed%E2%80%93Solomon_codes_for_coders - // http://grandzebu.net/informatique/codbar-en/pdf417.htm - class ReedSolomon - { - - private const int Gprime = 929; - private const int A0 = 928; - - private static readonly int[] Exp = new int[1024]; - private static readonly int[] Log = new int[1024]; - - static ReedSolomon() - { - int powerOf3 = 1; - Log[1] = Gprime - 1; - - for (int ii = 0; ii < Gprime - 1; ii += 1) - { - Exp[ii] = powerOf3; - if (powerOf3 < Gprime) - { - if (ii != Gprime - 1) Log[powerOf3] = ii; - } - else - { - throw new Exception("Internal error: powers of 3 calculation"); - } - - powerOf3 = (powerOf3 * 3) % Gprime; - } - Log[0] = Gprime - 1; - Exp[Gprime - 1] = 1; - Log[Gprime] = A0; - } - - private static int Modulo(int x) - { - return x % (Gprime - 1); - } - - public int[] correcteddata = null; - - // data has the error correction codewords at the end of the array - // erasurepositions has a 0-based indexing, 0 = first data word, (datalength-1) = last error correction codeword - public bool Correct(int[] data, int[] erasurePositions, int errorCorrWordCount, out float confidence) - { - // confidence level (from 0 to 1.0f) - confidence = 0F; - - if (erasurePositions.Length > errorCorrWordCount) - return false; - - // make a copy of the data that will be treated as correct data - correcteddata = new int[data.Length]; - Array.Copy(data, correcteddata, data.Length); - - // number of erasures we have (zero or more) - int erasureCount = erasurePositions.Length; - - // input data length - int dataLength = data.Length; - - // temporary variable - int tmp; - // lambda - int[] lambda = new int[2048 + 1]; - // syndrome calculations - int[] syndrome = new int[2048 + 1]; /* Err+Eras Locator poly - * and syndrome poly */ - int[] b = new int[2048 + 1]; - int[] t = new int[2048 + 1]; - int[] omega = new int[2048 + 1]; - // roots - int[] root = new int[2048]; - int[] reg = new int[2048 + 1]; - - // array with locations (as indexes) of errors and erasures - // that we should correct - int[] errorLocations = new int[2048]; - int ci; - - // convert & reverse the erasurePositions array - // we will need this erasures array when we are checking - // found error locations to see if we found all erasures - for (int i = 0; i < erasureCount; ++i) - { - erasurePositions[i] = dataLength - erasurePositions[i]; - } - - /* form the syndromes; i.e. evaluate data(x) at roots of g(x) - namely @**(1+i)*Prim, i = 0, ... , (NN-KK-1) */ - //int[] syn2 = new int[errorCorrWordCount + 1]; - for (int i = 1; i <= errorCorrWordCount; i++) - { - syndrome[i] = 0; //data[data_len]; - // syn2[i] = 0; - } - - // calculate the syndrome - for (int j = 1; j <= dataLength; j++) - { - // check if we have erasure at this position - // erasure is marked with -1 or 0 (zero) - if (correcteddata[dataLength - j] <= 0) - { - correcteddata[dataLength - j] = 0; - continue; - } - - tmp = Log[correcteddata[dataLength - j]]; - - // evaluate the data - /* s[i] ^= AlphaTo[modbase(tmp + (1+i-1)*j)]; */ - for (int i = 1; i <= errorCorrWordCount; i++) - { - syndrome[i] = (syndrome[i] + Exp[Modulo(tmp + (i) * j)]) % Gprime; - } - } - - /* // commented by eugene on 27 march 2016 as it actually duplicates the blog above - for (int i = 0; i < errorCorrWordCount; i++) - { - int x = Exp[i + 1]; - //Console.WriteLine("Eval at x=" + x); - int result = correcteddata[0]; - for (int j = 1; j < dataLength; j++) - { - int coef = result != 0 ? Exp[(Log[result] + Log[x]) % (Gprime - 1)] : 0; - //Console.WriteLine(" x=" + x + "*result=" + result + "=" + coef); - result = (coef + correcteddata[j]) % Gprime; - //Console.WriteLine(" -->result:" + result); - } - syn2[i] = result; - //syndrome[i + 1] = result; - //Console.WriteLine("================================================"); - } - */ - - /* Convert syndromes to index form, checking for nonzero condition */ - int synError = 0; - for (int i = 1; i <= errorCorrWordCount; i++) - { - if (syndrome[i] != 0) - synError++; - syndrome[i] = Log[syndrome[i]]; - } - - if (synError == 0) - { - /* if syndrome is zero, correcteddata[] is a codeword and there are no - * errors to correct. So return data[] unmodified - */ - confidence = 1F; //100% right - return true; - } - - // reset lambda degree to zero - int degLambda = 0; - - int primitive = 1; // initial primitive set to 1 - - /* - // clear arrays - for (int ii = 0; ii < b.Length; ii++) - { - b[ii] = 0; - reg[ii] = 0; - t[ii] = 0; - } - for (int ii = 0; ii < root.Length; ii++) - { - root[ii] = 0; - loc[ii] = 0; - } - */ - - // put zero into lambda - for (ci = errorCorrWordCount - 1; ci >= 0; ci--) - lambda[ci + 1] = 0; - - lambda[0] = 1; // start lamda with 1 - - // check if we should proceed erasures - if (erasureCount > 0) - { - /* Init lambda to be the erasure locator polynomial */ - lambda[1] = Exp[Modulo(primitive * erasurePositions[0])]; - for (int i = 1; i < erasureCount; i++) - { - int u = Modulo(primitive * erasurePositions[i]); - for (int j = i + 1; j > 0; j--) - { - tmp = Log[lambda[j - 1]]; - if (tmp != A0) - lambda[j] = (lambda[j] + Exp[Modulo(u + tmp)]) % Gprime; - } - } - } - - for (int i = 0; i < errorCorrWordCount + 1; i++) - b[i] = Log[lambda[i]]; - - /* - * Begin Berlekamp-Massey algorithm to determine error+erasure - * locator polynomial - * and find locations of erasures and errors - * we may have zero or more additional errors (in addition to erasures) - * we should find all erasures + additional errors (if any) - */ - int r = erasureCount; - while (++r <= errorCorrWordCount) - { - /* r is the step number */ - /* Compute discrepancy at the r-th step in poly-form */ - int rThDiscr = 0; - for (int i = 0; i < r; i++) - { - if ((lambda[i] != 0) && (syndrome[r - i] != A0)) - { - if (i % 2 == 1) - { - rThDiscr = (rThDiscr + Exp[Modulo((Log[lambda[i]] + syndrome[r - i]))]) % Gprime; - } - else - { - rThDiscr = (rThDiscr + Gprime - Exp[Modulo((Log[lambda[i]] + syndrome[r - i]))]) % Gprime; - } - } - } - - rThDiscr = Log[rThDiscr]; /* Index form */ - - if (rThDiscr == A0) - { - /* 2 lines below: B(x) <-- x*B(x) */ - // COPYDOWN(&b[1],b,synd_len); - // - for (ci = errorCorrWordCount - 1; ci >= 0; ci--) b[ci + 1] = b[ci]; - b[0] = A0; - } - else - { - /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */ - /* the T(x) will become the next lambda */ - - t[0] = lambda[0]; - for (int i = 0; i < errorCorrWordCount; i++) - { - if (b[i] != A0) - { - - // t[i+1] = (lambda[i+1] + Gprime - - // AlphaTo[modbase(discr_r + Gprime - 1 - b[i])]) % Gprime; - t[i + 1] = (lambda[i + 1] + Exp[Modulo(rThDiscr + b[i])]) % Gprime; - - } - else - { - t[i + 1] = lambda[i + 1]; - } - } - if (0 <= r + erasureCount - 1) - { - /* - * 2 lines below: B(x) <-- inv(discr_r) * - * lambda(x) - */ - for (int i = 0; i <= errorCorrWordCount; i++) - { - - b[i] = lambda[i] == 0 ? A0 : Modulo(Log[lambda[i]] - rThDiscr + Gprime - 1); - } - - } - else - { - /* 2 lines below: B(x) <-- x*B(x) */ - // COPYDOWN(&b[1],b,synd_len); - for (ci = errorCorrWordCount - 1; ci >= 0; ci--) b[ci + 1] = b[ci]; - b[0] = A0; - } - // COPY(lambda,t,synd_len+1); - - for (ci = errorCorrWordCount + 1 - 1; ci >= 0; ci--) - { - lambda[ci] = t[ci]; - } - } - } - - /* Convert lambda to index form and compute deg(lambda(x)) */ - degLambda = 0; - for (int i = 0; i < errorCorrWordCount + 1; i++) - { - - lambda[i] = Log[lambda[i]]; - - if (lambda[i] != A0) - degLambda = i; - - } - - /* - * Find roots of the error+erasure locator polynomial by Chien - * Search - */ - - for (ci = errorCorrWordCount - 1; ci >= 0; ci--) - reg[ci + 1] = lambda[ci + 1]; - - // counting errors found (should be equal or larger than erasures count) - int errorCount = 0; /* Number of roots of lambda(x) */ - - // we are counting number of duplicated errors (if the same location is the error but was marked as erasure previously - // it should be equal to erasuresCount - int foundErasuresCount = 0; - - for (int i = 1, k = dataLength - 1; i <= Gprime; i++) - { - int q = 1; - for (int j = degLambda; j > 0; j--) - { - - if (reg[j] != A0) - { - reg[j] = Modulo(reg[j] + j); - // q = modbase( q + AlphaTo[reg[j]]); - if (degLambda != 1) - { - if (j % 2 == 0) - { - q = (q + Exp[reg[j]]) % Gprime; - } - else - { - q = (q + Gprime - Exp[reg[j]]) % Gprime; - } - } - else - { - q = Exp[reg[j]] % Gprime; - if (q == 1) --q; - } - } - } - - if (q == 0) - { - /* store root (index-form) and error location number */ - root[errorCount] = i; - - // storing the inverted location index (index of the codeword with error) - errorLocations[errorCount] = Gprime - 1 - i; - - // increase error count - if (errorCount < errorCorrWordCount) - { - errorCount += 1; - } - else - { - // exit if we exceeded the number of correction codewords available - return false; - } - - // check if the error location is erasure or not - if (errorLocations[errorCount - 1] <= dataLength) - { - // the location index of the error - int CurLoc = errorLocations[errorCount - 1]; - - // now check if we already have this error duplicated as erasure? - // NOTE: erasure positions already contain inverted locations (was inverted above at the beginning) - foreach (int ii in erasurePositions) - { - if (CurLoc == ii) - { - // increase number of errors - foundErasuresCount++; - break; - } - } - - - } - else - // exit if for some reason new error location - // exceeds the length of the input data - return false; - } - - if (k == 0) - { - k = dataLength - 1; - } - else - { - k -= 1; - } - - /* If we've already found max possible roots, - * abort the search to save time - */ - - if (errorCount >= degLambda) - break; - - } - - // if degreee lambda != error count then we should exit - if (degLambda != errorCount) - { - /* - * deg(lambda) unequal to number of roots => uncorrectable - * error detected - */ - return false; - } - - // check if we have more than zero erasures defined - if (erasureCount > 0) - { - // check if we have found less errors than erasures - // we should find at least the errors >= erasures count! - if (errorCount < erasureCount) - return false; - - // exit if we have NOT found all erasures - // (we should find all erasures as errors!) - if (foundErasuresCount != erasureCount) - return false; - - // finally we should exit if the total number of errors found - // exceed amount of error correction words available - - // foundErasureCount = number of erasuers at the same places as errors, i.e. the symbol at the index is both error and erasure - // this way the number of unique erasures = (erasureCount - foundErasuresCount) - // errorCount = number of unique corrections we have made (including errors and erasure alltogether) - //if (errorCount != errorCorrWordCount) // we should not have errors more than error code words - // return false; - if ((errorCount + (erasureCount - foundErasuresCount)) > errorCorrWordCount-1) - return false; - } - - // calculate the confidence - confidence = (float)(errorCorrWordCount - errorCount) / (float)(errorCorrWordCount); - - // checking the current confidence - // fail if confidence iz zero - if (confidence == 0.0f) - { - // confidence = 0f; - return false; - } - - /* - * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo - * x**(synd_len)). in index form. Also find deg(omega). - */ - int degOmega = 0; - for (int i = 0; i < errorCorrWordCount; i++) - { - tmp = 0; - int j = (degLambda < i) ? degLambda : i; - for (; j >= 0; j--) - { - if ((syndrome[i + 1 - j] != A0) && (lambda[j] != A0)) - { - if (j % 2 == 1) - { - tmp = (tmp + Gprime - Exp[Modulo(syndrome[i + 1 - j] + lambda[j])]) % Gprime; - } - else - { - - tmp = (tmp + Exp[Modulo(syndrome[i + 1 - j] + lambda[j])]) % Gprime; - } - } - } - - if (tmp != 0) degOmega = i; - omega[i] = Log[tmp]; - } - omega[errorCorrWordCount] = A0; - - /* - * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = - * inv(X(l))**(B0-1) and den = lambda_pr(inv(X(l))) all in poly-form - */ - for (int j = errorCount - 1; j >= 0; j--) - { - int num1 = 0; - for (int i = degOmega; i >= 0; i--) - { - if (omega[i] != A0) - { - // num1 = ( num1 + AlphaTo[modbase(omega[i] + (i * root[j])]) % Gprime; - num1 = (num1 + Exp[Modulo(omega[i] + ((i + 1) * root[j]))]) % Gprime; - } - } - - // denominator if product of all (1 - Bj Bk) for k != j - // if count = 1, then den = 1 - - int den = 1; - for (int k = 0; k < errorCount; k += 1) - { - if (k != j) - { - tmp = (1 + Gprime - Exp[Modulo(Gprime - 1 - root[k] + root[j])]) % Gprime; - den = Exp[Modulo(Log[den] + Log[tmp])]; - } - } - - if (den == 0) - { - /* Convert to dual- basis */ - errorCount = -1; - return false; - } - - int errorVal = Exp[Modulo(Log[num1] + Log[1] + - Gprime - 1 - Log[den])] % Gprime; - - /* Apply error to data */ - if (num1 != 0) - { - if (errorLocations[j] < dataLength + 1) - { - int fixLoc = -1; - fixLoc = dataLength - errorLocations[j]; - - if (fixLoc < dataLength + 1) - { - int newval = (correcteddata[fixLoc] + Gprime - errorVal) % Gprime; - correcteddata[fixLoc] = newval; - } - } - } - } - - return true; - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/SymbolDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/SymbolDecoder.cs deleted file mode 100644 index a961a124..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/SymbolDecoder.cs +++ /dev/null @@ -1,312 +0,0 @@ -using System; -using System.Collections; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.PDF417 -{ - // Handles the decoding of PDF417 data - class SymbolDecoder - { - #region Text Decoder - // Text charset - public static readonly int[] TextSet = new int[] - { - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 32, - (int) TextShiftLatch.LowerLatch, (int) TextShiftLatch.MixedLatch, - (int) TextShiftLatch.PunctShift, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, - 117, 118, 119, 120, 121, 122, 32, (int) TextShiftLatch.AlphaShift - , (int) TextShiftLatch.MixedLatch, - (int) TextShiftLatch.PunctShift, 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 38, 13, 9, 44, 58, 35, 45, 46, 36, 47, 43, 37, 42, 61, 94 - , (int) TextShiftLatch.PunctLatch, 32, (int) TextShiftLatch.LowerLatch, - (int) TextShiftLatch.AlphaLatch, (int) TextShiftLatch.PunctShift, - 59, 60, 62, 64, 91, 92, 93, 95, 96, 126, 33, 13, 9, 44, 58, 10, - 45, 46, 36, 47, 34, 124, 42, 40, 41, 63, 123, 125, 39, - (int) TextShiftLatch.AlphaLatch - }; - - public const int TextPageLength = 30; - - private enum TextShiftLatch - { - AlphaLatch = -1, LowerLatch = -2, MixedLatch = -3, PunctLatch = -4, AlphaShift = -5, PunctShift = -8 - } - - // decodes the given symbols using Text encodation - int rememberLatch=0; - public ABarCodeData[] DecodeText(int[] symbols, ref int index, bool rememberLastLatch) - { - int[] charData = UnfoldText(symbols, ref index); - StringBuilder chars = new StringBuilder(); - int lastLatch = -1; - int latch = rememberLastLatch?rememberLatch:0; - for (int i = 0; i < charData.Length; ++i) - { - int value = TextSet[charData[i] + TextPageLength*latch]; - if (value < 0) - { - if (value < -4) - { - lastLatch = latch; - //value /= 2; - value += 4; - } - - latch = -1 - value; - } - else - { - chars.Append((char) value); - if (lastLatch >= 0) - { - latch = lastLatch; - lastLatch = -1; - } - } - } - - //detect padding with 29 - if (index 0) - { - StringBarCodeData result = new StringBarCodeData(chars.ToString()); - return new ABarCodeData[] {result}; - } - - return null; - } - - // extracts the text bytes from symbols - private int[] UnfoldText(int[] symbols, ref int index) - { - ArrayList charData = new ArrayList(); - while (index < symbols.Length && symbols[index] < 900) - { - charData.Add(symbols[index]/TextPageLength); - charData.Add(symbols[index]%TextPageLength); - ++index; - } - - int[] result = new int[charData.Count]; - charData.CopyTo(result); - - return result; - } - - #endregion - - public ABarCodeData[] DecodeBase900(int[] symbols, ref int index, int length) - { - int start = index; - while (index < symbols.Length && symbols[index] < 900 && length != 0) - { - index++; - length--; - } - int[] data = new int[index - start]; - Array.Copy(symbols, start, data, 0, (index - start)); - return new ABarCodeData[] { new Base900BarCodeData(data) }; - } - - // Applies Base 900 --> Base 256 conversion to get byte data - public ABarCodeData[] DecodeBase256(int[] symbols, ref int index, bool isFull, System.Text.Encoding encoding) - { - ArrayList base900Data = new ArrayList(); - while (index < symbols.Length && symbols[index] < 900) - { - base900Data.Add(symbols[index++]); - } - - if (base900Data.Count == 0) return null; - - if (isFull && base900Data.Count%5 != 0) - { - throw new Exception( - "Invalid Byte compacted block found (said to be full, but has a wrong number of codewords)"); - } - - int blocks5 = base900Data.Count/5; - if (!isFull && base900Data.Count%5 == 0) - { - --blocks5; - } - - int remainder = base900Data.Count - 5*blocks5; - byte[] base256Data = new byte[6*blocks5 + remainder]; - for (int i = 0; i < blocks5; ++i) - { - long b900 = 0; - for (int b = 0; b < 5; ++b) - { - b900 = b900*900 + (int) base900Data[5*i + b]; - } - - for (int b = 0; b < 6; ++b) - { - base256Data[6*i + 5 - b] = (byte) (b900%256); - b900 = b900/256; - } - } - - for (int i = 5*blocks5, o = 6*blocks5; i < base900Data.Count; ++i, ++o) - { - base256Data[o] = (byte) ((int) base900Data[i]); - } - - return new ABarCodeData[] {new Base256BarCodeData(base256Data, encoding)}; - } - - #region Numeric Decoder - - public ABarCodeData[] DecodeNumeric(int[] symbols, ref int index, int lenght) - { - int[] block15 = new int[15]; - int symbolCount = 0; - ArrayList fullDigitList = new ArrayList(); - while (index < symbols.Length && symbols[index] < 900 && lenght!=0) - { - if (symbolCount < 15) - { - block15[symbolCount++] = symbols[index++]; - lenght--; - continue; - } - - fullDigitList.AddRange(UnfoldNumeric(block15, symbolCount)); - symbolCount = 0; - } - fullDigitList.AddRange(UnfoldNumeric(block15, symbolCount)); - - byte[] result = new byte[fullDigitList.Count]; - fullDigitList.CopyTo(result); - return new ABarCodeData[] { new NumericBarCodeData(result)}; - } - - // extracts numeric bytes from PDF symbols - private ArrayList UnfoldNumeric(int[] block15, int symbolCount) - { - /*long b900 = 0; - for (int b = 0; b < symbolCount; ++b) - { - b900 = b900 * 900 + block15[b]; - }*/ - - // cut the array - int[] block = new int[symbolCount]; - Array.Copy(block15, block, symbolCount); - Array.Reverse(block); - - /*ArrayList digitList = new ArrayList(); - while (b900 > 0) - { - digitList.Add((byte)(b900 % 10)); - b900 /= 10; - }*/ - - ArrayList digitList = new ArrayList(BaseConverter.Convert(900, 10, block)); - if (digitList.Count > 0) - { - digitList.RemoveAt(digitList.Count - 1); - } - digitList.Reverse(); - - return digitList; - } - - #endregion - } - - // used to convert number bases for very, very long numbers - class BaseConverter - { - // inNumber has the least significant digit first, so does the output - public static byte[] Convert(int fromBase, int toBase, int[] inNumber) - { - if (inNumber == null || inNumber.Length == 0) - { - throw new Exception("Input is empty"); - } - - // check the input for digits that exceed the allowable range - foreach (int i in inNumber) - { - if (i >= fromBase) - { - throw new Exception("Invalid digit encountered"); - } - } - - // find how many digits the output needs - int inLength = inNumber.Length; - int outLength = (int)(inLength * (Math.Log(fromBase) / Math.Log(toBase))) + 1; - int[] ts = new int[outLength + 10]; // assign accumulation array - int[] resultTmp = new int[outLength + 10]; // assign the result array - ts[0] = 1; // initialize array with number 1 - - for (int i = 0; i < inLength; i++) // for each input digit - { - for (int j = 0; j < outLength; j++) // add the input digit times (base:to from^i) to the output cumulator - { - resultTmp[j] += ts[j] * inNumber[i]; - int temp = resultTmp[j]; - int ip = j; - do // fix up any remainders in base:to - { - int rem = temp / toBase; - resultTmp[ip] = temp - rem * toBase; ip++; - resultTmp[ip] += rem; - temp = resultTmp[ip]; - } - while (temp >= toBase); - } - - // calculate the next power from^i in base:to format - for (int j = 0; j < outLength; j++) - { - ts[j] = ts[j] * fromBase; - } - - for (int j = 0; j < outLength; j++) // check for any remainders - { - int temp = ts[j]; - int ip = j; - do // fix up any remainders - { - int rem = temp / toBase; - ts[ip] = temp - rem * toBase; ip++; - ts[ip] += rem; - temp = ts[ip]; - } - while (temp >= toBase); - } - } - - int realLength = outLength - 1; - for (; realLength >= 0; --realLength) - { - if (resultTmp[realLength] != 0) - { - break; - } - } - realLength++; - - byte[] result = new byte[realLength]; - for (int i = 0; i < realLength; ++i ) - { - result[i] = (byte) resultTmp[i]; - } - - return result; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/SymbolReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/SymbolReader.cs deleted file mode 100644 index 7b3b8c0a..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/SymbolReader.cs +++ /dev/null @@ -1,335 +0,0 @@ -using System; -using System.Collections.Generic; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.PDF417 -{ - class SymbolReader - { - ImageScaner scan; - Bresenham br; - float moduleLength; - int nBars; - bool currentIsBlack; - bool checkEnd; - - public SymbolReader(ImageScaner scan, MyPointF a, MyPointF b, int nBars, bool startsWithBlack, bool checkEnd, float moduleLength) - { - this.scan = scan; - br = new Bresenham((MyPoint)a, (MyPoint)b); - this.nBars = nBars; - this.moduleLength = moduleLength; - while (!br.End() && scan.getGrayIsBlack(br.Current) != startsWithBlack) - br.Next(); - currentIsBlack = startsWithBlack; - this.checkEnd = checkEnd; - } - - public float ModuleLength { get { return moduleLength; } } - public MyPoint Current { get { return br.Current; } } - - public void MoveTo(MyPointF p) - { - br.MoveTo((MyPoint)p); - } - - public MyPoint Adjust(MyPointF p) - { - Bresenham l = new Bresenham(br); - int maxN = (int)Math.Round(moduleLength*1.5F); - l.MoveTo(p); - if (scan.isBlack(l.Current)) - { - MyPoint a = AdjustBack(l, maxN); - if (a == MyPoint.Empty) - { - l = new Bresenham(br); - l.MoveTo(p); - a = AdjustForward(l, maxN); - } - return a; - } - else - { //go fowards - MyPoint a = AdjustForward(l, maxN); - if (a == MyPoint.Empty) - { - l = new Bresenham(br); - l.MoveTo(p); - a = AdjustBack(l, maxN); - } - return a; - } - } - - MyPoint AdjustBack(Bresenham l, int maxN) - { - int n = 0; - MyPoint prev = l.Current; - maxN++; //because we are going 1px beyond - //make sure we are on black - while (scan.In(l.Current) && n < maxN && !scan.isBlack(l.Current)) - { l.Previous(); n++; } - //search previous white - while (scan.In(l.Current) && n < maxN && scan.isBlack(l.Current)) - { prev = l.Current; l.Previous(); n++; } - return n < maxN ?prev : MyPoint.Empty; - //return n (int)(moduleLength/3F)) //a valid module - { - barLenghts[nTransitions++] = n; - sumBarLengths += n; - n = 1; - currentIsBlack = !currentIsBlack; - } - else //consider as noise (join to the previous module) - { - if (nTransitions > 0) - { - nTransitions--; - n = barLenghts[nTransitions] + n + 1; - } - else n++; - currentIsBlack = !currentIsBlack; - } - if (nTransitions0.6 are considered black - //Modules between 0.4 and 0.6 can be white or black --> the algorithm must follow 2 different paths. - private float blackLevel, whiteLevel; - public LinkedList NextSymbol(MyPoint a, MyPoint b, float mid) - { - //Console.Write("a:" + a.ToString() + " -- b:" + b.ToString()+" ==> "); - float[] modules = new float[17]; - float ml = (a - b).Length / 17F; //module length - Bresenham l = new Bresenham(a, b); - int n = 0; - float currentModule = 0F; - float nextModule = ml; - float lastD = 0F; - float offset = 0F; - if (!scan.isBlack(l.Current)) - { l.Next(); offset = 1F; } - - while (!l.End()) - { - bool isBlack = scan.isBlack(l.Current); - l.Next(); - float d = (l.Current - a).Length - offset; - if (d > nextModule) - { - if (isBlack) currentModule += (nextModule - lastD); - modules[n++] = currentModule / ml; - currentModule = (isBlack ? d - nextModule : 0F); - nextModule += ml; - } - else - { - if (isBlack) currentModule += (d - lastD); - } - lastD = d; - } - - modules[0] = 1F; //first must be black - modules[16] = 0F; //last must be white - - //foreach(float m in modules) Console.Write(" - "+m); - //Console.WriteLine(); - - this.blackLevel = mid - 0.1f; - this.whiteLevel = mid + 0.1f; - Lengths ll = new Lengths(blackLevel, whiteLevel); - LinkedList solutions = new LinkedList(); - ProcessModules(modules, 1, ll, solutions); - return solutions; - } - - //alternative way to read symbols based on a simpler algorithm - public LinkedList NextSymbolSimple(MyPoint a, MyPoint b) - { - float[] modules = new float[17]; - float ml = (a - b).Length/17F; //module length - Bresenham l = new Bresenham(a, b); - float nextModule=ml; - if (!scan.isBlack(l.Current)) - { l.Next();} - - //second method--> scan bar lengths - int nBars=0; - int[] bars = new int[8]; - bool currentIsBlack = true; - int nErrors = 0; - - while (!l.End()) - { - bool isBlack=scan.isBlack(l.Current); - l.Next(); - if (currentIsBlack != isBlack) { nBars++; currentIsBlack = isBlack; } - if (nBars < 8) bars[nBars]++; else nErrors++; - } - - LinkedList solutions = new LinkedList(); - if (nBars >= 7 && nErrors < ml) - { - int length = 0; for (int i = 0; i < 8; i++) length += bars[i]; - double moduleLength = (double)length / 17f; - int[] E = new int[8]; - for (int i = 0; i < 8; i++) E[i] = (int)Math.Round((double)(bars[i] + bars[(i + 1) % 8]) / moduleLength); - solutions.AddLast(E); - } - - return solutions; - } - - //Recursive method that explores all possible paths given an array of modules mean color. - void ProcessModules(float[] modules, int i, Lengths l, LinkedList solutions) - { - while (i < 17 && (modules[i] < blackLevel || modules[i] > whiteLevel)) - l.AddModule(modules[i++]); - - if (i < 17) - { - if (modules[i] < (blackLevel+whiteLevel)/2f) - { - float m = modules[i]; - modules[i] = 0F; - ProcessModules(modules, i, new Lengths(l), solutions); - modules[i] = 1F; - ProcessModules(modules, i, new Lengths(l), solutions); - modules[i] = m; - } - else - { - float m = modules[i]; - modules[i] = 1F; - ProcessModules(modules, i, new Lengths(l), solutions); - modules[i] = 0F; - ProcessModules(modules, i, new Lengths(l), solutions); - modules[i] = m; - } - } - else - { - if (l.Finish()) solutions.AddLast(l.ToE()); - } - } - } - - class Lengths { - public int[] lengths=new int[8]; - public int nBlock=0; //current index; - public bool processingBlack = true; - public int n = 1; - public float blackLevel, whiteLevel; - - public Lengths(float blackLevel, float whiteLevel) - { - this.blackLevel = blackLevel; - this.whiteLevel = whiteLevel; - } - - public Lengths(Lengths l) - { - this.lengths = l.lengths; //all share the same array! - this.nBlock = l.nBlock; - this.processingBlack = l.processingBlack; - this.n = l.n; - this.blackLevel = l.blackLevel; - this.whiteLevel = l.whiteLevel; - } - - public void AddModule(float g) - { - if (nBlock < 8) //if not full - { - if (g < blackLevel && processingBlack || g > whiteLevel && !processingBlack) - { - lengths[nBlock++] = n; - n = 1; - processingBlack = !processingBlack; - } - else n++; - } - } - - public bool Finish() - { - if (nBlock != 7) return false; - lengths[7] = n; - return true; - } - - public int[] ToE() - { - int[] E = new int[8]; - for (int i = 0; i < 8; i++) E[i] = lengths[i] + lengths[(i + 1)%8]; - return E; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/symbolmap.txt b/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/symbolmap.txt deleted file mode 100644 index 0ea45839..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/PDF417/symbolmap.txt +++ /dev/null @@ -1,929 +0,0 @@ -0 31111136 51111125 21111155 -1 41111144 61111133 31111163 -2 51111152 41111216 11111246 -3 31111235 51111224 21111254 -4 41111243 61111232 31111262 -5 51111251 41111315 11111345 -6 21111326 51111323 21111353 -7 31111334 61111331 31111361 -8 21111425 41111414 11111444 -9 11111516 51111422 21111452 -10 21111524 41111513 11111543 -11 11111615 51111521 61112114 -12 21112136 41111612 11112155 -13 31112144 41112125 21112163 -14 41112152 51112133 61112213 -15 21112235 61112141 11112254 -16 31112243 31112216 21112262 -17 41112251 41112224 61112312 -18 11112326 51112232 11112353 -19 21112334 31112315 21112361 -20 11112425 41112323 61112411 -21 11113136 51112331 11112452 -22 21113144 31112414 51113114 -23 31113152 41112422 61113122 -24 11113235 31112513 11113163 -25 21113243 41112521 51113213 -26 31113251 31112612 61113221 -27 11113334 31113125 11113262 -28 21113342 41113133 51113312 -29 11114144 51113141 11113361 -30 21114152 21113216 51113411 -31 11114243 31113224 41114114 -32 21114251 41113232 51114122 -33 11115152 21113315 41114213 -34 51116111 31113323 51114221 -35 31121135 41113331 41114312 -36 41121143 21113414 41114411 -37 51121151 31113422 31115114 -38 21121226 21113513 41115122 -39 31121234 31113521 31115213 -40 41121242 21113612 41115221 -41 21121325 21114125 31115312 -42 31121333 31114133 31115411 -43 11121416 41114141 21116114 -44 21121424 11114216 31116122 -45 31121432 21114224 21116213 -46 11121515 31114232 31116221 -47 21121523 11114315 21116312 -48 11121614 21114323 11121146 -49 21122135 31114331 21121154 -50 31122143 11114414 31121162 -51 41122151 21114422 11121245 -52 11122226 11114513 21121253 -53 21122234 21114521 31121261 -54 31122242 11115125 11121344 -55 11122325 21115133 21121352 -56 21122333 31115141 11121443 -57 31122341 11115224 21121451 -58 11122424 21115232 11121542 -59 21122432 11115323 61122113 -60 11123135 21115331 11122154 -61 21123143 11115422 21122162 -62 31123151 11116133 61122212 -63 11123234 21116141 11122253 -64 21123242 11116232 21122261 -65 11123333 11116331 61122311 -66 21123341 41121116 11122352 -67 11124143 51121124 11122451 -68 21124151 61121132 51123113 -69 11124242 41121215 61123121 -70 11124341 51121223 11123162 -71 21131126 61121231 51123212 -72 31131134 41121314 11123261 -73 41131142 51121322 51123311 -74 21131225 41121413 41124113 -75 31131233 51121421 51124121 -76 41131241 41121512 41124212 -77 11131316 41121611 41124311 -78 21131324 31122116 31125113 -79 31131332 41122124 41125121 -80 11131415 51122132 31125212 -81 21131423 31122215 31125311 -82 11131514 41122223 21126113 -83 11131613 51122231 31126121 -84 11132126 31122314 21126212 -85 21132134 41122322 21126311 -86 31132142 31122413 11131145 -87 11132225 41122421 21131153 -88 21132233 31122512 31131161 -89 31132241 31122611 11131244 -90 11132324 21123116 21131252 -91 21132332 31123124 11131343 -92 11132423 41123132 21131351 -93 11132522 21123215 11131442 -94 11133134 31123223 11131541 -95 21133142 41123231 61132112 -96 11133233 21123314 11132153 -97 21133241 31123322 21132161 -98 11133332 21123413 61132211 -99 11134142 31123421 11132252 -100 21141125 21123512 11132351 -101 31141133 21123611 51133112 -102 41141141 11124116 11133161 -103 11141216 21124124 51133211 -104 21141224 31124132 41134112 -105 31141232 11124215 41134211 -106 11141315 21124223 31135112 -107 21141323 31124231 31135211 -108 31141331 11124314 21136112 -109 11141414 21124322 21136211 -110 21141422 11124413 11141144 -111 11141513 21124421 21141152 -112 21141521 11124512 11141243 -113 11142125 11125124 21141251 -114 21142133 21125132 11141342 -115 31142141 11125223 11141441 -116 11142224 21125231 61142111 -117 21142232 11125322 11142152 -118 11142323 11125421 11142251 -119 21142331 11126132 51143111 -120 11142422 11126231 41144111 -121 11142521 41131115 31145111 -122 21143141 51131123 11151143 -123 11143331 61131131 21151151 -124 11151116 41131214 11151242 -125 21151124 51131222 11151341 -126 31151132 41131313 11152151 -127 11151215 51131321 11161142 -128 21151223 41131412 11161241 -129 31151231 41131511 12111146 -130 11151314 31132115 22111154 -131 21151322 41132123 32111162 -132 11151413 51132131 12111245 -133 21151421 31132214 22111253 -134 11151512 41132222 32111261 -135 11152124 31132313 12111344 -136 11152223 41132321 22111352 -137 11152322 31132412 12111443 -138 11161115 31132511 22111451 -139 31161131 21133115 12111542 -140 21161222 31133123 62112113 -141 21161321 41133131 12112154 -142 11161511 21133214 22112162 -143 32111135 31133222 62112212 -144 42111143 21133313 12112253 -145 52111151 31133321 22112261 -146 22111226 21133412 62112311 -147 32111234 21133511 12112352 -148 42111242 11134115 12112451 -149 22111325 21134123 52113113 -150 32111333 31134131 62113121 -151 42111341 11134214 12113162 -152 12111416 21134222 52113212 -153 22111424 11134313 12113261 -154 12111515 21134321 52113311 -155 22112135 11134412 42114113 -156 32112143 11134511 52114121 -157 42112151 11135123 42114212 -158 12112226 21135131 42114311 -159 22112234 11135222 32115113 -160 32112242 11135321 42115121 -161 12112325 11136131 32115212 -162 22112333 41141114 32115311 -163 12112424 51141122 22116113 -164 12112523 41141213 32116121 -165 12113135 51141221 22116212 -166 22113143 41141312 22116311 -167 32113151 41141411 21211145 -168 12113234 31142114 31211153 -169 22113242 41142122 41211161 -170 12113333 31142213 11211236 -171 12113432 41142221 21211244 -172 12114143 31142312 31211252 -173 22114151 31142411 11211335 -174 12114242 21143114 21211343 -175 12115151 31143122 31211351 -176 31211126 21143213 11211434 -177 41211134 31143221 21211442 -178 51211142 21143312 11211533 -179 31211225 21143411 21211541 -180 41211233 11144114 11211632 -181 51211241 21144122 12121145 -182 21211316 11144213 22121153 -183 31211324 21144221 32121161 -184 41211332 11144312 11212145 -185 21211415 11144411 12121244 -186 31211423 11145122 22121252 -187 41211431 11145221 11212244 -188 21211514 41151113 21212252 -189 31211522 51151121 22121351 -190 22121126 41151212 11212343 -191 32121134 41151311 12121442 -192 42121142 31152113 11212442 -193 21212126 41152121 12121541 -194 22121225 31152212 11212541 -195 32121233 31152311 62122112 -196 42121241 21153113 12122153 -197 21212225 31153121 22122161 -198 31212233 21153212 61213112 -199 41212241 21153311 62122211 -200 11212316 11154113 11213153 -201 12121415 21154121 12122252 -202 22121423 11154212 61213211 -203 32121431 11154311 11213252 -204 11212415 41161112 12122351 -205 21212423 41161211 11213351 -206 11212514 31162112 52123112 -207 12122126 31162211 12123161 -208 22122134 21163112 51214112 -209 32122142 21163211 52123211 -210 11213126 42111116 11214161 -211 12122225 52111124 51214211 -212 22122233 62111132 42124112 -213 32122241 42111215 41215112 -214 11213225 52111223 42124211 -215 21213233 62111231 41215211 -216 31213241 42111314 32125112 -217 11213324 52111322 31216112 -218 12122423 42111413 32125211 -219 11213423 52111421 31216211 -220 12123134 42111512 22126112 -221 22123142 42111611 22126211 -222 11214134 32112116 11221136 -223 12123233 42112124 21221144 -224 22123241 52112132 31221152 -225 11214233 32112215 11221235 -226 21214241 42112223 21221243 -227 11214332 52112231 31221251 -228 12124142 32112314 11221334 -229 11215142 42112322 21221342 -230 12124241 32112413 11221433 -231 11215241 42112421 21221441 -232 31221125 32112512 11221532 -233 41221133 32112611 11221631 -234 51221141 22113116 12131144 -235 21221216 32113124 22131152 -236 31221224 42113132 11222144 -237 41221232 22113215 12131243 -238 21221315 32113223 22131251 -239 31221323 42113231 11222243 -240 41221331 22113314 21222251 -241 21221414 32113322 11222342 -242 31221422 22113413 12131441 -243 21221513 32113421 11222441 -244 21221612 22113512 62132111 -245 22131125 22113611 12132152 -246 32131133 12114116 61223111 -247 42131141 22114124 11223152 -248 21222125 32114132 12132251 -249 22131224 12114215 11223251 -250 32131232 22114223 52133111 -251 11222216 32114231 51224111 -252 12131315 12114314 42134111 -253 31222232 22114322 41225111 -254 32131331 12114413 32135111 -255 11222315 22114421 31226111 -256 12131414 12114512 22136111 -257 22131422 12115124 11231135 -258 11222414 22115132 21231143 -259 21222422 12115223 31231151 -260 22131521 22115231 11231234 -261 12131612 12115322 21231242 -262 12132125 12115421 11231333 -263 22132133 12116132 21231341 -264 32132141 12116231 11231432 -265 11223125 51211115 11231531 -266 12132224 61211123 12141143 -267 22132232 11211164 22141151 -268 11223224 51211214 11232143 -269 21223232 61211222 12141242 -270 22132331 11211263 11232242 -271 11223323 51211313 12141341 -272 12132422 61211321 11232341 -273 12132521 11211362 12142151 -274 12133133 51211412 11233151 -275 22133141 51211511 11241134 -276 11224133 42121115 21241142 -277 12133232 52121123 11241233 -278 11224232 62121131 21241241 -279 12133331 41212115 11241332 -280 11224331 42121214 11241431 -281 11225141 61212131 12151142 -282 21231116 41212214 11242142 -283 31231124 51212222 12151241 -284 41231132 52121321 11242241 -285 21231215 41212313 11251133 -286 31231223 42121412 21251141 -287 41231231 41212412 11251232 -288 21231314 42121511 11251331 -289 31231322 41212511 12161141 -290 21231413 32122115 11252141 -291 31231421 42122123 11261132 -292 21231512 52122131 11261231 -293 21231611 31213115 13111145 -294 12141116 32122214 23111153 -295 22141124 42122222 33111161 -296 32141132 31213214 13111244 -297 11232116 41213222 23111252 -298 12141215 42122321 13111343 -299 22141223 31213313 23111351 -300 32141231 32122412 13111442 -301 11232215 31213412 13111541 -302 21232223 32122511 63112112 -303 31232231 31213511 13112153 -304 11232314 22123115 23112161 -305 12141413 32123123 63112211 -306 22141421 42123131 13112252 -307 11232413 21214115 13112351 -308 21232421 22123214 53113112 -309 11232512 32123222 13113161 -310 12142124 21214214 53113211 -311 22142132 31214222 43114112 -312 11233124 32123321 43114211 -313 12142223 21214313 33115112 -314 22142231 22123412 33115211 -315 11233223 21214412 23116112 -316 21233231 22123511 23116211 -317 11233322 21214511 12211136 -318 12142421 12124115 22211144 -319 11233421 22124123 32211152 -320 11234132 32124131 12211235 -321 11234231 11215115 22211243 -322 21241115 12124214 32211251 -323 31241123 22124222 12211334 -324 41241131 11215214 22211342 -325 21241214 21215222 12211433 -326 31241222 22124321 22211441 -327 21241313 11215313 12211532 -328 31241321 12124412 12211631 -329 21241412 11215412 13121144 -330 21241511 12124511 23121152 -331 12151115 12125123 12212144 -332 22151123 22125131 13121243 -333 32151131 11216123 23121251 -334 11242115 12125222 12212243 -335 12151214 11216222 22212251 -336 22151222 12125321 12212342 -337 11242214 11216321 13121441 -338 21242222 12126131 12212441 -339 22151321 51221114 63122111 -340 11242313 61221122 13122152 -341 12151412 11221163 62213111 -342 11242412 51221213 12213152 -343 12151511 61221221 13122251 -344 12152123 11221262 12213251 -345 11243123 51221312 53123111 -346 11243222 11221361 52214111 -347 11243321 51221411 43124111 -348 31251122 42131114 42215111 -349 31251221 52131122 33125111 -350 21251411 41222114 32216111 -351 22161122 42131213 23126111 -352 12161213 52131221 21311135 -353 11252213 41222213 31311143 -354 11252312 51222221 41311151 -355 11252411 41222312 11311226 -356 23111126 42131411 21311234 -357 33111134 41222411 31311242 -358 43111142 32132114 11311325 -359 23111225 42132122 21311333 -360 33111233 31223114 31311341 -361 13111316 32132213 11311424 -362 23111324 42132221 21311432 -363 33111332 31223213 11311523 -364 13111415 41223221 21311531 -365 23111423 31223312 11311622 -366 13111514 32132411 12221135 -367 13111613 31223411 22221143 -368 13112126 22133114 32221151 -369 23112134 32133122 11312135 -370 33112142 21224114 12221234 -371 13112225 22133213 22221242 -372 23112233 32133221 11312234 -373 33112241 21224213 21312242 -374 13112324 31224221 22221341 -375 23112332 21224312 11312333 -376 13112423 22133411 12221432 -377 13112522 21224411 11312432 -378 13113134 12134114 12221531 -379 23113142 22134122 11312531 -380 13113233 11225114 13131143 -381 23113241 12134213 23131151 -382 13113332 22134221 12222143 -383 13114142 11225213 13131242 -384 13114241 21225221 11313143 -385 32211125 11225312 12222242 -386 42211133 12134411 13131341 -387 52211141 11225411 11313242 -388 22211216 12135122 12222341 -389 32211224 11226122 11313341 -390 42211232 12135221 13132151 -391 22211315 11226221 12223151 -392 32211323 51231113 11314151 -393 42211331 61231121 11321126 -394 22211414 11231162 21321134 -395 32211422 51231212 31321142 -396 22211513 11231261 11321225 -397 32211521 51231311 21321233 -398 23121125 42141113 31321241 -399 33121133 52141121 11321324 -400 43121141 41232113 21321332 -401 22212125 51232121 11321423 -402 23121224 41232212 21321431 -403 33121232 42141311 11321522 -404 12212216 41232311 11321621 -405 13121315 32142113 12231134 -406 32212232 42142121 22231142 -407 33121331 31233113 11322134 -408 12212315 32142212 12231233 -409 22212323 31233212 22231241 -410 23121422 32142311 11322233 -411 12212414 31233311 21322241 -412 13121513 22143113 11322332 -413 12212513 32143121 12231431 -414 13122125 21234113 11322431 -415 23122133 31234121 13141142 -416 33122141 21234212 12232142 -417 12213125 22143311 13141241 -418 13122224 21234311 11323142 -419 32213141 12144113 12232241 -420 12213224 22144121 11323241 -421 22213232 11235113 11331125 -422 23122331 12144212 21331133 -423 12213323 11235212 31331141 -424 13122422 12144311 11331224 -425 12213422 11235311 21331232 -426 13123133 12145121 11331323 -427 23123141 11236121 21331331 -428 12214133 51241112 11331422 -429 13123232 11241161 11331521 -430 12214232 51241211 12241133 -431 13123331 42151112 22241141 -432 13124141 41242112 11332133 -433 12215141 42151211 12241232 -434 31311116 41242211 11332232 -435 41311124 32152112 12241331 -436 51311132 31243112 11332331 -437 31311215 32152211 13151141 -438 41311223 31243211 12242141 -439 51311231 22153112 11333141 -440 31311314 21244112 11341124 -441 41311322 22153211 21341132 -442 31311413 21244211 11341223 -443 41311421 12154112 21341231 -444 31311512 11245112 11341322 -445 22221116 12154211 11341421 -446 32221124 11245211 12251132 -447 42221132 51251111 11342132 -448 21312116 42161111 12251231 -449 22221215 41252111 11342231 -450 41312132 32162111 11351123 -451 42221231 31253111 21351131 -452 21312215 22163111 11351222 -453 31312223 21254111 11351321 -454 41312231 43111115 12261131 -455 21312314 53111123 11352131 -456 22221413 63111131 11361122 -457 32221421 43111214 11361221 -458 21312413 53111222 14111144 -459 31312421 43111313 24111152 -460 22221611 53111321 14111243 -461 13131116 43111412 24111251 -462 23131124 43111511 14111342 -463 33131132 33112115 14111441 -464 12222116 43112123 14112152 -465 13131215 53112131 14112251 -466 23131223 33112214 54113111 -467 33131231 43112222 44114111 -468 11313116 33112313 34115111 -469 12222215 43112321 24116111 -470 22222223 33112412 13211135 -471 32222231 33112511 23211143 -472 11313215 23113115 33211151 -473 21313223 33113123 13211234 -474 31313231 43113131 23211242 -475 23131421 23113214 13211333 -476 11313314 33113222 23211341 -477 12222413 23113313 13211432 -478 22222421 33113321 13211531 -479 11313413 23113412 14121143 -480 13131611 23113511 24121151 -481 13132124 13114115 13212143 -482 23132132 23114123 14121242 -483 12223124 33114131 13212242 -484 13132223 13114214 14121341 -485 23132231 23114222 13212341 -486 11314124 13114313 14122151 -487 12223223 23114321 13213151 -488 22223231 13114412 12311126 -489 11314223 13114511 22311134 -490 21314231 13115123 32311142 -491 13132421 23115131 12311225 -492 12223421 13115222 22311233 -493 13133132 13115321 32311241 -494 12224132 13116131 12311324 -495 13133231 52211114 22311332 -496 11315132 62211122 12311423 -497 12224231 12211163 22311431 -498 31321115 52211213 12311522 -499 41321123 62211221 12311621 -500 51321131 12211262 13221134 -501 31321214 52211312 23221142 -502 41321222 12211361 12312134 -503 31321313 52211411 13221233 -504 41321321 43121114 23221241 -505 31321412 53121122 12312233 -506 31321511 42212114 13221332 -507 22231115 43121213 12312332 -508 32231123 53121221 13221431 -509 42231131 42212213 12312431 -510 21322115 52212221 14131142 -511 22231214 42212312 13222142 -512 41322131 43121411 14131241 -513 21322214 42212411 12313142 -514 31322222 33122114 13222241 -515 32231321 43122122 12313241 -516 21322313 32213114 21411125 -517 22231412 33122213 31411133 -518 21322412 43122221 41411141 -519 22231511 32213213 11411216 -520 21322511 42213221 21411224 -521 13141115 32213312 31411232 -522 23141123 33122411 11411315 -523 33141131 32213411 21411323 -524 12232115 23123114 31411331 -525 13141214 33123122 11411414 -526 23141222 22214114 21411422 -527 11323115 23123213 11411513 -528 12232214 33123221 21411521 -529 22232222 22214213 11411612 -530 23141321 32214221 12321125 -531 11323214 22214312 22321133 -532 21323222 23123411 32321141 -533 13141412 22214411 11412125 -534 11323313 13124114 12321224 -535 12232412 23124122 22321232 -536 13141511 12215114 11412224 -537 12232511 13124213 21412232 -538 13142123 23124221 22321331 -539 23142131 12215213 11412323 -540 12233123 22215221 12321422 -541 13142222 12215312 11412422 -542 11324123 13124411 12321521 -543 12233222 12215411 11412521 -544 13142321 13125122 13231133 -545 11324222 12216122 23231141 -546 12233321 13125221 12322133 -547 13143131 12216221 13231232 -548 11325131 61311113 11413133 -549 31331114 11311154 12322232 -550 41331122 21311162 13231331 -551 31331213 61311212 11413232 -552 41331221 11311253 12322331 -553 31331312 21311261 11413331 -554 31331411 61311311 14141141 -555 22241114 11311352 13232141 -556 32241122 11311451 12323141 -557 21332114 52221113 11414141 -558 22241213 62221121 11421116 -559 32241221 12221162 21421124 -560 21332213 51312113 31421132 -561 31332221 61312121 11421215 -562 21332312 11312162 21421223 -563 22241411 12221261 31421231 -564 21332411 51312212 11421314 -565 13151114 52221311 21421322 -566 23151122 11312261 11421413 -567 12242114 51312311 21421421 -568 13151213 43131113 11421512 -569 23151221 53131121 11421611 -570 11333114 42222113 12331124 -571 12242213 43131212 22331132 -572 22242221 41313113 11422124 -573 11333213 51313121 12331223 -574 21333221 43131311 22331231 -575 13151411 41313212 11422223 -576 11333312 42222311 21422231 -577 12242411 41313311 11422322 -578 11333411 33132113 12331421 -579 12243122 43132121 11422421 -580 11334122 32223113 13241132 -581 11334221 33132212 12332132 -582 41341121 31314113 13241231 -583 31341311 32223212 11423132 -584 32251121 33132311 12332231 -585 22251212 31314212 11423231 -586 22251311 32223311 11431115 -587 13161113 31314311 21431123 -588 12252113 23133113 31431131 -589 11343113 33133121 11431214 -590 13161311 22224113 21431222 -591 12252311 23133212 11431313 -592 24111125 21315113 21431321 -593 14111216 22224212 11431412 -594 24111224 23133311 11431511 -595 14111315 21315212 12341123 -596 24111323 22224311 22341131 -597 34111331 21315311 11432123 -598 14111414 13134113 12341222 -599 24111422 23134121 11432222 -600 14111513 12225113 12341321 -601 24111521 13134212 11432321 -602 14112125 11316113 13251131 -603 24112133 12225212 12342131 -604 34112141 13134311 11433131 -605 14112224 11316212 11441114 -606 24112232 12225311 21441122 -607 14112323 11316311 11441213 -608 24112331 13135121 21441221 -609 14112422 12226121 11441312 -610 14112521 61321112 11441411 -611 14113133 11321153 12351122 -612 24113141 21321161 11442122 -613 14113232 61321211 12351221 -614 14113331 11321252 11442221 -615 14114141 11321351 11451113 -616 23211116 52231112 21451121 -617 33211124 12231161 11451212 -618 43211132 51322112 11451311 -619 23211215 52231211 12361121 -620 33211223 11322161 11452121 -621 23211314 51322211 15111143 -622 33211322 43141112 25111151 -623 23211413 42232112 15111242 -624 33211421 43141211 15111341 -625 23211512 41323112 15112151 -626 14121116 42232211 14211134 -627 24121124 41323211 24211142 -628 34121132 33142112 14211233 -629 13212116 32233112 24211241 -630 14121215 33142211 14211332 -631 33212132 31324112 14211431 -632 34121231 32233211 15121142 -633 13212215 31324211 14212142 -634 23212223 23143112 15121241 -635 33212231 22234112 14212241 -636 13212314 23143211 13311125 -637 14121413 21325112 23311133 -638 24121421 22234211 33311141 -639 13212413 21325211 13311224 -640 23212421 13144112 23311232 -641 14121611 12235112 13311323 -642 14122124 13144211 23311331 -643 24122132 11326112 13311422 -644 13213124 12235211 13311521 -645 14122223 11326211 14221133 -646 24122231 61331111 24221141 -647 13213223 11331152 13312133 -648 23213231 11331251 14221232 -649 13213322 52241111 13312232 -650 14122421 51332111 14221331 -651 14123132 43151111 13312331 -652 13214132 42242111 15131141 -653 14123231 41333111 14222141 -654 13214231 33152111 13313141 -655 32311115 32243111 12411116 -656 42311123 31334111 22411124 -657 52311131 23153111 32411132 -658 32311214 22244111 12411215 -659 42311222 21335111 22411223 -660 32311313 13154111 32411231 -661 42311321 12245111 12411314 -662 32311412 11336111 22411322 -663 32311511 11341151 12411413 -664 23221115 44111114 22411421 -665 33221123 54111122 12411512 -666 22312115 44111213 12411611 -667 23221214 54111221 13321124 -668 33221222 44111312 23321132 -669 22312214 44111411 12412124 -670 32312222 34112114 13321223 -671 33221321 44112122 23321231 -672 22312313 34112213 12412223 -673 23221412 44112221 22412231 -674 22312412 34112312 12412322 -675 23221511 34112411 13321421 -676 22312511 24113114 12412421 -677 14131115 34113122 14231132 -678 24131123 24113213 13322132 -679 13222115 34113221 14231231 -680 14131214 24113312 12413132 -681 33222131 24113411 13322231 -682 12313115 14114114 12413231 -683 13222214 24114122 21511115 -684 23222222 14114213 31511123 -685 24131321 24114221 41511131 -686 12313214 14114312 21511214 -687 22313222 14114411 31511222 -688 14131412 14115122 21511313 -689 12313313 14115221 31511321 -690 13222412 53211113 21511412 -691 14131511 63211121 21511511 -692 13222511 13211162 12421115 -693 14132123 53211212 22421123 -694 24132131 13211261 32421131 -695 13223123 53211311 11512115 -696 14132222 44121113 12421214 -697 12314123 54121121 22421222 -698 13223222 43212113 11512214 -699 14132321 44121212 21512222 -700 12314222 43212212 22421321 -701 13223321 44121311 11512313 -702 14133131 43212311 12421412 -703 13224131 34122113 11512412 -704 12315131 44122121 12421511 -705 41411114 33213113 11512511 -706 51411122 34122212 13331123 -707 41411213 33213212 23331131 -708 51411221 34122311 12422123 -709 41411312 33213311 13331222 -710 41411411 24123113 11513123 -711 32321114 34123121 12422222 -712 42321122 23214113 13331321 -713 31412114 24123212 11513222 -714 41412122 23214212 12422321 -715 42321221 24123311 11513321 -716 31412213 23214311 14241131 -717 41412221 14124113 13332131 -718 31412312 24124121 12423131 -719 32321411 13215113 11514131 -720 31412411 14124212 21521114 -721 23231114 13215212 31521122 -722 33231122 14124311 21521213 -723 22322114 13215311 31521221 -724 23231213 14125121 21521312 -725 33231221 13216121 21521411 -726 21413114 62311112 12431114 -727 22322213 12311153 22431122 -728 32322221 22311161 11522114 -729 21413213 62311211 12431213 -730 31413221 12311252 22431221 -731 23231411 12311351 11522213 -732 21413312 53221112 21522221 -733 22322411 13221161 11522312 -734 21413411 52312112 12431411 -735 14141114 53221211 11522411 -736 24141122 12312161 13341122 -737 13232114 52312211 12432122 -738 14141213 44131112 13341221 -739 24141221 43222112 11523122 -740 12323114 44131211 12432221 -741 13232213 42313112 11523221 -742 23232221 43222211 21531113 -743 11414114 42313211 31531121 -744 12323213 34132112 21531212 -745 22323221 33223112 21531311 -746 14141411 34132211 12441113 -747 11414213 32314112 22441121 -748 21414221 33223211 11532113 -749 13232411 32314211 12441212 -750 11414312 24133112 11532212 -751 14142122 23224112 12441311 -752 13233122 24133211 11532311 -753 14142221 22315112 13351121 -754 12324122 23224211 12442121 -755 13233221 22315211 11533121 -756 11415122 14134112 21541112 -757 12324221 13225112 21541211 -758 11415221 14134211 12451112 -759 41421113 12316112 11542112 -760 51421121 13225211 12451211 -761 41421212 12316211 11542211 -762 41421311 11411144 16111142 -763 32331113 21411152 16111241 -764 42331121 11411243 15211133 -765 31422113 21411251 25211141 -766 41422121 11411342 15211232 -767 31422212 11411441 15211331 -768 32331311 62321111 16121141 -769 31422311 12321152 15212141 -770 23241113 61412111 14311124 -771 33241121 11412152 24311132 -772 22332113 12321251 14311223 -773 23241212 11412251 24311231 -774 21423113 53231111 14311322 -775 22332212 52322111 14311421 -776 23241311 51413111 15221132 -777 21423212 44141111 14312132 -778 22332311 43232111 15221231 -779 21423311 42323111 14312231 -780 14151113 41414111 13411115 -781 24151121 34142111 23411123 -782 13242113 33233111 33411131 -783 23242121 32324111 13411214 -784 12333113 31415111 23411222 -785 13242212 24143111 13411313 -786 14151311 23234111 23411321 -787 11424113 22325111 13411412 -788 12333212 21416111 13411511 -789 13242311 14144111 14321123 -790 11424212 13235111 24321131 -791 12333311 12326111 13412123 -792 11424311 11421143 23412131 -793 13243121 21421151 13412222 -794 11425121 11421242 14321321 -795 41431211 11421341 13412321 -796 31432112 12331151 15231131 -797 31432211 11422151 14322131 -798 22342112 11431142 13413131 -799 21433112 11431241 22511114 -800 21433211 11441141 32511122 -801 13252112 45111113 22511213 -802 12343112 45111212 32511221 -803 11434112 45111311 22511312 -804 11434211 35112113 22511411 -805 15111116 45112121 13421114 -806 15111215 35112212 23421122 -807 25111223 35112311 12512114 -808 15111314 25113113 22512122 -809 15111413 35113121 23421221 -810 15111512 25113212 12512213 -811 15112124 25113311 13421312 -812 15112223 15114113 12512312 -813 15112322 25114121 13421411 -814 15112421 15114212 12512411 -815 15113132 15114311 14331122 -816 15113231 15115121 13422122 -817 24211115 54211112 14331221 -818 24211214 14211161 12513122 -819 34211222 54211211 13422221 -820 24211313 45121112 12513221 -821 34211321 44212112 31611113 -822 24211412 45121211 41611121 -823 24211511 44212211 31611212 -824 15121115 35122112 31611311 -825 25121123 34213112 22521113 -826 14212115 35122211 32521121 -827 24212123 34213211 21612113 -828 25121222 25123112 22521212 -829 14212214 24214112 21612212 -830 24212222 25123211 22521311 -831 14212313 24214211 21612311 -832 24212321 15124112 13431113 -833 14212412 14215112 23431121 -834 15121511 15124211 12522113 -835 14212511 14215211 13431212 -836 15122123 63311111 11613113 -837 25122131 13311152 12522212 -838 14213123 13311251 13431311 -839 24213131 54221111 11613212 -840 14213222 53312111 12522311 -841 15122321 45131111 11613311 -842 14213321 44222111 14341121 -843 15123131 43313111 13432121 -844 14214131 35132111 12523121 -845 33311114 34223111 11614121 -846 33311213 33314111 31621112 -847 33311312 25133111 31621211 -848 33311411 24224111 22531112 -849 24221114 23315111 21622112 -850 23312114 15134111 22531211 -851 33312122 14225111 21622211 -852 34221221 13316111 13441112 -853 23312213 12411143 12532112 -854 33312221 22411151 13441211 -855 23312312 12411242 11623112 -856 24221411 12411341 12532211 -857 23312411 13321151 11623211 -858 15131114 12412151 31631111 -859 14222114 11511134 22541111 -860 15131213 21511142 21632111 -861 25131221 11511233 13451111 -862 13313114 21511241 12542111 -863 14222213 11511332 11633111 -864 15131312 11511431 16211132 -865 13313213 12421142 16211231 -866 14222312 11512142 15311123 -867 15131411 12421241 25311131 -868 13313312 11512241 15311222 -869 14222411 11521133 15311321 -870 15132122 21521141 16221131 -871 14223122 11521232 15312131 -872 15132221 11521331 14411114 -873 13314122 12431141 24411122 -874 14223221 11522141 14411213 -875 13314221 11531132 24411221 -876 42411113 11531231 14411312 -877 42411212 11541131 14411411 -878 42411311 36112112 15321122 -879 33321113 36112211 14412122 -880 32412113 26113112 15321221 -881 42412121 26113211 14412221 -882 32412212 16114112 23511113 -883 33321311 16114211 33511121 -884 32412311 45212111 23511212 -885 24231113 36122111 23511311 -886 34231121 35213111 14421113 -887 23322113 26123111 24421121 -888 33322121 25214111 13512113 -889 22413113 16124111 23512121 -890 23322212 15215111 13512212 -891 24231311 14311151 14421311 -892 22413212 13411142 13512311 -893 23322311 13411241 15331121 -894 22413311 12511133 14422121 -895 15141113 22511141 13513121 -896 25141121 12511232 32611112 -897 14232113 12511331 32611211 -898 24232121 13421141 23521112 -899 13323113 12512141 22612112 -900 14232212 11611124 23521211 -901 15141311 21611132 22612211 -902 12414113 11611223 14431112 -903 13323212 21611231 13522112 -904 14232311 11611322 14431211 -905 12414212 11611421 12613112 -906 13323311 12521132 13522211 -907 15142121 11612132 12613211 -908 14233121 12521231 32621111 -909 13324121 11612231 23531111 -910 12415121 11621123 22622111 -911 51511112 21621131 14441111 -912 51511211 11621222 13532111 -913 42421112 11621321 12623111 -914 41512112 12531131 16311122 -915 42421211 11622131 16311221 -916 41512211 11631122 15411113 -917 33331112 11631221 25411121 -918 32422112 14411141 15411212 -919 33331211 13511132 15411311 -920 31513112 13511231 16321121 -921 32422211 12611123 15412121 -922 31513211 22611131 24511112 -923 24241112 12611222 24511211 -924 23332112 12611321 15421112 -925 24241211 13521131 14512112 -926 22423112 12612131 15421211 -927 23332211 12621122 14512211 -928 21514112 12621221 33611111 \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/PatchCode/PatchCode.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/PatchCode/PatchCode.cs deleted file mode 100644 index 657e0038..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/PatchCode/PatchCode.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System.Collections.Generic; -using System.Drawing; -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.PatchCode -{ - //Patch code has only 6 different codes. There are no messages. - //The approach is to consider patch codes as finders and use generic methods to find finders - //The current version scans only patch codes with vertical bars. To read horizontal bars, the image - //should be rotated up front. - - //Once a finder is found, we add it to a StackedPattern to accumulate next finders. Other possible - //implementations should track start and end edges to detect the region of the barcode. -#if CORE_DEV - public -#else - internal -#endif - class PatchCodeReader : SymbologyReader2D - { - //Process start patterns of mínim 2 pixels height - protected int stackedPatternMinHeight = 2; - - //Min aspect ratio Y/X for stacked patterns - protected float stackedPatternMinAspectRatio = 1.5f; - - //When check for start quiet zone, check 4 * modules with white pixels - protected float startPatternQuietZone = 3f; - - //Min number of pixels to check for a quiet zone - protected int minStartPatternQuiezZone=5; - - //Proportion of noise pixels accepted in quiet zone - protected float acceptedNoiseInQuietZone=0.15f; - - protected ImageScaner scan; - protected LinkedList candidates = new LinkedList(); - - int[][] finders = new int[][] { new int[]{2,1,2,1,1,1,1} , new int[]{2,1,1,1,1,1,2}, - new int[]{2,1,1,1,2,1,1}, new int[]{1,1,2,1,2,1,1}, new int[]{1,1,2,1,1,1,2}, - new int[]{1,1,1,1,2,1,2} }; - string[] names = new string[] { "1", "2", "3", "4", "T", "6" }; - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.PatchCode; - } - - protected override FoundBarcode[] DecodeBarcode() - { -#if DEBUG_IMAGE - // image.Save(@"d:\out.png"); -#endif - scan = new ImageScaner(BWImage); - var maxEDist = 0.5f; - IPatternFinderNoiseRow finderSearch = new PatternFinderNoiseRow(finders, true, true, 2, -1, maxEDist); - LinkedList foundPatterns=new LinkedList(); - LinkedList removedPatterns; - LinkedList candidates = new LinkedList(); - - for (int y = 0; y < BWImage.Height; y += ScanStep) - { - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - XBitArray row = BWImage.GetRow(y); - finderSearch.NewSearch(row); - FoundPattern foundPattern; - while ((foundPattern = finderSearch.NextPattern()) != null) - { - //foundPattern is the index of the found pattern - MyPoint a = new MyPoint(finderSearch.First, y); - MyPoint b = new MyPoint(finderSearch.Last, y); - //Check if the same pattern was processed in the last row. - Pattern p = new Pattern(foundPattern, a.X, b.X, y); - LinkedListNode prev = foundPatterns.Find(p); - if (prev != null) - { //pattern already processed the last row - if (prev.Value.y != y) - { - StackedPattern sp = (StackedPattern)prev.Value; - sp.NewRow(a.X, b.X, y); - } - } - else - { //new - StackedPattern sp = new StackedPattern(foundPattern, a.X, b.X, y); - foundPatterns.AddLast(sp); - } - } - - //clean old patterns and process them - removedPatterns = Pattern.RemoveOldPatterns(foundPatterns, y); - foreach (Pattern p in removedPatterns) - { - StackedPattern sp = (StackedPattern)p; - if (sp.y - sp.startY > stackedPatternMinHeight && (float)(sp.y - sp.startY) / (float)(sp.startXEnd - sp.startXIn) > stackedPatternMinAspectRatio) - if (checkQuietZone(sp)) - { - BarCodeRegion r = new BarCodeRegion(new MyPoint(sp.startXIn, sp.startY), - new MyPoint(sp.startXEnd, sp.startY), new MyPoint(sp.xEnd, sp.y), - new MyPoint(sp.xIn, sp.y)); - r.Data = new ABarCodeData[] { new StringBarCodeData(names[sp.nPattern]) }; - candidates.AddLast(r); - } - // timeout check - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - } - } - - FoundBarcode[] results = new FoundBarcode[candidates.Count]; - int nn = 0; - foreach (BarCodeRegion r in candidates) - { - FoundBarcode f = new FoundBarcode(); - f.BarcodeFormat = SymbologyType.PatchCode; - f.Polygon = new SKPointI[] { r.A, r.B, r.C, r.D, r.A }; - f.Color = SKColors.Blue; - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(f.Polygon, pointTypes); - //f.Rect = Rectangle.Round(path.GetBounds()); - f.Rect = Utils.DrawPath(f.Polygon); - f.Value = (r.Data != null ? r.Data[0].ToString() : "?"); - f.Confidence = r.Confidence; - results[nn++] = f; - } - return results; - } - - bool checkQuietZone(StackedPattern sp) - { - MyPoint pIn, pEnd; - sp.MidPoints(out pIn, out pEnd); - int moduleLength = (int)(startPatternQuietZone * (pEnd - pIn).Length / 9f); //3 bars length quiet zone - if (moduleLength < minStartPatternQuiezZone) moduleLength = minStartPatternQuiezZone; - int MAX = (int)(acceptedNoiseInQuietZone*(float)sp.LPoints.Count*(float)moduleLength); - - int n = 0; - foreach (MyPoint p in sp.LPoints) - if ((n += countBlackPixels(p, -1, moduleLength)) > MAX) return false; - foreach (MyPoint p in sp.RPoints) - if ((n += countBlackPixels(p, 1, moduleLength)) > MAX) return false; - return true; - } - - int countBlackPixels(MyPoint p, int incX, int count) - { - int n = 0; - p.X += incX; //move 1px since the first pixel is still on the bar - while (scan.In(p) && count > 0) { if (scan.isBlack(p)) n++; p.X += incX; count--; } - return n; - } - - bool checkQuietZone(MyPoint p, int incX, int moduleLentgh) - { - int n = 0; - while (scan.In(p) && scan.isBlack(p)) { p.X += incX; n++; } - if (n > moduleLentgh) return false; - - int i = 0; - while (scan.In(p) && i 3) - if (resultRow == null) - { - resultRow = row; - count = 1; - vd = last - a; - } - else if (row.Length == resultRow.Length) - { - bool equals = true; - for (int i = 0; i < row.Length && equals; i++) - if (row[i] != resultRow[i]) equals = false; - if (equals) count++; - } - } - - if (count>=3) - if (Decode(r, resultRow)) - { - r.B = r.A + vd; - r.C = r.D + vd; - r.Confidence /= (float)coefs.Length; - result = r; - } - return result; - } - - bool Decode(BarCodeRegion r, int[] row) - { - int v = 0; - string s = ""; - for (int i = 0; i < row.Length; i++) - { - v += row[row.Length - 1 - i] == 0 ? (int)Math.Pow(2, i) : (int)Math.Pow(2, i + 1); - s += row[i]; - } - string msg = Convert.ToString(v); -#if DEBUG - //msg=s+"-->"+msg; -#endif - r.Data = new ABarCodeData[] { new StringBarCodeData(msg)}; - return true; - } - - static readonly int[][] _startPatterns = new int[][] { - new int[] {1,2,1,2,1,2 }, - new int[] {1,2,1,2,3,2 }, - new int[] {1,2,3,2,1,2 }, - new int[] {1,2,3,2,3,2 }, - new int[] {3,2,1,2,1,2 }, - new int[] {3,2,1,2,3,2 }, - new int[] {3,2,3,2,1,2 }, - new int[] {3,2,3,2,3,2 } - }; - - static readonly int[][] _patterns = new int[][] { - new int[] { 1,2 }, new int[] { 3,2}}; - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/PostNet/PostNetDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/PostNet/PostNetDecoder.cs deleted file mode 100644 index 52b9ccc9..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/PostNet/PostNetDecoder.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.PostNet -{ - //Class to decode a set of two-state bars. There are only 10 digits, from 0..9. - class PostNetDecoder : IDecoderAscDescBars - { - int[][] chars = new int[][] { - new int[]{ 1,1,0,0,0 }, - new int[]{ 0,0,0,1,1 }, - new int[]{ 0,0,1,0,1 }, - new int[]{ 0,0,1,1,0 }, - new int[]{ 0,1,0,0,1 }, - new int[]{ 0,1,0,1,0 }, - new int[]{ 0,1,1,0,0 }, - new int[]{ 1,0,0,0,1 }, - new int[]{ 1,0,0,1,0 }, - new int[]{ 1,0,1,0,0 } - }; - - public PostNetDecoder() - { - } - - //Decodes a set of two-state samples in groups of 5 bars. Each group leads to 1 digit (0..9). - //Receives a double array of samples, but two-state barcodes only use the upper subset of samples. - protected int DecodeChar(bool[][] samples, int index) - { - //find best match - int minDist = Int32.MaxValue; - int minChar = -1; - for (int i = 0; i < chars.Length; i++) - { - int d = 0; - for (int j = 0; j < 5; j++) - { - d += (chars[i][j] - (samples[index + j][0]?1:0) == 0 ? 0 : 1); - } - if (d < minDist) { minDist = d; minChar = i; } - } - - if (minDist < 3) return minChar; - return -1; - } - - //Decode each group of 5 bars using the sample bool array. - public virtual string Decode(bool[][] samples, out float confidence) - { - //Bars to chars - confidence = 1.0f; - string code = ""; - int sum = 0; - for (int i = 1; i < samples.Length - 1; i += 5) - { - int ch = DecodeChar(samples, i); - if (i == samples.Length - 6) //checksum - { - sum = sum % 10; - if (sum != 0) sum = 10 - sum; - if (sum != ch) confidence = (confidence == 1f ? 0.1f : 0f); - } - else - { - if (ch != -1) { code += Convert.ToString(ch); sum += ch; } - else { code += "*"; confidence = 0.0f; } - } - } - return code; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/PostNet/PostNetReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/PostNet/PostNetReader.cs deleted file mode 100644 index 924ea755..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/PostNet/PostNetReader.cs +++ /dev/null @@ -1,40 +0,0 @@ -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.PostNet -{ - //The only difference between IM and RM reader is the length and ratio of the barcode. - //A different decoder is also set. -#if CORE_DEV - public -#else - internal -#endif - class PostNetReader : TwoFourStateBarcodesReader - { - public override SymbologyType GetBarCodeType() - { - return SymbologyType.PostNet; - } - - protected override bool IsFourState() - { - return false; //postnet is a two-state barcode - } - - protected override bool CheckNBars(int nBars) - { - return nBars>=32 && nBars<=62 && (nBars-2)%5==0; //start + 5*N+ stop where N=6, 7, 10 or 12 - } - - protected override bool CheckRatio(float ratio) - { - return ratio > 2f && ratio < 25f; - } - - protected override IDecoderAscDescBars GetDecoder() - { - return new PostNetDecoder(); - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRFinder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRFinder.cs deleted file mode 100644 index 53de5597..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRFinder.cs +++ /dev/null @@ -1,127 +0,0 @@ -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.QR -{ - class QRFactory : FinderFactory - { - public float GetOffsetFactor() { return 1F; } //0.75 + deformation - public float GetCenterRatio() { return 3F / 7F; } - public int GetNumModules() { return 7; } - public bool CanHaveHoles() { return true; } - public SquareFinder IsFinder(ImageScaner scan, MyPointF A, MyPointF B, MyPointF C, MyPointF D) - { - QRFinder finder = null; - finder = new QRFinder(A, B, C, D); - - //Sample the finder to be discard wrong candidates. - //This test is not complete, but seems to be enough. - MyVectorF luRight = finder.RightNormal * finder.Width / 7F; - MyVectorF luDown = finder.DownNormal * finder.Height / 7F; - Grid grid = new Grid(finder.Center(), luRight, luDown); - int maxErrors = 6; - //out border horizontal - for (int i = -3; i <=3; i++) - { - if (scan.getSample(grid.GetSamplePointRegular(i, -3),0f) > 0.7F) if (--maxErrors == 0) return null; - if (scan.getSample(grid.GetSamplePointRegular(i, 3),0f) > 0.7F) if (--maxErrors == 0) return null; - } - - //out vertical border - for (int i = -2; i <= 2; i++) - { - if (scan.getSample(grid.GetSamplePointRegular(-3, i),0f) > 0.7F) if (--maxErrors == 0) return null; - if (scan.getSample(grid.GetSamplePointRegular(3, i),0f) > 0.7F) if (--maxErrors == 0) return null; - } - - for (int i = -2; i <= 2; i++) - { - if (scan.getSample(grid.GetSamplePointRegular(i, -2),0f) < 0.3F) if (--maxErrors == 0) return null; - if (scan.getSample(grid.GetSamplePointRegular(i, 2),0f) < 0.3F) if (--maxErrors == 0) return null; - } - for (int i = -1; i <= 1; i++) - { - if (scan.getSample(grid.GetSamplePointRegular(-2, i),0f) < 0.3F) if (--maxErrors == 0) return null; - if (scan.getSample(grid.GetSamplePointRegular(2, i),0f) < 0.3F) if (--maxErrors == 0) return null; - } - - //inner black - for (int i = -1; i <= 1; i++) - for (int j = -1; j <= 1; j++) - if (scan.getSample(grid.GetSamplePointRegular(i, j),0f) > 0.7F) if (--maxErrors == 0) return null; - - return finder; - } - } - - class SmallQRFactory : FinderFactory - { - public float GetOffsetFactor() { return 1F; } //0.75 + deformation - public float GetCenterRatio() { return 3F / 7F; } - public int GetNumModules() { return 5; } - public bool CanHaveHoles() { return true; } - public SquareFinder IsFinder(ImageScaner scan, MyPointF A, MyPointF B, MyPointF C, MyPointF D) - { - QRFinder finder = null; - finder = new QRFinder(A, B, C, D); - - //Sample the finder to be discard wrong candidates. - //This test is not complete, but seems to be enough. - MyVectorF luRight = finder.RightNormal * finder.Width / 5F; - MyVectorF luDown = finder.DownNormal * finder.Height / 5F; - Grid grid = new Grid(finder.Center(), luRight, luDown); - int maxErrors = 6; - - //out border horizontal - for (int i = -3; i <= 3; i++) - { - if (scan.getSample(grid.GetSamplePointRegular(i, -3), 0f) > 0.7F) if (--maxErrors == 0) return null; - if (scan.getSample(grid.GetSamplePointRegular(i, 3), 0f) > 0.7F) if (--maxErrors == 0) return null; - } - - //out vertical border - for (int i = -2; i <= 2; i++) - { - if (scan.getSample(grid.GetSamplePointRegular(-3, i), 0f) > 0.7F) if (--maxErrors == 0) return null; - if (scan.getSample(grid.GetSamplePointRegular(3, i), 0f) > 0.7F) if (--maxErrors == 0) return null; - } - - for (int i = -2; i <= 2; i++) - { - if (scan.getSample(grid.GetSamplePointRegular(i, -2), 0f) < 0.3F) if (--maxErrors == 0) return null; - if (scan.getSample(grid.GetSamplePointRegular(i, 2), 0f) < 0.3F) if (--maxErrors == 0) return null; - } - for (int i = -1; i <= 1; i++) - { - if (scan.getSample(grid.GetSamplePointRegular(-2, i), 0f) < 0.3F) if (--maxErrors == 0) return null; - if (scan.getSample(grid.GetSamplePointRegular(2, i), 0f) < 0.3F) if (--maxErrors == 0) return null; - } - - //inner black - for (int i = -1; i <= 1; i++) - for (int j = -1; j <= 1; j++) - if (scan.getSample(grid.GetSamplePointRegular(i, j), 0f) > 0.7F) if (--maxErrors == 0) return null; - - MyVectorF d1 = (A - D)/5f; - MyVectorF d2 = (B - C) / 5f; - - A += d1; - D -= d1; - B += d2; - C -= d2; - - return new QRFinder(A,B,C,D); - } - } - - - class QRFinder: SquareFinder - { - //Pattern of QR finder - public static readonly int[][] finder = new int[][] { new int[] { 1, 1, 3, 1, 1 } }; - public static readonly int[][] smallFinder = new int[][] { new int[] { 1, 3, 1 } }; - public static readonly int[] midFinder = { 1, 1 }; - - public QRFinder(MyPoint A, MyPoint B, MyPoint C, MyPoint D) : base(A,B,C,D,7) { } - public QRFinder(QRFinder f) : base(f) { } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRLocation.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRLocation.cs deleted file mode 100644 index 3820fa5b..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRLocation.cs +++ /dev/null @@ -1,273 +0,0 @@ -using System.Collections; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.QR -{ - //Class to hold a QRcode defined by its 3 finders, always normalized (top-left, top-right and bottom-left - class QRLocation: BarCodeRegion - { - public QRFinder LU, LD, RU; - public float NominalWidth, NominalHeight; - public float BaselineLength; - public MyPointF[] AlignmentSubstitutionPoints; - public MyPointF UpperLeft, UpperRight, BottomLeft; - public MyVectorF LeftNormal, DownNormal; - public bool Inverted = false; - - public float Wul { get { return LU.Width;} } - public float Hul { get { return LU.Height;} } - public float Wur { get { return RU.Width;} } - public float Hur { get { return RU.Height;} } - public float Wbl { get { return LD.Width;} } - public float Hbl { get { return LD.Height;} } - - static readonly MyVector RIGHT = new MyVector(1, 0), LEFT = new MyVector(-1, 0), UP = new MyVector(0, -1), DOWN = new MyVector(0, 1); - static readonly MyVector[] DIRS = new MyVector[] { RIGHT, LEFT, UP, DOWN }; - class Finder - { - public QRFinder finder; - public MyVector dir; - public Finder[] next; - - public Finder(QRFinder finder, MyVector dir, int N) - { - this.finder = finder; - this.dir = dir; - this.next = new Finder[N]; - } - public MyVector[] NextDirs() - { - if (dir == new MyVector(0,0)) return DIRS; - else return new MyVector[] { new MyVector(-dir.Y, dir.X), new MyVector(dir.Y, -dir.X)}; - } - } - - static void Scan(ImageScaner scan, PatternFinderNoise patternFinders, PatternFinderNoise crossFinder, - PatternFinderNoise smallPatternFinder, PatternFinderNoise smallCrossFinder, Finder finder, int n) - { - QRFinder next = null; - int d = 0; - foreach (MyVector dir in finder.NextDirs()) - { - next = Scan(scan, patternFinders, crossFinder, smallPatternFinder, smallCrossFinder, finder.finder, dir); - if (next != null) - { - if (dir == UP || dir == DOWN) next.Rotate(); - finder.next[d] = new Finder(next, dir,2); - if (n < 3) Scan(scan, patternFinders, crossFinder, smallPatternFinder, smallCrossFinder, finder.next[d], n + 1); - } - d++; - } - } - - static QRFinder Scan(ImageScaner scan, PatternFinderNoise patternFinder, PatternFinderNoise crossFinder, - PatternFinderNoise smallPatternFinder, PatternFinderNoise smallCrossFinder, - QRFinder f, MyVector dir) - { - Bresenham br = null; - MyPointF A=new MyPointF(0,0),B=new MyPointF(0,0); - if (dir==RIGHT) {br = new Bresenham(f.A, f.B); A = f.B; B = f.D; } - else if (dir==LEFT) {br = new Bresenham(f.B, f.A); A = f.A; B = f.C;} - else if (dir== UP){ br = new Bresenham(f.A, f.C); A = f.C; B = f.D; } - else if (dir==DOWN){ br = new Bresenham(f.C, f.A); A = f.A; B = f.B; } - float[] points = new float[] { 3.5f/7f, 2.5f/7f, 4.5f/7f }; - //MyPoint[] points = new MyPoint[] { (A + B) / 2, A*3.5f/7f (A*3+B)/4, (A+B*3)/4}; - var markerSizeSq = (A - B).LengthSq; - - //first step: try looking for full pattern 11311 - QRFactory factory = new QRFactory(); - foreach (float coef in points) - { - MyPointF p = A * coef + B * (1f - coef); - br.MoveTo(p); - patternFinder.NewSearch(br, false, -1); - QRFinder next = null; - while (next == null && patternFinder.NextPattern()>-1) - { - MyPoint a = patternFinder.First; - MyPoint b = patternFinder.Last; - - //check distance between a and A - if ((A - (MyPointF)a).LengthSq < markerSizeSq * 0.8f)//too close to first square? - continue; - - // - SquareFinder sqFinder = SquareFinder.IsFinder(scan, a, b, crossFinder, factory); - if (sqFinder!=null) - { - next = (QRFinder)sqFinder; - if (next != null && ( !Calc.Around(f.Width, next.Width, f.Width * 0.2F) && - !Calc.Around(f.Height, next.Height, f.Height * 0.2F) && - !Calc.Around(f.Width, next.Height, f.Width * 0.2F) && - !Calc.Around(f.Height, next.Width, f.Height * 0.2F) ) - ) next = null; - if (next != null) return next; - } - } - } - - //secod step: try looking for small pattern 131 - SmallQRFactory smallFactory = new SmallQRFactory(); - foreach (float coef in points) - { - MyPointF p = A * coef + B * (1f - coef); - br.MoveTo(p); - smallPatternFinder.NewSearch(br, false, -1); - QRFinder next = null; - while (next == null && smallPatternFinder.NextPattern() > -1) - { - MyPoint a = smallPatternFinder.First; - MyPoint b = smallPatternFinder.Last; - SquareFinder sqFinder = SquareFinder.IsFinder(scan, a, b, smallCrossFinder, smallFactory); - if (sqFinder != null) - { - next = (QRFinder)sqFinder; - if (next != null && (!Calc.Around(f.Width, next.Width, f.Width * 0.2F) && - !Calc.Around(f.Height, next.Height, f.Height * 0.2F) && - !Calc.Around(f.Width, next.Height, f.Width * 0.2F) && - !Calc.Around(f.Height, next.Width, f.Height * 0.2F)) - ) next = null; - if (next != null) return next; - } - } - } - - return null; - } - - const float MaxAspectRatio = 0.5F; - - //given 3 finders add them as a new location if the 3 finders are well placed (90 degree and - //defining and square). Finders are rotated to be normalized. - static void AddLocation(ImageScaner scan, ArrayList locations, Finder ff0, Finder ff1, Finder ff2) - { - QRFinder f0 = new QRFinder(ff0.finder); //clone finders - QRFinder f1 = new QRFinder(ff1.finder); - QRFinder f2 = new QRFinder(ff2.finder); - MyPointF a = f0.Center(); - MyPointF b = f1.Center(); - MyPointF c = f2.Center(); - MyVectorF AB = a - b; - MyVectorF BC = b - c; - MyVectorF CA = c - a; - - float ab = AB.Length; - float bc = BC.Length; - float ca = CA.Length; - QRLocation location = null; - if (ab > bc && ab > ca) - { - if (Calc.Around(bc, ca, bc * MaxAspectRatio)) - { - float crossProduct = AB.X * BC.Y - AB.Y * BC.X; - if (crossProduct > 0) - location = new QRLocation(f2, f1, f0); - else - location = new QRLocation(f2, f0, f1); - } - } - else if (bc > ab && bc > ca) - { - if (Calc.Around(ab, ca, ca * MaxAspectRatio)) - { - float crossProduct = BC.X * CA.Y - BC.Y * CA.X; - if (crossProduct > 0) - location = new QRLocation(f0, f2, f1); - else - location = new QRLocation(f0, f1, f2); - } - } - else //ca>ab && ca>bc - { - if (Calc.Around(bc, ab, ab * MaxAspectRatio)) - { - float crossProduct = CA.X * AB.Y - CA.Y * AB.X; - if (crossProduct > 0) - location = new QRLocation(f1, f0, f2); - else - location = new QRLocation(f1, f2, f0); - } - } - if (location != null) locations.Add(location); - } - - //Main method to find finders around a given finder. It wors recursively, but only 2 levels depth. - //At the first level tries 4 directions, and and second level only 2, and 90º from the incoming direction. - //All valid combinations are added to the result array. - public static ArrayList Scan(ImageScaner scan, PatternFinderNoise patternFinder, PatternFinderNoise crossFinder, - PatternFinderNoise smallPatternFinder, PatternFinderNoise smallCrossFinder, QRFinder lu) - { - Finder finders = new Finder(lu, new MyVector(0, 0), 4); - Scan(scan, patternFinder, crossFinder, smallPatternFinder, smallCrossFinder, finders, 1); - - ArrayList locations = new ArrayList(); - //find valid combinations at depth level 3 - Finder f0=finders; - for (int i = 0; i < 4; i++) if (f0.next[i] != null) - { - Finder f1 = f0.next[i]; - for (int j = 0; j < 2; j++) if (f1.next[j] != null) - AddLocation(scan, locations, f0, f1, f1.next[j]); - } - //find valid combinations at depth level 2 - if (f0.next[0] != null) - { - if (f0.next[2] != null) AddLocation(scan, locations, f0, f0.next[0], f0.next[2]); - if (f0.next[3] != null) AddLocation(scan, locations, f0, f0.next[0], f0.next[3]); - } - if (f0.next[1] != null) - { - if (f0.next[2] != null) AddLocation(scan, locations, f0, f0.next[1], f0.next[2]); - if (f0.next[3] != null) AddLocation(scan, locations, f0, f0.next[1], f0.next[3]); - } - - return locations; - } - - //rotate the finder according to the leftNormal and downNormal - void Align(QRFinder f) - { - float h = f.RightNormal * this.LeftNormal; - if (Calc.Around(h, 0, 0.5F)) f.Rotate(); - h = f.RightNormal * this.LeftNormal; - if (h<0) { f.A.swap(ref f.B); f.C.swap(ref f.D); f.RightNormal *= -1.0F; } - float v = f.DownNormal * this.DownNormal; - if (v < 0) { f.A.swap(ref f.C); f.B.swap(ref f.D); f.DownNormal *= -1.0F; } - } - - void Init(QRFinder lu, QRFinder ld, QRFinder ru) - { - LU = lu; - LD = ld; - RU = ru; - - UpperLeft = lu.Center(); - UpperRight=ru.Center(); - BottomLeft=ld.Center(); - - LeftNormal = (UpperRight - UpperLeft).Normalized; - DownNormal = (BottomLeft - UpperLeft).Normalized; - - Align(LU); - Align(LD); - Align(RU); - - NominalWidth = (Wur + Wul) / 14.0F; - NominalHeight = (Hul + Hbl) / 14.0F; - MyVectorF mid = LeftNormal * (NominalWidth / 2.0F)+ DownNormal * (NominalHeight / 2.0F); - AlignmentSubstitutionPoints = new MyPointF[3]; - AlignmentSubstitutionPoints[0] = lu.B -mid; - AlignmentSubstitutionPoints[1] = ru.A + new MyVectorF(mid.Y, -mid.X); - AlignmentSubstitutionPoints[2] = ld.D + new MyVectorF(-mid.Y, mid.X); - - BaselineLength = (UpperLeft - UpperRight).Length; - } - - public QRLocation(QRFinder lu, QRFinder ld, QRFinder ru) - { - Init(lu, ld, ru); - SetCorners(LD.A, LD.A + (RU.D - LU.C), LU.C, RU.D); - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRReader.cs deleted file mode 100644 index 5fd18860..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRReader.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace BarcodeReader.Core.QR -{ -#if CORE_DEV - public -#else - internal -#endif - partial class QRReader : SymbologyReader2D - { - int width, height; - bool mergePartialBarcodes = false; - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.QRCode; - } - - protected override FoundBarcode[] DecodeBarcode() - { - this.width = BWImage.Width; - this.height = BWImage.Height; - - return Scan(); - } - - public bool MergePartialBarcodes { get { return this.mergePartialBarcodes; } set { this.mergePartialBarcodes = value; } } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRReaderRow.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRReaderRow.cs deleted file mode 100644 index becb3fb6..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRReaderRow.cs +++ /dev/null @@ -1,1241 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using SkiaSharp; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.QR -{ - -#if !OLD_QRReader - -#if CORE_DEV - public -#else - internal -#endif - class QRReaderRow - { - //object that holds the bw image + methods to sample, follow vertices,... - ImageScaner scan; - - //list of found and rejected finders, and QR codes to avoid exploring twice the same barcode - LinkedList exclusion; - - //objects to find finder patterns. PatternFinder mantains state, so a patternfinder can not be reused - //until the scan is completed. patternFinder is used in the main scan loop (scanning horizontal lines). - //When a finder is found, the second patterFinder2 is used to find the other two finders of the QRcode. - //Then the main scan can be resumed (since patternFinder is not lost). - - IPatternFinderNoiseRow patternFinder, crossFinder; - PatternFinderNoise patternFinder2, crossFinder2; - PatternFinderNoise smallPatternFinder2, smallCrossFinder2; - LinkedList foundPatterns; - LinkedList removedPatterns; - - //list of found QR and QRMicro codes found and correctly decoded. - private LinkedList candidates;//need to be synchronized!! - - Encoding encoding; - - public QRReaderRow(ImageScaner scan, BlackAndWhiteImage bwSourceImage, LinkedList candidates, Encoding encoding) - { - this.scan = scan; - this.candidates = candidates; - this.encoding = encoding; - - foundPatterns = new LinkedList(); - patternFinder = new PatternFinderNoiseRow(QRFinder.finder, true, true, 2); - crossFinder = new PatternFinderNoiseRow(QRFinder.finder, true, true, 2); - patternFinder2 = new PatternFinderNoise(bwSourceImage, QRFinder.finder[0], true, 2); //both share the same cache to speed up detection. - crossFinder2 = new PatternFinderNoise(bwSourceImage, QRFinder.finder[0], true, 2); //both share the same cache to speed up detection. - smallPatternFinder2 = new PatternFinderNoise(bwSourceImage, QRFinder.smallFinder[0], false, 2); - smallCrossFinder2 = new PatternFinderNoise(bwSourceImage, QRFinder.smallFinder[0], false, 2); - exclusion = new LinkedList(); - } - - //Scans a horizontal line looking for finder patterns (1011101). - //For each finder pattern found tries to find the finder. - //If the finder is found, then tries to locate the other two finders of the QR code. - //Thus, uses the patternFinder2 to trace lines (0, 90, 180, 270 degree, 4 directions). If another - //finder is found, traces 2 more perpendicular lines (at 90 and 270 degrees from the incoming direction). - //Foreach valid location (group of 3 finders), the decoding algorithm is executed. Notice that the algorithm - //can find goups of 3 finders comming from different QRcodes, close one from each other. - public void ScanRow(int y, XBitArray row) - { - //expand size to find micro QR - var size = row.Size + 1; - - //look for the finder - patternFinder.NewSearch(row, 0, size, 1, 0); - while (patternFinder.NextPattern()!=null) - { - MyPoint a = new MyPoint(patternFinder.First, y); - MyPoint b = new MyPoint(patternFinder.Last, y); - Pattern p = new Pattern(null, a.X, b.X, y); - LinkedListNode prev = foundPatterns.Find(p); - if (prev != null) - { - //pattern already processed the last row - if (prev.Value.y != y) - { - StackedPattern sp = (StackedPattern)prev.Value; - sp.NewRow(a.X, b.X, y); - } - } - else - { - //new - StackedPattern sp = new StackedPattern(null, a.X, b.X, y); - foundPatterns.AddLast(sp); - } - } - - //clean old patterns and process them - LinkedList removedPatterns = Pattern.RemoveOldPatterns(foundPatterns, y); - - foreach (Pattern p in removedPatterns) - { - StackedPattern sp = (StackedPattern)p; - ProcessPattern(sp); - } - } - - bool AlreadyExists(MyPoint center) - { - var node = candidates.First; - while (node != null) - { - if (node.Value.In(center)) - return true; - node = node.Next; - } - - return false; - } - - /// - /// QR code is mirrorred - /// - public bool IsMirrored { get; private set; } - - private bool ProcessPattern(StackedPattern p) - { - MyPoint a, b; - p.MidPoints(out a, out b); - MyPoint center = (a + b) / 2; - - if (AlreadyExists(center)) - return false; - -#if FIND_PATTERN - MyPoint Y = new MyPoint(0, 1); - SquareFinder f = new SquareFinder(a + Y, b + Y, a, b, 7); - candidates.AddLast(f); -#else - //checks if a vertical pattern crosses the horizontal pattern in the middle - MyPoint pUp, pDown; - QRFactory factory = new QRFactory(); - QRFinder finder = null; - if (SquareFinder.CheckCrossPattern(scan, a, b, center, crossFinder, factory, out pUp, out pDown)) - { - //calculates black - white threshold using the pixels in the pattern - scan.setBWThreshold(a, b); - //tries to create a new finder from horizontal and vertical pattenrs - //finder = (QRFinder)SquareFinder.IsFinder(scan, a, b, center, patternFinder2, crossFinder2, factory); - - finder = (QRFinder)SquareFinder.IsFinder(scan, a, b, pUp, pDown, factory); - if (finder != null) - { - //add finder to the exclusion list, so no more patterns in this area will be processed. - exclusion.AddFirst(finder); -#if FIND_FINDER - QRLocation l = new QRLocation(finder, finder, finder); - candidates.AddLast(l); -#else - //detect finders around the first found finder (at 0, 90, 180, 270 degrees), and - //finders at 90 and 270º from them. Max: 4 * 2 leafs. - //Returns an array of valid triplets of finders. Finders are always normalized (rotated) - //to match a top-left, top-right and bottom-left finders. - var locations = QRLocation.Scan(scan, patternFinder2, crossFinder2, smallPatternFinder2, smallCrossFinder2, finder); - - QRSymbol symbol = null; - BarCodeRegion best = null; - if (locations != null && locations.Count > 0) - { - foreach (QRLocation location in locations) - { -#if FIND_LOCATION - candidates.AddLast(location); - exclusion.AddFirst(location); -#else - int[] columns = HoughScan(location.A, location.B, location.C, location.D, location.Wbl / 7f, location.Hbl / 7f); - int[] rows = HoughScan(location.B, location.D, location.A, location.C, location.Hbl / 7f, location.Wbl / 7f); - int version = (int)Math.Round((float)(columns.Length - 1 - 17) / 4f); //-1 because columns.length has 1 counts transitions, and transitions are modules+1 - if (version < 1) version = 1; else if (version > 44) version = 44; - symbol = new QRSymbol(version); - if (columns.Length == rows.Length) - { - if (symbol.SideModuleCount == columns.Length - 1) - { - scanHough(location, symbol, columns, rows); - BarCodeRegion br = ReadSymbol(symbol, location); - if (br != null && (best == null || best.Confidence < br.Confidence)) { best = br; break; } - } - } - else - { - columns = HoughScan2(location.A, location.B, location.C, location.D, location.Wbl / 7f, location.Hbl / 7f, symbol.SideModuleCount); - rows = HoughScan2(location.B, location.D, location.A, location.C, location.Hbl / 7f, location.Wbl / 7f, symbol.SideModuleCount); - scanHough(location, symbol, columns, rows); - BarCodeRegion br = ReadSymbol(symbol, location); - if (br != null && (best == null || best.Confidence < br.Confidence)) { best = br; break; } - } - - symbol = CountModulesForQRCode(location); - if (symbol != null) - { - scanSimple(symbol, location); - BarCodeRegion br = ReadSymbol(symbol, location); - if (br != null && (best == null || best.Confidence < br.Confidence)) { best = br; break; } - else if (scanAdaptive(symbol, location)) - { - //second pass - br = ReadSymbol(symbol, location); - if (br != null && (best == null || best.Confidence < br.Confidence)) { best = br; break; } - } - } -#endif - } - } - else //Try microQR - { - //try different angles (0, 90, 180, 270) - for (int i = 0; i < 4; i++) - { - symbol = CountModulesForMicroQRCode(scan, finder); - if (symbol != null) - { - //recognize - best = ReadSymbol(symbol, finder); - //calc correct MicroQR rectangle - CalcMicroQRrectangle(finder, symbol); - // - break; - } - //rotate on 90 degree - finder.RightNormal = finder.RightNormal.Rotate((float) (Math.PI / 2)); - finder.DownNormal = finder.DownNormal.Rotate((float) (Math.PI / 2)); - } - } - - if (best != null) - { - lock (candidates) - { - candidates.AddFirst(best); - } - - exclusion.AddFirst(best); - } -#endif - } - } -#endif - return false; - } - - private void CalcMicroQRrectangle(QRFinder finder, QRSymbol symbol) - { - var points = new MyPointF[4] { finder.A, finder.B, finder.C, finder.D }; - //find main corner - var axis = (finder.RightNormal + finder.DownNormal); - var O = new MyPointF(-axis.X, -axis.Y) * 100000;//virtual center of coordinates - var min = float.MaxValue; - var corner = finder.A; - foreach (var p in points) - { - var r = (p - O).LengthSq;//distance from center of coordinates - if (r < min)//find point nearest to O - { - corner = p; - min = r; - } - } - //calc ABCD - var modR = finder.ModuleRight.Length * symbol.SideModuleCount; - var modD = finder.ModuleDown.Length * symbol.SideModuleCount; - var right = new MyPointF(finder.RightNormal.X * modR, finder.RightNormal.Y * modR); - var down = new MyPointF(finder.DownNormal.X * modD, finder.DownNormal.Y * modD); - var A = corner + down; - var B = corner + down + right; - var C = corner; - var D = corner + right; - //set ABCD - finder.SetCorners(A, B, C, D); - } - - private void scanHough(QRLocation location, QRSymbol symbol, int[] cols, int[]rows) - { -#if DEBUG_IMAGE - scan.Reset(); -#endif - MyPointF A = location.A, B = location.B, C = location.C, D = location.D; - B.Y += 1; //right down corner - C.X -= 1; //upper left corner - - MyVectorF vdL = C - A; - MyVectorF vdR = D - B; - bool[][] m=symbol.MaskedBitarray; - int lRows=rows[rows.Length-1]-rows[0]; - int lCols=cols[cols.Length-1]-cols[0]; - for (int row = 0; row < symbol.SideModuleCount; row++) - { - float r=((float)(rows[row]+rows[row+1])/2f-(float)rows[0])/(float)lRows; - MyPointF L = A + vdL * r; - MyPointF R = B + vdR * r; - for (int col = 0; col < symbol.SideModuleCount; col++) - { - float c=((float)(cols[col]+cols[col+1])/2f-(float)cols[0])/(float)lCols; - MyPointF p = L * (1f - c) + R * c; - bool isBlack = scan.isBlackSample(p, 0f); - symbol.MaskedBitarray[symbol.SideModuleCount-1-row][col] = isBlack; - } - } -#if DEBUG_IMAGE - scan.Save(@"outSamples.png"); -#endif - } - - - private int[] HoughScan(MyPointF A, MyPointF B, MyPointF C, MyPointF D, float hModuleLength, float vModuleLength) { - MyVectorF vdL = C - A; - MyVectorF vdR = D - B; - float width = (B - A).Length; - float height=(C-A).Length; - int nVModules = (int)Math.Round(height / vModuleLength); - int nHModules = (int)Math.Round(width / hModuleLength); - float incY = 1f / (1f* nVModules); //one sample per row - int MODULE_DISCRETIZATION = 5; - int N=(nHModules+1)*MODULE_DISCRETIZATION; //add half module at the left, and half at the right - int[] h = new int[N]; - MyVectorF vdX = (B - A).Normalized; - A = A - vdX * (hModuleLength / 2f); - B = B + vdX * (hModuleLength / 2); - for (float y = incY / 2f; y <= 1f; y += incY) - { - MyPointF L = A + vdL * y; - MyPointF R = B + vdR * y; - Bresenham br = new Bresenham(L, R); - float length=(R-L).Length; - bool isBlack = scan.isBlack(br.Current); - while (!br.End()) - { - if (isBlack == scan.isBlack(br.Current)) { } - else - { //histogram each transition - float x = (br.CurrentF - L).Length / length; //from 0..1 - int iX = (int)Math.Round(x * N); - if (iX < 0) iX = 0; else if (iX >= N) iX = N - 1; - h[iX]++; - isBlack = !isBlack; - } - br.Next(); - } - } - - //find local max around moduleLength*i - /* - LinkedList transitions = new LinkedList(); - int currentModuleLength = h.Length / (nModules+1); - int i = -currentModuleLength / 2; - int first = -1; - while (i < N) - { - int halfModule=currentModuleLength/2; - int x = i +currentModuleLength - halfModule; - int max = -1; - for (int j = 0; j <= currentModuleLength; j++, x++) if (x < N && isMax(h, x)) { max = x; break;} - if (max==-1 ) max=i+currentModuleLength;//if not found, use the same step - if (max < h.Length) - { - if (transitions.Count == 0) first = max; - else currentModuleLength = (max - first) / transitions.Count; - transitions.AddLast(max); - } - i = max; - } - * */ - LinkedList maxs=new LinkedList(); - int lastMax = -1; - for (int i = 0; i < h.Length; i++) if (isMax(h, i)) { maxs.AddLast(i); lastMax = i; } - int[] aMaxs = new int[maxs.Count]; - maxs.CopyTo(aMaxs, 0); - - aMaxs = removeSmallPeaks(h, aMaxs); - - aMaxs = removePeaksWithoutValley(h, aMaxs); - - aMaxs = removePeaksTooClose(h, aMaxs, MODULE_DISCRETIZATION / 2); - - - LinkedList transitions = new LinkedList(); - for (int i = 0; i < aMaxs.Length; i++) - { - if (i>0) - { - int lastPeak=aMaxs[i-1]; - int d = aMaxs[i] - lastPeak; - float lostPeaks = (float)d / (float)MODULE_DISCRETIZATION; - if (lostPeaks > 1.7f) - { - int n = (int)Math.Round(lostPeaks); //n>=2 - float l = (float)d / (float)n; - for (int j = 1; j < n; j++) transitions.AddLast(lastPeak + (int)Math.Round((float)j * l)); //interpolate lost peaks - } - } - transitions.AddLast(aMaxs[i]); - } - - /* - Array.Sort(aMaxs, new InverseComparer(h)); - - int minModuleLength = 3; // MODULE_DISCRETIZATION / 2; //5-->2 - ArrayList transitions = new ArrayList(); - for (int i = 0; i < aMaxs.Length; i++) - { - int pos = canAddNewMax(transitions, aMaxs[i], minModuleLength); - if (pos != -1) transitions.Insert(pos, aMaxs[i]); - }*/ - int[] aTransitions=new int[transitions.Count]; - transitions.CopyTo(aTransitions, 0); - return aTransitions; - } - - private int[] HoughScan2(MyPointF A, MyPointF B, MyPointF C, MyPointF D, float hModuleLength, float vModuleLength, int nModules) - { - MyVectorF vdL = C - A; - MyVectorF vdR = D - B; - float width = (B - A).Length; - float height = (C - A).Length; - int nVModules = (int)Math.Round(height / vModuleLength); - int nHModules = (int)Math.Round(width / hModuleLength); - float incY = 1f / (1f * nVModules); //one sample per row - int MODULE_DISCRETIZATION = 5; - int N = (nHModules + 1) * MODULE_DISCRETIZATION; //add half module at the left, and half at the right - int[] h = new int[N]; - MyVectorF vdX = (B - A).Normalized; - A = A - vdX * (hModuleLength / 2f); - B = B + vdX * (hModuleLength / 2); - for (float y = incY / 2f; y <= 1f; y += incY) - { - MyPointF L = A + vdL * y; - MyPointF R = B + vdR * y; - Bresenham br = new Bresenham(L, R); - float length = (R - L).Length; - bool isBlack = scan.isBlack(br.Current); - while (!br.End()) - { - if (isBlack == scan.isBlack(br.Current)) { } - else - { //histogram each transition - float x = (br.CurrentF - L).Length / length; //from 0..1 - int iX = (int)Math.Round(x * N); - if (iX < 0) iX = 0; else if (iX >= N) iX = N - 1; - h[iX]++; - isBlack = !isBlack; - } - br.Next(); - } - } - - int[] aTransitions = new int[nModules + 1]; - aTransitions[0] = findFirstMax(h, 0, 1, MODULE_DISCRETIZATION); - aTransitions[nModules] = findFirstMax(h, h.Length - 1, -1, MODULE_DISCRETIZATION); - float moduleLength = ((float)(aTransitions[nModules] - aTransitions[0])) / ((float)nModules); - float pos = (float)aTransitions[0]; - for (int i = 1; i < nModules; i++) - { - pos += moduleLength; - int estimated = (int)Math.Round(pos); - int prevMax = findFirstMax(h, estimated, -1, MODULE_DISCRETIZATION); - int nextMax = findFirstMax(h, estimated, 1, MODULE_DISCRETIZATION); - if (prevMax - aTransitions[i - 1] < MODULE_DISCRETIZATION / 2) aTransitions[i] = nextMax; - else if (estimated - prevMax < nextMax - estimated) aTransitions[i] = prevMax; - else aTransitions[i] = nextMax; - } - return aTransitions; - } - - int findFirstMax(int[] h, int pos, int inc, int max) - { - int offset = inc < 0 ? 1 : 0; - int p = pos + offset * inc; - while (offset < max && p >= 0 && p < h.Length) - { - if (isMax(h, p)) break; - offset++; - p += inc; - } - return p; - } - - class InverseComparer : IComparer { - private int[] h; - public InverseComparer(int[] h) { this.h = h; } - public int Compare(int x, int y) { return h[y] - h[x]; } - } - - //can we add a new transition x in transitions? return position - private int canAddNewMax(ArrayList transitions, int x, int minModuleLength) - { - if (transitions.Count==0) return 0; //yes, add to 0 - int i = 0; - while (i < transitions.Count && (int)transitions[i] < x) i++; - if (i > 0) //check if i is far enough from the previous one - { - int l = x - (int)transitions[i - 1]; - if (l <= minModuleLength) return -1; - } - if (i =0 && h[x] == h[l]) l--; - - int r = x + 1; - while (r h[l]) && (r == h.Length || h[x] > h[r]); - } - - private int[] removeSmallPeaks(int[] h, int[] peaks) - { - int maxPeak = -1; - for (int i = 0; i < peaks.Length; i++) if (maxPeak == -1 || h[peaks[i]] > h[maxPeak]) maxPeak = peaks[i]; - - int threshold = h[maxPeak] / 4; - LinkedList l = new LinkedList(); - for (int i = 0; i < peaks.Length; i++) - if (h[peaks[i]] > threshold) l.AddLast(peaks[i]); - - int[] filtered = new int[l.Count]; - l.CopyTo(filtered, 0); - return filtered; - } - - private int[] removePeaksWithoutValley(int[] h, int[] peaks) - { - LinkedList l = new LinkedList(); - - //find deeper points between peaks - ArrayList aPeaks=new ArrayList(peaks); - ArrayList depth = new ArrayList(); - for (int i = 0; i < peaks.Length - 1; i++) - { - int current= peaks[i]; - int next=peaks[i+1]; - int min=current; - for (int j = current + 1; j < next; j++) - if (h[j] < h[min]) min = j; - depth.Add(h[min]); - } - - //remove peaks without enough deep around - bool done = false; - while (!done) - { - int found = -1; - for (int i = 0; i < depth.Count && found == -1; i++) - { - int lPeak = h[(int)aPeaks[i]]; - int rPeak = h[(int)aPeaks[i + 1]]; - int d = (int)depth[i]; - if (d > lPeak / 3 || d > rPeak / 3) found = i; - } - if (found != -1) //found a valley not deep enough - { - int lPeak = h[(int)aPeaks[found]]; - int rPeak = h[(int)aPeaks[found + 1]]; - if (lPeak < rPeak) { - aPeaks.RemoveAt(found); - if (found>1 && (int)depth[found] < (int)depth[found - 1]) depth[found - 1] = depth[found]; - depth.RemoveAt(found); - } - else - { - aPeaks.RemoveAt(found+1); - if (found +1 l = new LinkedList(); - - ArrayList aPeaks = new ArrayList(); - aPeaks.Add(peaks[0]); - for (int i = 1; i < peaks.Length; i++) - { - int dist = peaks[i] - peaks[i - 1]; - if (dist > minDist) aPeaks.Add(peaks[i]); - } - - int[] filtered = new int[aPeaks.Count]; - aPeaks.CopyTo(filtered, 0); - return filtered; - } - - - - - - //count modules and extract them for MicroQR - //Uses a regular (no perspective deformation) grid using horizontal and vertical vectors from the finder - private QRSymbol CountModulesForMicroQRCode(ImageScaner scan, QRFinder finder) - { - bool[][] patternUnit = new bool[1][]; - patternUnit[0] = new bool[2]; - - Grid grid = new Grid( finder.Center(), finder.RightNormal * finder.Width / 7F, finder.DownNormal * finder.Height/7F); - - // horizontal checks - int vx = 0; - for (; vx < 5; ++vx) - { - try - { - grid.ExtractPointsRegular(scan, patternUnit, new MyPoint(4 + 2 * vx, -3), new MyPoint(0, 0), 2, 1); - if (!(!patternUnit[0][0] && patternUnit[0][1])) // test for alternating timing pattern - { - break; - } - } - catch (Exception) - { - break; - } - } - - // vertical checks - patternUnit = new bool[2][]; - patternUnit[0] = new bool[1]; - patternUnit[1] = new bool[1]; - int vy = 0; - for (; vy < 5; ++vy) - { - try - { - grid.ExtractPointsRegular(scan,patternUnit, new MyPoint(-3, 4 + 2 * vy), new MyPoint(0, 0), 1, 2); - if (!(!patternUnit[0][0] && patternUnit[1][0])) // test for alternating timing pattern - { - break; - } - } - catch (Exception) - { - break; - } - } - - int version = Math.Min(vx, vy); - if (version < 2 || version > 5) - { - return null; - } - - QRSymbol symbol= new QRSymbol(39 + version); - int cy = symbol.MaskedBitarray.Length; - int cx = cy > 0 ? symbol.MaskedBitarray[0].Length : 0; - grid.ExtractPointsRegular(scan, symbol.MaskedBitarray, new MyPoint(-3, -3), new MyPoint(0, 0), cx, cy); - - return symbol; - } - - - - //estimate module count from distance between finders (roughVersion). - //If roughVersion is >7 then count modules sampling the image. - //Then samples the image locating alignment patterns. The number of sampling - //regions depend on the number of alignment patterns. For each region, an irregular - //sampling is done (allowing perspective deformation). - private QRSymbol CountModulesForQRCode(QRLocation location) - { -#if DEBUG_IMAGE - scan.Reset(); -#endif - QRSymbol symbol; - // determine the symbol version - int roughVersion = (int)Math.Round((location.BaselineLength / location.NominalWidth - 10) / 4); - if (roughVersion < 1) return null; - if (roughVersion < 7) - { - symbol = new QRSymbol(roughVersion); - } - else - { - int fineVersion = QRUtils.ExtractSymbolVersion(scan, location); - if (fineVersion < 0) - { - // let's give it one more try, maybe the image is so bad that we miscalculated the version slightly - if (roughVersion == 7) - { - fineVersion = 6; - } - else fineVersion = roughVersion; - } - if (fineVersion > 40) return null; - symbol = new QRSymbol(fineVersion); - } - - location.NominalWidth = (location.UpperLeft - location.UpperRight).Length / (symbol.SideModuleCount - 7); - location.NominalHeight = (location.UpperLeft - location.BottomLeft).Length / (symbol.SideModuleCount - 7); - if (symbol.patternCoords == null) return null; - int patternsPerSide = symbol.patternCoords.Length; - - //horizontal and vertical directions for each finder - MyVectorF luRight = location.LU.RightNormal * location.LU.Width / 7F; - MyVectorF luDown = location.LU.DownNormal * location.LU.Height / 7F; - MyVectorF ldRight = location.LD.RightNormal * location.LD.Width / 7F; - MyVectorF ldDown = location.LD.DownNormal * location.LD.Height / 7F; - MyVectorF ruRight = location.RU.RightNormal * location.RU.Width / 7F; - MyVectorF ruDown = location.RU.DownNormal * location.RU.Height / 7F; - - //If only one sampling region (no alignment patterns) - if (patternsPerSide != 0) - { - //fill region coordinates (numOfAlignmentPatterns+2)*(numOfAlignmentPatterns+2) - int N = patternsPerSide + 2; - MyPointF[][] align = symbol.align= new MyPointF[N][]; - float[][] moduleW = symbol.moduleW = new float[N][]; - float[][] moduleH = symbol.moduleH = new float[N][]; - for (int y = 0; y < N; y++) - { - align[y] = new MyPointF[N]; - moduleW[y] = new float[N]; - moduleH[y] = new float[N]; - for (int x = 0; x < N; x++) - { - align[y][x] = MyPointF.Empty; - moduleW[y][x] = 0f; - moduleH[y][x] = 0f; - } - } - - //first: alignment points from QRFinders corners - align[0][0] = location.UpperLeft - luRight * 3.5F - luDown * 3.5F; - align[0][1] = location.UpperLeft + luRight * 2.5F - luDown * 3.5F; - align[1][0] = location.UpperLeft - luRight * 3.5F + luDown * 2.5F; - align[1][1] = location.UpperLeft + luRight * 2.5F + luDown * 2.5F; - moduleW[0][0] = moduleW[0][1] = moduleW[1][0] = moduleW[1][1] = location.LU.ModuleWidth; - moduleH[0][0] = moduleH[0][1] = moduleH[1][0] = moduleH[1][1] = location.LU.ModuleHeight; - - align[0][N - 1] = location.UpperRight + ruRight * 3.5F - ruDown * 3.5F; - align[1][N - 1] = location.UpperRight + ruRight * 3.5F + ruDown * 2.5F; - moduleW[0][N - 1] = moduleW[1][N - 1] = location.RU.ModuleWidth; - moduleH[0][N - 1] = moduleH[1][N - 1] = location.RU.ModuleHeight; - - align[N - 1][0] = location.BottomLeft - ldRight * 3.5F + ldDown * 3.5F; - align[N - 1][1] = location.BottomLeft + ldRight * 2.5F + ldDown * 3.5F; - moduleW[N - 1][0] = moduleW[N - 1][1] = location.LD.ModuleWidth; - moduleH[N - 1][0] = moduleH[N - 1][1] = location.LD.ModuleHeight; - - //If the last alignment pattern falls close to the finder it is not draws. - //So, alignment coordinates are computed from the finder coords. - int d = symbol.SideModuleCount - symbol.patternCoords[patternsPerSide - 1]; - if (d <= 11) - { - float dd = (float)(d) - 3.5F; - align[0][N - 2] = location.UpperRight - ruRight * dd - ruDown * 3.5F; - align[1][N - 2] = location.UpperRight - ruRight * dd + ruDown * 2.5F; - moduleW[0][N - 2] = moduleW[1][N - 2] = location.RU.ModuleWidth; - moduleH[0][N - 2] = moduleH[1][N - 2] = location.RU.ModuleHeight; - - align[N - 2][0] = location.BottomLeft - ldRight * 3.5F - ldDown * dd; - align[N - 2][1] = location.BottomLeft + ldRight * 2.5F - ldDown * dd; - moduleW[N - 2][0] = moduleW[N - 2][1] = location.LD.ModuleWidth; - moduleH[N - 2][0] = moduleH[N - 2][1] = location.LD.ModuleHeight; - } - - //second: top border. Adaptative interpolation between the top left and top right finders. - //Module length starts with top-left module length, and ends with top-right module length. - for (int i = 1; i < patternsPerSide - 1; i++) - { - MyPointF fromLeft = align[0][0] + luRight * symbol.patternCoords[i]; - MyPointF fromRight = align[0][N - 1] - ruRight * (symbol.SideModuleCount - 1 - symbol.patternCoords[i]); - float rightFactor = (float)symbol.patternCoords[i] / (float)(symbol.SideModuleCount - 1); - float leftFactor = 1F - rightFactor; - rightFactor *= rightFactor; - leftFactor *= leftFactor; - MyPointF mean = (fromLeft * leftFactor + fromRight * rightFactor) / (leftFactor + rightFactor); - align[0][1 + i] = mean; - moduleW[0][1 + i] = (moduleW[0][0] * leftFactor + moduleW[0][N - 1] * rightFactor) / (leftFactor + rightFactor); - moduleH[0][1 + i] = (moduleW[0][0] * leftFactor + moduleW[0][N - 1] * rightFactor) / (leftFactor + rightFactor); - } - - //third: left border. Also adaptative interpolation between top-left and bottom-left. - for (int i = 1; i < patternsPerSide - 1; i++) - { - MyPointF fromTop = align[0][0] + luDown * symbol.patternCoords[i]; - MyPointF fromBottom = align[N - 1][0] - ldDown * (symbol.SideModuleCount - 1 - symbol.patternCoords[i]); - float bottomFactor = (float)symbol.patternCoords[i] / (float)(symbol.SideModuleCount - 1); - float topFactor = 1F - bottomFactor; - bottomFactor *= bottomFactor; - topFactor *= topFactor; - MyPointF mean = (fromTop * topFactor + fromBottom * bottomFactor) / (topFactor + bottomFactor); - align[1 + i][0] = mean; - moduleW[1 + i][0] = (moduleW[0][0] * bottomFactor + moduleW[N - 1][0] * topFactor) / (bottomFactor + topFactor); - moduleH[1 + i][0] = (moduleW[0][0] * bottomFactor + moduleW[N - 1][0] * topFactor) / (bottomFactor + topFactor); - } - - //four: find alignment patterns. Main loop to scan each region. If all 4 corners are already - // computed, only sampling is done. If the bottom-right corner is empty, the alignment pattern will - // be found. - for (int y = 1; y < N; y++) - { - for (int x = 1; x < N; x++) - { - if (align[y][x].IsEmpty) - { - MyPointF p = MyPoint.Empty; - if (x > 1 && y > 1) - { - MyVectorF up = align[y - 1][x] - align[y - 2][x]; - MyVectorF left = align[y][x - 1] - align[y][x - 2]; - if (up.Length > 15f && left.Length > 15f) //otherwise use default calculation - if (x < N - 1 && y < N - 1) //mid align patterns - { - Regression v = new Regression(align[y-2][x]); - v.AddPointL(align[y - 2][x]); - v.AddPointL(align[y - 1][x]); - Regression h = new Regression(align[y][x-2]); - h.AddPointL(align[y][x - 2]); - h.AddPointL(align[y][x - 1]); - RegressionLine vl = v.LineL; - RegressionLine hl = h.LineL; - p = vl.Intersection(hl); - } - else if (x < N - 1) //bottom (y==N-1) - { - MyVectorF lu = align[y - 1][x - 1] - align[y - 2][x - 1]; - MyVectorF ld = align[y][x - 1] - align[y - 1][x - 1]; - MyVectorF ru = align[y - 1][x] - align[y - 2][x]; - float factor = ld.Length / lu.Length; - p = align[y - 1][x] + ru * factor; - } - else //left - { - MyVectorF ul = align[y - 1][x - 1] - align[y - 1][x - 2]; - MyVectorF ur = align[y - 1][x] - align[y - 1][x - 1]; - MyVectorF dl = align[y][x - 1] - align[y][x - 2]; - float factor = ur.Length / ul.Length; - p = align[y][x - 1] + dl * factor; - } - } - if (p.IsEmpty) - { - //first aproximation assuming no deformation - p = align[y][x - 1] + (align[y - 1][x] - align[y - 1][x - 1]); - } - align[y][x] = p; - moduleW[y][x] = (moduleW[y][x - 1] + moduleW[y - 1][x]) / 2f; //by now just the mean of previous ones - moduleH[y][x] = (moduleH[y][x - 1] + moduleH[y - 1][x]) / 2f; - - - //Only in a non skewed barcodes, alignment pattern falls in p. - //This loop tries to find the alignment patterns near to p using - //neibourhood array (the displacement from p) - if (x != N - 1 && y != N - 1) - { - MyPointF p0 = p; - float width = location.NominalWidth * 3F; - MyPoint[] neibourhood = new MyPoint[] { new MyPoint(0, 0), new MyPoint(1, 0), new MyPoint(-1, 0), new MyPoint(0, 1), new MyPoint(0, -1), - new MyPoint(2, 0), new MyPoint(-2, 0), new MyPoint(0, 2), new MyPoint(0, -2)}; - foreach (MyPoint n in neibourhood) - { - float w, h; - if (FindAlignmentPattern(location, p0, n, width, out p, out w, out h)) - { - //correct interpoled borders, in case - if (y == 1) align[0][x] += (p - p0); - if (x == 1) align[y][0] += (p - p0); - align[y][x] = p; - //moduleW[y][x] = w; - //moduleH[y][x] = h; - break; - } - } - } - } - } - } - location.B=align[N-1][N-1]; - } - return symbol; - } - - - bool scanSimple(QRSymbol symbol, QRLocation location) - { - int patternsPerSide = symbol.patternCoords.Length; - - //If only one sampling region (no alignment patterns) - if (patternsPerSide == 0) - { - NotUniformGrid grid = new NotUniformGrid(symbol.SideModuleCount, symbol.SideModuleCount, 7, location.LU, location.LD, location.RU); - grid.ExtractPoints(scan, symbol.MaskedBitarray); - } - else - { - int N = patternsPerSide + 2; - //scan barcode - int prevRow = 0; - for (int y = 1; y < N; y++) - { - int row = y <= patternsPerSide ? symbol.patternCoords[y - 1] : symbol.SideModuleCount; - int prevCol = 0; - for (int x = 1; x < N; x++) - { - int col = x <= patternsPerSide ? symbol.patternCoords[x - 1] : symbol.SideModuleCount; - //Once the alignment pattern is found, sample the image using these 4 corners - MyPointF[][] align = symbol.align; - Grid grid = new Grid(col - prevCol, row - prevRow, align[y - 1][x - 1], align[y][x - 1], align[y - 1][x], align[y][x], true); - grid.ExtractPoints(scan, symbol.MaskedBitarray, new MyPoint(0, 0), - new MyPoint(prevCol, prevRow), col - prevCol, row - prevRow); -#if DEBUG_IMAGE - //scan.Save(@"outSamples.png"); -#endif - prevCol = col; - } - prevRow = row; - } - } -#if DEBUG_IMAGE - scan.Save(@"outSamples.png"); -#endif - return true; - } - - bool scanAdaptive(QRSymbol symbol, QRLocation location) - { - int patternsPerSide = symbol.patternCoords.Length; -#if DEBUG_IMAGE - scan.Reset(); -#endif - - //If only one sampling region (no alignment patterns) - if (patternsPerSide != 0) - { - for (int i = 0; i < symbol.MaskedBitarray.Length; i++) - for (int j = 0; j < symbol.MaskedBitarray[i].Length; j++) - symbol.MaskedBitarray[i][j] = false; - - int N = patternsPerSide + 2; - //scan barcode - int prevRow = 0; - for (int y = 1; y < N; y++) - { - int row = y <= patternsPerSide ? symbol.patternCoords[y - 1] : symbol.SideModuleCount; - int prevCol = 0; - for (int x = 1; x < N; x++) - { - int col = x <= patternsPerSide ? symbol.patternCoords[x - 1] : symbol.SideModuleCount; - //Once the alignment pattern is found, sample the image using these 4 corners - //Grid grid = new Grid(col - prevCol, row - prevRow, align[y-1][x-1], align[y][x-1], align[y-1][x], align[y][x], true); - //grid.ExtractPoints(scan, symbol.MaskedBitarray, new MyPoint(0, 0), - // new MyPoint(prevCol, prevRow), col - prevCol, row - prevRow); - MyPointF[][] align = symbol.align; - float[][] moduleW = symbol.moduleW; - float[][] moduleH = symbol.moduleH; - AdaptiveGrid grid = new AdaptiveGrid(col - prevCol, row - prevRow, - align[y - 1][x - 1], align[y][x - 1], align[y - 1][x], align[y][x], - moduleW[y - 1][x - 1], moduleW[y][x - 1], moduleW[y - 1][x], moduleW[y][x], - moduleH[y - 1][x - 1], moduleH[y][x - 1], moduleH[y - 1][x], moduleH[y][x]); - grid.ExtractPoints(scan, symbol.MaskedBitarray, new MyPoint(prevCol, prevRow)); -#if DEBUG_IMAGE - //scan.Save(@"outSamples.png"); -#endif - prevCol = col; - } - prevRow = row; - } -#if DEBUG_IMAGE - scan.Save(@"outSamples.png"); -#endif - return true; - } - return false; - } - - //Starting from the estimated center of the alignment pattern (p0+offset), finds the next transition - //from white to black at left, right, up and down. Usually, the center of the alignment pattern - //produces an error in one direction, vertical or horizontal. Calculate h and v distances of the - //found limits and use the bigger one to recalculate the other 2. - bool FindAlignmentPattern(QRLocation location, MyPointF p0, MyPoint offset, float width, out MyPointF corrected, out float W, out float H) - { - //adjunt alignment pattern center - MyPointF p = p0 + location.LeftNormal * (location.NominalWidth * (0.5F + (float)offset.X/2f)) + - location.DownNormal * (location.NominalHeight * (0.5F + (float)offset.Y/2f)); - MyPoint ip=new MyPoint((int)Math.Truncate(p.X),(int)Math.Truncate(p.Y)); - int halfModuleLength = (int)Math.Round(width / 3F /2F)-1; - if (halfModuleLength < 0) halfModuleLength = 0; - - MyPointF up = scan.NextBlack(ip, -location.DownNormal, halfModuleLength); - MyPointF down = scan.NextBlack(ip, location.DownNormal, halfModuleLength); - MyPointF left = scan.NextBlack(ip, -location.LeftNormal, halfModuleLength); - MyPointF right = scan.NextBlack(ip, location.LeftNormal, halfModuleLength); - float h = (left - right).Length; - float v = (up - down).Length; - - if (!Calc.Around(h / v, 1.0F, 0.3F) || !Calc.Around(h / width, 1.0F, 0.2F)) - if (h > v) - { - p = left * (5F / 6F) + right * (1F / 6F); - ip = new MyPoint((int)Math.Truncate(p.X), (int)Math.Truncate(p.Y)); - up = scan.NextBlack(ip, -location.DownNormal, halfModuleLength); - down = scan.NextBlack(ip, location.DownNormal, halfModuleLength); - v = (up - down).Length; - } - else - { - p = up * (5F / 6F) + down * (1F / 6F); - ip = new MyPoint((int)Math.Truncate(p.X), (int)Math.Truncate(p.Y)); - left = scan.NextBlack(ip, -location.LeftNormal, halfModuleLength); - right = scan.NextBlack(ip, location.LeftNormal, halfModuleLength); - h = (left - right).Length; - } - - W = H = 0f; - - //if h and v are regular enough, we consider them as a good alignment pattern and claculate - // the top-left coordinates of the center of the alignment pattern. - if (Calc.Around(h / v, 1.0F, 0.4F) && Calc.Around(h / width, 1.0F, 0.3F)) - { - MyVectorF vdD = (down - up); - p = new MyPointF(0.5F + ip.X, 0.5F + ip.Y); - MyVectorF vdR = (right - left); - float toUp = (p - up).Length; - float toDown = (p - down).Length; - float factor = toUp / (toUp + toDown); - MyPointF LU = left - vdD * factor; - corrected = LU + vdD / 3F + vdR / 3F; - - W = h / 3f; - H = v / 3f; - return true; - } - corrected = MyPointF.Empty; - return false; - } - - BarCodeRegion ReadSymbol(QRSymbol symbol, BarCodeRegion result) - { - if (symbol != null) - { - float confidence = 1F; - BitArray stream = ReadSymbol(symbol, out confidence); - if (stream != null) - { - ABarCodeData[] data = DecodeData(symbol, stream); - if (data != null) - { - if (encoding != null) - foreach (ABarCodeData d in data) - if (d is Base256BarCodeData codeData) - if (codeData.encoding == null) - codeData.encoding = encoding; - - result.Data = data; - result.Confidence = confidence; - return result; - } - } - - if (symbol.IsMirrored) - IsMirrored = true; - } - - return null; - } - - - //method form the old code to create the byte array from array bit, and correct them. - BitArray ReadSymbol(QRSymbol symbol, out float confidence) - { - confidence = 1F; - if (!symbol.EnrichFormatInformation()) - { - return null; - } - symbol.ReadCodewords(); - int[][] blockInfo = symbol.GenerateECBlockInfo(); - int blockCount = blockInfo.Length; - int[][] blocks = new int[blockCount][]; - for (int i = 0; i < blockCount; i++) - { - blocks[i] = new int[blockInfo[i][0] + blockInfo[i][1]]; - } - - // rearrange the data codewords to their original order - int[] indices = new int[blockCount]; - int blockIndex = 0; - int failsNumber = 0; - for (int i = 0; i < symbol.CodewordCount && failsNumber= symbol.DataCodewordCount) - { - if (indices[blockIndex] < blocks[blockIndex].Length ) - blocks[blockIndex][indices[blockIndex]] = symbol.RawCodewords[i]; - ++indices[blockIndex]; - ++i; - } - else failsNumber++; - - blockIndex = (blockIndex + 1) % blockCount; - } - - if (failsNumber >= blockCount) - { - //throw new Exception("ERROR: wrong QR parameters table"); - return null; - } - - - // attempt to correct errors - float meanConfidence = 0F, conf; - for (int i = 0; i < blockCount; ++i) - { - ReedSolomon rs = new ReedSolomon(blocks[i], blockInfo[i][1], 8, 285, 0); - rs.Correct(out conf); - - if (!rs.CorrectionSucceeded) - return null; - - //RS rs2 = new RS(new GF(2, 8, 285), blocks[i], blockInfo[i][1],true); - //rs2.correct(); - - meanConfidence += conf; - - blocks[i] = rs.CorrectedData; - } - confidence = meanConfidence / (float)blockCount; - - BitArray dataStream = new BitArray(symbol.DataCodewordCount * 8); - for (int di = 0, bi = 0; bi < blockCount; ++bi) - { - for (int i = 0; i < blockInfo[bi][0]; ++i) - { - for (int b = 7; b >= 0; --b) - { - dataStream[di++] = (blocks[bi][i] & (1 << b)) != 0; - } - } - } - - if (symbol.Version == (int)MicroQRVersion.M1 || symbol.Version == (int)MicroQRVersion.M3) - { - dataStream.Length -= 4; - } - return dataStream; - } - - ABarCodeData[] DecodeData(QRSymbol symbol, BitArray dataStream) - { - ABarCodeData[] ddsData = null; - ArrayList data = new ArrayList(); - int index = 0; - - if (symbol.Version == (int) MicroQRVersion.M1) - { - ddsData = new ABarCodeData[] { SymbolDecoder.DecodeNumeric((int) MicroQRVersion.M1, dataStream, null, ref index) }; - return null; - } - - int indicatorLength; - Hashtable decoderMap; - switch (symbol.Version) - { - case (int) MicroQRVersion.M2: - indicatorLength = 1; - decoderMap = SymbolDecoder.QRDecoderMapM2; - break; - case (int) MicroQRVersion.M3: - indicatorLength = 2; - decoderMap = SymbolDecoder.QRDecoderMapM3; - break; - case (int) MicroQRVersion.M4: - indicatorLength = 3; - decoderMap = SymbolDecoder.QRDecoderMapM4; - break; - default: - indicatorLength = 4; - decoderMap = SymbolDecoder.QRDecoderMap; - break; - } - - Encoding textEncoding = null; - - while (index < dataStream.Length && !QRUtils.IsStreamEnd(dataStream, index, symbol.Version)) - { - int mode = QRUtils.BitSliceValue(dataStream, ref index, indicatorLength); - if (decoderMap.ContainsKey(mode)) - { - Decoder decoder = (Decoder) decoderMap[mode]; - ABarCodeData newData = decoder(symbol.Version, dataStream, textEncoding, ref index); - if (newData != null) - { - if (mode == 7) // ECI - { - textEncoding = EncodingUtils.GetEncodingFromECI(newData.ToString()); - if (textEncoding == null) - data.Add(newData); - } - else - data.Add(newData); - } - } - else if (mode == 0) break; //terminator - } - - ddsData = new ABarCodeData[data.Count]; - data.CopyTo(ddsData); - return ddsData; - } - } -#endif - -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRScaner.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRScaner.cs deleted file mode 100644 index 755e6ef0..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRScaner.cs +++ /dev/null @@ -1,273 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using SkiaSharp; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.QR -{ - -#if !OLD_QRReader - -#if CORE_DEV - public -#else - internal -#endif - partial class QRReader - { - /// - /// Need to recognize mirrored QR codes? - /// - public bool CheckMirrors { get; set; } = true; - - //Scan image rows main step - protected int scanRowStep = 1; - - //object that holds the bw image + methods to sample, follow vertices,... - ImageScaner scan; - - //list of found QR and QRMicro codes found and correctly decoded. - LinkedList candidates; - - FoundBarcode[] Scan() - { - scan = new ImageScaner(BWImage); - candidates = new LinkedList(); - -#if DEBUG_IMAGE - //BWImage.GetAsBitmap().Save(@"out.png"); -#endif - //to cache rows - var rows = new XBitArray[height]; - for (int y = 0; y < height; y += scanRowStep) - rows[y] = BWImage.GetRow(y); - - bool isMirroredFound = false; - - //scan - Scan(rows, out isMirroredFound); - - //check mirrors - //If some barcodes are mirrored => reverse rows and try scan again - if (CheckMirrors && isMirroredFound) - { - //reverse rows - for (int i = 0; i < rows.Length; i++) - rows[i].ReverseMe(); - - //reset columns for reverse rows - BWImage.ResetColumns(); - - //mark no mirrored candidates - foreach (var c in candidates) - c.Reversed = true; - - //repeat scan for mirrored - Scan(rows, out isMirroredFound); - - //reverse mirrored candidates - foreach (BarCodeRegion c in candidates) - { - if (!c.Reversed) - { - //reverse candidate - c.A = ReverseX(c.A, scan.Width); - c.B = ReverseX(c.B, scan.Width); - c.C = ReverseX(c.C, scan.Width); - c.D = ReverseX(c.D, scan.Width); - } - else - { - c.Reversed = false; - } - } - } - - // - var result = new List(); - foreach (BarCodeRegion c in candidates) - { - //skip duplicates - if (AlreadyExists(c)) - continue; - - FoundBarcode foundBarcode = new FoundBarcode(); - foundBarcode.BarcodeFormat = SymbologyType.QRCode; - - StringBuilder data = new StringBuilder(); - if (c.Data!=null) - foreach (ABarCodeData d in c.Data) - { - string s=d.ToString(); - data.Append(s); - if (s.StartsWith("]Q2\\MI")) - { - int pos1=s.IndexOf("\\MO1"); - int pos2=s.IndexOf("\\MF001\\MY"); - foundBarcode.StructureAppendIndex=Convert.ToInt32(s.Substring(6,pos1-6)); - foundBarcode.StructureAppendCount=Convert.ToInt32(s.Substring(pos1+4,pos2-(pos1+4))); - } - } - foundBarcode.Value = data.ToString(); - foundBarcode.Color = SKColors.Blue; - foundBarcode.Polygon = new SKPointI[5] { c.A, c.B, c.D, c.C, c.A }; - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(foundBarcode.Polygon, pointTypes); - //foundBarcode.Rect = Rectangle.Round(path.GetBounds()); - foundBarcode.Rect = Utils.DrawPath(foundBarcode.Polygon); - foundBarcode.Confidence = c.Confidence; - result.Add(foundBarcode); - } - - //sort by coordinates - result.Sort((r1, r2) => - { - var p1 = r1.Polygon[0]; - var p2 = r2.Polygon[0]; - var res = p1.Y.CompareTo(p2.Y); - if (res == 0) - res = p1.X.CompareTo(p2.X); - return res; - }); - - if (this.mergePartialBarcodes) - { - //group partial barcodes by their count - var mergedResults = new List(); - SortedDictionary structureAppend = new SortedDictionary(); - foreach (FoundBarcode f in result) - if (f.StructureAppendCount!=-1) - { - if (!structureAppend.ContainsKey(f.StructureAppendCount)) - { - structureAppend.Add(f.StructureAppendCount, new FoundBarcode[f.StructureAppendCount]); - } - if (f.StructureAppendIndex >= 1 && f.StructureAppendIndex <= f.StructureAppendCount) - { - structureAppend[f.StructureAppendCount][f.StructureAppendIndex - 1] = f; - } - } - else - { - mergedResults.Add(f); - } - - foreach(FoundBarcode[] mergedResult in structureAppend.Values) { - bool hasAllParts = true; - foreach (FoundBarcode f in mergedResult) if (f == null) hasAllParts = false; - if (hasAllParts) - { - FoundBarcode m = new FoundBarcode(); - int minX=Int32.MaxValue, minY=Int32.MaxValue, maxX=Int32.MinValue, maxY=Int32.MinValue; - m.Value = ""; - m.Confidence = 1f; - foreach (FoundBarcode f in mergedResult) - { - int pos2 = f.Value.IndexOf("\\MF001\\MY"); - m.Value += f.Value.Substring(pos2+9); - this.updateMinMaxPolygon(f.Polygon, ref minX, ref minY, ref maxX, ref maxY); - m.Confidence *= f.Confidence; - } - m.Polygon = new SKPointI[5] { new SKPointI(minX, minY), new SKPointI(maxX, minY), new SKPointI(maxX, maxY), new SKPointI(minX, maxY), new SKPointI(minX, minY) }; - m.Color = SKColors.Blue; - mergedResults.Add(m); - } - else - { - foreach (FoundBarcode f in mergedResult) if (f!=null) mergedResults.Add(f); - } - } - - result = mergedResults; - } - return (FoundBarcode[])result.ToArray(); - } - - private void Scan(XBitArray[] rows, out bool isMirroredFound) - { - var mirroredFound = false; - - var parallelSupported = BWImage.IsParallelSupported; -#if DEBUG - //parallelSupported = false; -#endif - - var maxIndexY = height / scanRowStep; - var minPartsize = 30; - var threadCount = -1; - - if (!parallelSupported) - { - threadCount = 1; - minPartsize = int.MaxValue; - } - - //main loop to scan horizontal line - Parallel.For(0, maxIndexY, threadCount, minPartsize, (part) => - { - var rowScanner = new QRReaderRow(scan, BWImage, candidates, DefaultEncoding == Encoding ? null : Encoding); - - for (int iY = Math.Max(0, part.From - 5); iY < part.To; iY++) - { - if (ExpectedNumberOfBarcodes <= 0 || candidates.Count < ExpectedNumberOfBarcodes) - { - var y = iY * scanRowStep; - rowScanner.ScanRow(y, rows[y]); - } - // timeout check - if (IsTimeout()) - break; - } - if (rowScanner.IsMirrored) - mirroredFound = true; - } - ); - - // check for timeout - if (IsTimeout()) - throw new SymbologyReader2DTimeOutException(); - - isMirroredFound = mirroredFound; - } - - private MyPointF ReverseX(MyPointF p, int scanWidth) - { - p.X = scanWidth - p.X - 1; - return p; - } - - void updateMinMaxPolygon(SKPointI[] p, ref int minX, ref int minY, ref int maxX, ref int maxY) - { - foreach (SKPointI q in p) - { - if (minX > q.X) minX = q.X; - if (minY > q.Y) minY = q.Y; - if (maxX < q.X) maxX = q.X; - if (maxY < q.Y) maxY = q.Y; - } - } - - bool AlreadyExists(BarCodeRegion barcode) - { - var node = candidates.First; - while (node != null) - { - if (node.Value == barcode) - break; - - if (node.Value.In((barcode.A + barcode.D) / 2)) - return true; - - node = node.Next; - } - - return false; - } - } -#endif - -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRScaner_old.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRScaner_old.cs deleted file mode 100644 index 36215ccc..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRScaner_old.cs +++ /dev/null @@ -1,1297 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using SkiaSharp; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.QR -{ -#if OLD_QRReader - -#if CORE_DEV - public -#else - internal -#endif - partial class QRReader - { - //Scan image rows main step - protected int scanRowStep = 1; - - - //object that holds the bw image + methods to sample, follow vertices,... - ImageScaner scan; - //list of found QR and QRMicro codes found and correctly decoded. - LinkedList candidates; - //list of found and rejected finders, and QR codes to avoid exploring twice the same barcode - LinkedList exclusion; - //objects to find finder patterns. PatternFinder mantains state, so a patternfinder can not be reused - //until the scan is completed. patternFinder is used in the main scan loop (scanning horizontal lines). - //When a finder is found, the second patterFinder2 is used to find the other two finders of the QRcode. - //Then the main scan can be resumed (since patternFinder is not lost). - - PatternFinderNoiseRow patternFinder, crossFinder; - PatternFinderNoise patternFinder2, crossFinder2; - PatternFinderNoise smallPatternFinder2, smallCrossFinder2; - LinkedList foundPatterns; - LinkedList removedPatterns; - - FoundBarcode[] Scan() - { - scan = new ImageScaner(BWImage); - patternFinder = new PatternFinderNoiseRow(QRFinder.finder,true, true, 2); - crossFinder = new PatternFinderNoiseRow(QRFinder.finder, true, true, 2, patternFinder.Hash); - patternFinder2 = new PatternFinderNoise(BWImage, QRFinder.finder[0], true, 2, patternFinder.Hash); //both share the same cache to speed up detection. - crossFinder2 = new PatternFinderNoise(BWImage, QRFinder.finder[0], true, 2, patternFinder.Hash); //both share the same cache to speed up detection. - smallPatternFinder2 = new PatternFinderNoise(BWImage, QRFinder.smallFinder[0], false, 2); - smallCrossFinder2 = new PatternFinderNoise(BWImage, QRFinder.smallFinder[0], false, 2, smallPatternFinder2.Hash); - candidates = new LinkedList(); - exclusion = new LinkedList(); - foundPatterns = new LinkedList(); - - -#if DEBUG_IMAGE - // bwSourceImage.GetAsBitmap().Save(@"out.png"); -#endif - //main loop to scan horizontal lines - for (int y = 0; y < height && (ExpectedNumberOfBarcodes <= 0 || candidates.Count < ExpectedNumberOfBarcodes); y += scanRowStep) - { - ScanRow(y, BWImage.GetRow(y)); - // timeout check - if (IsTimeout()) - break; - } - - ArrayList result = new ArrayList(); - foreach (BarCodeRegion c in candidates) - { - FoundBarcode foundBarcode = new FoundBarcode(); - foundBarcode.BarcodeFormat = SymbologyType.QRCode; - - String data = ""; - if (c.Data!=null) foreach (ABarCodeData d in c.Data) { - string s=d.ToString(); - data += s; - if (s.StartsWith("]Q2\\MI")) { - int pos1=s.IndexOf("\\MO1"); - int pos2=s.IndexOf("\\MF001\\MY"); - foundBarcode.StructureAppendIndex=Convert.ToInt32(s.Substring(6,pos1-6)); - foundBarcode.StructureAppendCount=Convert.ToInt32(s.Substring(pos1+4,pos2-(pos1+4))); - } - } - foundBarcode.Value = data; - foundBarcode.Color = Color.Blue; - foundBarcode.Polygon = new SKPointI[5] { c.A, c.B, c.D, c.C, c.A }; - byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - GraphicsPath path = new GraphicsPath(foundBarcode.Polygon, pointTypes); - foundBarcode.Rect = Rectangle.Round(path.GetBounds()); - foundBarcode.Confidence = c.Confidence; - result.Add(foundBarcode); - - } - - if (this.mergePartialBarcodes) - { - //group partial barcodes by their count - ArrayList mergedResults = new ArrayList(); - SortedDictionary structureAppend = new SortedDictionary(); - foreach (FoundBarcode f in result) - if (f.StructureAppendCount!=-1) - { - if (!structureAppend.ContainsKey(f.StructureAppendCount)) - { - structureAppend.Add(f.StructureAppendCount, new FoundBarcode[f.StructureAppendCount]); - } - if (f.StructureAppendIndex >= 1 && f.StructureAppendIndex <= f.StructureAppendCount) - { - structureAppend[f.StructureAppendCount][f.StructureAppendIndex - 1] = f; - } - } - else - { - mergedResults.Add(f); - } - - foreach(FoundBarcode[] mergedResult in structureAppend.Values) { - bool hasAllParts = true; - foreach (FoundBarcode f in mergedResult) if (f == null) hasAllParts = false; - if (hasAllParts) - { - FoundBarcode m = new FoundBarcode(); - int minX=Int32.MaxValue, minY=Int32.MaxValue, maxX=Int32.MinValue, maxY=Int32.MinValue; - m.Value = ""; - m.Confidence = 1f; - foreach (FoundBarcode f in mergedResult) - { - int pos2 = f.Value.IndexOf("\\MF001\\MY"); - m.Value += f.Value.Substring(pos2+9); - this.updateMinMaxPolygon(f.Polygon, ref minX, ref minY, ref maxX, ref maxY); - m.Confidence *= f.Confidence; - } - m.Polygon = new SKPointI[5] { new SKPointI(minX, minY), new SKPointI(maxX, minY), new SKPointI(maxX, maxY), new SKPointI(minX, maxY), new SKPointI(minX, minY) }; - m.Color = Color.Blue; - mergedResults.Add(m); - } - else - { - foreach (FoundBarcode f in mergedResult) if (f!=null) mergedResults.Add(f); - } - } - - result = mergedResults; - } - return (FoundBarcode[])result.ToArray(typeof(FoundBarcode)); - } - - void updateMinMaxPolygon(SKPointI[] p, ref int minX, ref int minY, ref int maxX, ref int maxY) - { - foreach (SKPointI q in p) - { - if (minX > q.X) minX = q.X; - if (minY > q.Y) minY = q.Y; - if (maxX < q.X) maxX = q.X; - if (maxY < q.Y) maxY = q.Y; - } - } - - //Scans a horizontal line looking for finder patterns (1011101). - //For each finder pattern found tries to find the finder. - //If the finder is found, then tries to locate the other two finders of the QR code. - //Thus, uses the patternFinder2 to trace lines (0, 90, 180, 270 degree, 4 directions). If another - //finder is found, traces 2 more perpendicular lines (at 90 and 270 degrees from the incoming direction). - //Foreach valid location (group of 3 finders), the decoding algorithm is executed. Notice that the algorithm - //can find goups of 3 finders comming from different QRcodes, close one from each other. - private void ScanRow(int y, XBitArray row) - { - //expand size to find micro QR - var size = row.Size + 1; - - //look for the finder - patternFinder.NewSearch(row, 0, size, 1, 0); - while (patternFinder.NextPattern()!=null) - { - MyPoint a = new MyPoint(patternFinder.First, y); - MyPoint b = new MyPoint(patternFinder.Last, y); - Pattern p = new Pattern(null, a.X, b.X, y); - LinkedListNode prev = foundPatterns.Find(p); - if (prev != null) - { //pattern already processed the last row - if (prev.Value.y != y) - { - StackedPattern sp = (StackedPattern)prev.Value; - sp.NewRow(a.X, b.X, y); - } - } - else - { //new - StackedPattern sp = new StackedPattern(null, a.X, b.X, y); - foundPatterns.AddLast(sp); - } - } - - //clean old patterns and process them - removedPatterns = Pattern.RemoveOldPatterns(foundPatterns, y); - foreach (Pattern p in removedPatterns) - { - //Debug.WriteLine(p.xIn + " " + p.xEnd); - StackedPattern sp = (StackedPattern)p; - ProcessPattern(sp); - } - } - - private bool ProcessPattern(StackedPattern p) - { - MyPoint a, b; - p.MidPoints(out a, out b); - MyPoint center = (a+b)/2; - - foreach (BarCodeRegion br in candidates) if (br.In(center)) return false; - -#if FIND_PATTERN - MyPoint Y = new MyPoint(0, 1); - SquareFinder f = new SquareFinder(a + Y, b + Y, a, b, 7); - candidates.AddLast(f); -#else - //checks if a vertical pattern crosses the horizontal pattern in the middle - MyPoint pUp, pDown; - QRFactory factory = new QRFactory(); - QRFinder finder = null; - if (SquareFinder.CheckCrossPattern(scan, a, b, center, crossFinder, factory, out pUp, out pDown)) - { - //calculates black - white threshold using the pixels in the pattern - scan.setBWThreshold(a, b); - //tries to create a new finder from horizontal and vertical pattenrs - //finder = (QRFinder)SquareFinder.IsFinder(scan, a, b, center, patternFinder2, crossFinder2, factory); - - finder = (QRFinder)SquareFinder.IsFinder(scan, a, b, pUp, pDown, factory); - if (finder != null) - { - //add finder to the exclusion list, so no more patterns in this area will be processed. - exclusion.AddFirst(finder); -#if FIND_FINDER - QRLocation l = new QRLocation(finder, finder, finder); - candidates.AddLast(l); -#else - //detect finders around the first found finder (at 0, 90, 180, 270 degrees), and - //finders at 90 and 270º from them. Max: 4 * 2 leafs. - //Returns an array of valid triplets of finders. Finders are always normalized (rotated) - //to match a top-left, top-right and bottom-left finders. - ArrayList locations = QRLocation.Scan(scan, patternFinder2, crossFinder2, smallPatternFinder2, smallCrossFinder2, finder); - QRSymbol symbol = null; - BarCodeRegion best = null; - if (locations != null && locations.Count > 0) - { - foreach (QRLocation location in locations) - { -#if FIND_LOCATION - candidates.AddLast(location); - exclusion.AddFirst(location); -#else - int[] columns = HoughScan(location.A, location.B, location.C, location.D, location.Wbl / 7f, location.Hbl / 7f); - int[] rows = HoughScan(location.B, location.D, location.A, location.C, location.Hbl / 7f, location.Wbl / 7f); - int version = (int)Math.Round((float)(columns.Length - 1 - 17) / 4f); //-1 because columns.length has 1 counts transitions, and transitions are modules+1 - if (version < 1) version = 1; else if (version > 44) version = 44; - symbol = new QRSymbol(version); - if (columns.Length == rows.Length) - { - if (symbol.SideModuleCount == columns.Length - 1) - { - scanHough(location, symbol, columns, rows); - BarCodeRegion br = ReadSymbol(symbol, location); - if (br != null && (best == null || best.Confidence < br.Confidence)) { best = br; break; } - } - } - else - { - columns = HoughScan2(location.A, location.B, location.C, location.D, location.Wbl / 7f, location.Hbl / 7f, symbol.SideModuleCount); - rows = HoughScan2(location.B, location.D, location.A, location.C, location.Hbl / 7f, location.Wbl / 7f, symbol.SideModuleCount); - scanHough(location, symbol, columns, rows); - BarCodeRegion br = ReadSymbol(symbol, location); - if (br != null && (best == null || best.Confidence < br.Confidence)) { best = br; break; } - } - - symbol = CountModulesForQRCode(location); - if (symbol != null) - { - scanSimple(symbol, location); - BarCodeRegion br = ReadSymbol(symbol, location); - if (br != null && (best == null || best.Confidence < br.Confidence)) { best = br; break; } - else if (scanAdaptive(symbol, location)) - { - //second pass - br = ReadSymbol(symbol, location); - if (br != null && (best == null || best.Confidence < br.Confidence)) { best = br; break; } - } - } -#endif - } - } - else //Try microQR - { - //try different angles (0, 90, 180, 270) - for (int i = 0; i < 4; i++) - { - symbol = CountModulesForMicroQRCode(scan, finder); - if (symbol != null) - { - //recognize - best = ReadSymbol(symbol, finder); - //calc correct MicroQR rectangle - CalcMicroQRrectangle(finder, symbol); - // - break; - } - //rotate on 90 degree - finder.RightNormal = finder.RightNormal.Rotate((float) (Math.PI / 2)); - finder.DownNormal = finder.DownNormal.Rotate((float) (Math.PI / 2)); - } - } - - if (best != null) - { - candidates.AddFirst(best); - exclusion.AddFirst(best); - } -#endif - } - } -#endif - return false; - } - - private void CalcMicroQRrectangle(QRFinder finder, QRSymbol symbol) - { - var points = new MyPointF[4] { finder.A, finder.B, finder.C, finder.D }; - //find main corner - var axis = (finder.RightNormal + finder.DownNormal); - var O = new MyPointF(-axis.X, -axis.Y) * 100000;//virtual center of coordinates - var min = float.MaxValue; - var corner = finder.A; - foreach (var p in points) - { - var r = (p - O).LengthSq;//distance from center of coordinates - if (r < min)//find point nearest to O - { - corner = p; - min = r; - } - } - //calc ABCD - var modR = finder.ModuleRight.Length * symbol.SideModuleCount; - var modD = finder.ModuleDown.Length * symbol.SideModuleCount; - var right = new MyPointF(finder.RightNormal.X * modR, finder.RightNormal.Y * modR); - var down = new MyPointF(finder.DownNormal.X * modD, finder.DownNormal.Y * modD); - var A = corner + down; - var B = corner + down + right; - var C = corner; - var D = corner + right; - //set ABCD - finder.SetCorners(A, B, C, D); - } - - private void scanHough(QRLocation location, QRSymbol symbol, int[] cols, int[]rows) - { -#if DEBUG_IMAGE - scan.Reset(); -#endif - MyPointF A = location.A, B = location.B, C = location.C, D = location.D; - B.Y += 1; //right down corner - C.X -= 1; //upper left corner - - MyVectorF vdL = C - A; - MyVectorF vdR = D - B; - bool[][] m=symbol.MaskedBitarray; - int lRows=rows[rows.Length-1]-rows[0]; - int lCols=cols[cols.Length-1]-cols[0]; - for (int row = 0; row < symbol.SideModuleCount; row++) - { - float r=((float)(rows[row]+rows[row+1])/2f-(float)rows[0])/(float)lRows; - MyPointF L = A + vdL * r; - MyPointF R = B + vdR * r; - for (int col = 0; col < symbol.SideModuleCount; col++) - { - float c=((float)(cols[col]+cols[col+1])/2f-(float)cols[0])/(float)lCols; - MyPointF p = L * (1f - c) + R * c; - bool isBlack = scan.isBlackSample(p, 0f); - symbol.MaskedBitarray[symbol.SideModuleCount-1-row][col] = isBlack; - } - } -#if DEBUG_IMAGE - scan.Save(@"outSamples.png"); -#endif - } - - - private int[] HoughScan(MyPointF A, MyPointF B, MyPointF C, MyPointF D, float hModuleLength, float vModuleLength) { - MyVectorF vdL = C - A; - MyVectorF vdR = D - B; - float width = (B - A).Length; - float height=(C-A).Length; - int nVModules = (int)Math.Round(height / vModuleLength); - int nHModules = (int)Math.Round(width / hModuleLength); - float incY = 1f / (1f* nVModules); //one sample per row - int MODULE_DISCRETIZATION = 5; - int N=(nHModules+1)*MODULE_DISCRETIZATION; //add half module at the left, and half at the right - int[] h = new int[N]; - MyVectorF vdX = (B - A).Normal; - A = A - vdX * (hModuleLength / 2f); - B = B + vdX * (hModuleLength / 2); - for (float y = incY / 2f; y <= 1f; y += incY) - { - MyPointF L = A + vdL * y; - MyPointF R = B + vdR * y; - Bresenham br = new Bresenham(L, R); - float length=(R-L).Length; - bool isBlack = scan.isBlack(br.Current); - while (!br.End()) - { - if (isBlack == scan.isBlack(br.Current)) { } - else - { //histogram each transition - float x = (br.CurrentF - L).Length / length; //from 0..1 - int iX = (int)Math.Round(x * N); - if (iX < 0) iX = 0; else if (iX >= N) iX = N - 1; - h[iX]++; - isBlack = !isBlack; - } - br.Next(); - } - } - - //find local max around moduleLength*i - /* - LinkedList transitions = new LinkedList(); - int currentModuleLength = h.Length / (nModules+1); - int i = -currentModuleLength / 2; - int first = -1; - while (i < N) - { - int halfModule=currentModuleLength/2; - int x = i +currentModuleLength - halfModule; - int max = -1; - for (int j = 0; j <= currentModuleLength; j++, x++) if (x < N && isMax(h, x)) { max = x; break;} - if (max==-1 ) max=i+currentModuleLength;//if not found, use the same step - if (max < h.Length) - { - if (transitions.Count == 0) first = max; - else currentModuleLength = (max - first) / transitions.Count; - transitions.AddLast(max); - } - i = max; - } - * */ - LinkedList maxs=new LinkedList(); - int lastMax = -1; - for (int i = 0; i < h.Length; i++) if (isMax(h, i)) { maxs.AddLast(i); lastMax = i; } - int[] aMaxs = new int[maxs.Count]; - maxs.CopyTo(aMaxs, 0); - - aMaxs = removeSmallPeaks(h, aMaxs); - - aMaxs = removePeaksWithoutValley(h, aMaxs); - - aMaxs = removePeaksTooClose(h, aMaxs, MODULE_DISCRETIZATION / 2); - - - LinkedList transitions = new LinkedList(); - for (int i = 0; i < aMaxs.Length; i++) - { - if (i>0) - { - int lastPeak=aMaxs[i-1]; - int d = aMaxs[i] - lastPeak; - float lostPeaks = (float)d / (float)MODULE_DISCRETIZATION; - if (lostPeaks > 1.7f) - { - int n = (int)Math.Round(lostPeaks); //n>=2 - float l = (float)d / (float)n; - for (int j = 1; j < n; j++) transitions.AddLast(lastPeak + (int)Math.Round((float)j * l)); //interpolate lost peaks - } - } - transitions.AddLast(aMaxs[i]); - } - - /* - Array.Sort(aMaxs, new InverseComparer(h)); - - int minModuleLength = 3; // MODULE_DISCRETIZATION / 2; //5-->2 - ArrayList transitions = new ArrayList(); - for (int i = 0; i < aMaxs.Length; i++) - { - int pos = canAddNewMax(transitions, aMaxs[i], minModuleLength); - if (pos != -1) transitions.Insert(pos, aMaxs[i]); - }*/ - int[] aTransitions=new int[transitions.Count]; - transitions.CopyTo(aTransitions, 0); - return aTransitions; - } - - private int[] HoughScan2(MyPointF A, MyPointF B, MyPointF C, MyPointF D, float hModuleLength, float vModuleLength, int nModules) - { - MyVectorF vdL = C - A; - MyVectorF vdR = D - B; - float width = (B - A).Length; - float height = (C - A).Length; - int nVModules = (int)Math.Round(height / vModuleLength); - int nHModules = (int)Math.Round(width / hModuleLength); - float incY = 1f / (1f * nVModules); //one sample per row - int MODULE_DISCRETIZATION = 5; - int N = (nHModules + 1) * MODULE_DISCRETIZATION; //add half module at the left, and half at the right - int[] h = new int[N]; - MyVectorF vdX = (B - A).Normal; - A = A - vdX * (hModuleLength / 2f); - B = B + vdX * (hModuleLength / 2); - for (float y = incY / 2f; y <= 1f; y += incY) - { - MyPointF L = A + vdL * y; - MyPointF R = B + vdR * y; - Bresenham br = new Bresenham(L, R); - float length = (R - L).Length; - bool isBlack = scan.isBlack(br.Current); - while (!br.End()) - { - if (isBlack == scan.isBlack(br.Current)) { } - else - { //histogram each transition - float x = (br.CurrentF - L).Length / length; //from 0..1 - int iX = (int)Math.Round(x * N); - if (iX < 0) iX = 0; else if (iX >= N) iX = N - 1; - h[iX]++; - isBlack = !isBlack; - } - br.Next(); - } - } - - int[] aTransitions = new int[nModules + 1]; - aTransitions[0] = findFirstMax(h, 0, 1, MODULE_DISCRETIZATION); - aTransitions[nModules] = findFirstMax(h, h.Length - 1, -1, MODULE_DISCRETIZATION); - float moduleLength = ((float)(aTransitions[nModules] - aTransitions[0])) / ((float)nModules); - float pos = (float)aTransitions[0]; - for (int i = 1; i < nModules; i++) - { - pos += moduleLength; - int estimated = (int)Math.Round(pos); - int prevMax = findFirstMax(h, estimated, -1, MODULE_DISCRETIZATION); - int nextMax = findFirstMax(h, estimated, 1, MODULE_DISCRETIZATION); - if (prevMax - aTransitions[i - 1] < MODULE_DISCRETIZATION / 2) aTransitions[i] = nextMax; - else if (estimated - prevMax < nextMax - estimated) aTransitions[i] = prevMax; - else aTransitions[i] = nextMax; - } - return aTransitions; - } - - int findFirstMax(int[] h, int pos, int inc, int max) - { - int offset = inc < 0 ? 1 : 0; - int p = pos + offset * inc; - while (offset < max && p >= 0 && p < h.Length) - { - if (isMax(h, p)) break; - offset++; - p += inc; - } - return p; - } - - class InverseComparer : IComparer { - private int[] h; - public InverseComparer(int[] h) { this.h = h; } - public int Compare(int x, int y) { return h[y] - h[x]; } - } - - //can we add a new transition x in transitions? return position - private int canAddNewMax(ArrayList transitions, int x, int minModuleLength) - { - if (transitions.Count==0) return 0; //yes, add to 0 - int i = 0; - while (i < transitions.Count && (int)transitions[i] < x) i++; - if (i > 0) //check if i is far enough from the previous one - { - int l = x - (int)transitions[i - 1]; - if (l <= minModuleLength) return -1; - } - if (i =0 && h[x] == h[l]) l--; - - int r = x + 1; - while (r h[l]) && (r == h.Length || h[x] > h[r]); - } - - private int[] removeSmallPeaks(int[] h, int[] peaks) - { - int maxPeak = -1; - for (int i = 0; i < peaks.Length; i++) if (maxPeak == -1 || h[peaks[i]] > h[maxPeak]) maxPeak = peaks[i]; - - int threshold = h[maxPeak] / 4; - LinkedList l = new LinkedList(); - for (int i = 0; i < peaks.Length; i++) - if (h[peaks[i]] > threshold) l.AddLast(peaks[i]); - - int[] filtered = new int[l.Count]; - l.CopyTo(filtered, 0); - return filtered; - } - - private int[] removePeaksWithoutValley(int[] h, int[] peaks) - { - LinkedList l = new LinkedList(); - - //find deeper points between peaks - ArrayList aPeaks=new ArrayList(peaks); - ArrayList depth = new ArrayList(); - for (int i = 0; i < peaks.Length - 1; i++) - { - int current= peaks[i]; - int next=peaks[i+1]; - int min=current; - for (int j = current + 1; j < next; j++) - if (h[j] < h[min]) min = j; - depth.Add(h[min]); - } - - //remove peaks without enough deep around - bool done = false; - while (!done) - { - int found = -1; - for (int i = 0; i < depth.Count && found == -1; i++) - { - int lPeak = h[(int)aPeaks[i]]; - int rPeak = h[(int)aPeaks[i + 1]]; - int d = (int)depth[i]; - if (d > lPeak / 3 || d > rPeak / 3) found = i; - } - if (found != -1) //found a valley not deep enough - { - int lPeak = h[(int)aPeaks[found]]; - int rPeak = h[(int)aPeaks[found + 1]]; - if (lPeak < rPeak) { - aPeaks.RemoveAt(found); - if (found>1 && (int)depth[found] < (int)depth[found - 1]) depth[found - 1] = depth[found]; - depth.RemoveAt(found); - } - else - { - aPeaks.RemoveAt(found+1); - if (found +1 l = new LinkedList(); - - ArrayList aPeaks = new ArrayList(); - aPeaks.Add(peaks[0]); - for (int i = 1; i < peaks.Length; i++) - { - int dist = peaks[i] - peaks[i - 1]; - if (dist > minDist) aPeaks.Add(peaks[i]); - } - - int[] filtered = new int[aPeaks.Count]; - aPeaks.CopyTo(filtered, 0); - return filtered; - } - - - - - - //count modules and extract them for MicroQR - //Uses a regular (no perspective deformation) grid using horizontal and vertical vectors from the finder - private QRSymbol CountModulesForMicroQRCode(ImageScaner scan, QRFinder finder) - { - bool[][] patternUnit = new bool[1][]; - patternUnit[0] = new bool[2]; - - Grid grid = new Grid( finder.Center(), finder.RightNormal * finder.Width / 7F, finder.DownNormal * finder.Height/7F); - - // horizontal checks - int vx = 0; - for (; vx < 5; ++vx) - { - try - { - grid.ExtractPointsRegular(scan, patternUnit, new MyPoint(4 + 2 * vx, -3), new MyPoint(0, 0), 2, 1); - if (!(!patternUnit[0][0] && patternUnit[0][1])) // test for alternating timing pattern - { - break; - } - } - catch (Exception) - { - break; - } - } - - // vertical checks - patternUnit = new bool[2][]; - patternUnit[0] = new bool[1]; - patternUnit[1] = new bool[1]; - int vy = 0; - for (; vy < 5; ++vy) - { - try - { - grid.ExtractPointsRegular(scan,patternUnit, new MyPoint(-3, 4 + 2 * vy), new MyPoint(0, 0), 1, 2); - if (!(!patternUnit[0][0] && patternUnit[1][0])) // test for alternating timing pattern - { - break; - } - } - catch (Exception) - { - break; - } - } - - int version = Math.Min(vx, vy); - if (version < 2 || version > 5) - { - return null; - } - - QRSymbol symbol= new QRSymbol(39 + version); - int cy = symbol.MaskedBitarray.Length; - int cx = cy > 0 ? symbol.MaskedBitarray[0].Length : 0; - grid.ExtractPointsRegular(scan, symbol.MaskedBitarray, new MyPoint(-3, -3), new MyPoint(0, 0), cx, cy); - - return symbol; - } - - - - //estimate module count from distance between finders (roughVersion). - //If roughVersion is >7 then count modules sampling the image. - //Then samples the image locating alignment patterns. The number of sampling - //regions depend on the number of alignment patterns. For each region, an irregular - //sampling is done (allowing perspective deformation). - private QRSymbol CountModulesForQRCode(QRLocation location) - { -#if DEBUG_IMAGE - scan.Reset(); -#endif - QRSymbol symbol; - // determine the symbol version - int roughVersion = (int)Math.Round((location.BaselineLength / location.NominalWidth - 10) / 4); - if (roughVersion < 1) return null; - if (roughVersion < 7) - { - symbol = new QRSymbol(roughVersion); - } - else - { - int fineVersion = QRUtils.ExtractSymbolVersion(scan, location); - if (fineVersion < 0) - { - // let's give it one more try, maybe the image is so bad that we miscalculated the version slightly - if (roughVersion == 7) - { - fineVersion = 6; - } - else fineVersion = roughVersion; - } - if (fineVersion > 40) return null; - symbol = new QRSymbol(fineVersion); - } - - location.NominalWidth = (location.UpperLeft - location.UpperRight).Length / (symbol.SideModuleCount - 7); - location.NominalHeight = (location.UpperLeft - location.BottomLeft).Length / (symbol.SideModuleCount - 7); - if (symbol.patternCoords == null) return null; - int patternsPerSide = symbol.patternCoords.Length; - - //horizontal and vertical directions for each finder - MyVectorF luRight = location.LU.RightNormal * location.LU.Width / 7F; - MyVectorF luDown = location.LU.DownNormal * location.LU.Height / 7F; - MyVectorF ldRight = location.LD.RightNormal * location.LD.Width / 7F; - MyVectorF ldDown = location.LD.DownNormal * location.LD.Height / 7F; - MyVectorF ruRight = location.RU.RightNormal * location.RU.Width / 7F; - MyVectorF ruDown = location.RU.DownNormal * location.RU.Height / 7F; - - //If only one sampling region (no alignment patterns) - if (patternsPerSide != 0) - { - //fill region coordinates (numOfAlignmentPatterns+2)*(numOfAlignmentPatterns+2) - int N = patternsPerSide + 2; - MyPointF[][] align = symbol.align= new MyPointF[N][]; - float[][] moduleW = symbol.moduleW = new float[N][]; - float[][] moduleH = symbol.moduleH = new float[N][]; - for (int y = 0; y < N; y++) - { - align[y] = new MyPointF[N]; - moduleW[y] = new float[N]; - moduleH[y] = new float[N]; - for (int x = 0; x < N; x++) - { - align[y][x] = MyPointF.Empty; - moduleW[y][x] = 0f; - moduleH[y][x] = 0f; - } - } - - //first: alignment points from QRFinders corners - align[0][0] = location.UpperLeft - luRight * 3.5F - luDown * 3.5F; - align[0][1] = location.UpperLeft + luRight * 2.5F - luDown * 3.5F; - align[1][0] = location.UpperLeft - luRight * 3.5F + luDown * 2.5F; - align[1][1] = location.UpperLeft + luRight * 2.5F + luDown * 2.5F; - moduleW[0][0] = moduleW[0][1] = moduleW[1][0] = moduleW[1][1] = location.LU.ModuleWidth; - moduleH[0][0] = moduleH[0][1] = moduleH[1][0] = moduleH[1][1] = location.LU.ModuleHeight; - - align[0][N - 1] = location.UpperRight + ruRight * 3.5F - ruDown * 3.5F; - align[1][N - 1] = location.UpperRight + ruRight * 3.5F + ruDown * 2.5F; - moduleW[0][N - 1] = moduleW[1][N - 1] = location.RU.ModuleWidth; - moduleH[0][N - 1] = moduleH[1][N - 1] = location.RU.ModuleHeight; - - align[N - 1][0] = location.BottomLeft - ldRight * 3.5F + ldDown * 3.5F; - align[N - 1][1] = location.BottomLeft + ldRight * 2.5F + ldDown * 3.5F; - moduleW[N - 1][0] = moduleW[N - 1][1] = location.LD.ModuleWidth; - moduleH[N - 1][0] = moduleH[N - 1][1] = location.LD.ModuleHeight; - - //If the last alignment pattern falls close to the finder it is not draws. - //So, alignment coordinates are computed from the finder coords. - int d = symbol.SideModuleCount - symbol.patternCoords[patternsPerSide - 1]; - if (d <= 11) - { - float dd = (float)(d) - 3.5F; - align[0][N - 2] = location.UpperRight - ruRight * dd - ruDown * 3.5F; - align[1][N - 2] = location.UpperRight - ruRight * dd + ruDown * 2.5F; - moduleW[0][N - 2] = moduleW[1][N - 2] = location.RU.ModuleWidth; - moduleH[0][N - 2] = moduleH[1][N - 2] = location.RU.ModuleHeight; - - align[N - 2][0] = location.BottomLeft - ldRight * 3.5F - ldDown * dd; - align[N - 2][1] = location.BottomLeft + ldRight * 2.5F - ldDown * dd; - moduleW[N - 2][0] = moduleW[N - 2][1] = location.LD.ModuleWidth; - moduleH[N - 2][0] = moduleH[N - 2][1] = location.LD.ModuleHeight; - } - - //second: top border. Adaptative interpolation between the top left and top right finders. - //Module length starts with top-left module length, and ends with top-right module length. - for (int i = 1; i < patternsPerSide - 1; i++) - { - MyPointF fromLeft = align[0][0] + luRight * symbol.patternCoords[i]; - MyPointF fromRight = align[0][N - 1] - ruRight * (symbol.SideModuleCount - 1 - symbol.patternCoords[i]); - float rightFactor = (float)symbol.patternCoords[i] / (float)(symbol.SideModuleCount - 1); - float leftFactor = 1F - rightFactor; - rightFactor *= rightFactor; - leftFactor *= leftFactor; - MyPointF mean = (fromLeft * leftFactor + fromRight * rightFactor) / (leftFactor + rightFactor); - align[0][1 + i] = mean; - moduleW[0][1 + i] = (moduleW[0][0] * leftFactor + moduleW[0][N - 1] * rightFactor) / (leftFactor + rightFactor); - moduleH[0][1 + i] = (moduleW[0][0] * leftFactor + moduleW[0][N - 1] * rightFactor) / (leftFactor + rightFactor); - } - - //third: left border. Also adaptative interpolation between top-left and bottom-left. - for (int i = 1; i < patternsPerSide - 1; i++) - { - MyPointF fromTop = align[0][0] + luDown * symbol.patternCoords[i]; - MyPointF fromBottom = align[N - 1][0] - ldDown * (symbol.SideModuleCount - 1 - symbol.patternCoords[i]); - float bottomFactor = (float)symbol.patternCoords[i] / (float)(symbol.SideModuleCount - 1); - float topFactor = 1F - bottomFactor; - bottomFactor *= bottomFactor; - topFactor *= topFactor; - MyPointF mean = (fromTop * topFactor + fromBottom * bottomFactor) / (topFactor + bottomFactor); - align[1 + i][0] = mean; - moduleW[1 + i][0] = (moduleW[0][0] * bottomFactor + moduleW[N - 1][0] * topFactor) / (bottomFactor + topFactor); - moduleH[1 + i][0] = (moduleW[0][0] * bottomFactor + moduleW[N - 1][0] * topFactor) / (bottomFactor + topFactor); - } - - //four: find alignment patterns. Main loop to scan each region. If all 4 corners are already - // computed, only sampling is done. If the bottom-right corner is empty, the alignment pattern will - // be found. - for (int y = 1; y < N; y++) - { - for (int x = 1; x < N; x++) - { - if (align[y][x].IsEmpty) - { - MyPointF p = MyPoint.Empty; - if (x > 1 && y > 1) - { - MyVectorF up = align[y - 1][x] - align[y - 2][x]; - MyVectorF left = align[y][x - 1] - align[y][x - 2]; - if (up.Length > 15f && left.Length > 15f) //otherwise use default calculation - if (x < N - 1 && y < N - 1) //mid align patterns - { - Regression v = new Regression(align[y-2][x]); - v.AddPointL(align[y - 2][x]); - v.AddPointL(align[y - 1][x]); - Regression h = new Regression(align[y][x-2]); - h.AddPointL(align[y][x - 2]); - h.AddPointL(align[y][x - 1]); - RegressionLine vl = v.LineL; - RegressionLine hl = h.LineL; - p = vl.Intersection(hl); - } - else if (x < N - 1) //bottom (y==N-1) - { - MyVectorF lu = align[y - 1][x - 1] - align[y - 2][x - 1]; - MyVectorF ld = align[y][x - 1] - align[y - 1][x - 1]; - MyVectorF ru = align[y - 1][x] - align[y - 2][x]; - float factor = ld.Length / lu.Length; - p = align[y - 1][x] + ru * factor; - } - else //left - { - MyVectorF ul = align[y - 1][x - 1] - align[y - 1][x - 2]; - MyVectorF ur = align[y - 1][x] - align[y - 1][x - 1]; - MyVectorF dl = align[y][x - 1] - align[y][x - 2]; - float factor = ur.Length / ul.Length; - p = align[y][x - 1] + dl * factor; - } - } - if (p.IsEmpty) - { - //first aproximation assuming no deformation - p = align[y][x - 1] + (align[y - 1][x] - align[y - 1][x - 1]); - } - align[y][x] = p; - moduleW[y][x] = (moduleW[y][x - 1] + moduleW[y - 1][x]) / 2f; //by now just the mean of previous ones - moduleH[y][x] = (moduleH[y][x - 1] + moduleH[y - 1][x]) / 2f; - - - //Only in a non skewed barcodes, alignment pattern falls in p. - //This loop tries to find the alignment patterns near to p using - //neibourhood array (the displacement from p) - if (x != N - 1 && y != N - 1) - { - MyPointF p0 = p; - float width = location.NominalWidth * 3F; - MyPoint[] neibourhood = new MyPoint[] { new MyPoint(0, 0), new MyPoint(1, 0), new MyPoint(-1, 0), new MyPoint(0, 1), new MyPoint(0, -1), - new MyPoint(2, 0), new MyPoint(-2, 0), new MyPoint(0, 2), new MyPoint(0, -2)}; - foreach (MyPoint n in neibourhood) - { - float w, h; - if (FindAlignmentPattern(location, p0, n, width, out p, out w, out h)) - { - //correct interpoled borders, in case - if (y == 1) align[0][x] += (p - p0); - if (x == 1) align[y][0] += (p - p0); - align[y][x] = p; - //moduleW[y][x] = w; - //moduleH[y][x] = h; - break; - } - } - } - } - } - } - location.B=align[N-1][N-1]; - } - return symbol; - } - - - bool scanSimple(QRSymbol symbol, QRLocation location) - { - int patternsPerSide = symbol.patternCoords.Length; - - //If only one sampling region (no alignment patterns) - if (patternsPerSide == 0) - { - NotUniformGrid grid = new NotUniformGrid(symbol.SideModuleCount, symbol.SideModuleCount, 7, location.LU, location.LD, location.RU); - grid.ExtractPoints(scan, symbol.MaskedBitarray); - } - else - { - int N = patternsPerSide + 2; - //scan barcode - int prevRow = 0; - for (int y = 1; y < N; y++) - { - int row = y <= patternsPerSide ? symbol.patternCoords[y - 1] : symbol.SideModuleCount; - int prevCol = 0; - for (int x = 1; x < N; x++) - { - int col = x <= patternsPerSide ? symbol.patternCoords[x - 1] : symbol.SideModuleCount; - //Once the alignment pattern is found, sample the image using these 4 corners - MyPointF[][] align = symbol.align; - Grid grid = new Grid(col - prevCol, row - prevRow, align[y - 1][x - 1], align[y][x - 1], align[y - 1][x], align[y][x], true); - grid.ExtractPoints(scan, symbol.MaskedBitarray, new MyPoint(0, 0), - new MyPoint(prevCol, prevRow), col - prevCol, row - prevRow); -#if DEBUG_IMAGE - //scan.Save(@"outSamples.png"); -#endif - prevCol = col; - } - prevRow = row; - } - } -#if DEBUG_IMAGE - scan.Save(@"outSamples.png"); -#endif - return true; - } - - bool scanAdaptive(QRSymbol symbol, QRLocation location) - { - int patternsPerSide = symbol.patternCoords.Length; -#if DEBUG_IMAGE - scan.Reset(); -#endif - - //If only one sampling region (no alignment patterns) - if (patternsPerSide != 0) - { - for (int i = 0; i < symbol.MaskedBitarray.Length; i++) - for (int j = 0; j < symbol.MaskedBitarray[i].Length; j++) - symbol.MaskedBitarray[i][j] = false; - - int N = patternsPerSide + 2; - //scan barcode - int prevRow = 0; - for (int y = 1; y < N; y++) - { - int row = y <= patternsPerSide ? symbol.patternCoords[y - 1] : symbol.SideModuleCount; - int prevCol = 0; - for (int x = 1; x < N; x++) - { - int col = x <= patternsPerSide ? symbol.patternCoords[x - 1] : symbol.SideModuleCount; - //Once the alignment pattern is found, sample the image using these 4 corners - //Grid grid = new Grid(col - prevCol, row - prevRow, align[y-1][x-1], align[y][x-1], align[y-1][x], align[y][x], true); - //grid.ExtractPoints(scan, symbol.MaskedBitarray, new MyPoint(0, 0), - // new MyPoint(prevCol, prevRow), col - prevCol, row - prevRow); - MyPointF[][] align = symbol.align; - float[][] moduleW = symbol.moduleW; - float[][] moduleH = symbol.moduleH; - AdaptiveGrid grid = new AdaptiveGrid(col - prevCol, row - prevRow, - align[y - 1][x - 1], align[y][x - 1], align[y - 1][x], align[y][x], - moduleW[y - 1][x - 1], moduleW[y][x - 1], moduleW[y - 1][x], moduleW[y][x], - moduleH[y - 1][x - 1], moduleH[y][x - 1], moduleH[y - 1][x], moduleH[y][x]); - grid.ExtractPoints(scan, symbol.MaskedBitarray, new MyPoint(prevCol, prevRow)); -#if DEBUG_IMAGE - //scan.Save(@"outSamples.png"); -#endif - prevCol = col; - } - prevRow = row; - } -#if DEBUG_IMAGE - scan.Save(@"outSamples.png"); -#endif - return true; - } - return false; - } - - - - - - //Starting from the estimated center of the alignment pattern (p0+offset), finds the next transition - //from white to black at left, right, up and down. Usually, the center of the alignment pattern - //produces an error in one direction, vertical or horizontal. Calculate h and v distances of the - //found limits and use the bigger one to recalculate the other 2. - bool FindAlignmentPattern(QRLocation location, MyPointF p0, MyPoint offset, float width, out MyPointF corrected, out float W, out float H) - { - //adjunt alignment pattern center - MyPointF p = p0 + location.LeftNormal * (location.NominalWidth * (0.5F + (float)offset.X/2f)) + - location.DownNormal * (location.NominalHeight * (0.5F + (float)offset.Y/2f)); - MyPoint ip=new MyPoint((int)Math.Truncate(p.X),(int)Math.Truncate(p.Y)); - int halfModuleLength = (int)Math.Round(width / 3F /2F)-1; - if (halfModuleLength < 0) halfModuleLength = 0; - - MyPointF up = scan.NextBlack(ip, -location.DownNormal, halfModuleLength); - MyPointF down = scan.NextBlack(ip, location.DownNormal, halfModuleLength); - MyPointF left = scan.NextBlack(ip, -location.LeftNormal, halfModuleLength); - MyPointF right = scan.NextBlack(ip, location.LeftNormal, halfModuleLength); - float h = (left - right).Length; - float v = (up - down).Length; - - if (!Calc.Around(h / v, 1.0F, 0.3F) || !Calc.Around(h / width, 1.0F, 0.2F)) - if (h > v) - { - p = left * (5F / 6F) + right * (1F / 6F); - ip = new MyPoint((int)Math.Truncate(p.X), (int)Math.Truncate(p.Y)); - up = scan.NextBlack(ip, -location.DownNormal, halfModuleLength); - down = scan.NextBlack(ip, location.DownNormal, halfModuleLength); - v = (up - down).Length; - } - else - { - p = up * (5F / 6F) + down * (1F / 6F); - ip = new MyPoint((int)Math.Truncate(p.X), (int)Math.Truncate(p.Y)); - left = scan.NextBlack(ip, -location.LeftNormal, halfModuleLength); - right = scan.NextBlack(ip, location.LeftNormal, halfModuleLength); - h = (left - right).Length; - } - - W = H = 0f; - - //if h and v are regular enough, we consider them as a good alignment pattern and claculate - // the top-left coordinates of the center of the alignment pattern. - if (Calc.Around(h / v, 1.0F, 0.4F) && Calc.Around(h / width, 1.0F, 0.3F)) - { - MyVectorF vdD = (down - up); - p = new MyPointF(0.5F + ip.X, 0.5F + ip.Y); - MyVectorF vdR = (right - left); - float toUp = (p - up).Length; - float toDown = (p - down).Length; - float factor = toUp / (toUp + toDown); - MyPointF LU = left - vdD * factor; - corrected = LU + vdD / 3F + vdR / 3F; - - W = h / 3f; - H = v / 3f; - return true; - } - corrected = MyPointF.Empty; - return false; - } - - BarCodeRegion ReadSymbol(QRSymbol symbol, BarCodeRegion result) - { - if (symbol != null) - { - float confidence = 1F; - BitArray stream = ReadSymbol(symbol, out confidence); - if (stream != null) - { - ABarCodeData[] data = DecodeData(symbol, stream); - if (Encoding != DefaultEncoding) - foreach (ABarCodeData d in data) - if (d is Base256BarCodeData) - (d as Base256BarCodeData).encoding = Encoding; - - if (data != null) - { - result.Data = data; - result.Confidence = confidence; - return result; - } - } - } - return null; - } - - - //method form the old code to create the byte array from array bit, and correct them. - BitArray ReadSymbol(QRSymbol symbol, out float confidence) - { - confidence = 1F; - if (!symbol.EnrichFormatInformation()) return null; - symbol.ReadCodewords(); - int[][] blockInfo = symbol.GenerateECBlockInfo(); - int blockCount = blockInfo.Length; - int[][] blocks = new int[blockCount][]; - for (int i = 0; i < blockCount; i++) - { - blocks[i] = new int[blockInfo[i][0] + blockInfo[i][1]]; - } - - // rearrange the data codewords to their original order - int[] indices = new int[blockCount]; - int blockIndex = 0; - int failsNumber = 0; - for (int i = 0; i < symbol.CodewordCount && failsNumber= symbol.DataCodewordCount) - { - if (indices[blockIndex] < blocks[blockIndex].Length ) - blocks[blockIndex][indices[blockIndex]] = symbol.RawCodewords[i]; - ++indices[blockIndex]; - ++i; - } - else failsNumber++; - - blockIndex = (blockIndex + 1) % blockCount; - } - - if (failsNumber >=blockCount) throw new Exception("ERROR: wrong QR parameters table"); - - - // attempt to correct errors - float meanConfidence = 0F, conf; - for (int i = 0; i < blockCount; ++i) - { - ReedSolomon rs = new ReedSolomon(blocks[i], blockInfo[i][1], 8, 285, 0); - rs.Correct(out conf); - - if (!rs.CorrectionSucceeded) - return null; - - //RS rs2 = new RS(new GF(2, 8, 285), blocks[i], blockInfo[i][1],true); - //rs2.correct(); - - meanConfidence += conf; - - blocks[i] = rs.CorrectedData; - } - confidence = meanConfidence / (float)blockCount; - - BitArray dataStream = new BitArray(symbol.DataCodewordCount * 8); - for (int di = 0, bi = 0; bi < blockCount; ++bi) - { - for (int i = 0; i < blockInfo[bi][0]; ++i) - { - for (int b = 7; b >= 0; --b) - { - dataStream[di++] = (blocks[bi][i] & (1 << b)) != 0; - } - } - } - - if (symbol.Version == (int)MicroQRVersion.M1 || symbol.Version == (int)MicroQRVersion.M3) - { - dataStream.Length -= 4; - } - return dataStream; - } - - ABarCodeData[] DecodeData(QRSymbol symbol, BitArray dataStream) - { - ABarCodeData[] ddsData = null; - ArrayList data = new ArrayList(); - int index = 0; - - if (symbol.Version == (int)MicroQRVersion.M1) - { - ddsData = new ABarCodeData[] { SymbolDecoder.DecodeNumeric((int)MicroQRVersion.M1, dataStream, ref index) }; - return null; - } - - int indicatorLength; - Hashtable decoderMap; - switch (symbol.Version) - { - case (int)MicroQRVersion.M2: - indicatorLength = 1; - decoderMap = SymbolDecoder.QRDecoderMapM2; - break; - case (int)MicroQRVersion.M3: - indicatorLength = 2; - decoderMap = SymbolDecoder.QRDecoderMapM3; - break; - case (int)MicroQRVersion.M4: - indicatorLength = 3; - decoderMap = SymbolDecoder.QRDecoderMapM4; - break; - default: - indicatorLength = 4; - decoderMap = SymbolDecoder.QRDecoderMap; - break; - } - - while (index < dataStream.Length && !QRUtils.IsStreamEnd(dataStream, index, symbol.Version)) - { - int mode = QRUtils.BitSliceValue(dataStream, ref index, indicatorLength); - if (decoderMap.ContainsKey(mode)) - { - Decoder decoder = (Decoder)decoderMap[mode]; - ABarCodeData newData = decoder(symbol.Version, dataStream, ref index); - if (newData != null) - { - data.Add(newData); - } - } - else if (mode == 0) break; //terminator - } - - ddsData = new ABarCodeData[data.Count]; - data.CopyTo(ddsData); - return ddsData; - } - } -#endif - -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRSymbol.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRSymbol.cs deleted file mode 100644 index f542bb20..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRSymbol.cs +++ /dev/null @@ -1,336 +0,0 @@ -using System; - -namespace BarcodeReader.Core.QR -{ - class QRSymbol - { - public readonly bool isMicroQR; - - public readonly int Version; - - public readonly int SideModuleCount; - - public readonly int CodewordCount; - - public int ECCodewordCount; - - public int DataCodewordCount; - - public readonly MyPoint[,] AlignmentPatternCoordinates; - - public readonly int[] patternCoords; - - public readonly bool[][] MaskedBitarray; - - public readonly bool[][] UnmaskedBitarray; - - public readonly int[] RawCodewords; - - private readonly bool[][] exclusionMask; - - public MyPointF[][] align; - public float[][] moduleW; - public float[][] moduleH; - - - public ErrorCorrectionLevel ECLevel; - - public byte MaskCode; - - /// - /// QR code is mirrorred - /// - public bool IsMirrored { get; private set; } - - - public QRSymbol(int version) - { - Version = version; - isMicroQR = Version > 40; - SideModuleCount = isMicroQR ? 9 + (Version - 40) * 2 : 17 + 4 * Version; - CodewordCount = QRUtils.CodewordCount[Version - 1]; - RawCodewords = new int[CodewordCount]; - - if (!isMicroQR) - { - // generate the location of alignment patterns - patternCoords = QRUtils.AlignmentPatternLocations[Version - 1]; - int patternSideCount = patternCoords.Length; - AlignmentPatternCoordinates = new MyPoint[patternSideCount, patternSideCount]; - for (int y = 0; y < patternSideCount; ++y) - { - for (int x = 0; x < patternSideCount; ++x) - { - AlignmentPatternCoordinates[y, x] = new MyPoint(patternCoords[x], patternCoords[y]); - } - } - } - - MaskedBitarray = new bool[SideModuleCount][]; - UnmaskedBitarray = new bool[SideModuleCount][]; - exclusionMask = new bool[SideModuleCount][]; - for (int i = 0; i < SideModuleCount; ++i) - { - MaskedBitarray[i] = new bool[SideModuleCount]; - UnmaskedBitarray[i] = new bool[SideModuleCount]; - exclusionMask[i] = new bool[SideModuleCount]; - } - } - - public bool EnrichFormatInformation() - { - var dist = 0; - var distMirror = 0; - Int16 formatInfo = GetFormatInformation(false, out dist); - GetFormatInformation(true, out distMirror);//try mirrored - - //is it mirrored image ? - if (distMirror < dist && distMirror <= 3) - { - IsMirrored = true; - return false; - } - - if (formatInfo == 0) - { - return false; - } - - if (!isMicroQR) // QR code - { - formatInfo ^= QRUtils.FormatMask; - formatInfo >>= 10; - ECLevel = QRUtils.ErrorCorrectionLevels[(formatInfo >> 3)]; - MaskCode = (byte)(formatInfo & 0x07); - } - else // Micro QR code - { - formatInfo ^= QRUtils.FormatMaskMicro; - formatInfo >>= 10; - ECLevel = QRUtils.MicroErrorCorrectionLevels[(formatInfo >> 2)]; - MaskCode = (byte)(formatInfo & 0x03); - } - - if (Version != (int)MicroQRVersion.M1 && ECLevel == ErrorCorrectionLevel.DetectionOnly) - { - return false; - } - - ECCodewordCount = ECLevel == ErrorCorrectionLevel.DetectionOnly ? 2 : QRUtils.ErrorCorrectionCodewordCount[Version - 1][(int)ECLevel]; - if (ECCodewordCount < 0) return false; - DataCodewordCount = CodewordCount - ECCodewordCount; - - return true; - } - - private Int16 GetFormatInformation(bool mirror, out int distance) - { - MyPoint[] formatInfoLocation = Version <= 40 - ? QRUtils.PrimaryFormatInfoLocation - : QRUtils.MicroFormatInfoLocation; - int[] formatData = Version <= 40 ? QRUtils.FormatData : QRUtils.FormatDataMicro; - int formatInfo = ReadMaskedArray(formatInfoLocation, mirror); - formatInfo = formatData[QRUtils.FindClosestMatchIndex(formatData, formatInfo, out distance)]; - if (distance > 3 && Version <= 40) - { - formatInfoLocation = new MyPoint[15]; - for (int i = 0; i < 8; ++i) - { - formatInfoLocation[i] = new MyPoint(SideModuleCount - 1 - i, 8); - } - for (int i = 0; i < 7; ++i) - { - formatInfoLocation[i + 8] = new MyPoint(8, SideModuleCount - 7 + i); - } - - formatInfo = ReadMaskedArray(formatInfoLocation, mirror); - formatInfo = formatData[QRUtils.FindClosestMatchIndex(formatData, formatInfo, out distance)]; - if (distance > 3) - { - return 0; - } - } - - return (Int16)formatInfo; - } - - private int ReadMaskedArray(MyPoint[] points, bool mirror) - { - int formatInfo = 0; - for (int i = 0; i < 15; ++i) - { - var ii = mirror ? 14 - i : i; - MyPoint bitCoords = points[ii]; - //Console.WriteLine("("+bitCoords.X+","+bitCoords.Y+")->"+MaskedBitarray[bitCoords.Y][bitCoords.X]); - if (MaskedBitarray[bitCoords.Y][bitCoords.X]) - { - formatInfo |= (1 << i); - } - } - - return formatInfo; - } - - private void Unmask() - { - MaskFunction maskFunction = isMicroQR ? QRUtils.MicroMasks[MaskCode] : QRUtils.Masks[MaskCode]; - for (int y = 0; y < SideModuleCount; ++y) - { - for (int x = 0; x < SideModuleCount; ++x) - { - UnmaskedBitarray[y][x] = MaskedBitarray[y][x] ^ maskFunction(x, y); - } - } - } - - public void ReadCodewords() - { - Array.Clear(RawCodewords, 0, RawCodewords.Length); - - Unmask(); - GenerateExclusionMask(); - CodeWalker cw = new CodeWalker(SideModuleCount, exclusionMask, isMicroQR); -#if DEBUG - double[][] moduledata = new double[SideModuleCount][]; - for (int i = 0; i < moduledata.Length; i++) - { - moduledata[i] = new double[SideModuleCount]; - } -#endif - for (int i = 0; i < CodewordCount; ++i) - { - int firstBit = 0; - if (Version == (int)MicroQRVersion.M1 && i == 2 - || Version == (int)MicroQRVersion.M3 && ECLevel == ErrorCorrectionLevel.L && i == 10 - || Version == (int)MicroQRVersion.M3 && ECLevel == ErrorCorrectionLevel.M && i == 8) - { - firstBit = 4; - } - for (int b = 7; b >= firstBit; --b) - { - MyPoint coord = cw.NextFreePosition(); - if (UnmaskedBitarray[coord.Y][coord.X]) - { - RawCodewords[i] |= (1 << b); - } -#if DEBUG - if (coord.X >= 0 && coord.Y >= 0) - moduledata[coord.Y][coord.X] = (i * 90) % 255; -#endif - } - } -#if DEBUG - //Debug.joinBitmap = Common.ImageDataToImage(moduledata); -#endif - } - - private void GenerateExclusionMask() - { - // Upper left locator pattern + primary format info - for (int y = 0; y <= 8; ++y) - { - for (int x = 0; x <= 8; ++x) - { - exclusionMask[y][x] = true; - } - } - - if (isMicroQR) - { - for (int i = 0; i < SideModuleCount; ++i) - { - exclusionMask[0][i] = true; - exclusionMask[i][0] = true; - } - - return; - } - // else: - - // Upper right and bottom left locator patterns + secondary format info - for (int y = 0; y <= 8; ++y) - { - for (int x = SideModuleCount - 8; x < SideModuleCount; ++x) - { - exclusionMask[y][x] = true; - exclusionMask[x][y] = true; - } - } - - // fine version information - if (Version > 6) - { - for (int y = 0; y <= 5; ++y) - { - for (int x = SideModuleCount - 11; x < SideModuleCount - 8; ++x) - { - exclusionMask[y][x] = true; - exclusionMask[x][y] = true; - } - } - } - - // timing pattern - for (int x = 8; x < SideModuleCount - 8; ++x) - { - exclusionMask[6][x] = true; - exclusionMask[x][6] = true; - } - - // alignment patterns - int alignmentCount = AlignmentPatternCoordinates.GetLength(0); - for (int y = 0; y < alignmentCount; ++y) - { - for (int x = 0; x < alignmentCount; ++x) - { - if ((x == 0 && y == 0) || (x == 0 && y == alignmentCount - 1) || (y == 0 && x == alignmentCount - 1)) - { - continue; - } - - MyPoint center = AlignmentPatternCoordinates[y, x]; - for (int cy = center.Y - 2; cy <= center.Y + 2; ++cy) - { - for (int cx = center.X - 2; cx <= center.X + 2; ++cx) - { - exclusionMask[cy][cx] = true; - } - } - } - } - } - - // Returns an info array about Error correction blocks. - // The format is: {{1st block data #, 1st block ec #},{2nd block data #, 2nd block ec #},...} - public int[][] GenerateECBlockInfo() - { - if (Version == (int)MicroQRVersion.M1) - { - return new int[][] { new int[] { 3, 2 } }; - } - int ecBlockCount = QRUtils.ErrorCorrectionBlockCount[Version - 1][(int)ECLevel]; - if (ecBlockCount == 1) - { - return new int[][] { new int[] { DataCodewordCount, ECCodewordCount } }; - } - - int[][] ecInfo = new int[ecBlockCount][]; - int longBlockCount = DataCodewordCount % ecBlockCount; - int shortBlockCount = ecBlockCount - longBlockCount; - int ecsPerBlock = ECCodewordCount / ecBlockCount; - int dataPerBlock = DataCodewordCount / ecBlockCount; - for (int i = 0; i < shortBlockCount; ++i) - { - ecInfo[i] = new int[] { dataPerBlock, ecsPerBlock }; - } - - dataPerBlock++; - for (int i = shortBlockCount; i < ecBlockCount; ++i) - { - ecInfo[i] = new int[] { dataPerBlock, ecsPerBlock }; - } - - return ecInfo; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRUtils.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRUtils.cs deleted file mode 100644 index be67e038..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/QRUtils.cs +++ /dev/null @@ -1,493 +0,0 @@ -using System; -using System.Collections; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.QR -{ - enum ErrorCorrectionLevel - { - L, - M, - Q, - H, - DetectionOnly - } - - enum MicroQRVersion - { - M1 = 41, - M2 = 42, - M3 = 43, - M4 = 44 - } - - delegate bool MaskFunction(int x, int j); - - - - class QRUtils - { - #region Reference data - private static readonly int[] VersionData = new int[] - { - 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, 0x0C762, 0x0D847, - 0x0E60D, 0x0F928, 0x10B78, 0x1145D, 0x12A17, 0x13532, 0x149A6, - 0x15683, 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, 0x1B08E, - 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, 0x209D5, 0x216F0, 0x228BA, - 0x2379F, 0x24B0B, 0x2542E, 0x26A64, 0x27541, 0x28C69 - }; - - public const Int16 FormatMask = 21522; - - public const Int16 FormatMaskMicro = 17477; - - public static readonly ErrorCorrectionLevel[] ErrorCorrectionLevels = new ErrorCorrectionLevel[] - { - ErrorCorrectionLevel.M, - ErrorCorrectionLevel.L, - ErrorCorrectionLevel.H, - ErrorCorrectionLevel.Q - }; - - public static readonly ErrorCorrectionLevel[] MicroErrorCorrectionLevels = new ErrorCorrectionLevel[] - { - ErrorCorrectionLevel.DetectionOnly, - ErrorCorrectionLevel.L, - ErrorCorrectionLevel.M, - ErrorCorrectionLevel.L, - ErrorCorrectionLevel.M, - ErrorCorrectionLevel.L, - ErrorCorrectionLevel.M, - ErrorCorrectionLevel.Q - }; - - public static readonly MyPoint[] MicroFormatInfoLocation = { - new MyPoint(8, 1), new MyPoint(8, 2), - new MyPoint(8, 3), new MyPoint(8, 4), - new MyPoint(8, 5), new MyPoint(8, 6), - new MyPoint(8, 7), new MyPoint(8, 8), - new MyPoint(7, 8), new MyPoint(6, 8), - new MyPoint(5, 8), new MyPoint(4, 8), - new MyPoint(3, 8), new MyPoint(2, 8), - new MyPoint(1, 8) - }; - - public static readonly MyPoint[] PrimaryFormatInfoLocation = { - new MyPoint(8, 0), new MyPoint(8, 1), - new MyPoint(8, 2), new MyPoint(8, 3), - new MyPoint(8, 4), new MyPoint(8, 5), - new MyPoint(8, 7), new MyPoint(8, 8), - new MyPoint(7, 8), new MyPoint(5, 8), - new MyPoint(4, 8), new MyPoint(3, 8), - new MyPoint(2, 8), new MyPoint(1, 8), - new MyPoint(0, 8) - }; - - public static readonly int[] FormatData = new int[] - { - 0x5412, 0x5125, 0x5E7C, 0x5B4B, 0x45F9, 0x40CE, 0x4F97, 0x4AA0, - 0x77C4, 0x72F3, 0x7DAA, 0x789D, 0x662F, 0x6318, 0x6C41, 0x6976, - 0x1689, 0x13BE, 0x1CE7, 0x19D0, 0x0762, 0x0255, 0x0D0C, 0x083B, - 0x355F, 0x3068, 0x3F31, 0x3A06, 0x24B4, 0x2183, 0x2EDA, 0x2BED - }; - - public static readonly int[] FormatDataMicro = new int[] - { - 0x4445, 0x4172, 0x4E2B, 0x4B1C, 0x55AE, 0x5099, 0x5FC0, - 0x5AF7, 0x6793, 0x62A4, 0x6DFD, 0x68CA, 0x7678, 0x734F, - 0x7C16, 0x7921, 0x06DE, 0x03E9, 0x0CB0, 0x0987, 0x1735, - 0x1202, 0x1D5B, 0x186C, 0x2508, 0x203F, 0x2F66, 0x2A51, - 0x34E3 - }; - - public static readonly int[] CodewordCount = new int[] - { - 26, 44, 70, 100, 134, 172, 196, 242, 292, 346, 404, 466, - 532, 581, 655, 733, 815, 901, 991, 1085, 1156, 1258, 1364, - 1474, 1588, 1706, 1828, 1921, 2051, 2185, 2323, 2465, - 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706, 5, 10, 17, 24 - }; - - public static readonly int[][] ErrorCorrectionCodewordCount = new int[][] - { - new int[] {7, 10, 13, 17}, - new int[] {10, 16, 22, 28}, - new int[] {15, 26, 36, 44}, - new int[] {20, 36, 52, 64}, - new int[] {26, 48, 72, 88}, - new int[] {36, 64, 96, 112}, - new int[] {40, 72, 108, 130}, - new int[] {48, 88, 132, 156}, - new int[] {60, 110, 160, 192}, - new int[] {72, 130, 192, 224}, - new int[] {80, 150, 224, 264}, - new int[] {96, 176, 260, 308}, - new int[] {104, 198, 288, 352}, - new int[] {120, 216, 320, 384}, - new int[] {132, 240, 360, 432}, - new int[] {144, 280, 408, 480}, - new int[] {168, 308, 448, 532}, - new int[] {180, 338, 504, 588}, - new int[] {196, 364, 546, 650}, - new int[] {224, 416, 600, 700}, - new int[] {224, 442, 644, 750}, - new int[] {252, 476, 690, 816}, - new int[] {270, 504, 750, 900}, - new int[] {300, 560, 810, 960}, - new int[] {312, 588, 870, 1050}, - new int[] {336, 644, 952, 1110}, - new int[] {360, 700, 1020, 1200}, - new int[] {390, 728, 1050, 1260}, - new int[] {420, 784, 1140, 1350}, - new int[] {450, 812, 1200, 1440}, - new int[] {480, 868, 1290, 1530}, - new int[] {510, 924, 1350, 1620}, - new int[] {540, 980, 1440, 1710}, - new int[] {570, 1036, 1530, 1800}, - new int[] {570, 1064, 1590, 1890}, - new int[] {600, 1120, 1680, 1980}, - new int[] {630, 1204, 1770, 2100}, - new int[] {660, 1260, 1860, 2220}, - new int[] {720, 1316, 1950, 2310}, - new int[] {750, 1372, 2040, 2430}, - // Micro QR data - new int[] {-1, -1, -1, 2}, - new int[] {5, 6, -1, -1}, - new int[] {6, 8, -1, -1}, - new int[] {8, 10, 14, -1} - }; - - public static readonly int[][] ErrorCorrectionBlockCount = new int[][] - { - new int[] {1, 1, 1, 1}, - new int[] {1, 1, 1, 1}, new int[] {1, 1, 2, 2}, - new int[] {1, 2, 2, 4}, new int[] {1, 2, 4, 4}, - new int[] {2, 4, 4, 4}, - new int[] {2, 4, 6, 5}, new int[] {2, 4, 6, 6}, - new int[] {2, 5, 8, 8}, new int[] {4, 5, 8, 8}, - new int[] {4, 5, 8, 11}, new int[] {4, 8, 10, 11}, - new int[] {4, 9, 12, 16}, - new int[] {4, 9, 16, 16}, new int[] {6, 10, 12, 18}, - new int[] {6, 10, 17, 16}, new int[] {6, 11, 16, 19}, - new int[] {6, 13, 18, 21}, new int[] {7, 14, 21, 25}, - new int[] {8, 16, 20, 25}, - new int[] {8, 17, 23, 25}, new int[] {9, 17, 23, 34}, - new int[] {9, 18, 25, 30}, new int[] {10, 20, 27, 32}, - new int[] {12, 21, 29, 35}, new int[] {12, 23, 34, 37}, - new int[] {12, 25, 34, 40}, - new int[] {13, 26, 35, 42}, new int[] {14, 28, 38, 45}, - new int[] {15, 29, 40, 48}, new int[] {16, 31, 43, 51}, - new int[] {17, 33, 45, 54}, new int[] {18, 35, 48, 57}, - new int[] {19, 37, 51, 60}, - new int[] {19, 38, 53, 63}, new int[] {20, 40, 56, 66}, - new int[] {21, 43, 59, 70}, new int[] {22, 45, 62, 74}, - new int[] {24, 47, 65, 77}, new int[] {25, 49, 68, 81}, - new int[] {1, 1, 1, 1}, new int[] {1, 1, 1, 1}, - new int[] {1, 1, 1, 1}, new int[] {1, 1, 1, 1} - }; - - public static readonly int[] QRLocatorPattern = new int[] { 1, 1, 3, 1, 1 }; - - public static readonly int[] QRAlignmentPattern = new int[] { 1, 1, 1 }; - - public static readonly int[][] AlignmentPatternLocations = new int[][] - { - new int[] {}, - new int[] {6, 18}, - new int[] {6, 22}, - new int[] {6, 26}, - new int[] {6, 30}, - new int[] {6, 34}, - new int[] {6, 22, 38}, - new int[] {6, 24, 42}, - new int[] {6, 26, 46}, - new int[] {6, 28, 50}, - new int[] {6, 30, 54}, - new int[] {6, 32, 58}, - new int[] {6, 34, 62}, - new int[] {6, 26, 46, 66}, - new int[] {6, 26, 48, 70}, - new int[] {6, 26, 50, 74}, - new int[] {6, 30, 54, 78}, - new int[] {6, 30, 56, 82}, - new int[] {6, 30, 58, 86}, - new int[] {6, 34, 62, 90}, - new int[] {6, 28, 50, 72, 94}, - new int[] {6, 26, 50, 74, 98}, - new int[] {6, 30, 54, 78, 102}, - new int[] {6, 28, 54, 80, 106}, - new int[] {6, 32, 58, 84, 110}, - new int[] {6, 30, 58, 86, 114}, - new int[] {6, 34, 62, 90, 118}, - new int[] {6, 26, 50, 74, 98, 122}, - new int[] {6, 30, 54, 78, 102, 126}, - new int[] {6, 26, 52, 78, 104, 130}, - new int[] {6, 30, 56, 82, 108, 134}, - new int[] {6, 34, 60, 86, 112, 138}, - new int[] {6, 30, 58, 86, 114, 142}, - new int[] {6, 34, 62, 90, 118, 146}, - new int[] {6, 30, 54, 78, 102, 126, 150}, - new int[] {6, 24, 50, 76, 102, 128, 154}, - new int[] {6, 28, 54, 80, 106, 132, 158}, - new int[] {6, 32, 58, 84, 110, 136, 162}, - new int[] {6, 26, 54, 82, 110, 138, 166}, - new int[] {6, 30, 58, 86, 114, 142, 170} - }; - - public static readonly MaskFunction[] Masks = { - new MaskFunction(Mask000), new MaskFunction(Mask001), - new MaskFunction(Mask010), new MaskFunction(Mask011), - new MaskFunction(Mask100), new MaskFunction(Mask101), - new MaskFunction(Mask110), new MaskFunction(Mask111) - }; - - public static readonly MaskFunction[] MicroMasks = { - new MaskFunction(Mask001), new MaskFunction(Mask100), - new MaskFunction(Mask110), new MaskFunction(Mask111) - }; - - private static bool Mask000(int x, int y) - { - return (y + x) % 2 == 0; - } - - private static bool Mask001(int x, int y) - { - return y % 2 == 0; - } - - private static bool Mask010(int x, int y) - { - return x % 3 == 0; - } - - private static bool Mask011(int x, int y) - { - return (y + x) % 3 == 0; - } - - private static bool Mask100(int x, int y) - { - return (y / 2 + x / 3) % 2 == 0; - } - - private static bool Mask101(int x, int y) - { - return y * x % 2 + y * x % 3 == 0; - } - - private static bool Mask110(int x, int y) - { - return (y * x % 2 + y * x % 3) % 2 == 0; - } - - private static bool Mask111(int x, int y) - { - return ((y + x) % 2 + y * x % 3) % 2 == 0; - } - - #endregion - public static int ExtractSymbolVersion(ImageScaner scan, QRLocation location) - { - float nominalWur = location.Wur / 7.0F; - float nominalHur = location.Hur / 7.0F; - int versionValue = ExtractVersionBits(scan, location.UpperRight, location.LeftNormal * nominalWur, - location.DownNormal * nominalHur, false); - int distance; - int index = FindClosestMatchIndex(VersionData, versionValue, out distance); - if (distance < 4) - { - return index + 7; - } - - versionValue = ExtractVersionBits(scan, location.BottomLeft, location.DownNormal * nominalWur, - location.LeftNormal * nominalWur, false); - index = FindClosestMatchIndex(VersionData, versionValue, out distance); - if (distance < 4) - { - return index + 7; - } - -#if QR_SUPPORT_NONSTANDARD - versionValue = ExtractVersionBits(scan, location.UpperRight, location.LeftNormal * nominalWur , - location.DownNormal * nominalHur, true); - index = FindClosestMatchIndex(VersionData, versionValue, out distance); - if (distance < 4) - { - return index + 7; - } - versionValue = ExtractVersionBits(scan, location.BottomLeft, location.DownNormal * nominalWur, - location.LeftNormal * nominalWur,true); - index = FindClosestMatchIndex(VersionData, versionValue, out distance); - if (distance < 4) - { - return index + 7; - } -#endif - return -1; - } - - private static int ExtractVersionBits(ImageScaner scan, MyPointF center, MyVectorF perpendicularNorm, MyVectorF parallelNorm, bool alternativeDirection) - { - Grid grid = new Grid(center, parallelNorm, perpendicularNorm); - - bool[][] versionBits = new bool[3][]; - for (int i = 0; i < 3; ++i) - { - versionBits[i] = new bool[6]; - } - - grid.ExtractPointsRegular(scan, versionBits, new MyPoint(-3,-7), new MyPoint(0,0),6,3); - int bitIndex = alternativeDirection ? 17 : 0; - int version = 0; - if (alternativeDirection) - { - for (int y = 0; y < 3; ++y) - { - for (int x = 0; x < 6; ++x, --bitIndex) - { - if (versionBits[y][x]) - { - version |= (1 << bitIndex); - } - } - } - } - else - { - for (int x = 0; x < 6; ++x) - { - for (int y = 0; y < 3; ++y, ++bitIndex) - { - if (versionBits[y][x]) - { - version |= (1 << bitIndex); - } - } - } - } - - return version; - } - - public static int FindClosestMatchIndex(int[] sourceArray, int value, out int distance) - { - distance = int.MaxValue; - int result = 0; - for (int i = 0; i < sourceArray.Length; ++i) - { - int d = HammingDistance(value, sourceArray[i]); - if (d <= distance) - { - distance = d; - result = i; - } - } - - return result; - } - - public static int HammingDistance(int value1, int value2) - { - int hammingDistance = 0; - int difference = value1 ^ value2; - for (int i = 0; i < 32; ++i) - { - if ((difference & (1 << i)) != 0) - { - hammingDistance++; - } - } - - return hammingDistance; - } - - public static int BitSliceValue(BitArray dataStream, ref int index, int length) - { - int value = 0; - for (int i = length - 1; i >= 0; --i, ++index) - { - if (index < dataStream.Count && dataStream[index]) //TODO index= 0 && position.Y >= 0) && exclusionMask[position.Y][position.X]); - - return nextPoint; - } - - private void MoveToNextPosition() - { - MyPoint nextPosition = position + transitions[transitionMode][transitionStep]; - transitionStep = 1 - transitionStep; - if (nextPosition.Y < 0 || nextPosition.Y == codeSize) - { - position.X--; - if (position.X == 6 && !isMicroCode) // this is because of the silly vertical timing pattern ruining the otherwise simple transition logic. - { - position.X--; - } - transitionMode = 1 - transitionMode; - } - else - { - position = nextPosition; - } - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/SymbolDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/SymbolDecoder.cs deleted file mode 100644 index 1d4a8823..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/QR/SymbolDecoder.cs +++ /dev/null @@ -1,256 +0,0 @@ -using System.Collections; -using System.Text; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.QR -{ - internal delegate ABarCodeData Decoder(int version, BitArray dataStream, Encoding textEncoding, ref int index); - - class SymbolDecoder - { - public static readonly char[] AlphaSet = new char[] - { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', - 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', - '*', '+', '-', '.', '/', ':' - }; - - public static readonly int[] QRCountBitsNumeric = new int[] - { - 10, 10, 10, 10, 10, 10, 10, 10, 10, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 3, 4, 5, - 6 - }; - - public static readonly int[] QRCountBitsAlpha = new int[] - { - 9, 9, 9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 11, 11, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 0, 3, 4, 5 - }; - - public static readonly int[] QRCountBitsByte = new int[] - { - 8, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 0, 0, 4, 5 - }; - - public static readonly int[] QRCountBitsKanji = new int[] - { - 8, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 3, 4 - }; - - public static readonly Hashtable QRDecoderMap = new Hashtable(); - - public static readonly Hashtable QRDecoderMapM2 = new Hashtable(); - - public static readonly Hashtable QRDecoderMapM3 = new Hashtable(); - - public static readonly Hashtable QRDecoderMapM4 = new Hashtable(); - - static SymbolDecoder() - { - QRDecoderMap.Add(1, new Decoder(DecodeNumeric)); - QRDecoderMap.Add(2, new Decoder(DecodeAlpha)); - QRDecoderMap.Add(4, new Decoder(DecodeByte)); - QRDecoderMap.Add(8, new Decoder(DecodeKanji)); - QRDecoderMap.Add(7, new Decoder(DecodeECI)); - QRDecoderMap.Add(5, new Decoder(DecodeFNC1first)); - QRDecoderMap.Add(9, new Decoder(DecodeFNC1second)); - QRDecoderMap.Add(3, new Decoder(DecodeStructuredAppend)); - - QRDecoderMapM2.Add(0, new Decoder(DecodeNumeric)); - QRDecoderMapM2.Add(1, new Decoder(DecodeAlpha)); - - QRDecoderMapM3.Add(0, new Decoder(DecodeNumeric)); - QRDecoderMapM3.Add(1, new Decoder(DecodeAlpha)); - QRDecoderMapM3.Add(2, new Decoder(DecodeByte)); - QRDecoderMapM3.Add(3, new Decoder(DecodeKanji)); - - QRDecoderMapM4.Add(0, new Decoder(DecodeNumeric)); - QRDecoderMapM4.Add(1, new Decoder(DecodeAlpha)); - QRDecoderMapM4.Add(2, new Decoder(DecodeByte)); - QRDecoderMapM4.Add(3, new Decoder(DecodeKanji)); - } - - public static ABarCodeData DecodeECI(int version, BitArray dataStream, Encoding textEncoding, ref int index) - { - if (index >= dataStream.Count) - { - return null; - } - - int countOnes = 0; - while (index < dataStream.Count && dataStream[index] == true) { index++; countOnes++; } - - if (index >= dataStream.Count) - { - return null; - } - - index++; //skip first 0 - if (countOnes > 2) - { - return null; - } - - int[] lengths=new int[]{7,14,21}; - int ECIAssignment= QRUtils.BitSliceValue(dataStream, ref index, lengths[countOnes]); - string assignment = "000000" + ECIAssignment; - return new StringBarCodeData("]Q2\\"+assignment.Substring(assignment.Length-6)); - } - - public static ABarCodeData DecodeFNC1first(int version, BitArray dataStream, Encoding textEncoding, ref int index) - { - return new StringBarCodeData("]Q3\\"); - } - - public static ABarCodeData DecodeFNC1second(int version, BitArray dataStream, Encoding textEncoding, ref int index) - { - int application = QRUtils.BitSliceValue(dataStream, ref index, 8); - return new StringBarCodeData("]Q5\\"+application); - } - - public static ABarCodeData DecodeStructuredAppend(int version, BitArray dataStream, Encoding textEncoding, ref int index) - { - int N = QRUtils.BitSliceValue(dataStream, ref index, 4); //number of barcode - int total = QRUtils.BitSliceValue(dataStream, ref index, 4); //total of barcodes - int parity = QRUtils.BitSliceValue(dataStream, ref index, 8); - return new StringBarCodeData("]Q2\\MI"+(N+1)+"\\MO1"+(total+1)+"\\MF001\\MY"); - } - - public static ABarCodeData DecodeNumeric(int version, BitArray dataStream, Encoding textEncoding, ref int index) - { - if (version < 1 || version - 1 >= QRCountBitsNumeric.Length) - { - return null; - } - - int countBits = QRCountBitsNumeric[version - 1]; - int dataLength = QRUtils.BitSliceValue(dataStream, ref index, countBits); - int remainderLength = dataLength%3; - int mainPartBlockCount = dataLength/3; - int dataIndex = 0; - byte[] data = new byte[dataLength]; - for (int i = 0; i < mainPartBlockCount; ++i) - { - int tripletValue = QRUtils.BitSliceValue(dataStream, ref index, 10); - data[dataIndex++] = (byte) (tripletValue/100); - tripletValue %= 100; - data[dataIndex++] = (byte)(tripletValue / 10); - tripletValue %= 10; - data[dataIndex++] = (byte)(tripletValue); - } - - if (remainderLength == 2) - { - int pairValue = QRUtils.BitSliceValue(dataStream, ref index, 7); - data[dataIndex++] = (byte)(pairValue / 10); - pairValue %= 10; - data[dataIndex] = (byte)(pairValue); - } - else if (remainderLength == 1) - { - int value = QRUtils.BitSliceValue(dataStream, ref index, 4); - data[dataIndex] = (byte)(value); - } - - return new NumericBarCodeData(data); - } - - public static ABarCodeData DecodeAlpha(int version, BitArray dataStream, Encoding textEncoding, ref int index) - { - if (version < 1 || version - 1 >= QRCountBitsAlpha.Length) - { - return null; - } - - int countBits = QRCountBitsAlpha[version - 1]; - int dataLength = QRUtils.BitSliceValue(dataStream, ref index, countBits); - int remainderLength = dataLength%2; - int mainPartBlockCount = dataLength/2; - int dataIndex = 0; - char[] data = new char[dataLength]; - for (int i = 0; i < mainPartBlockCount; ++i) - { - int pairValue = QRUtils.BitSliceValue(dataStream, ref index, 11); - int first = (pairValue/45)%45; //TODO %45 added to avoid crash - int second = pairValue%45; - if (first >= AlphaSet.Length || second >= AlphaSet.Length) - { - return null; - } - - data[dataIndex++] = AlphaSet[first]; - data[dataIndex++] = AlphaSet[second]; - } - - if (remainderLength > 0) - { - int remainderValue = QRUtils.BitSliceValue(dataStream, ref index, 6); - if (remainderValue >= AlphaSet.Length) - { - return null; - } - data[dataIndex] = AlphaSet[remainderValue]; - } - - return new StringBarCodeData(new string(data)); - } - - public static ABarCodeData DecodeByte(int version, BitArray dataStream, Encoding textEncoding, ref int index) - { - if (version < 1 || version - 1 >= QRCountBitsByte.Length) - { - return null; - } - - int countBits = QRCountBitsByte[version - 1]; - int dataLength = QRUtils.BitSliceValue(dataStream, ref index, countBits); - byte[] data = new byte[dataLength]; - for (int i = 0; i < dataLength; ++i) - { - data[i] = (byte) QRUtils.BitSliceValue(dataStream, ref index, 8); - } - - if (textEncoding != null) - return new Base256BarCodeData(data, textEncoding); - - //try to guess encoding - var encName = EncodingUtils.GuessEncoding(data); - var enc = Encoding.GetEncoding(encName); - - return new Base256BarCodeData(data, enc); - } - - public static ABarCodeData DecodeKanji(int version, BitArray dataStream, Encoding textEncoding, ref int index) - { - if (version < 1 || version - 1 >= QRCountBitsKanji.Length) - { - return null; - } - - int countBits = QRCountBitsKanji[version - 1]; - int dataLength = QRUtils.BitSliceValue(dataStream, ref index, countBits); - byte[] data = new byte[dataLength*2]; - for (int i = 0; i < dataLength; ++i) - { - int value = QRUtils.BitSliceValue(dataStream, ref index, 13); - int originalValue = value%0xC0 + ((value/0xC0) << 8); - int offset = originalValue < 0x1F00 ? 0x8140 : 0xC140; - originalValue += offset; - data[2 * i + 1] = (byte)(originalValue & 0xFF); - data[2*i] = (byte) ((originalValue >> 8) & 0xFF); - } - - Encoding shiftJIS = Encoding.GetEncoding(932); - char[] shiftJISChars = shiftJIS.GetChars(data); - return new StringBarCodeData(new string(shiftJISChars)); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/RoyalMail/RMDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/RoyalMail/RMDecoder.cs deleted file mode 100644 index 027f6fa0..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/RoyalMail/RMDecoder.cs +++ /dev/null @@ -1,68 +0,0 @@ -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.RoyalMail -{ - //In RM, each 4 bars encodes a different char. Ascendent bars encodes a value (indexs table). - //The same applies to descendent bars. These 2 indexs are used to access the char table. - //RM has not CRC - class RMDecoder : IDecoderAscDescBars - { - static readonly int[] indexs = new int[] { -1, -1, -1, 1, -1, 2, 3, -1, -1, 4, 5, -1, 0, -1, -1, -1 }; - string[][] chars = new string[][] { - new string[]{ "Z", "U", "V", "W", "X", "Y" }, - new string[]{ "5", "0", "1", "2", "3", "4" }, - new string[]{ "B", "6", "7", "8", "9", "A" }, - new string[]{ "H", "C", "D", "E", "F", "G" }, - new string[]{ "N", "I", "J", "K", "L", "M" }, - new string[]{ "T", "O", "P", "Q", "R", "S" } - }; - - public RMDecoder() - { - } - - - //First decode ascendent and descendent bars to 2 indexs, and then access to the char array. - protected string DecodeChar(bool[][] samples, int index) { int a,b; return DecodeChar(samples,index,out a, out b);} - protected string DecodeChar(bool[][] samples, int index, out int iAsc, out int iDesc) - { - int ascendant = 0; - int descendant = 0; - for (int j = 0; j < 4; j++) - { - ascendant = (ascendant << 1) + (samples[index + j][0] ? 1 : 0); - descendant = (descendant << 1) + (samples[index + j][1] ? 1 : 0); - } - - iAsc = indexs[ascendant]; - iDesc = indexs[descendant]; - if (iAsc == -1 || iDesc == -1) return null; - return chars[iAsc][iDesc]; - } - - //Decode each group of 4 bars using the sample bool array. - public virtual string Decode(bool[][] samples, out float confidence) - { - //Bars to chars - confidence = 1.0f; - string code = ""; - int sumAsc = 0, sumDesc = 0; - for (int i = 1; i < samples.Length - 1; i += 4) - { - int iAsc, iDesc; - string ch = DecodeChar(samples, i, out iAsc, out iDesc); - if (i == samples.Length - 5) - { - if (iAsc != sumAsc % 6 || iDesc != sumDesc % 6) confidence=0f; - } - else - { - code += ch; - sumAsc += iAsc; - sumDesc += iDesc; - } - } - return code; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/RoyalMail/RMReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/RoyalMail/RMReader.cs deleted file mode 100644 index d26c66bc..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/RoyalMail/RMReader.cs +++ /dev/null @@ -1,40 +0,0 @@ -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core.RoyalMail -{ - //The only difference between IM and RM reader is the length and ratio of the barcode. - //A different decoder is also set. -#if CORE_DEV - public -#else - internal -#endif - class RMReader : TwoFourStateBarcodesReader - { - public override SymbologyType GetBarCodeType() - { - return SymbologyType.RoyalMail; - } - - protected override bool IsFourState() - { - return true; - } - - protected override bool CheckNBars(int nBars) - { - return nBars>18 && (nBars-2)%4==0; //start + 4*N+ stop - } - - protected override bool CheckRatio(float ratio) - { - return ratio > 2f && ratio < 25f; - } - - protected override IDecoderAscDescBars GetDecoder() - { - return new RMDecoder(); - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/RoyalMailKIX/KIXDecoder.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/RoyalMailKIX/KIXDecoder.cs deleted file mode 100644 index 19779acd..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/RoyalMailKIX/KIXDecoder.cs +++ /dev/null @@ -1,28 +0,0 @@ -using BarcodeReader.Core.RoyalMail; - -namespace BarcodeReader.Core.RoyalMailKIX -{ - //Same decoder as RM but of variable length - internal class KIXDecoder: RMDecoder - { - public KIXDecoder() - { - } - - const int MAX_ERRORS = 2; - public override string Decode(bool[][] samples, out float confidence) - { - //Bars to chars - confidence = 1.0f; - int errors = 0; - string code = ""; - for (int i = 0; i < samples.Length; i += 4) - { - string ch = DecodeChar(samples, i); - if (ch == null) { code += "?"; confidence = 0f; if (errors++ > MAX_ERRORS) return null; } - else code += ch; - } - return code; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/RoyalMailKIX/KIXReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/RoyalMailKIX/KIXReader.cs deleted file mode 100644 index dd370de4..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/RoyalMailKIX/KIXReader.cs +++ /dev/null @@ -1,31 +0,0 @@ -using BarcodeReader.Core.Common; -using BarcodeReader.Core.RoyalMail; - -namespace BarcodeReader.Core.RoyalMailKIX -{ - //The only difference between KIX and RM reader is the length of the barcode. KIX is a variable length barcode - //thus nBars must be a multiple of 4. - //A different decoder is also set. -#if CORE_DEV - public -#else - internal -#endif - class KIXReader : RMReader - { - public override SymbologyType GetBarCodeType() - { - return SymbologyType.RoyalMailKIX; - } - - protected override bool CheckNBars(int nBars) - { - return nBars>18 && (nBars)%4==0; //4*N - } - - protected override IDecoderAscDescBars GetDecoder() - { - return new KIXDecoder(); - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/NewDecoders/TriOptic/TriOpticLinearReader.cs b/MuPDF.NET/Barcode/Decoders/NewDecoders/TriOptic/TriOpticLinearReader.cs deleted file mode 100644 index bf2632c2..00000000 --- a/MuPDF.NET/Barcode/Decoders/NewDecoders/TriOptic/TriOpticLinearReader.cs +++ /dev/null @@ -1,332 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Text; -using BarcodeReader.Core.Common; -using SkiaSharp; - -namespace BarcodeReader.Core.TriOptic -{ - /// - /// TriOptic reader. - /// -#if CORE_DEV - public -#else - internal -#endif - class TriOpticLinearReader : LinearReader - { - /// - /// do decode extended Code 39 symbols or not (disabled by default) - /// - protected bool DoDecodeExtended = false; - /// - /// Check sum mode - /// - public Code39.Code39LinearReader.Mode CheckSumMode = Code39.Code39LinearReader.Mode.None; - - public enum Mode { None, CheckMod43Checksum, CheckMod11ChecksumPZN8, CheckMod11ChecksumISBN10, CheckMod11ChecksumUPU }; - - - //private static int[][] _startStopPatterns = { new int[] { 1, 2, 1, 2, 1, 2, 1, 1, 1 } }; - private static int[][] _startStopPatterns = { new int[] { 1 } }; - - string _alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"; - - int[][] _patterns = new int[][] { - new int[]{1,1,1,2,2,1,2,1,1,1},//0 - new int[]{2,1,1,2,1,1,1,1,2,1}, - new int[]{1,1,2,2,1,1,1,1,2,1}, - new int[]{2,1,2,2,1,1,1,1,1,1}, - new int[]{1,1,1,2,2,1,1,1,2,1}, - new int[]{2,1,1,2,2,1,1,1,1,1},//5 - new int[]{1,1,2,2,2,1,1,1,1,1}, - new int[]{1,1,1,2,1,1,2,1,2,1}, - new int[]{2,1,1,2,1,1,2,1,1,1}, - new int[]{1,1,2,2,1,1,2,1,1,1}, - new int[]{2,1,1,1,1,2,1,1,2,1},//10 - new int[]{1,1,2,1,1,2,1,1,2,1}, - new int[]{2,1,2,1,1,2,1,1,1,1}, - new int[]{1,1,1,1,2,2,1,1,2,1}, - new int[]{2,1,1,1,2,2,1,1,1,1}, - new int[]{1,1,2,1,2,2,1,1,1,1},//15 - new int[]{1,1,1,1,1,2,2,1,2,1}, - new int[]{2,1,1,1,1,2,2,1,1,1}, - new int[]{1,1,2,1,1,2,2,1,1,1}, - new int[]{1,1,1,1,2,2,2,1,1,1}, - new int[]{2,1,1,1,1,1,1,2,2,1},//20 - new int[]{1,1,2,1,1,1,1,2,2,1}, - new int[]{2,1,2,1,1,1,1,2,1,1}, - new int[]{1,1,1,1,2,1,1,2,2,1}, - new int[]{2,1,1,1,2,1,1,2,1,1}, - new int[]{1,1,2,1,2,1,1,2,1,1},//25 - new int[]{1,1,1,1,1,1,2,2,2,1}, - new int[]{2,1,1,1,1,1,2,2,1,1}, - new int[]{1,1,2,1,1,1,2,2,1,1}, - new int[]{1,1,1,1,2,1,2,2,1,1}, - new int[]{2,2,1,1,1,1,1,1,2,1},//30 - new int[]{1,2,2,1,1,1,1,1,2,1}, - new int[]{2,2,2,1,1,1,1,1,1,1}, - new int[]{1,2,1,1,2,1,1,1,2,1}, - new int[]{2,2,1,1,2,1,1,1,1,1}, - new int[]{1,2,2,1,2,1,1,1,1,1},//35 - new int[]{1,2,1,1,1,1,2,1,2,1}, - new int[]{2,2,1,1,1,1,2,1,1,1}, - new int[]{1,2,2,1,1,1,2,1,1,1}, - new int[]{1,2,1,2,1,2,1,1,1,1}, - new int[]{1,2,1,2,1,1,1,2,1,1},//40 - new int[]{1,2,1,1,1,2,1,2,1,1}, - new int[]{1,1,1,2,1,2,1,2,1,1}, - new int[]{1,2,1,1,2,1,2,1,1,1} - }; - - public TriOpticLinearReader() - { - MaxPatternSymbolDifference = 1.9f;//1.2f;// 0.83f; - MaxPatternAverageSymbolDifference = 1.6f;//0.8//2 /0.55f; - //MaxReadError = 1f;//1.3f; - - MaxLeftAndRightModulesDifference = 1.9f; - UseE = false; - MinConfidence = 0.7f; - MaxReadError = 1f; - - MidPoints = new float[] {0.5f}; - MinQuietZone = 3.5f; - } - - private BarSymbolReaderNaive reader; - - internal override void GetParams(out int[] startPattern, out int[] stopPattern, out int minModulesPerBarcode, out int maxModulesPerBarcode) - { - startPattern = stopPattern = _startStopPatterns[0]; - - minModulesPerBarcode = 13 * 3; - maxModulesPerBarcode = int.MaxValue; - - if (reader == null) - { - reader = new BarSymbolReaderNaive(10, 13, _patterns); - } - } - - public override SymbologyType GetBarCodeType() - { - return SymbologyType.TriopticCode39; - } - - internal override int[] ReadSymbols(Pattern leftPattern, Pattern rightPattern, MyPointF from, MyPointF to, float module, out float error, out float maxError, out float confidence) - { - var line = Scan.GetPixels(from, to, 1.3f);//1.17 - - var res = reader.Read(line, out error, out maxError, out confidence); - - if (maxError > 0.99f) - return new int[0]; - - if (confidence > 0) - { -#if DEBUG - DebugHelper.AddDebugItem(from.ToString(), line, from, to); - DebugHelper.DrawArrow(from.X, from.Y, to.X, to.Y, SKColors.Magenta); -#endif - } - - return res; - } - - internal override bool Decode(BarCodeRegion r, int[] row) - { - if (row == null) - return false; - - var res = Decode(r, row, 1, row.Length - 1); - if (res) - { - var data = r.Data[0] as StringBarCodeData; - if (data.Value.Length == 6) - { - var p1 = data.Value.Substring(0, 3); - var p2 = data.Value.Substring(3, 3); - data.Value = p2 + p1; - } - } - - return res; - } - - internal virtual bool Decode(BarCodeRegion r, int[] row, int iStart, int iEnd) - { - string pre = ""; - if (row != null && row.Length > 2) - { - if (CheckSumMode == Code39.Code39LinearReader.Mode.CheckMod43Checksum) - { - iEnd--; - int sum = 0; - for (int i = iStart; i < iEnd; i++) sum += row[i]; - sum = sum % 43; - if (sum != row[iEnd]) return false; - } - else if (CheckSumMode == Code39.Code39LinearReader.Mode.CheckMod11ChecksumPZN8) - { - iEnd--; - int sum = 0; - int[] weights = new int[] { 1, 2, 3, 4, 5, 6, 7 }; - bool isPzn7 = false; - // check if it is PZN8 - if (iEnd - iStart != 8) - { - // check if it is PZN7 - isPzn7 = iEnd - iStart == 7; - - // otherwise exit - if (!isPzn7) - return false; - } - - if (row[iStart] != 36) return false; //PZN starts with - - iStart++; - - if (isPzn7) - // count weights as 2,3,4,5,6.. - for (int i = iStart; i < iEnd; i++) sum += row[i] * weights[i - iStart + 1]; - else - // PZN8, count weights as 1,2,3,4,5,6 - for (int i = iStart; i < iEnd; i++) sum += row[i] * weights[i - iStart]; - int checkdigit = sum % 11; - if (checkdigit == 10) checkdigit = 0; - if (checkdigit != row[iEnd]) return false; - pre = "";// "PZN-"; - } - else if (CheckSumMode == Code39.Code39LinearReader.Mode.CheckMod11ChecksumISBN10) - { - iEnd--; - int sum = 0; - int[] weights = new int[] { 10, 9, 8, 7, 6, 5, 4, 3, 2 }; - if (iEnd - iStart != 9) return false; - for (int i = iStart; i < iEnd; i++) sum += row[i] * weights[i - iStart]; - int checkdigit = 11 - sum % 11; - if (checkdigit == 10) checkdigit = 0; - else if (checkdigit == 11) checkdigit = 5; - if (checkdigit != row[iEnd]) return false; - } - else if (CheckSumMode == Code39.Code39LinearReader.Mode.CheckMod11ChecksumUPU) - { - iEnd--; - int sum = 0; - int[] weights = new int[] { 8, 6, 4, 2, 3, 5, 9, 7 }; - if (iEnd - iStart != 8) return false; //UPU has just 8 digits + Checksum - for (int i = iStart; i < iEnd; i++) sum += row[i] * weights[i - iStart]; - int checkdigit = 11 - sum % 11; - if (checkdigit == 10) checkdigit = 0; - else if (checkdigit == 11) checkdigit = 5; - if (checkdigit != row[iEnd]) return false; - } - - var s = new StringBuilder(); - for (int i = iStart; i < iEnd; i++) - if (row[i] >= 0 && row[i] < _patterns.Length) - s.Append(_alphabet.Substring(row[i], 1)); - else - return false; - - var res = s.ToString(); - if (DoDecodeExtended) - res = DecodeExtended(res); - - r.Data = new ABarCodeData[] { new StringBarCodeData(res) }; - return true; - } - return false; - } - - /// - /// Checks if value using extended alphabet and changes value accordingly. - /// - string DecodeExtended(string value) - { - StringBuilder result = new StringBuilder(); - char newChar; - - for (int i = 0; i < value.Length; i++) - { - char c = value[i]; - if (i < value.Length - 1 && "$/+%".IndexOf(c) != -1) - { - char next = value[i + 1]; - switch (c) - { - case '$': - if (next >= 'A' && next <= 'Z') - { - newChar = (char)(next - 64); - break; - } - - newChar = next; - result.Append(c); - break; - - case '/': - if (next >= 'A' && next <= 'O') - { - newChar = (char)(next - 32); - break; - } - else if (next == 'Z') - { - newChar = ':'; - break; - } - - newChar = next; - result.Append(c); - break; - - case '+': - if (next >= 'A' && next <= 'Z') - { - newChar = (char)(next + 32); - break; - } - - newChar = next; - result.Append(c); - break; - - case '%': - if (next >= 'A' && next <= 'E') - { - newChar = (char)(next - 38); - break; - } - else if (next >= 'F' && next <= 'W') - { - newChar = (char)(next - 11); - break; - } - - newChar = next; - result.Append(c); - break; - - default: - return null; - } - - result.Append(newChar); - i++; - } - else - { - result.Append(c); - } - } - - return result.ToString(); - } - - } -} diff --git a/MuPDF.NET/Barcode/Decoders/OtsuThreshold.cs b/MuPDF.NET/Barcode/Decoders/OtsuThreshold.cs deleted file mode 100644 index 7983624d..00000000 --- a/MuPDF.NET/Barcode/Decoders/OtsuThreshold.cs +++ /dev/null @@ -1,291 +0,0 @@ -using SkiaSharp; -using System; -using System.Drawing; -using System.Runtime.InteropServices; - -namespace BarcodeReader.Core -{ - internal class OtsuThreshold - { - private static float Px(int init, int end, int[] hist) - { - int sum = 0; - int i; - for (i = init; i <= end; i++) - sum += hist[i]; - - return (float) sum; - } - - private static float Mx(int init, int end, int[] hist) - { - int sum = 0; - int i; - for (i = init; i <= end; i++) - sum += i * hist[i]; - - return (float) sum; - } - - private static int FindMax(float[] vec, int n) - { - float maxVec = 0; - int idx=0; - int i; - - for (i = 1; i < n - 1; i++) - { - if (vec[i] > maxVec) - { - maxVec = vec[i]; - idx = i; - } - } - return idx; - } - - private static void GetHistogram(byte[] data, int width, int height, int stride, int[] hist) - { - hist.Initialize(); - - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width * 3; x += 3) - { - int i = y * stride + x; - hist[data[i]]++; - } - } - } - /* - public int GetOtsuThreshold(SKBitmap bmp) - { - byte t = 0; - float[] vet = new float[256]; - int[] hist = new int[256]; - vet.Initialize(); - - float p1, p2, p12; - int k; - - BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); - int size = bitmapData.Stride * bitmapData.Height; - byte[] data = new byte[size]; - Marshal.Copy(bitmapData.Scan0, data, 0, size); - - GetHistogram(data, bitmapData.Width, bitmapData.Height, bitmapData.Stride, hist); - - // loop through all possible t values and maximize between class variance - for (k = 1; k != 255; k++) - { - p1 = Px(0, k, hist); - p2 = Px(k + 1, 255, hist); - p12 = p1 * p2; - if (p12 == 0) - p12 = 1; - float diff = (Mx(0, k, hist) * p2) - (Mx(k + 1, 255, hist) * p1); - vet[k] = (float) diff * diff / p12; - } - - bmp.UnlockBits(bitmapData); - - t = (byte) FindMax(vet, 256); - - return t; - } - */ - public int GetOtsuThreshold(SKBitmap bmp) - { - int width = bmp.Width; - int height = bmp.Height; - int[] hist = new int[256]; - float[] vet = new float[256]; - - // Step 1: Calculate grayscale histogram - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - SKColor color = bmp.GetPixel(x, y); - - // Convert to grayscale using luminance formula (ITU-R BT.601) - int gray = (int)(color.Red * 0.299 + color.Green * 0.587 + color.Blue * 0.114); - gray = Common.Utils.Clamp(gray, 0, 255); - hist[gray]++; - } - } - - // Step 2: Otsu's method - byte t = 0; - float p1, p2, p12; - for (int k = 1; k < 255; k++) - { - p1 = Px(0, k, hist); - p2 = Px(k + 1, 255, hist); - p12 = p1 * p2; - if (p12 == 0) - p12 = 1; - float diff = (Mx(0, k, hist) * p2) - (Mx(k + 1, 255, hist) * p1); - vet[k] = diff * diff / p12; - } - - t = (byte)FindMax(vet, 256); - return t; - } - - // Threshold the image to binary using given threshold value - public static int GetOtsuThreshold(byte[] data, int height, int width, int stride) - { - byte t = 0; - float[] vet = new float[256]; - int[] hist = new int[256]; - vet.Initialize(); - - float p1, p2, p12; - int k; - - GetHistogram(data, width, height, stride, hist); - - // loop through all possible t values and maximize between class variance - for (k = 1; k != 255; k++) - { - p1 = Px(0, k, hist); - p2 = Px(k + 1, 255, hist); - p12 = p1 * p2; - if (p12 == 0) - p12 = 1; - float diff = (Mx(0, k, hist) * p2) - (Mx(k + 1, 255, hist) * p1); - vet[k] = (float) diff * diff / p12; - } - - t = (byte) FindMax(vet, 256); - - return t; - - } - /* - public void Convert2GrayScaleFast(SKBitmap bmp) - { - int width = bmp.Width; - int height = bmp.Height; - - BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); - int size = bitmapData.Stride * bitmapData.Height; - byte[] data = new byte[size]; - - Marshal.Copy(bitmapData.Scan0, data, 0, size); - - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - int i = bitmapData.Stride * y + x * 3; - data[i] = (byte) (.299 * data[i + 2] + .587 * data[i + 1] + .114 * data[i]); - data[i + 1] = data[i]; - data[i + 2] = data[i]; - } - } - - Marshal.Copy(data, 0, bitmapData.Scan0, size); - bmp.UnlockBits(bitmapData); - } - */ - public void Convert2GrayScaleFast(SKBitmap bmp) - { - using (var pixmap = bmp.PeekPixels()) - { - int width = bmp.Width; - int height = bmp.Height; - int rowBytes = pixmap.RowBytes; - - IntPtr pixels = pixmap.GetPixels(); - - unsafe - { - byte* ptr = (byte*)pixels.ToPointer(); - - for (int y = 0; y < height; y++) - { - byte* row = ptr + y * rowBytes; - - for (int x = 0; x < width; x++) - { - byte* px = row + x * 4; // 4 bytes per pixel: B, G, R, A - - byte b = px[0]; - byte g = px[1]; - byte r = px[2]; - - byte gray = (byte)(0.299 * r + 0.587 * g + 0.114 * b); - - px[0] = gray; // Blue - px[1] = gray; // Green - px[2] = gray; // Red - // Leave alpha (px[3]) unchanged - } - } - } - } - } - /* - public void Threshold(SKBitmap bmp, int thresh) - { - int width = bmp.Width; - int height = bmp.Height; - - BitmapData bitmapData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); - int size = bitmapData.Stride * bitmapData.Height; - byte[] data = new byte[size]; - - Marshal.Copy(bitmapData.Scan0, data, 0, size); - - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - int i = bitmapData.Stride * y + x * 3; - data[i] = (byte) (data[i] > thresh ? 255 : 0); - data[i + 1] = (byte) (data[i + 1] > thresh ? 255 : 0); - data[i + 2] = (byte) (data[i + 2] > thresh ? 255 : 0); - } - } - - Marshal.Copy(data, 0, bitmapData.Scan0, size); - bmp.UnlockBits(bitmapData); - } - */ - public void Threshold(SKBitmap bmp, int thresh) - { - int width = bmp.Width; - int height = bmp.Height; - - using (var pixmap = bmp.PeekPixels()) - { - int rowBytes = pixmap.RowBytes; - IntPtr pixels = pixmap.GetPixels(); - - unsafe - { - byte* ptr = (byte*)pixels.ToPointer(); - - for (int y = 0; y < height; y++) - { - byte* row = ptr + y * rowBytes; - - for (int x = 0; x < width; x++) - { - byte* px = row + x * 4; // BGRA - - // Apply threshold on each channel - px[0] = (byte)(px[0] > thresh ? 255 : 0); // Blue - px[1] = (byte)(px[1] > thresh ? 255 : 0); // Green - px[2] = (byte)(px[2] > thresh ? 255 : 0); // Red - // Alpha (px[3]) stays unchanged - } - } - } - } - } - } -} - diff --git a/MuPDF.NET/Barcode/Decoders/SymbologyReader.cs b/MuPDF.NET/Barcode/Decoders/SymbologyReader.cs deleted file mode 100644 index ae4ba22c..00000000 --- a/MuPDF.NET/Barcode/Decoders/SymbologyReader.cs +++ /dev/null @@ -1,641 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections; -using System.Drawing; -using SkiaSharp; -using System.Diagnostics; -using BarcodeReader.Core.Common; - -namespace BarcodeReader.Core -{ -#if CORE_DEV - public -#else - internal -#endif - class SymbologyReader1DTimeOutException : Exception { }; - -#if CORE_DEV - public -#else - internal -#endif - abstract class SymbologyReader : IBarcodeDecoder - { - protected const int IntegerShift = 8; - protected const int DifferenceScaleFactor = 1 << IntegerShift; - - private IBarcodeConsumer _consumer; - - public BlackAndWhiteImage BWImage { get; private set; } - public int ScanStep { get; set; } = 1; - public int MaxNumberOfBarcodesPerPage { get; set; } = 0; // max number of barcodes per page allowed (-1 or 0 for unlimited) - public virtual bool SearchMirrored { get; set; } = false; - public bool RequireQuietZones { get; set; } = true; - public int MinimalDataLength { get; set; } - - protected SymbologyReader(IBarcodeConsumer consumer) - { - _consumer = consumer; - } - - public virtual void Dispose() { } - public virtual void Reset() { } - public virtual void BeforeDecoding() { } // to be called before decoding (used in ProductReader to reset previously stored ean13 barcode as new decoding process may work with rotated image ) - public abstract FoundBarcode[] DecodeRow(int rowNumber, XBitArray row); - public abstract SymbologyType GetBarCodeType(); - - public virtual FoundBarcode[] Decode(BlackAndWhiteImage image) - { - return Decode(image, 0f); - } - - public virtual FoundBarcode[] Decode(BlackAndWhiteImage bwImage, float rotationAngle) - { - BWImage = bwImage; - List result = new List(); - int passCount = 1; - - if (SearchMirrored) - { - // we'll make two decode passes - // second pass is made with reversed row (it allows to decode - // right-to-left oriented barcodes) - passCount = 2; - } - - BeforeDecoding(); - - bool foundDecodablePrev = false; - - for (int pass = 0; pass < passCount; pass++) - { - // check timeout - TimeoutCheck(); - - for (int y = 0; y < BWImage.Height; y += ScanStep) - { - // check timeout - TimeoutCheck(); - - XBitArray row = BWImage.GetRow(y); - - if (pass == 1) - row = row.Reverse(); - - // check timeout - TimeoutCheck(); - - bool foundDecodable = false; - - if (row.IsNil()) - Reset(); - - FoundBarcode[] rowResults = DecodeRow(y, row); - if (rowResults != null) - { - bool shouldStopDecoding; - foundDecodable = ProcessFound(false, rowResults, pass, row, result, out shouldStopDecoding, BWImage, rotationAngle); - - if (foundDecodable && y == BWImage.Height - 1) // if barcode lasts to the last row having no the bottom quite zone - { - ProcessFound(true, null, pass, row, result, out shouldStopDecoding, BWImage, rotationAngle); - } - - if (shouldStopDecoding) - { - return result.ToArray(); - } - - foundDecodablePrev = true; - } - else - { - if (foundDecodablePrev) - { - bool shouldStopDecoding; - foundDecodable = ProcessFound(true, null, pass, row, result, out shouldStopDecoding, BWImage, rotationAngle); - foundDecodablePrev = false; - - if (shouldStopDecoding) - { - return result.ToArray(); - } - } - } - - if (!foundDecodable) - { - // current row does not contain decodable barcode - if (MaxNumberOfBarcodesPerPage > 0 && result.Count > MaxNumberOfBarcodesPerPage-1) // or we reached max number of barcodes allowed - { - // already have one barcode and were told - // to search for only one - // OR we have reached max allowed number of barcodes - // so, we just ran off last line of previously - // found barcode. exit - return result.ToArray(); - } - } - - // check timeout - TimeoutCheck(); - } - } - - if (this is GS1DataBar.GS1DataBar) - { - if (result.Count == 0) - return result.ToArray(); - - result.Sort(new BarcodeComparer()); - List newResult = new List(); - for (int i = 0; i < result.Count; i++) - { - FoundBarcode a = result[i]; - if (a.Rect.Height > 1) - { - bool intersects = false; - for (int j = 0; j < newResult.Count && !intersects; j++) - { - FoundBarcode b = newResult[j]; - if (a.Rect.IntersectsWith(b.Rect)) - intersects = true; - } - if (!intersects) - newResult.Add(a); - } - } - - return newResult.ToArray(); - } - - return result.ToArray(); - } - - private static SKRect AdjustBarcodeRectangle(BlackAndWhiteImage bwImage, SKRect barcodeRect, float rotationAngle) - { - if (Math.Abs(rotationAngle) > float.Epsilon) - { - SKPointI[] unrotatedPoints = bwImage.Unrotate(barcodeRect); - //byte[] pointTypes = new byte[5] { (byte) PathPointType.Start, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line, (byte) PathPointType.Line }; - //GraphicsPath path = new GraphicsPath(unrotatedPoints, pointTypes); - - SKRect rect = Utils.DrawPath(unrotatedPoints); - - return rect; - } - - return barcodeRect; - } - - - /// - /// Post filtering retrieved results from decoder - /// - /// True if we need to force consolidate with previously found results - /// current results from single row - /// pass id (we have 2 passes if we process negative barcodes by reversing rows - /// row itself as bit array - /// array of currently approved results. it is used to check if new found barcodes are just update to existing ones (if we scan high barcodes so we have several rows with the same barcode and we should update existing result barcode by increasing its height rather then creating new barcode) - /// out param if we should stop decoding - /// - /// rotation angle of the currently passed image (as we are trying to decode 0 angle, 90 dergees, 45 degrees, 135 degrees to find rotated barcodes - /// returns true if we found new barcode instead of consolidating with existing one (false in this case) - private bool ProcessFound( - bool forceConsolidatePreviouslyFound, - IEnumerable rowResults, int pass, - XBitArray row, List result, out bool shouldStopDecoding, - BlackAndWhiteImage bwImage, float rotationAngle) - { - bool foundDecodable = false; - shouldStopDecoding = false; - - // check if we have empty row results passed so we should extract the latest barcode and fire the event for it - if (rowResults == null)// && forceConsolidatePreviouslyFound) - { - if (result.Count > 0) - { - // getting the latest barcode we got previously - FoundBarcode bb = result[result.Count - 1] as FoundBarcode; - // no need to adjust its rect as result array contains already adjusted rectangles if needed - - // fire the event for the barcode - if (_consumer != null && _consumer.consumeBarcode(bb)) - { - // stop if event told us so - shouldStopDecoding = true; - } - } - - // check if we have exceeded max number of barcodes expected - if (MaxNumberOfBarcodesPerPage > 0 && result.Count > MaxNumberOfBarcodesPerPage - 1) - { - shouldStopDecoding = true; - } - - return true; - } - - // else we process each new given results - foreach (FoundBarcode rowResult in rowResults) - { - // check against min required value length - if (rowResult.Value.Length >= MinimalDataLength) - { - foundDecodable = true; - // check if we have duplicated barcode rather then new - bool updated = checkIfAlreadyFound(rowResult, pass, row, result); - - // if we have new barcode - if (!updated) - { - // decoded a new barcode in current row - - if (MaxNumberOfBarcodesPerPage > 0 && result.Count > MaxNumberOfBarcodesPerPage - 1) // or we reached max number of barcodes allowed - { - // already have one barcode and were told - // to search for only one - // OR we have reached max allowed number of barcodes - // so, ignore newly found barcode and exit - shouldStopDecoding = true; - break; - } - - // add to main results array - result.Add(rowResult); - - // we are NOT firing event right now! - // we are firing the event only when we got forceConsolidatePreviouslyFound = true - // see below for forceConsolidatePreviouslyFound=true case - // this way we are not firing the event for the first row in the barcode - // but we are waiting until we have scanned several rows and got new row with different result - //if (forceConsolidatePreviouslyFound) - //{ - // no need to adjust the rectangle any more: the rotation is superseded by BlackAndWhiteImage. - //rowResult.SrcRect = AdjustBarcodeRectangle(bwImage, rowResult.SrcRect, rotationAngle); - - // fire the event - if (_consumer != null && _consumer.consumeBarcode(rowResult)) - { - shouldStopDecoding = true; - break; - } - // } - } - } - } - - - return foundDecodable; - } - - /// - /// Check if we already have this barcode previously found - /// - /// - /// - /// - /// - /// - private bool checkIfAlreadyFound(FoundBarcode rowResult, int pass, XBitArray row, List result) - { - bool updated = false; - SKRect curRect = rowResult.Rect; - - if (pass == 1) - { - // we should reverse found barcode rect - curRect.Left = row.Size - curRect.Right; - rowResult.Rect = curRect; - } - - for (int i = 0; i < result.Count; i++) - { - FoundBarcode prevResult = result[i] as FoundBarcode; - - if ( - (RawDataEquals(prevResult.RawData, rowResult.RawData) || Math.Abs(curRect.Top-prevResult.Rect.Top) < 5) && - prevResult.BarcodeFormat == rowResult.BarcodeFormat && - Math.Abs(prevResult.Confidence - rowResult.Confidence) < 0.1f - ) - { - SKRect prevRect = prevResult.Rect; - - if (Math.Abs(curRect.Left - prevRect.Left) < 0.2 * prevRect.Width) - { - updated = true; - - //prevRect.Offset(0, -1); - SKRect newRect = new SKRect(Math.Min(prevRect.Left, curRect.Left), - Math.Min(prevRect.Top, curRect.Top), - Math.Max(prevRect.Right, curRect.Right), - Math.Max(prevRect.Bottom, curRect.Bottom)); - - prevResult.Rect = newRect; - - if (rowResult.Value.Length > prevResult.Value.Length) - { - result[i] = rowResult; - } - - prevResult.Confidence = (1f + (prevResult.Confidence * (float)prevRect.Height + rowResult.Confidence) / (float)(prevRect.Height + 1)) / 2f; - - break; - } - } - } - - return updated; - } - - protected bool readModules(XBitArray row, int offset, int[] moduleWidths) - { - Array.Clear(moduleWidths, 0, moduleWidths.Length); - - if (offset >= row.Size) - return false; - - bool processingWhite = !row[offset]; - int currentModule = 0; - int x = offset; - int RowSize = row.Size; - - for (; x < RowSize; x++) - { - // if pixel is white and processing white - // or - // pixel is black and processing black - if (row[x] ^ processingWhite) - { - moduleWidths[currentModule]++; - } - else - { - // color changed - currentModule++; - if (currentModule == moduleWidths.Length) - return true; - - moduleWidths[currentModule] = 1; - processingWhite = !processingWhite; - } - } - - if (currentModule == moduleWidths.Length - 1 && x == row.Size) - { - // last module ended on row boundary - return true; - } - - return false; - } - - /// - /// Calculates difference by measuring widths and getting overall difference - /// - /// array with widths from barcode - /// array with widths from pattern - /// max difference to stop - /// true if equal, false otherwise - protected static double calcDifference(int[] moduleWidths, int[] pattern, double maxDifference) - { - return calcDifference(moduleWidths, pattern, maxDifference, false); - } - - /// - /// Minimizes module widths array to use minimal possible values - /// For example we give: 2 4 8 and it outputs 1 2 4 - /// - /// array with module widths - protected static void MinimizeModuleWidths(ref int[] moduleWidths) { - - // now search for min width in the array of widths - int minWidth = 0; - foreach (int iWidth in moduleWidths) - { - if (minWidth > 0) - { - // search min value - if (minWidth > iWidth) - minWidth = iWidth; - } - else - minWidth = iWidth; - } - - // now we divide each value in widths array by the min width to find - // relative widths (relative to the min width) - for (int i = 0; i < moduleWidths.Length; i++) - { - moduleWidths[i] = (int)Math.Round((float)(moduleWidths[i] / minWidth)); - } - - } - - /// - /// Calculates difference by measuring NUMBER of different cells - /// I.e. 1 of 4 different values means 25% difference, 2 of 4 different values means 50% etc - /// - /// array with widths from barcode - /// array with widths from pattern - /// max difference to stop - /// true if equal, false otherwise - protected static float calcDifference2(int[] moduleWidths, int[] pattern, float maxDifference) - { - int index = 0; - float stepValue = 1.0f / moduleWidths.Length; // get step value - float result = 0; - foreach (int iW in moduleWidths) - { - if (iW != pattern[index++]) - result += stepValue; - - if (result > 1) - break; - } - - return result; - } - - public static double calcDifference(int[] moduleWidths, int[] pattern, double maxDifference, bool patternIsShorter) - { - int[] shortArray; - if (patternIsShorter) - shortArray = pattern; - else - shortArray = moduleWidths; - - int scaledMaxDifference = (int)(maxDifference * DifferenceScaleFactor); - - int totalWidth = 0; - int totalPatternWidth = 0; - for (int i = 0; i < shortArray.Length; i++) - { - totalWidth += moduleWidths[i]; - totalPatternWidth += pattern[i]; - } - - if (totalWidth < totalPatternWidth) - { - // all modules width less then pattern width - return 1; // 100% difference - } - - int moduleScaleFactor = (totalWidth << IntegerShift) / totalPatternWidth; - scaledMaxDifference = (scaledMaxDifference * moduleScaleFactor) >> IntegerShift; - - int totalDifference = 0; - for (int x = 0; x < shortArray.Length; x++) - { - int scaledModuleWidth = moduleWidths[x] << IntegerShift; - int scaledPatternModuleWidth = pattern[x] * moduleScaleFactor; - - int difference; - if (scaledModuleWidth > scaledPatternModuleWidth) - { - difference = scaledModuleWidth - scaledPatternModuleWidth; - } - else - { - difference = scaledPatternModuleWidth - scaledModuleWidth; - } - - if (difference > scaledMaxDifference) - return 1; // 100% difference - - totalDifference += difference; - } - - return ((double) totalDifference / totalWidth) / DifferenceScaleFactor; - } - - - /// - /// Check for white space before symbol - /// - /// The row. - /// The current offset. - /// The start offset. - /// - protected bool HaveWhiteSpaceBefore(XBitArray row, int offset, int startOffset) - { - return row.IsRange(Math.Max(0, startOffset - (offset - startOffset) / 2), startOffset, false); - } - - /// - /// Check for white space after symbol - /// - /// The row. - /// The current offset. - /// The next symbol start offset. - /// True if strict checking of the whitespace, not checking bounds. - /// - protected bool HaveWhiteSpaceAfter(XBitArray row, int offset, int nextOffset, bool strict) - { - if (strict) - { - // return false if outside the row's size - if (nextOffset > row.Size) - return false; - - if (offset >= nextOffset) - return false; - - int endOffset = nextOffset; - - // return false if outside the row's size - if (endOffset >= row.Size) - return false; - - return row.IsRange(offset, endOffset, false); - } - else - { - return row.IsRange(Math.Min(row.Size, nextOffset), Math.Min(row.Size, nextOffset + (nextOffset - offset) / 2), false); - } - } - - protected bool HaveWhiteSpaceAfter(XBitArray row, int offset, int nextOffset) - { - return HaveWhiteSpaceAfter(row, offset, nextOffset, false); - } - - /// - /// Skips the two modules. - /// NOTE: we skip two modules in order to retain start module color. - /// - /// The offset. - /// The module widths. - protected void SkipTwoModules(ref int offset, int[] moduleWidths) - { - offset += moduleWidths[0] + moduleWidths[1]; - - for (int i = 2; i < moduleWidths.Length; i++) - moduleWidths[i - 2] = moduleWidths[i]; - - moduleWidths[moduleWidths.Length - 2] = 0; - moduleWidths[moduleWidths.Length - 1] = 0; - } - - protected bool RawDataEquals(int[] left, int[] right) - { - if (left == right) - return true; - - if (left == null || right == null) - return false; - - if (left.Length != right.Length) - return false; - - for (int i = 0; i < left.Length; i++) - { - if (left[i] != right[i]) - return false; - } - - return true; - } - - protected int[] AsIntArray(ArrayList list) - { - int[] array = new int[list.Count]; - for (int i = 0; i < list.Count; i++) - array[i] = (int)list[i]; - - return array; - } - - /// - /// Timeout (in ticks) to abort decoding if exceeded - /// 0 means no timeout is checked - /// stores max allowed time, when the current time is greater - /// then we should throw the exception - /// - public long TimeoutTimeInTicks = 0; - - protected void TimeoutCheck() - { - if (TimeoutTimeInTicks == 0) - return; - - long curTicks = DateTime.Now.Ticks; - - // else check the current time against max end time - if (TimeoutTimeInTicks < curTicks) - { - Debug.WriteLine(String.Format("Timeout: exceeded by {0} mseconds", (curTicks - TimeoutTimeInTicks) * TimeSpan.TicksPerMillisecond)); - throw new SymbologyReader1DTimeOutException(); - } - } - - public class BarcodeComparer : IComparer - { - public int Compare(FoundBarcode x, FoundBarcode y) - { - return (int)(y.Rect.Height - x.Rect.Height); - } - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/SymbologyReader2D.cs b/MuPDF.NET/Barcode/Decoders/SymbologyReader2D.cs deleted file mode 100644 index a6b6d0ea..00000000 --- a/MuPDF.NET/Barcode/Decoders/SymbologyReader2D.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Diagnostics; - -namespace BarcodeReader.Core -{ - /// - /// Timeout exception thrown on timeout - /// -#if CORE_DEV - public -#else - internal -#endif - class TimeOutException : Exception { }; - /// - /// 2D decoder timeout exception - /// -#if CORE_DEV - public -#else - internal -#endif - class SymbologyReader2DTimeOutException : TimeOutException { }; - -#if CORE_DEV - public -#else - internal -#endif - abstract class SymbologyReader2D : IBarcodeDecoder - { - /// - /// Threshold filter method to use by default - /// - public ThresholdFilterMethod ThresholdFilterMethodToUse { get; set;} = ThresholdFilterMethod.Block; - - public BlackAndWhiteImage BWImage { get; private set; } - public int ScanStep { get; set; } = 1; - public int MinAllowedBarcodeSideSize { get; set; } = 0; - public int MaxAllowedBarcodeSideSize { get; set; } = 100; - public int ExpectedNumberOfBarcodes { get; set; } = 0; - public bool StopOnFirstFoundBarcodeInTheRow { get; set; } = false; - protected System.Text.Encoding DefaultEncoding => System.Text.Encoding.GetEncoding(28591); //default is iso-8859-1 (8-bit encoding) - public System.Text.Encoding Encoding { get; set; } = System.Text.Encoding.GetEncoding(28591); - public abstract SymbologyType GetBarCodeType(); - - protected abstract FoundBarcode[] DecodeBarcode(); - - public virtual FoundBarcode[] Decode(BlackAndWhiteImage bwImage) - { - BWImage = bwImage; - return DecodeBarcode(); - } - - public virtual void Dispose() { } - - /// - /// Skips the two modules. - /// NOTE: we skip two modules in order to retain start module color. - /// - /// The offset. - /// The module widths. - protected void SkipTwoModules(ref int offset, int[] moduleWidths) - { - offset += moduleWidths[0] + moduleWidths[1]; - - for (int i = 2; i < moduleWidths.Length; i++) - moduleWidths[i - 2] = moduleWidths[i]; - - moduleWidths[moduleWidths.Length - 2] = 0; - moduleWidths[moduleWidths.Length - 1] = 0; - } - - /// - /// Timeout (in ticks) to abort decoding if exceeded - /// 0 means no timeout is checked - /// stores max allowed time, when the current time is greater - /// then we should throw the exception - /// - public long TimeoutTimeInTicks = 0; - - protected bool IsTimeout() - { - if (TimeoutTimeInTicks == 0) - return false; - - long curTicks = DateTime.Now.Ticks; - - // else check the current time against max end time - if (TimeoutTimeInTicks < curTicks) - { - Debug.WriteLine(string.Format("Timeout: exceeded by {0} mseconds", (curTicks - TimeoutTimeInTicks) * TimeSpan.TicksPerMillisecond)); - return true; - } - - return false; - } - - public override string ToString() - { - return GetType().Name; - } - } -} diff --git a/MuPDF.NET/Barcode/Decoders/SymbologyType.cs b/MuPDF.NET/Barcode/Decoders/SymbologyType.cs deleted file mode 100644 index edaf6368..00000000 --- a/MuPDF.NET/Barcode/Decoders/SymbologyType.cs +++ /dev/null @@ -1,332 +0,0 @@ -namespace BarcodeReader.Core -{ - /// - /// Describes all supported barcode symbologies (types). - /// -#if CORE_DEV - public -#else - internal -#endif - enum SymbologyType - { - /// - /// (0) Indicates that barcode symbology is unknown. - /// - Unknown = 0, - - /// - /// (1) Codabar barcode (Also known as Ames Code, USD-4, NW-7, - /// Code 2 of 7). Codabar symbology allows only symbols from this - /// string '0123456789-$:/.+' to be encoded. This symbology used - /// for example in libraries and blood banks. - /// - Codabar = 1, - - /// - /// (2) Code 128 barcode. It is a very effective, high-density - /// symbology which permits the encoding of alphanumeric (subject to - /// alphabet selection) data. Code 128 is a very dense code, used - /// extensively worldwide. - /// - Code128 = 2, - - /// - /// (3) GS1-128 symbology (new name for EAN128 symbology). - /// - GS1 = 3, - - /// - /// (4) Code 39 barcode (aka USD-3, 3 of 9). Code 39 symbology allows - /// all ASCII symbols to be encoded in extended mode or symbols from - /// this string "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%" in - /// standard mode. This symbology used for example by U.S. Government - /// and military, required for DoD applications. - /// - Code39 = 4, - - /// - /// (5) Interleaved 2 of 5 barcode (also known as Code 2 of 5 Interleaved). - /// Interleaved 2 of 5 symbology allows only numeric values to be - /// encoded. This symbology is used primarily in the distribution - /// and warehouse industry. - /// - I2of5 = 5, - - /// - /// (6) EAN-13 barcode. Used with consumer products internationally. - /// EAN-13 symbology allows only numeric values to be encoded. - /// - EAN13 = 6, - - /// - /// (7) EAN-8 barcode. This symbology is a short version of EAN-13 that - /// is intended to be used on packaging which would be otherwise - /// too small to use one of the other versions. Used with consumer - /// products internationally. EAN-8 symbology allows only numeric - /// values to be encoded. - /// - EAN8 = 7, - - /// - /// (8) UPC-A barcode. Used with consumer products in U.S. UPC-A - /// symbology allows only numeric values to be encoded. - /// - UPCA = 8, - - /// - /// (9) UPC-E barcode. This symbology is zero-suppression version - /// of UPC-A. It is intended to be used on packaging which would - /// be otherwise too small to use one of the other versions. The - /// code is smaller because it drops out zeros which would otherwise - /// occur in a symbol. Used with consumer products in U.S. - /// UPC-E symbology allows only numeric values to be encoded. - /// - UPCE = 9, - - /// - /// (10) The EAN-2 (Also known as EAN/2 and EAN 2) is a supplement to - /// the EAN-13 and UPC-A barcodes. It is often used on magazines - /// and periodicals to indicate an issue number. - /// - EAN2 = 10, - - /// - /// (11) The EAN-5 (Also known as EAN/5 and EAN 5) is a supplement to - /// EAN-13 and UPC-A barcodes. It is often used to give a - /// suggestion for the price of the book. - /// - EAN5 = 11, - - /// - /// (12) PDF417 symbology. This symbology is heavily used in the parcel - /// industry. The PDF417 symbology can encode a vast amount of data - /// into a small space. This symbology allows a maximum data size of - /// 1850 text characters, or 2710 digits. - /// - PDF417 = 12, - - /// - /// (13) DataMatrix symbology. The most popular application for DataMatrix - /// is marking small items. The Data Matrix can encode text - /// and raw data. Usual data size is from a few bytes up to 2 kilobytes. - /// - DataMatrix = 13, - - /// - /// (14) QR Code symbology. QR Code initially was used for tracking - /// parts in vehicle manufacturing, but now QR Codes used in a much - /// broader context, including both commercial tracking applications - /// and convenience-oriented applications aimed at mobile phone - /// users (known as mobile tagging). - /// - QRCode = 14, - - /// - /// (15) Aztec symbology. - /// - Aztec = 15, - - /// - /// (16) Trioptic Code 39 symbology. - /// - TriopticCode39 = 16, - - /// - /// (17) Patch Code symbology. - /// - PatchCode = 17, - - /// - /// (18) GS1 DataBar Omnidirectional symbology. - /// - GS1DataBarOmnidirectional = 18, - - /// - /// (19) GS1 DataBar Expanded symbology. - /// - GS1DataBarExpanded = 19, - - /// - /// (20) GS1 DataBar Limited symbology. - /// - GS1DataBarLimited = 20, - - /// - /// (21) GS1 DataBar Stacked symbology. - /// - GS1DataBarStacked = 21, - - /// - /// (22) GS1 DataBar Expanded Stacked symbology. - /// - GS1DataBarExpandedStacked = 22, - - /// - /// (23) MaxiCode symbology. - /// - MaxiCode = 23, - - /// - /// (24) MICR symbology. - /// - MICR = 24, - - /// - /// (25) USPS Intelligent Mail symbology. - /// - IntelligentMail = 25, - - /// - /// (26) Royal Mail symbology. - /// - RoyalMail = 26, - - /// - /// (27) Royal Mail KIX symbology. - /// - RoyalMailKIX = 27, - - /// - /// (28) Australian Post 4 State Customer Code symbology. - /// - AustralianPostCode = 28, - - /// - /// (29) Codablock F symbology. - /// - CodablockF = 29, - - /// - /// (30) Code 16K symbology. - /// - Code16K = 30, - - /// - /// (31) PostNet symbology. - /// - PostNet = 31, - - /// - /// (32) MicroPDF symbology. - /// - MicroPDF = 32, - - /// - /// (33) Code 93 symbology. - /// - Code93 = 33, - - /// - /// (34) MSI symbology. - /// - MSI = 34, - - /// - /// (35) ITF-14 symbology (Interleaved 2 of 5 restricted to 14 symbols). - /// - ITF14 = 35, - - /// - /// (36) GTIN-14 barcode (also known as Code 2 of 5 Interleaved with 14 digits). - /// GTIN-14 symbology allows only 14 numeric values to be - /// encoded. This symbology is used primarily in the distribution - /// and warehouse industry. - /// - GTIN14 = 36, - - /// - /// (37) GTIN-13 barcode. Used with consumer products internationally. - /// GTIN-13 symbology allows only 13 numeric values to be encoded. - /// - GTIN13 = 37, - - /// - /// (38) GTIN-8 barcode. This symbology is a short version of GTIN-13 that - /// is intended to be used on packaging which would be otherwise - /// too small to use one of the other versions. Used with consumer - /// products internationally. GTIN-8 symbology allows only 8 numeric - /// values to be encoded. - /// - GTIN8 = 38, - - /// - /// (39) GTIN-12 barcode. Used with consumer products in U.S. GTIN-12 - /// symbology allows only 12 numeric values to be encoded. - /// - GTIN12 = 39, - - /// - /// (40) Circular variation of Interleaved 2 of 5 barcode. - /// - CircularI2of5 = 40, - - /// - /// (41) PZN barcode. German Pharmacy Barcode. - /// - PZN = 41, - - /// - /// (42) Pharmacode, also known as Pharmaceutical Binary Code, is a barcode standard, used in the pharmaceutical industry as a packing control system. - /// It is designed to be readable despite printing errors. - /// - Pharmacode = 42, - - /// - /// (43) Extended Code 39 barcode symbology. - /// - Code39Ext = 43, - - /// - /// (44) Code 39 mod 43 barcode symbology. This is Code 39 with modulo 43 check digit. - /// - Code39Mod43 = 44, - - /// - /// (45) Code 39 mod 43 Extended barcode symbology. This is Extended Code 39 with modulo 43 check digit. - /// - Code39Mod43Ext = 45, - - /// - /// (46) UPU (Universal Postal Union) barcode symbology. This is the Code 39 with modulo 11 check digit. - /// - UPU = 46, -//#else - /// - /// (47) Segment element of Optical Mark Recognition (OMR). - /// - Segment = 47, - /// - /// (48) Circle element of Optical Mark Recognition (OMR). - /// - Circle = 48, - /// - /// (49) Oval element of Optical Mark Recognition (OMR). - /// - Oval = 49, - /// - /// (50) Checkbox element of Optical Mark Recognition (OMR). - /// - Checkbox = 50, - /// - /// (51) Horizontal line element of Optical Mark Recognition (OMR). - /// - HorizontalLine = 51, - /// - /// (52) Vertical line element of Optical Mark Recognition (OMR). - /// - VerticalLine = 52, - /// - /// (53) Underlined field element of Optical Mark Recognition (OMR). - /// - UnderlinedField = 53, - /// - /// (54) Bordered table element of Optical Mark Recognition (OMR). - /// - Table = 54, - /// - /// (55) DPM (Direct Part Marking) DataMatrix symbology. - /// - DPMDataMatrix = 55, - } -} diff --git a/MuPDF.NET/Barcode/Decoders/ThresholdFilterMethod.cs b/MuPDF.NET/Barcode/Decoders/ThresholdFilterMethod.cs deleted file mode 100644 index 6e51231d..00000000 --- a/MuPDF.NET/Barcode/Decoders/ThresholdFilterMethod.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace BarcodeReader.Core -{ - /// - /// Image threshold filter types. - /// -#if CORE_DEV - public -#else - internal -#endif - enum ThresholdFilterMethod - { - /// - /// (0) WholeImage method - /// - WholeImage, - - /// - /// (1) Block method - /// - Block, - - BlockOld, - - /// - /// Block method + dilate morphology - /// - BlockSmoothed, - - /// - /// Block method + median filtration (removes thin lines and single pixels) - /// - BlockMedian, - - /// - /// Block method + grid filtration (removes thin single lines and single pixels) - /// - BlockGrid, - - /// - /// Simple cutoff binarization with predefined threshold level - /// - Threshold, - - /// - /// For internal use only. - /// - None, - - /// - /// For internal use only. - /// - Rotated, - - /// - /// New enhancing BW filter. - /// - Enhancing, - - /// - /// Simple cutoff binarization with predefined threshold level - /// - ThresholdEx, - } -} diff --git a/MuPDF.NET/Barcode/Decoders/XBitArray.cs b/MuPDF.NET/Barcode/Decoders/XBitArray.cs deleted file mode 100644 index 01874a33..00000000 --- a/MuPDF.NET/Barcode/Decoders/XBitArray.cs +++ /dev/null @@ -1,223 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeReader.Core -{ - /// - /// Array of bits. - /// -#if CORE_DEV - public -#else - internal -#endif - class XBitArray - { - public int[] _bits; - public int _size; - - public XBitArray(int size) - { - if (size < 1) - throw new ArgumentException("Size must be greater then 0"); - - _size = size; - - //to avoid out of range - size = (int)(size * 2f); - - // - var arraySize = size >> 5; - if ((size & 0x1F) != 0) - arraySize++; - - _bits = new int[arraySize]; - } - - private XBitArray() - { - } - - public XBitArray Clone() - { - XBitArray copy = new XBitArray(); - - int[] newBits = new int[_bits.Length]; - Array.Copy(_bits, newBits, _bits.Length); - copy._size = _size; - copy._bits = newBits; - - return copy; - } - - public int Size - { - get - { - return _size; - } - } - - /// - /// Gets or sets the value of the bit at a specific position. - /// - /// The zero-based index of the value to get or set - public bool this[int index] - { - get - { - //var i = index >> 5; - //if (i >= arraySize) - // return false; - //return (_bits[i] & (1 << (index & 0x1F))) != 0; - - try - { - return (_bits[index >> 5] & (1 << (index & 0x1F))) != 0; - } - catch (IndexOutOfRangeException) - { - return false; - } - } - set - { - if (value) - _bits[index >> 5] |= 1 << (index & 0x1F); - else - _bits[index >> 5] &= ~(1 << (index & 0x1F)); - } - } - - /// - /// Sets a block of 32 bits, starting at bit i. - /// - /// The index of a first bit to set. - /// The new value for the next 32 bits. - public void Set32(int i, int newBits) - { - _bits[i >> 5] = newBits; - } - - /// - /// Clears all bits (sets to false). - /// - public void Clear() - { - Array.Clear(_bits, 0, _bits.Length); - } - - /// - /// Checks all bits are 0. - /// - public bool IsNil() - { - for (int i = 0; i < _bits.Length; i++) - { - if (_bits[i] != 0) - return false; - } - - return true; - } - - /// - /// Checks if a range of bits [start, end) is set or not set. - /// - /// The start index. - /// The end index. - /// if set to true then checks that bits - /// are set; otherwise checks that bits are not set. - /// - /// true if all bits in range are set or not set (according - /// to value argument); otherwise, false. - /// - public bool IsRange(int start, int end, bool value) - { - if (end < start) - throw new ArgumentException("end index should be greater or equal to start index", "end"); - - if (end == start) - return true; - - end--; - int firstInt = start >> 5; - int lastInt = end >> 5; - for (int i = firstInt; i <= lastInt; i++) - { - int firstBit = i > firstInt ? 0 : start & 0x1F; - int lastBit = i < lastInt ? 31 : end & 0x1F; - int mask; - if (firstBit == 0 && lastBit == 31) - { - mask = -1; - } - else - { - mask = 0; - for (int j = firstBit; j <= lastBit; j++) - mask |= 1 << j; - } - - if ((_bits[i] & mask) != (value ? mask : 0)) - { - // Return false if we're looking for 1s and the masked - // bits[i] isn't all 1s (that is, equals the mask, or we're - // looking for 0s and the masked portion is not all 0s - return false; - } - } - - return true; - } - - /// - /// Reverses all bits in the array. - /// - public XBitArray Reverse() - { - int[] newBits = new int[_bits.Length]; - int size = _size; - for (int i = 0; i < size; i++) - { - if (this[size - i - 1]) - newBits[i >> 5] |= 1 << (i & 0x1F); - } - - XBitArray array = new XBitArray(); - array._size = _size; - array._bits = newBits; - return array; - } - - /// - /// Reverses all bits in the array. - /// - public void ReverseMe() - { - int[] newBits = new int[_bits.Length]; - int size = _size; - for (int i = 0; i < size; i++) - { - if (this[size - i - 1]) - newBits[i >> 5] |= 1 << (i & 0x1F); - } - - _bits = newBits; - } - - public override string ToString() - { - StringBuilder result = new StringBuilder(); - for (int i = 0; i < _size; i++) - { - if ((i & 0x07) == 0) - result.Append(' '); - - result.Append(this[i] ? '|' : '.'); - } - - return result.ToString(); - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/AztecCompactionMode.cs b/MuPDF.NET/Barcode/Encoders/AztecCompactionMode.cs deleted file mode 100644 index 7818b958..00000000 --- a/MuPDF.NET/Barcode/Encoders/AztecCompactionMode.cs +++ /dev/null @@ -1,24 +0,0 @@ - -using System; -using System.Text; - -namespace BarcodeWriter.Core -{ - /// - /// Describes all possible compaction (encoding) modes for Aztec symbology. - /// - public enum AztecCompactionMode - { - /// - /// (-1) Default. Library is mixing binary and ASCII encoding modes to get minimal size possible - /// - Auto = -1, - - /// - /// (0) Library forces use of binary encoding - /// For binary encoding you can set value from byte[] array like this: - /// barcode.value = Encoding.Default.GetString(new byte[] { 0, 10, 11, 12, 13, 14, 15}) - /// - Binary = 0 - } -} diff --git a/MuPDF.NET/Barcode/Encoders/AztecErrorCorrectionLevel.cs b/MuPDF.NET/Barcode/Encoders/AztecErrorCorrectionLevel.cs deleted file mode 100644 index c64563db..00000000 --- a/MuPDF.NET/Barcode/Encoders/AztecErrorCorrectionLevel.cs +++ /dev/null @@ -1,37 +0,0 @@ - -using System; -using System.Text; - -namespace BarcodeWriter.Core -{ - /// - /// Level of error correction in Aztec Code symbols - /// - public enum AztecErrorCorrectionLevel - { - /// - /// (0) Library will choose best possible error correction level. - /// - Auto = 0, - - /// - /// (1) Level 1 error correction. 10% of data region will be filled by error correction data. - /// - Level1 = 1, - - /// - /// (2) Level 2 error correction. 23% of data region will be filled by error correction data. - /// - Level2 = 2, - - /// - /// (3) Level 3 error correction. 36% of data region will be filled by error correction data. - /// - Level3 = 3, - - /// - /// (4) Level 1 error correction. 50% of data region will be filled by error correction data. - /// - Level4 = 4 - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Barcode.cs b/MuPDF.NET/Barcode/Encoders/Barcode.cs deleted file mode 100644 index 981eb6e8..00000000 --- a/MuPDF.NET/Barcode/Encoders/Barcode.cs +++ /dev/null @@ -1,2546 +0,0 @@ - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; -using BarcodeWriter.Core.Internal; -using Encoding = System.Text.Encoding; -using Newtonsoft.Json; -using SkiaSharp; -using BarcodeWriter.Core; - -namespace BarcodeWriter.Core -{ - /// - /// Represents barcode generator. - /// -#if QRCODESDK - internal class BarcodeEncoder : IDisposable -#else - [ClassInterface(ClassInterfaceType.AutoDual)] - public class BarcodeEncoder : IBarcodeEncoder, IDisposable -#endif - { - internal static bool Forbid1D = false; - internal static bool Forbid2D = false; - - private SymbologyDrawing _drawing = null; - private float _zoomLevel = 1.0f; - private SKSize _outputSize = SKSize.Empty; - - private MemoryStream _valueStream = new MemoryStream(); - - // This graphics and bitmap are used for size measures - private SKCanvas _graphicsForMeasures = null; - private SKBitmap _bitmapForMeasures = null; - - private SKImage _decorationImage = null; - private int _decorationImageScale = -1; - -#if !BARCODESDK_EMBEDDED_SOURCES - private ProfileManager _profileManager = null; -#endif - private string _profiles = null; - - /// - /// Initializes a new instance of the class. - /// - public BarcodeEncoder() - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - if (SingleInstanceDetector.IsAlreadyRunning(GetType().FullName)) - throw new BarcodeException("Community Edition does not allow multiple concurrent instances."); -#endif - -#if !BARCODESDK_EMBEDDED_SOURCES - _profileManager = new ProfileManager(this); -#endif - -#if NETCOREAPP2_1 - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); -#endif - -#if QRCODESDK - _drawing = new QRSymbology(); -#else - _drawing = new Code39Symbology(); -#endif - _drawing.Options.Changed += OptionsChanged; - } - - /// - /// Initializes a new instance of the class. - /// - /// The barcode type (symbology). - public BarcodeEncoder(SymbologyType type) - : this() - { - switch (type) - { - case SymbologyType.QRCode: - _drawing = new QRSymbology(); - break; - case SymbologyType.GS1_QRCode: - _drawing = new GS1QRSymbology(); - break; -#if !QRCODESDK - case SymbologyType.Code39: - _drawing = new Code39Symbology(); - break; - case SymbologyType.Code128: - _drawing = new Code128Symbology(); - break; - case SymbologyType.Postnet: - _drawing = new PostnetSymbology(); - break; - case SymbologyType.UPCA: - _drawing = new UPCASymbology(); - break; - case SymbologyType.EAN8: - _drawing = new EAN8Symbology(); - break; - case SymbologyType.ISBN: - _drawing = new ISBNSymbology(); - break; - case SymbologyType.Codabar: - _drawing = new CodabarSymbology(); - break; - case SymbologyType.I2of5: - _drawing = new I2of5Symbology(); - break; - case SymbologyType.Code93: - _drawing = new Code93Symbology(); - break; - case SymbologyType.EAN13: - _drawing = new EAN13Symbology(); - break; - case SymbologyType.JAN13: - _drawing = new JAN13Symbology(); - break; - case SymbologyType.Bookland: - _drawing = new BooklandSymbology(); - break; - case SymbologyType.UPCE: - _drawing = new UPCESymbology(); - break; - case SymbologyType.PDF417: - case SymbologyType.MacroPDF417: - _drawing = new PDF417Symbology(); - if (type == SymbologyType.MacroPDF417) - _drawing.Options.PDF417CreateMacro = true; - else - _drawing.Options.PDF417CreateMacro = false; - break; - case SymbologyType.MicroPDF417: - _drawing = new PDF417MicroSymbology(); - break; - case SymbologyType.PDF417Truncated: - _drawing = new PDF417TruncatedSymbology(); - break; - case SymbologyType.DataMatrix: - _drawing = new DataMatrixSymbology(); - break; - case SymbologyType.Aztec: - _drawing = new AztecSymbology(); - break; - case SymbologyType.Planet: - _drawing = new PlanetSymbology(); - break; - case SymbologyType.EAN128: - case SymbologyType.GS1_128: - _drawing = new EAN128Symbology(); - break; - case SymbologyType.USPSSackLabel: - _drawing = new USPSSackLabelSymbology(); - break; - case SymbologyType.USPSTrayLabel: - _drawing = new USPSTrayLabelSymbology(); - break; - case SymbologyType.DeutschePostIdentcode: - _drawing = new DeutschePostIdentcodeSymbology(); - break; - case SymbologyType.DeutschePostLeitcode: - _drawing = new DeutschePostLeitcodeSymbology(); - break; - case SymbologyType.Numly: - _drawing = new NumlySymbology(); - break; - case SymbologyType.PZN: - _drawing = new PZNSymbology(); - break; - case SymbologyType.OpticalProduct: - _drawing = new OPCSymbology(); - break; - case SymbologyType.SwissPostParcel: - _drawing = new SwissPostParcelsymbology(); - break; - case SymbologyType.RoyalMail: - _drawing = new RoyalMailSymbology(); - break; - case SymbologyType.DutchKix: - _drawing = new DutchKixSymbology(); - break; - case SymbologyType.SingaporePostalCode: - _drawing = new SingaporePostSymbology(); - break; - case SymbologyType.EAN2: - _drawing = new EAN2Symbology(); - break; - case SymbologyType.EAN5: - _drawing = new EAN5Symbology(); - break; - case SymbologyType.EAN14: - _drawing = new EAN14Symbology(); - break; - case SymbologyType.GS1_DataMatrix: - _drawing = new GS1DataMatrixSymbology(); - break; - case SymbologyType.Telepen: - _drawing = new TelepenSymbology(); - break; - case SymbologyType.IntelligentMail: - _drawing = new IntelligentMailSymbology(); - break; - case SymbologyType.GS1_DataBar_Omnidirectional: - _drawing = new GS1DataBarOmnidirectionalSymbology(); - break; - case SymbologyType.GS1_DataBar_Truncated: - _drawing = new GS1DataBarTruncatedSymbology(); - break; - case SymbologyType.GS1_DataBar_Stacked: - _drawing = new GS1DataBarStackedSymbology(); - break; - case SymbologyType.GS1_DataBar_Stacked_Omnidirectional: - _drawing = new GS1DataBarStackedOmnidirectionalSymbology(); - break; - case SymbologyType.GS1_DataBar_Limited: - _drawing = new GS1DataBarLimitedSymbology(); - break; - case SymbologyType.GS1_DataBar_Expanded: - _drawing = new GS1DataBarExpandedSymbology(); - break; - case SymbologyType.GS1_DataBar_Expanded_Stacked: - _drawing = new GS1DataBarStackedExpandedSymbology(); - break; - case SymbologyType.MaxiCode: - _drawing = new MaxiCodeSymbology(); - break; - case SymbologyType.Plessey: - _drawing = new PlesseySymbology(); - break; - case SymbologyType.MSI: - _drawing = new MSISymbology(); - break; - case SymbologyType.ITF14: - _drawing = new ITF14Symbology(); - break; - case SymbologyType.GTIN12: - _drawing = new GTIN12Symbology(); - break; - case SymbologyType.GTIN8: - _drawing = new GTIN8Symbology(); - break; - case SymbologyType.GTIN13: - _drawing = new GTIN13Symbology(); - break; - case SymbologyType.GTIN14: - _drawing = new GTIN14Symbology(); - break; - case SymbologyType.PharmaCode: - _drawing = new PharmaCodeSymbology(); - break; -#endif - default: - throw new BarcodeException("Unknown symbology type."); - } - - setOptimizedOptions(false); - - _drawing.Options.Changed += OptionsChanged; - } - - /// - /// Initializes a new instance of the class. - /// - /// The existing barcode from which to create new barcode with different symbology type. - /// The type (symbology) of new barcode. - public BarcodeEncoder(BarcodeEncoder prototype, SymbologyType type) - : this(type) - { - AddChecksum = prototype.AddChecksum; - AddChecksumToCaption = prototype.AddChecksumToCaption; - AdditionalCaption = prototype.AdditionalCaption; - AdditionalCaptionFont = new SKFont( - prototype.AdditionalCaptionFont.Typeface, - prototype.AdditionalCaptionFont.Size, - prototype.AdditionalCaptionFont.ScaleX, - prototype.AdditionalCaptionFont.SkewX - ); - AdditionalCaptionPosition = prototype.AdditionalCaptionPosition; - AdditionalCaptionAlignment = prototype.AdditionalCaptionAlignment; - BackColor = prototype.BackColor; - BarHeight = prototype.BarHeight; - CaptionFont = new SKFont(prototype.CaptionFont.Typeface, prototype.CaptionFont.Size); - CaptionPosition = prototype.CaptionPosition; - CaptionAlignment = prototype.CaptionAlignment; - DrawCaption = prototype.DrawCaption; - ForeColor = prototype.ForeColor; - Margins = (Margins) prototype.Margins.Clone(); - NarrowBarWidth = prototype.NarrowBarWidth; - Options = (SymbologyOptions) prototype.Options.Clone(); - Angle = prototype.Angle; - Value = (string) prototype.Value.Clone(); - SupplementValue = (string) prototype.SupplementValue.Clone(); - WideToNarrowRatio = prototype.WideToNarrowRatio; - - _drawing.Options.Changed += OptionsChanged; - } - - /// - public void Dispose() - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - SingleInstanceDetector.Close(); -#endif - -#if !BARCODESDK_EMBEDDED_SOURCES - _profileManager?.Dispose(); - _profileManager = null; -#endif - _valueStream?.Dispose(); - _valueStream = null; - - _decorationImage?.Dispose(); - _decorationImage = null; - - if (_drawing != null) - { - _drawing.Options.Changed -= OptionsChanged; - _drawing.Dispose(); - _drawing = null; - } - - _additionalCaptionFont?.Dispose(); - _additionalCaptionFont = null; - - disposeGraphicsForMeasures(); - } - - /// - /// Resets barcode options and properties to non optimized defaults. - /// - public void ResetToNonOptimizedDefaults() - { - setDefaultOptions(false); - removeFitSize(); - } - - /// - /// Gets or sets a value indicating whether TIFF images should be saved as monochrome (1-bit, black and white) images. - /// This property is obsolete since has been superseded with ProduceMonochromeImages property. - /// - /// true if Tiff images should be saved in monochrome; otherwise, false. - [Obsolete] - public bool TIFFUse1BitFormat - { - get => ProduceMonochromeImages; - set => ProduceMonochromeImages = value; - } - - /// - public bool ProduceMonochromeImages - { - get => _produceMonochromeImages; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _produceMonochromeImages = value; - } - } - private bool _produceMonochromeImages = false; - - /// - public string AdditionalCaption - { - get => _additionalCaption; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _additionalCaption = value; - removeFitSize(); - } - } - private string _additionalCaption = ""; - - /// - public bool AddChecksum - { - get => _drawing.AddChecksum; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.AddChecksum = value; - removeFitSize(); - } - } - - /// - public bool AddChecksumToCaption - { - get => _drawing.AddChecksumToCaption; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.AddChecksumToCaption = value; - removeFitSize(); - } - } - - /// - public string Caption - { - get => _drawing.CustomCaption; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.CustomCaption = value; - removeFitSize(); - } - } - - /// - public bool DrawCaption - { - get => _drawing.DrawCaption; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.DrawCaption = value; - removeFitSize(); - } - } - - /// - public bool DrawCaptionFor2DBarcodes - { - get => _drawing.DrawCaption2D; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.DrawCaption2D = value; - removeFitSize(); - } - } - - /// - public bool DrawQuietZones - { - get => _drawQuietZones; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawQuietZones = value; - _drawing.DrawQuietZones = value; - } - } - private bool _drawQuietZones = true; - - /// - public SymbologyType Symbology - { - get => _drawing.Symbology; - set - { -#if !QRCODESDK - if (value == _drawing.Symbology) - return; - - SymbologyDrawing previous = _drawing; - - switch (value) - { - default: - throw new ArgumentOutOfRangeException("value"); - case SymbologyType.Code39: - _drawing = new Code39Symbology(_drawing); - break; - case SymbologyType.Code128: - _drawing = new Code128Symbology(_drawing); - break; - case SymbologyType.Postnet: - _drawing = new PostnetSymbology(_drawing); - break; - case SymbologyType.UPCA: - _drawing = new UPCASymbology(_drawing); - break; - case SymbologyType.EAN8: - _drawing = new EAN8Symbology(_drawing); - break; - case SymbologyType.ISBN: - _drawing = new ISBNSymbology(_drawing); - break; - case SymbologyType.Codabar: - _drawing = new CodabarSymbology(_drawing); - break; - case SymbologyType.I2of5: - _drawing = new I2of5Symbology(_drawing); - break; - case SymbologyType.Code93: - _drawing = new Code93Symbology(_drawing); - break; - case SymbologyType.EAN13: - _drawing = new EAN13Symbology(_drawing); - break; - case SymbologyType.JAN13: - _drawing = new JAN13Symbology(_drawing); - break; - case SymbologyType.Bookland: - _drawing = new BooklandSymbology(_drawing); - break; - case SymbologyType.UPCE: - _drawing = new UPCESymbology(_drawing); - break; - case SymbologyType.PDF417: - case SymbologyType.MacroPDF417: - _drawing = new PDF417Symbology(_drawing); - _drawing.Options.PDF417CreateMacro = value == SymbologyType.MacroPDF417; - break; - case SymbologyType.PDF417Truncated: - _drawing = new PDF417TruncatedSymbology(_drawing); - break; - case SymbologyType.MicroPDF417: - _drawing = new PDF417MicroSymbology(_drawing); - break; - case SymbologyType.DataMatrix: - _drawing = new DataMatrixSymbology(_drawing); - break; - case SymbologyType.GS1_DataMatrix: - _drawing = new GS1DataMatrixSymbology(_drawing); - break; - case SymbologyType.QRCode: - _drawing = new QRSymbology(_drawing); - break; - case SymbologyType.Aztec: - _drawing = new AztecSymbology(_drawing); - break; - case SymbologyType.Planet: - _drawing = new PlanetSymbology(_drawing); - break; - case SymbologyType.EAN128: - case SymbologyType.GS1_128: - _drawing = new EAN128Symbology(_drawing); - break; - case SymbologyType.USPSSackLabel: - _drawing = new USPSSackLabelSymbology(_drawing); - break; - case SymbologyType.USPSTrayLabel: - _drawing = new USPSTrayLabelSymbology(_drawing); - break; - case SymbologyType.DeutschePostIdentcode: - _drawing = new DeutschePostIdentcodeSymbology(_drawing); - break; - case SymbologyType.DeutschePostLeitcode: - _drawing = new DeutschePostLeitcodeSymbology(_drawing); - break; - case SymbologyType.Numly: - _drawing = new NumlySymbology(_drawing); - break; - case SymbologyType.PZN: - _drawing = new PZNSymbology(_drawing); - break; - case SymbologyType.OpticalProduct: - _drawing = new OPCSymbology(_drawing); - break; - case SymbologyType.SwissPostParcel: - _drawing = new SwissPostParcelsymbology(_drawing); - break; - case SymbologyType.RoyalMail: - _drawing = new RoyalMailSymbology(_drawing); - break; - case SymbologyType.DutchKix: - _drawing = new DutchKixSymbology(_drawing); - break; - case SymbologyType.SingaporePostalCode: - _drawing = new SingaporePostSymbology(_drawing); - break; - case SymbologyType.EAN2: - _drawing = new EAN2Symbology(_drawing); - break; - case SymbologyType.EAN5: - _drawing = new EAN5Symbology(_drawing); - break; - case SymbologyType.EAN14: - _drawing = new EAN14Symbology(_drawing); - break; - case SymbologyType.Telepen: - _drawing = new TelepenSymbology(_drawing); - break; - case SymbologyType.IntelligentMail: - _drawing = new IntelligentMailSymbology(_drawing); - break; - case SymbologyType.GS1_DataBar_Omnidirectional: - _drawing = new GS1DataBarOmnidirectionalSymbology(_drawing); - break; - case SymbologyType.GS1_DataBar_Truncated: - _drawing = new GS1DataBarTruncatedSymbology(_drawing); - break; - case SymbologyType.GS1_DataBar_Stacked: - _drawing = new GS1DataBarStackedSymbology(_drawing); - break; - case SymbologyType.GS1_DataBar_Stacked_Omnidirectional: - _drawing = new GS1DataBarStackedOmnidirectionalSymbology(_drawing); - break; - case SymbologyType.GS1_DataBar_Limited: - _drawing = new GS1DataBarLimitedSymbology(_drawing); - break; - case SymbologyType.GS1_DataBar_Expanded: - _drawing = new GS1DataBarExpandedSymbology(_drawing); - break; - case SymbologyType.GS1_DataBar_Expanded_Stacked: - _drawing = new GS1DataBarStackedExpandedSymbology(_drawing); - break; - case SymbologyType.MaxiCode: - _drawing = new MaxiCodeSymbology(_drawing); - break; - case SymbologyType.Plessey: - _drawing = new PlesseySymbology(_drawing); - break; - case SymbologyType.MSI: - _drawing = new MSISymbology(_drawing); - break; - case SymbologyType.ITF14: - _drawing = new ITF14Symbology(_drawing); - break; - case SymbologyType.GTIN12: - _drawing = new GTIN12Symbology(_drawing); - break; - case SymbologyType.GTIN8: - _drawing = new GTIN8Symbology(_drawing); - break; - case SymbologyType.GTIN13: - _drawing = new GTIN13Symbology(_drawing); - break; - case SymbologyType.GTIN14: - _drawing = new GTIN14Symbology(_drawing); - break; - case SymbologyType.GS1_QRCode: - _drawing = new GS1QRSymbology(_drawing); - break; - case SymbologyType.PharmaCode: - _drawing = new PharmaCodeSymbology(_drawing); - break; - } - - // dispose previous drawing - previous.Options.Changed -= OptionsChanged; - previous.Dispose(); - - _drawing.DrawQuietZones = DrawQuietZones; - - setOptimizedOptions(true); - removeFitSize(); - - _drawing.Options.Changed += OptionsChanged; - -#endif // !QRCODESDK - } - } - - /// - public SymbologyOptions Options - { - get => _drawing.Options; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.Options = value; - removeFitSize(); - } - } - - /// - public string Value - { - get => _drawing.Value; - set - { - _drawing.Value = value; - - _valueStream.SetLength(0); - _valueStream.Position = 0; - byte[] bytes = Encoding.Unicode.GetBytes(_drawing.Value); - _valueStream.Write(bytes, 0, bytes.Length); - - removeFitSize(); - } - } - - /// - /// Gets the copy of barcode value as stream. - /// - /// The copy of barcode value as stream. - /// - /// Please use - /// if you want to set up value using a stream. - /// - [Obsolete] - public Stream ValueAsStream => _valueStream; - - /// - /// Loads the value from the given stream. Read begins from current - /// position within a stream. - /// - /// Loads the value from the given stream. - /// The stream to read value from. - /// The value's length. - [ComVisible(false)] - public void LoadValueFromStream(Stream stream, int length) - { - byte[] bytes = new byte[length]; - stream.Read(bytes, 0, length); - Value = Encoding.Default.GetString(bytes, 0, bytes.Length); - } - - /// - /// Loads the value from the given stream. Read begins from current - /// position within a stream. All bytes till the end of the stream - /// a considered to be a value's bytes. - /// - /// The stream to read value from. - [ComVisible(false)] - public void LoadValueFromStream(Stream stream) - { - int length = (int)(stream.Length - stream.Position); - LoadValueFromStream(stream, length); - } - - /// - public string SupplementValue - { - get => _supplementValue; - set - { - bool containsNonDigits = false; - foreach (char c in value) - { - if (!Char.IsDigit(c)) - { - containsNonDigits = true; - break; - } - } - - bool lengthIsOk = (value.Length == 0 || value.Length == 2 || value.Length == 5); - if (!lengthIsOk || containsNonDigits) - throw new BarcodeException(GetSupplementaryValueRestrictions()); - - _supplementValue = value; - removeFitSize(); - } - } - private string _supplementValue = ""; - - /// - public SKFont AdditionalCaptionFont - { - get => _additionalCaptionFont; - set - { - _additionalCaptionFont = value; - removeFitSize(); - } - } - // Create font with size 12 - private SKFont _additionalCaptionFont = new SKFont(SKTypeface.FromFamilyName("Arial", SKFontStyle.Normal), 12); - - /// - public int BarHeight - { - get => _drawing.BarHeight; - set - { - if (value < 1) - throw new BarcodeException("Bar can not be less than 1 pixel in height."); - - _drawing.BarHeight = value; - removeFitSize(); - } - } - - /// - public SKFont CaptionFont - { - get => _drawing.CaptionFont; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.CaptionFont = value; - removeFitSize(); - } - } - - /// - public int NarrowBarWidth - { - get => _drawing.NarrowBarWidth; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.NarrowBarWidth = value; - removeFitSize(); - } - } - - /// - public int WideToNarrowRatio - { - get => _drawing.WideToNarrowRatio; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.WideToNarrowRatio = value; - removeFitSize(); - } - } - - /// - public CaptionPosition AdditionalCaptionPosition - { - get => _additionalCaptionPosition; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _additionalCaptionPosition = value; - removeFitSize(); - } - } - private CaptionPosition _additionalCaptionPosition = CaptionPosition.Above; - - /// - public CaptionAlignment AdditionalCaptionAlignment - { - get => _additionalCaptionAlignment; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _additionalCaptionAlignment = value; - removeFitSize(); - } - } - private CaptionAlignment _additionalCaptionAlignment = CaptionAlignment.Auto; - - /// - public RotationAngle Angle - { - get => _angle; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _angle = value; - removeFitSize(); - } - } - private RotationAngle _angle = RotationAngle.Degrees0; - - /// - public SKColor BackColor - { - get => _backColor; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _backColor = value; - } - } - private SKColor _backColor = SKColors.White; - - /// - public CaptionPosition CaptionPosition - { - get => _drawing.CaptionPosition; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.CaptionPosition = value; - removeFitSize(); - } - } - - /// - public CaptionAlignment CaptionAlignment - { - get => _drawing.CaptionAlignment; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.CaptionAlignment = value; - removeFitSize(); - } - } - - /// - public SKColor ForeColor - { - get => _drawing.Color; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.Color = value; - } - } - - /// - public Margins Margins - { - get => _margins; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _margins = value; - RevertToNormalSize(); - } - } - private Margins _margins = new Margins(); - - /// - public float ResolutionX - { - get => _resolutionX; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - if (value <= 0) - throw new BarcodeException("Resolution should be greater then 0."); - - _resolutionX = value; - RevertToNormalSize(); - disposeGraphicsForMeasures(); - } - } - private float _resolutionX = 96; - - /// - public float ResolutionY - { - get => _resolutionY; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - if (value <= 0) - throw new BarcodeException("Resolution should be greater then 0"); - - _resolutionY = value; - RevertToNormalSize(); - disposeGraphicsForMeasures(); - } - } - private float _resolutionY = 96; - - /// - /// Gets or sets a value indicating whether unused space should be cut when - /// drawing or saving barcode images. Unused space is usually a result - /// of calling one of FitInto methods with size greater then needed - /// to draw barcode. - /// - /// true if unused space should be cut; otherwise, false. - public bool CutUnusedSpace - { - get => _cutUnusedSpace; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _cutUnusedSpace = value; - removeFitSize(); - } - } - private bool _cutUnusedSpace = false; - - /// - public bool PreserveMinReadableSize - { - get => _preserveMinReadableSize; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _preserveMinReadableSize = value; - } - } - private bool _preserveMinReadableSize = true; - - /// - public bool RoundDots - { - get => _drawing.RoundDots; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.RoundDots = value; - } - } - - /// - public int RoundDotsScale - { - get => _drawing.RoundDotsScale; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _drawing.RoundDotsScale = value; - } - } - - /// - public string Version => Assembly.GetExecutingAssembly().GetName().Version.ToString(); - - /// - public string Profiles - { - get => _profiles; - set - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - _profiles = value; -#if !BARCODESDK_EMBEDDED_SOURCES - _profileManager.ApplyProfiles(_profiles); -#endif - } - } - - /// - public bool ValueIsValid(string value) - { - return _drawing.ValueIsValid(value, false); - } - - /// - public bool ValueIsValid(string value, bool checksumIsMandatory) - { - return _drawing.ValueIsValid(value, checksumIsMandatory); - } - - /// - public bool ValueIsValidGS1(string value) - { - return GS1ValueChecker.Check(value); - } - - /// - public SKSize GetMinimalSize() - { - if (OutputSize != SKSize.Empty) - return OutputSize; - - return getDrawingSize(); - } - - /// - public void LoadProfiles(string fileName) - { - if (!File.Exists(fileName)) - throw new ArgumentException("File does not exist.", "fileName"); - -#if !BARCODEREADERSDK_EMBEDDED_SOURCES - _profileManager.LoadFromFile(fileName); -#endif - } - - /// - public void LoadProfilesFromString(string jsonString) - { -#if !BARCODEREADERSDK_EMBEDDED_SOURCES - _profileManager.LoadFromString(jsonString); -#endif - } - - /// - public void LoadAndApplyProfiles(string jsonString) - { -#if !BARCODEREADERSDK_EMBEDDED_SOURCES - _profileManager.LoadAndApplyProfiles(jsonString); - - List loadedProfileNames = new List(); - foreach (Profile profile in _profileManager.Profiles) - loadedProfileNames.Add(profile.Name); - _profiles = string.Join(",", loadedProfileNames.ToArray()); -#endif - } - - /// - public void CreateProfile(string profileName, string outputFileName) - { - File.WriteAllText(outputFileName, CreateProfile(profileName)); - } - - /// - public string CreateProfile(string profileName) - { - return $"\"profiles\": [ {{ \"{profileName}\": {JsonConvert.SerializeObject(this)} }} ]"; - } - - /// - public void FitInto(SKSize size) - { - FitInto(new SKSize(size.Width, size.Height), UnitOfMeasure.Pixel); - } - - /// - public void FitInto(int width, int height) - { - FitInto(new SKSize(width, height), UnitOfMeasure.Pixel); - } - - /// - [ComVisible(false)] - public void FitInto(SKSize size, UnitOfMeasure unit) - { -#if COMMUNITY_EDITION && !BARCODESDK_EMBEDDED_SOURCES - RegInfo.CommunityEditionDisclaimer(); - return; -#endif - SKSize sizePixels = Utils.GetSizeInPixels(_resolutionX, _resolutionY, size, unit); - fitInto(sizePixels); - } - - /// - public void FitInto(float width, float height, UnitOfMeasure unit) - { - FitInto(new SKSize(width, height), unit); - } - - /// - public void RevertToNormalSize() - { - _zoomLevel = 1.0f; - _outputSize = SKSize.Empty; - } - - /// - public void Draw(SKCanvas canvas, SKPoint position) - { - SKSize size = getDrawingSize(canvas); - SKSize barcodeSize = size; - - if (OutputSize != SKSize.Empty) - { - float left = 0; - float top = 0; - Utils.CalculateDrawPosition(size, OutputSize.Width, OutputSize.Height, out left, out top, - BarcodeHorizontalAlignment.Center, BarcodeVerticalAlignment.Middle); - - position.X += left; - position.Y += top; - - size = OutputSize; - } - - using (var paint = new SKPaint()) - { - paint.Style = SKPaintStyle.Fill; - paint.Color = BackColor; // assuming BackColor is System.Drawing.Color - - canvas.DrawRect(new SKRect( - position.X, - position.Y, - position.X + size.Width, - position.Y + size.Height), - paint); - } - - transformGraphicsAndDraw(canvas, barcodeSize, position.X, position.Y); - } - - /// - public SKBitmap GetImage() - { - float left = 0; - float top = 0; - SKSize size = getDrawingSize(); - SKSize barcodeSize = size; - - if (OutputSize != SKSize.Empty) - { - Utils.CalculateDrawPosition(size, OutputSize.Width, OutputSize.Height, out left, out top, - BarcodeHorizontalAlignment.Center, BarcodeVerticalAlignment.Middle); - - size = OutputSize; - } - - if (!ProduceMonochromeImages) - { - // Create SkiaSharp bitmap - var image = new SKBitmap((int)size.Width, (int)size.Height); - - using (var canvas = new SKCanvas(image)) - { - // Clear with background color - canvas.Clear(); - - // Call your custom drawing logic (need to adapt transformGraphicsAndDraw to SkiaSharp) - transformGraphicsAndDraw(canvas, barcodeSize, left, top); - } - - return image; - } - else - { - // Load TIFF image from bytes into a SkiaSharp bitmap - byte[] imageBytes = GetImageBytesTIFF(); - var monochromeImage = SKBitmap.Decode(imageBytes); - // (!) Do not dispose the stream as it should be live for the image lifetime. - - return monochromeImage; - } - } - - /// - public void SaveImage(string fileName) - { - using (FileStream fs = new FileStream(fileName, FileMode.Create)) - SaveImage(fs, Utils.FormatFromName(fileName)); - } - - /// - [ComVisible(false)] - public void SaveImage(string fileName, SKEncodedImageFormat imageFormat) - { - using (FileStream fs = new FileStream(fileName, FileMode.Create)) - { - SaveImage(fs, imageFormat); - } - } - - /// - [ComVisible(false)] - public void SaveImage(string fileName, SKEncodedImageFormat format, SKSize areaSize, int imageLeft, int imageTop) - { - using (FileStream fs = new FileStream(fileName, FileMode.Create)) - SaveImage(fs, format, areaSize, imageLeft, imageTop); - } - - /// - [ComVisible(false)] - public void SaveImage(Stream stream) - { - SaveImage(stream, SKEncodedImageFormat.Bmp); - } - - /// - [ComVisible(false)] - public void SaveImage(Stream stream, SKEncodedImageFormat format) - { - float left = 0; - float top = 0; - if (OutputSize != SKSize.Empty) - { - SKSize size = getDrawingSize(); - - Utils.CalculateDrawPosition(size, OutputSize.Width, OutputSize.Height, out left, out top, - BarcodeHorizontalAlignment.Center, BarcodeVerticalAlignment.Middle); - } - - saveImage(stream, format, OutputSize, (int)left, (int)top); - } - - /// - [ComVisible(false)] - public void SaveImage(Stream stream, SKEncodedImageFormat format, SKSize areaSize, int imageLeft, int imageTop) - { - saveImage(stream, format, areaSize, imageLeft, imageTop); - } - - /// - public byte[] GetImageBytes() - { - return getImageBytes(SKEncodedImageFormat.Bmp); - } - - /// - public byte[] GetImageBytesPNG() - { - return getImageBytes(SKEncodedImageFormat.Png); - } - - /// - public byte[] GetImageBytesGIF() - { - return getImageBytes(SKEncodedImageFormat.Gif); - } - - /// - public byte[] GetImageBytesTIFF() - { - return getImageBytes(SKEncodedImageFormat.Png); - } - - /// - public byte[] GetImageBytesJPG() - { - return getImageBytes(SKEncodedImageFormat.Jpeg); - } - - /// - public string GetValueRestrictions(SymbologyType symbology) - { - using (BarcodeEncoder temp = new BarcodeEncoder(symbology)) - return temp._drawing.getValueRestrictions(); - } - - /// - public string GetSupplementaryValueRestrictions() - { - return "Supplementary barcode value should be 0, 2 or 5 digits long.\n"; - } - - /// - public void SetCaptionFont(string familyName, int size) - { - var typeface = SKTypeface.FromFamilyName(familyName); - CaptionFont = new SKFont(typeface, size); - } - - /// - public void SetCaptionFont(string familyName, int size, bool bold, bool italic, bool underline, bool strikeout, byte gdiCharSet) - { - // Map bold/italic to SKFontStyle - SKFontStyleWeight weight = bold ? SKFontStyleWeight.Bold : SKFontStyleWeight.Normal; - SKFontStyleSlant slant = italic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright; - SKFontStyleWidth width = SKFontStyleWidth.Normal; - - var fontStyle = new SKFontStyle(weight, width, slant); - SKTypeface typeface = SKTypeface.FromFamilyName(familyName, fontStyle); - - CaptionFont = new SKFont(typeface, size); - - // Note: underline and strikeout are applied when drawing text with SKPaint - // e.g., paint.IsUnderline = underline; paint.IsStrikeThru = strikeout; - } - - /// - public void SetAdditionalCaptionFont(string familyName, int size) - { - // Create the typeface - SKTypeface typeface = SKTypeface.FromFamilyName(familyName); - - // Create the SKFont with the desired size - AdditionalCaptionFont = new SKFont(typeface, size); - } - - /// - public void SetAdditionalCaptionFont(string familyName, int size, bool bold, bool italic, bool underline, bool strikeout, byte gdiCharSet) - { - // Map bold/italic to SKFontStyle - SKFontStyleWeight weight = bold ? SKFontStyleWeight.Bold : SKFontStyleWeight.Normal; - SKFontStyleSlant slant = italic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright; - SKFontStyleWidth width = SKFontStyleWidth.Normal; - - // Create the typeface - SKTypeface typeface = SKTypeface.FromFamilyName(familyName, new SKFontStyle(weight, width, slant)); - - // Create the SKFont - AdditionalCaptionFont = new SKFont(typeface, size); - - // Underline and strikeout are applied when drawing text via SKPaint - // e.g.: - // paint.IsUnderline = underline; - // paint.IsStrikeThru = strikeout; - } - - /// - public void SetMargins(float left, float top, float right, float bottom, UnitOfMeasure unit) - { - SKSize sz1 = Utils.GetSizeInPixels(_resolutionX, _resolutionY, new SKSize(left, top), unit); - SKSize sz2 = Utils.GetSizeInPixels(_resolutionX, _resolutionY, new SKSize(right, bottom), unit); - Margins = new Margins((int)sz1.Width, (int)sz1.Height, (int)sz2.Width, (int)sz2.Height); - } - - /// - public void GetMargins(out float left, out float top, out float right, out float bottom, UnitOfMeasure unit) - { - SKSize sz1 = Utils.GetSizeInUnits(_resolutionX, _resolutionY, new SKSize(Margins.Left, Margins.Top), unit); - SKSize sz2 = Utils.GetSizeInUnits(_resolutionX, _resolutionY, new SKSize(Margins.Left, Margins.Top), unit); - - left = sz1.Width; - top = sz1.Height; - right = sz2.Width; - bottom = sz2.Height; - } - - /// - public float GetMarginLeft(UnitOfMeasure unit) - { - float left; - float top; - float right; - float bottom; - GetMargins(out left, out top, out right, out bottom, unit); - return left; - } - - /// - public float GetMarginTop(UnitOfMeasure unit) - { - float left; - float top; - float right; - float bottom; - GetMargins(out left, out top, out right, out bottom, unit); - return top; - } - - /// - public float GetMarginRight(UnitOfMeasure unit) - { - float left; - float top; - float right; - float bottom; - GetMargins(out left, out top, out right, out bottom, unit); - return right; - } - - /// - public float GetMarginBottom(UnitOfMeasure unit) - { - float left; - float top; - float right; - float bottom; - GetMargins(out left, out top, out right, out bottom, unit); - return bottom; - } - - /// - public void SetBarHeight(float height, UnitOfMeasure unit) - { - SKSize size = Utils.GetSizeInPixels(_resolutionX, _resolutionY, new SKSize(0, height), unit); - BarHeight = (int)Math.Max(size.Height, 1); - } - - /// - public float GetBarHeight(UnitOfMeasure unit) - { - SKSize size = Utils.GetSizeInUnits(_resolutionX, _resolutionY, new SKSize(0, BarHeight), unit); - return size.Height; - } - - /// - public void SetNarrowBarWidth(float width, UnitOfMeasure unit) - { - SKSize size = Utils.GetSizeInPixels(_resolutionX, _resolutionY, new SKSize(width, 0), unit); - NarrowBarWidth = (int)Math.Max(size.Width, 1); - } - - /// - public float GetNarrowBarWidth(UnitOfMeasure unit) - { - SKSize size = Utils.GetSizeInUnits(_resolutionX, _resolutionY, new SKSize(NarrowBarWidth, 0), unit); - return size.Width; - } - - /// - public SKSize GetMinimalSize(UnitOfMeasure unit) - { - SKSize sz = GetMinimalSize(); - SKSize res = Utils.GetSizeInUnits(_resolutionX, _resolutionY, sz, unit); - return res; - } - - /// - public float GetMinimalWidth(UnitOfMeasure unit) - { - SKSize size = GetMinimalSize(unit); - return size.Width; - } - - /// - public float GetMinimalHeight(UnitOfMeasure unit) - { - SKSize size = GetMinimalSize(unit); - return size.Height; - } - - /// - public void Draw(SKCanvas canvas, float left, float top, UnitOfMeasure unit) - { - SKSize size = Utils.GetSizeInPixels(canvas, new SKSize(left, top), unit); - Draw(canvas, new SKPoint(size.Width, size.Height)); - } - - // Handles changes in barcode options. - protected void OptionsChanged(Object sender, EventArgs e) - { - if (!_drawing.ValueIsValid(_drawing.Value, false)) - _drawing.Value = ""; - } - - private void disposeGraphicsForMeasures() - { - _graphicsForMeasures?.Dispose(); - _graphicsForMeasures = null; - - _bitmapForMeasures?.Dispose(); - _bitmapForMeasures = null; - } - - private void createGraphicsForMeasures() - { - disposeGraphicsForMeasures(); - _bitmapForMeasures = new SKBitmap(10, 10); - _graphicsForMeasures = new SKCanvas(_bitmapForMeasures); - } - - // Sets the default (the same for all symbologies) options and - private void setDefaultOptions(bool copyMode) - { - if (!copyMode) - { - _additionalCaption = ""; - _additionalCaptionPosition = CaptionPosition.Above; - _additionalCaptionAlignment = CaptionAlignment.Auto; - _margins = new Margins(); - - // Create a typeface for Arial, regular - SKTypeface typeface = SKTypeface.FromFamilyName("Arial"); - - // Create the SKFont with size 12 pixels - AdditionalCaptionFont = new SKFont(typeface, 12); - } - - _zoomLevel = 1.0f; - _outputSize = SKSize.Empty; - _cutUnusedSpace = false; - - if (!copyMode) - { - _drawing.Options = new SymbologyOptions(); - - _drawing.AddChecksum = true; - _drawing.AddChecksumToCaption = true; - - _drawing.CustomCaption = ""; - _drawing.DrawCaption = true; - SKTypeface typeface = SKTypeface.FromFamilyName("Arial"); - _drawing.CaptionFont = new SKFont(typeface, 12); - _drawing.CaptionPosition = CaptionPosition.Below; - _drawing.CaptionAlignment = CaptionAlignment.Auto; - } - - _drawing.BarHeight = 50; - _drawing.NarrowBarWidth = 3; - _drawing.WideToNarrowRatio = 3; - - if (!copyMode) - _drawing.Color = SKColors.Black; - } - - // Sets the symbology-specific optimized options and properties. - private void setOptimizedOptions(bool copyMode) - { - setDefaultOptions(copyMode); - -#if !QRCODESDK - - if (Symbology == SymbologyType.PDF417 || Symbology == SymbologyType.PDF417Truncated || - Symbology == SymbologyType.MacroPDF417 || Symbology == SymbologyType.DataMatrix || - Symbology == SymbologyType.GS1_DataMatrix) - { - _drawing.BarHeight = 6; - } - - if (Symbology == SymbologyType.MicroPDF417) - _drawing.BarHeight = 3; - - if (Symbology == SymbologyType.UPCA || Symbology == SymbologyType.UPCE || - Symbology == SymbologyType.EAN13 || Symbology == SymbologyType.EAN8 || - Symbology == SymbologyType.Plessey || Symbology == SymbologyType.MSI || - Symbology == SymbologyType.GTIN12 || Symbology == SymbologyType.GTIN8 || - Symbology == SymbologyType.GTIN13) - { - SetBarHeight(1.02f, UnitOfMeasure.Inch); - SetNarrowBarWidth(13f * 0.001f, UnitOfMeasure.Inch); // 1 mils == 0.001 inch - } - - if (Symbology == SymbologyType.GS1_DataBar_Omnidirectional) - _drawing.BarHeight = _drawing.NarrowBarWidth * 33; // Min height of symbols according to ISO/IEC 24724-2011 - // Max height is not limited - - if (Symbology == SymbologyType.PharmaCode) - { - SetNarrowBarWidth(0.6f, UnitOfMeasure.Millimeter); - _drawing.WideToNarrowRatio = 3; - } - -#endif - } - - private void removeFitSize() - { - RevertToNormalSize(); - } - - // IMPORTANT: Call this method AFTER setting the barcode value. - // Fits the barcode into specified size. - // Calling this method will change the barcode properties. - // Properties will be recalculated so that resulting barcode - // fit into specified size. - private void fitInto(SKSize size) - { - RevertToNormalSize(); - - SKSize drawingSize = getDrawingSize(); - float widthRatio = (float)drawingSize.Width / (float)size.Width; - float heightRatio = (float)drawingSize.Height / (float)size.Height; - - float ratio = 1.0f; - if (PreserveMinReadableSize) - { - ratio = 1.0f / widthRatio; - if (ratio < 1.0f) - { - size.Width = drawingSize.Width; - ratio = 1.0f; - } - - ratio = 1.0f / heightRatio; - if (ratio < 1.0f) - { - size.Height = drawingSize.Height; - ratio = 1.0f; - } - } - - widthRatio = (float)drawingSize.Width / (float)size.Width; - heightRatio = (float)drawingSize.Height / (float)size.Height; - - _zoomLevel = 1.0f / System.Math.Max(widthRatio, heightRatio); - - _outputSize = size; - } - - private void transformGraphicsAndDraw(SKCanvas canvas, SKSize occupiedByBarcodeOnly, float imageLeft, float imageTop) - { - // Move origin - canvas.Translate(imageLeft, imageTop); - - // Rotate (same logic you had for GDI+) - Utils.RotateGraphics(canvas, _angle, occupiedByBarcodeOnly.Width, occupiedByBarcodeOnly.Height); - - // Scale (zoom) - canvas.Scale(_zoomLevel, _zoomLevel); - - // --- Rendering quality settings --- - // In SkiaSharp, antialiasing is per SKPaint, not per canvas. - // So you’ll need to set IsAntialias = true on the SKPaints you use in drawOnCanvas. - // There is no direct TextRenderingHint equivalent, but you can tune - // SubpixelText and LcdRenderText on SKPaint for text quality. - - // Draw content - drawOnGraphics(canvas, false); - - // Reset transform - canvas.ResetMatrix(); - } - - private SKSize getDrawingSize() - { - if (_graphicsForMeasures == null) - createGraphicsForMeasures(); - - return getDrawingSize(_graphicsForMeasures); - } - - private SKSize getDrawingSize(SKCanvas canvas) - { - return drawOnGraphics(canvas, true); - } - - [Obsolete] - private SKSize drawOnGraphics(SKCanvas canvas, bool onlyCalculate) - { - // Final barcode image is as follows: - // - // ------------------------------------------------------------------------------------- - // | margin top | - // | additional caption | - // | QZ | - // | -------------------------- | - // | | | QZ | - // | | | ---------------- | - // | margin left QZ | barcode | QZ SS | supplement | QZ margin right | - // | | | ---------------- | - // | | | QZ | - // | -------------------------- | - // | QZ | - // | demo warning | - // | margin bottom | - // ------------------------------------------------------------------------------------- - // - // (QZ == Quiet Zone, SS == Supplement Space) - // - // Note: - // 1. Additional caption maybe drawn below barcode (just above demo warning) - // 2. There is always a blank space above additional caption. - // 3. Additional caption is centered between left and right margins and - // NOT between start and end of barcode. - // 4. Warning string starts right after margin (not where barcode starts) - - _drawing.BarcodeResolution = new SKSize(_resolutionX, _resolutionY); - - SKSize barcodeSize = _drawing.Draw(canvas, new SKPoint(0, 0), true); - - SKSize supplementBarcodeSize = new SKSize(); - BarcodeEncoder supplement = getSupplementaryBarcode(); - if (supplement != null) - { - supplementBarcodeSize = supplement._drawing.Draw(canvas, new SKPoint(0, 0), true); - supplementBarcodeSize.Width += Options.SupplementSpace + QuietZoneWidth; - supplementBarcodeSize.Height += QuietZoneHeight*2; - } - - string savedAdditionalCaption = setISBNCaption(); - - SKSize additionalCaptionSize = new SKSize(); - int additionalCaptionGap = 0; - if (AdditionalCaption.Length != 0) - { - SKRect sKRect = new SKRect(); - float width = AdditionalCaptionFont.MeasureText(AdditionalCaption, out sKRect); - SKSize sizeF = new SKSize(sKRect.Width, sKRect.Height); - additionalCaptionSize = new SKSize((int) (sizeF.Width + 1), (int) (sizeF.Height + 1)); - additionalCaptionGap = Utils.CalculateCaptionGap(AdditionalCaptionFont); - } - - // Main caption for left and right postions (SymbologyDrawing draws only standard top and bottom captions) - SKSize sideCaptionSize = new SKSize(); - int sideCaptionGap = 0; - if (DrawCaption && (CaptionPosition == CaptionPosition.Before || CaptionPosition == CaptionPosition.After)) - { - SKRect sKRect = new SKRect(); - float width = CaptionFont.MeasureText(_drawing.Caption, out sKRect); - SKSize sizeF = new SKSize(sKRect.Width, sKRect.Height); - sideCaptionSize = new SKSize((int) (sizeF.Width + 1), (int) (sizeF.Height + 1)); - sideCaptionGap = Utils.CalculateCaptionGap(CaptionFont); - } - - float widthOccupiedByBarcodes = QuietZoneWidth*2 + barcodeSize.Width + supplementBarcodeSize.Width; - - float additionalCaptionHeightAbove = 0; - float additionalCaptionWidthAbove = 0; - float additionalCaptionHeightBelow = 0; - float additionalCaptionWidthBelow = 0; - float additionalCaptionWidthBefore = 0; - float additionalCaptionHeightBefore = 0; - float additionalCaptionWidthAfter = 0; - float additionalCaptionHeightAfter = 0; - - if (!additionalCaptionSize.IsEmpty) - { - if (AdditionalCaptionPosition == CaptionPosition.Above) - { - additionalCaptionWidthAbove = additionalCaptionSize.Width; - additionalCaptionHeightAbove = additionalCaptionSize.Height + additionalCaptionGap; - } - else if (AdditionalCaptionPosition == CaptionPosition.Below) - { - additionalCaptionWidthBelow = additionalCaptionSize.Width; - additionalCaptionHeightBelow = additionalCaptionGap + additionalCaptionSize.Height; - } - else if (AdditionalCaptionPosition == CaptionPosition.Before) - { - additionalCaptionWidthBefore = additionalCaptionSize.Width + additionalCaptionGap; - additionalCaptionHeightBefore = additionalCaptionSize.Height; - } - else if (AdditionalCaptionPosition == CaptionPosition.After) - { - additionalCaptionWidthAfter = additionalCaptionGap + additionalCaptionSize.Width; - additionalCaptionHeightAfter = additionalCaptionSize.Height; - } - } - - float sideCaptionWidthBefore = 0; - float sideCaptionHeightBefore = 0; - float sideCaptionWidthAfter = 0; - float sideCaptionHeightAfter = 0; - - if (!sideCaptionSize.IsEmpty) - { - if (CaptionPosition == CaptionPosition.Before) - { - sideCaptionWidthBefore = sideCaptionSize.Width + sideCaptionGap; - sideCaptionHeightBefore = sideCaptionSize.Height; - } - else if (CaptionPosition == CaptionPosition.After) - { - sideCaptionWidthAfter = sideCaptionGap + sideCaptionSize.Width; - sideCaptionHeightAfter = sideCaptionSize.Height; - } - } - - if (!onlyCalculate) - { - // Determine horizontal alignment for SkiaSharp - SKTextAlign hAlign; - switch (AdditionalCaptionAlignment) - { - case CaptionAlignment.Auto: - hAlign = additionalCaptionWidthAbove > widthOccupiedByBarcodes - ? SKTextAlign.Left - : SKTextAlign.Center; - break; - case CaptionAlignment.Left: - hAlign = SKTextAlign.Left; - break; - case CaptionAlignment.Center: - hAlign = SKTextAlign.Center; - break; - case CaptionAlignment.Right: - hAlign = SKTextAlign.Right; - break; - default: - throw new ArgumentOutOfRangeException(); - } - - // Only draw if there’s text - if (!additionalCaptionSize.IsEmpty) - { - // Compute the rectangle where the caption will go - SKRect captionRect; - float verticalCenter; - - if (AdditionalCaptionPosition == CaptionPosition.Above) - { - captionRect = new SKRect( - _margins.Left + sideCaptionWidthBefore + QuietZoneWidth, - _margins.Top, - _margins.Left + sideCaptionWidthBefore + QuietZoneWidth + Math.Max(additionalCaptionWidthAbove, barcodeSize.Width), - _margins.Top + additionalCaptionHeightAbove); - - verticalCenter = captionRect.Top + additionalCaptionHeightAbove / 2f; - } - else if (AdditionalCaptionPosition == CaptionPosition.Below) - { - captionRect = new SKRect( - _margins.Left + sideCaptionWidthBefore + QuietZoneWidth, - _margins.Top + barcodeSize.Height + additionalCaptionGap, - _margins.Left + sideCaptionWidthBefore + QuietZoneWidth + Math.Max(additionalCaptionWidthBelow, barcodeSize.Width), - _margins.Top + barcodeSize.Height + additionalCaptionGap + additionalCaptionHeightBelow); - - verticalCenter = captionRect.Top + additionalCaptionHeightBelow / 2f; - } - else if (AdditionalCaptionPosition == CaptionPosition.Before) - { - captionRect = new SKRect( - _margins.Left, - _margins.Top, - _margins.Left + additionalCaptionWidthBefore, - _margins.Top + Math.Max(additionalCaptionHeightBefore, barcodeSize.Height)); - - verticalCenter = captionRect.Top + (Math.Max(additionalCaptionHeightBefore, barcodeSize.Height) / 2f); - if (AdditionalCaptionAlignment == CaptionAlignment.Auto) - hAlign = SKTextAlign.Right; - } - else // After - { - captionRect = new SKRect( - _margins.Left + sideCaptionWidthBefore + widthOccupiedByBarcodes + sideCaptionWidthAfter + additionalCaptionGap, - _margins.Top, - _margins.Left + sideCaptionWidthBefore + widthOccupiedByBarcodes + sideCaptionWidthAfter + additionalCaptionGap + additionalCaptionWidthAfter, - _margins.Top + Math.Max(additionalCaptionHeightAfter, barcodeSize.Height)); - - verticalCenter = captionRect.Top + (Math.Max(additionalCaptionHeightAfter, barcodeSize.Height) / 2f); - if (AdditionalCaptionAlignment == CaptionAlignment.Auto) - hAlign = SKTextAlign.Left; - } - - // Setup paint - using (var paint = new SKPaint - { - IsAntialias = true, - Color = _drawing.Color, - Typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Normal), - TextSize = 12, - TextAlign = hAlign - }) - { - // Get font metrics - SKFontMetrics metrics; - paint.GetFontMetrics(out metrics); - - // Compute y to center text vertically - float y = verticalCenter - (metrics.Ascent + metrics.Descent) / 2; - - // Compute x based on SKTextAlign - float x; - switch (hAlign) - { - case SKTextAlign.Left: - x = captionRect.Left; - break; - case SKTextAlign.Center: - x = (captionRect.Left + captionRect.Right) / 2f; // MidX - break; - case SKTextAlign.Right: - x = captionRect.Right; - break; - default: - x = captionRect.Left; - break; - } - - // Draw the text - canvas.DrawText(AdditionalCaption, x, y, paint); - } - } - - if (!sideCaptionSize.IsEmpty) - { - SKRect sideCaptionRect; - - if (CaptionPosition == CaptionPosition.Before) - { - sideCaptionRect = new SKRect( - _margins.Left + additionalCaptionWidthBefore, - _margins.Top + additionalCaptionHeightAbove, - _margins.Left + additionalCaptionWidthBefore + sideCaptionWidthBefore, - _margins.Top + additionalCaptionHeightAbove + Math.Max(sideCaptionHeightBefore, barcodeSize.Height)); - } - else // CaptionPosition.After - { - sideCaptionRect = new SKRect( - _margins.Left + additionalCaptionWidthBefore + sideCaptionGap + widthOccupiedByBarcodes + sideCaptionGap, - _margins.Top + additionalCaptionHeightAbove, - _margins.Left + additionalCaptionWidthBefore + sideCaptionGap + widthOccupiedByBarcodes + sideCaptionGap + sideCaptionWidthAfter, - _margins.Top + additionalCaptionHeightAbove + Math.Max(sideCaptionHeightAfter, barcodeSize.Height)); - } - - // Create SKPaint with font and color - using (var paint = new SKPaint()) - { - paint.IsAntialias = true; - paint.Color = _drawing.Color; // convert System.Drawing.Color to SKColor - paint.Typeface = CaptionFont.Typeface; // expose SKTypeface from your font wrapper - paint.TextSize = CaptionFont.Size; - - // Map horizontal alignment - switch (CaptionAlignment) - { - case CaptionAlignment.Left: - paint.TextAlign = SKTextAlign.Left; - break; - case CaptionAlignment.Center: - paint.TextAlign = SKTextAlign.Center; - break; - case CaptionAlignment.Right: - paint.TextAlign = SKTextAlign.Right; - break; - case CaptionAlignment.Auto: - paint.TextAlign = SKTextAlign.Center; // or Left/Right logic if needed - break; - } - - // Get font metrics for vertical alignment - SKFontMetrics metrics; - paint.GetFontMetrics(out metrics); - - // Compute X/Y - float x; - if (paint.TextAlign == SKTextAlign.Left) - x = sideCaptionRect.Left; - else if (paint.TextAlign == SKTextAlign.Center) - x = sideCaptionRect.MidX; - else // Right - x = sideCaptionRect.Right; - - float y = sideCaptionRect.Top + (sideCaptionRect.Height / 2) - (metrics.Ascent + metrics.Descent) / 2; - - // Draw the text - canvas.DrawText(_drawing.Caption, x, y, paint); - } - } - - // draw main barcode - SKPoint barcodePosition = new SKPoint(_margins.Left + additionalCaptionWidthBefore + sideCaptionWidthBefore + QuietZoneWidth, - _margins.Top + additionalCaptionHeightAbove); - _drawing.Draw(canvas, barcodePosition, false); - - if (Symbology == SymbologyType.QRCode && _decorationImage != null) - { - if (_decorationImage.Width > barcodeSize.Width && _decorationImageScale == -1) - throw new BarcodeException("The decoration image is large than the generated barcode."); - - // HighQualityBicubic equivalent - var sampling = new SKSamplingOptions(SKCubicResampler.Mitchell); - - // Compute scaled decoration image size - int decorationImageWidth = _decorationImageScale != -1 - ? (int)Math.Round(Math.Sqrt(barcodeSize.Width * barcodeSize.Width * _decorationImageScale / 100d)) - : _decorationImage.Width; - - int decorationImageHeight = _decorationImageScale != -1 - ? (int)Math.Round(Math.Sqrt(barcodeSize.Height * barcodeSize.Height * _decorationImageScale / 100d)) - : _decorationImage.Height; - - if (_decorationImageScale != -1 && (barcodeSize.Width - decorationImageWidth) % 2 > 0) - decorationImageWidth++; - - if (_decorationImageScale != -1 && (barcodeSize.Height - decorationImageHeight) % 2 > 0) - decorationImageHeight++; - - // Center position - float x = barcodePosition.X + barcodeSize.Width / 2 - decorationImageWidth / 2; - float y = barcodePosition.Y + barcodeSize.Height / 2 - decorationImageHeight / 2; - - // Destination rectangle - var destRect = new SKRect(x, y, x + decorationImageWidth, y + decorationImageHeight); - - // Source rectangle - var srcRect = new SKRect(0, 0, _decorationImage.Width, _decorationImage.Height); - - // Draw image with cubic interpolation - canvas.DrawImage(_decorationImage, srcRect, destRect, sampling); - } - - // draw supplement barcode, if needed - if (supplement != null) - { - supplement._drawing.Draw(canvas, - new SKPoint(_margins.Left + additionalCaptionWidthBefore + sideCaptionWidthBefore + QuietZoneWidth + barcodeSize.Width + - QuietZoneWidth + Options.SupplementSpace, - _margins.Top + additionalCaptionHeightAbove + QuietZoneHeight * 2), false); - } - } - - if (savedAdditionalCaption != null) - AdditionalCaption = savedAdditionalCaption; - - if (supplement != null) - supplement.Dispose(); - - float imageWidth = Margins.Left + additionalCaptionWidthBefore + sideCaptionWidthBefore + - Math.Max(widthOccupiedByBarcodes, Math.Max(additionalCaptionWidthAbove, additionalCaptionWidthBelow)) + - sideCaptionWidthAfter + additionalCaptionWidthAfter + Margins.Right; - float imageHeight = Margins.Top + additionalCaptionHeightAbove + - Math.Max(barcodeSize.Height, Math.Max(additionalCaptionHeightBefore, additionalCaptionHeightAfter)) + - additionalCaptionHeightBelow + /*warningSize.Height +*/ Margins.Bottom; - - imageWidth *= _zoomLevel; - imageHeight *= _zoomLevel; - - SKSize imageSize = rotateSize(new SKSize(imageWidth, imageHeight)); - imageSize.Width = Math.Max(imageSize.Width, 1); - imageSize.Height = Math.Max(imageSize.Height, 1); - - // draw license warning, if needed - if (true) - { - bool is2DSymbology = Utils.Is2DSymbology(_drawing.Symbology); - - if (is2DSymbology && Forbid2D || !is2DSymbology && Forbid1D) - { - /*using (SolidBrush brush = new SolidBrush(Color.Red)) - { - string message = RegInfo.GetLicenseWarningString(); - Font font = new Font(CaptionFont.Name, CaptionFont.Size, FontStyle.Bold); - Size size = g.MeasureString(message, font).ToSize(); - size.Width += 4; - size.Height += 4; - imageSize.Width = Math.Max(size.Width, imageSize.Width); - imageSize.Height = Math.Max(size.Height, imageSize.Height); - - if (!onlyCalculate) - { - using (Pen pen = new Pen(Color.White, 7)) - { - g.DrawLine(pen, 0, imageSize.Height / 2, imageSize.Width, imageSize.Height / 2); - g.DrawLine(pen, imageSize.Width / 2, 0, imageSize.Width / 2, imageSize.Height); - } - - StringFormat format = new StringFormat(StringFormatFlags.NoClip | StringFormatFlags.NoWrap); - format.Alignment = StringAlignment.Center; - format.LineAlignment = StringAlignment.Center; - g.DrawString(message, font, brush, new Rectangle(0, 0, imageSize.Width, imageSize.Height), format); - } - }*/ - - throw new BarcodeException("The barcode cannot be generated because the current license forbids it.\n"); - - } - } - return imageSize; - } - - private int QuietZoneWidth - { - get - { - if (!DrawQuietZones) - return 0; - - if (_drawing is SymbologyDrawing2D) - return (2 * NarrowBarWidth); - - return (10 * NarrowBarWidth); - } - } - - private int QuietZoneHeight - { - get - { - if (!DrawQuietZones) - return 0; - - if (_drawing is SymbologyDrawing2D) - return (2 * NarrowBarWidth); - - return 0; - } - } - - private SKSize OutputSize - { - get - { - if (_cutUnusedSpace) - return SKSize.Empty; - - return _outputSize; - } - } - - private byte[] getImageBytes(SKEncodedImageFormat format) - { - using (MemoryStream ms = new MemoryStream()) - { - SaveImage(ms, format); - return ms.ToArray(); - } - } - - private BarcodeEncoder getSupplementaryBarcode() - { - BarcodeEncoder supplement = null; - - if (Value.Length != 0 && SupplementValue.Length != 0) - { -#if !QRCODESDK - if (Symbology == SymbologyType.EAN13 || Symbology == SymbologyType.ISBN || Symbology == SymbologyType.UPCA) - { - supplement = new BarcodeEncoder(this, this.Symbology); - supplement.SupplementValue = ""; - - if (SupplementValue.Length == 2) - supplement.Symbology = SymbologyType.EAN2; - else - supplement.Symbology = SymbologyType.EAN5; - - supplement.Value = SupplementValue; - supplement.BarHeight = BarHeight * 3 / 4; - supplement.NarrowBarWidth = NarrowBarWidth; - - supplement.CaptionFont = new SKFont(CaptionFont.Typeface, CaptionFont.Size); - supplement.Angle = Angle; - supplement.ResolutionX = ResolutionX; - supplement.ResolutionY = ResolutionY; - - supplement.WideToNarrowRatio = WideToNarrowRatio; - } -#endif - } - - return supplement; - } - - private string setISBNCaption() - { - string savedAdditionalCaption = null; -#if !QRCODESDK - if (_drawing.Symbology == SymbologyType.ISBN && Options.ISBNAutoCaption) - { - ISBNSymbology isbn = _drawing as ISBNSymbology; - savedAdditionalCaption = AdditionalCaption; - AdditionalCaption = isbn.GetAutoCaption(); - } -#endif - return savedAdditionalCaption; - } - - // Rotates the given size object if needed. - private SKSize rotateSize(SKSize size) - { - if (_angle == RotationAngle.Degrees270 || _angle == RotationAngle.Degrees90) - { - float temp = size.Width; - size.Width = size.Height; - size.Height = temp; - } - - return size; - } - - // Saves the image with the barcode. - private void saveImage(Stream stream, SKEncodedImageFormat format, SKSize areaSize, int imageLeft, int imageTop) - { - if (stream == null) - throw new BarcodeException("Stream should not be null."); - - Stream metafileStream = null; - - SKSize size = getDrawingSize(); - SKSize barcodeSize = size; - - if (areaSize != SKSize.Empty) - { - if (PreserveMinReadableSize) - { - if (size.Width > areaSize.Width || size.Height > areaSize.Height) - { - string ex = string.Format( - "The barcode size you have specified is too small and this may cause the unreadable barcode.\n" + - "The minimal size of the barcode for the specified barcode value and symbology is\n" + - " {0}x{1} (in pixels) with DPI(X)={2} and DPI(Y)={3}.\n\n" + - " TIP: You can turn off such exceptions by setting PreserveMinReadableSize to false (NOT recommended)", - size.Width, size.Height, _resolutionX, _resolutionY); - - throw new BarcodeException(ex); - } - } - - size = areaSize; - } - - SKImage image = null; - - if (metafileStream != null) - { - // SkiaSharp cannot create a metafile. - // Instead, use SKPictureRecorder if you want vector output. - var recorder = new SKPictureRecorder(); - var rect = new SKRect(0, 0, size.Width, size.Height); - var canvas = recorder.BeginRecording(rect); - - // Fill background - canvas.Clear(BackColor); - - // Do your drawing - transformGraphicsAndDraw(canvas, barcodeSize, imageLeft, imageTop); - - // End recording → produces SKPicture - var picture = recorder.EndRecording(); - picture.Serialize(metafileStream); // vector serialization (Skia format, not EMF/WMF) - picture.Dispose(); - - return; // no further bitmap steps - } - else - { - // Create a raster image - var info = new SKImageInfo((int)size.Width, (int)size.Height, SKColorType.Rgba8888, SKAlphaType.Premul); - using (var surface = SKSurface.Create(info)) - { - var canvas = surface.Canvas; - - // Fill background - canvas.Clear(BackColor); - - // Do your drawing - transformGraphicsAndDraw(canvas, barcodeSize, imageLeft, imageTop); - - // Snapshot to SKImage - image = surface.Snapshot(); - - // If you need raw bitmap resolution handling (DPI), Skia doesn't have SetResolution. - // You embed DPI when encoding (e.g., PNG with pHYs chunk). - } - } - - // Create a new SKBitmap with the same size - SKBitmap copy = new SKBitmap(image.Width, image.Height); - // Copy pixels from SKImage into SKBitmap - bool success = image.ReadPixels(copy.Info, copy.GetPixels(), copy.RowBytes, 0, 0); - if (!success) - throw new Exception("Failed to convert SKImage to SKBitmap."); - - // Only do the clone if saving as GIF (optional in SkiaSharp) - if (format == SKEncodedImageFormat.Gif) - { - using (SKSurface surface = SKSurface.Create(new SKImageInfo(copy.Width, copy.Height))) - { - SKCanvas canvas = surface.Canvas; - - // Clear the surface (optional) - canvas.Clear(SKColors.Transparent); - - // Draw the original bitmap onto the new surface - canvas.DrawBitmap(copy, 0, 0); - - // Take a snapshot as a new SKImage - using (SKImage clonedImage = surface.Snapshot()) - { - // Convert back to SKBitmap if you need a mutable bitmap - SKBitmap clonedBitmap = new SKBitmap(clonedImage.Width, clonedImage.Height); - clonedImage.ReadPixels(clonedBitmap.Info, clonedBitmap.GetPixels(), clonedBitmap.RowBytes, 0, 0); - - copy = clonedBitmap; - } - } - } - - using (MemoryStream ms = new MemoryStream()) - { - if (ProduceMonochromeImages) - { - if (format == SKEncodedImageFormat.Png) - Utils.SaveAsBitonalTiff(copy, ms); - else - { - // Assume 'copy' is SKBitmap - using (SKBitmap bitonal = Utils.ConvertToBitonal(copy)) - using (SKData data = bitonal.Encode(format, 100)) - { - data.SaveTo(ms); - } - } - } - else - { - try - { - using (SKData data = copy.Encode(format, 100)) - { - data.SaveTo(ms); - } - } - catch (ExternalException) - { - // refs #526 (crashing on batch generation of barcodes) - // some delay as advised on https://social.msdn.microsoft.com/Forums/vstudio/en-US/b15357f1-ad9d-4c80-9ec1-92c786cca4e6/bitmapsave-a-generic-error-occurred-in-gdi?forum=netfxbcl - Thread.Sleep(30); - // try to save again - ms.Position = 0; // reset the position to zero - using (SKData data = copy.Encode(format, 100)) - { - data.SaveTo(ms); - } - } - } - - // finally write to the output stream - ms.WriteTo(stream); - } - - copy.Dispose(); - - image.Dispose(); - } - - /// - public void DrawToImage(string inputFile, int pageIndex, int x, int y, string outputFile) - { - if (String.IsNullOrEmpty(inputFile)) - throw new ArgumentException("inputFile should not by null or empty.", "inputFile"); - - if (!File.Exists(inputFile)) - throw new ArgumentException("inputFile should specify existing PDF file.", "inputFile"); - - if (String.Compare(inputFile, outputFile, StringComparison.OrdinalIgnoreCase) == 0) - throw new ArgumentException("Cannot not save to the same file.", "outputFile"); - - using (Stream inputStream = new FileStream(inputFile, FileMode.Open, FileAccess.Read)) - using (Stream outputStream = new FileStream(outputFile, FileMode.Create, FileAccess.ReadWrite)) - DrawToImage(inputStream, pageIndex, x, y, outputStream); - } - - /// - public void DrawToImage(Stream inputStream, int pageIndex, int x, int y, Stream outputStream) - { - if (inputStream == null) - throw new ArgumentNullException("inputStream"); - - if (outputStream == null) - throw new ArgumentNullException("outputStream"); - - if (!inputStream.CanRead) - throw new ArgumentException("inputStream must be readable.", "inputStream"); - - if (!outputStream.CanWrite) - throw new ArgumentException("outputStream must be writable.", "outputStream"); - - using (ImageStamper imageStamper = new ImageStamper()) - imageStamper.DrawToImage(inputStream, GetImage(), pageIndex, x, y, outputStream); - } - - /// - public void SetForeColorRGB(byte r, byte g, byte b) - { - ForeColor = new SKColor((byte)r, (byte)g, (byte)b); - } - - /// - public void SetBackColorRGB(byte r, byte g, byte b) - { - BackColor = new SKColor((byte)r, (byte)g, (byte)b); - } - - /// - public void SetCustomCaptionGap(float gap, UnitOfMeasure unit) - { - _drawing.CustomCaptionGap = (float.IsNaN(gap)) ? -1 : Utils.UnitsToPixels(_resolutionY, gap, unit); - } - - /// - /// Determines whether the symbology is of 2D type. - /// - /// BarcodeEncoder type to check whether it's of 2D type. - /// True if the specified symbology is of 2D type. - public static bool Is2DSymblogy(SymbologyType symbology) - { - return symbology == SymbologyType.Aztec || symbology == SymbologyType.DataMatrix || symbology == SymbologyType.MaxiCode || - symbology == SymbologyType.PDF417 || symbology == SymbologyType.PDF417Truncated || - symbology == SymbologyType.MacroPDF417 || symbology == SymbologyType.MicroPDF417 || symbology == SymbologyType.QRCode; - } - - /// - public string ProcessMacros(string value) - { - StringBuilder builder = new StringBuilder(value); - - builder.Replace("{NUL}", ((char) 0).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{SOH}", ((char) 1).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{STX}", ((char) 2).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{ETX}", ((char) 3).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{EOT}", ((char) 4).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{ENQ}", ((char) 5).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{ACK}", ((char) 6).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{BEL}", ((char) 7).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{BS}", ((char) 8).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{TAB}", ((char) 9).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{LF}", ((char) 10).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{VT}", ((char) 11).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{FF}", ((char) 12).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{CR}", ((char) 13).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{SO}", ((char) 14).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{SI}", ((char) 15).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{DLE}", ((char) 16).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{DC1}", ((char) 17).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{DC2}", ((char) 18).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{DC3}", ((char) 19).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{DC4}", ((char) 20).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{NAK}", ((char) 21).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{SYN}", ((char) 22).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{ETB}", ((char) 23).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{CAN}", ((char) 24).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{EM}", ((char) 25).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{SUB}", ((char) 26).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{ESC}", ((char) 27).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{FS}", ((char) 28).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{GS}", ((char) 29).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{RS}", ((char) 30).ToString(CultureInfo.InvariantCulture)); - builder.Replace("{US}", ((char) 31).ToString(CultureInfo.InvariantCulture)); - - return builder.ToString(); - } - - /// - public void AddDecorationImage(string imageFileName, int scale) - { - byte[] bytes = File.ReadAllBytes(imageFileName); - MemoryStream memoryStream = new MemoryStream(bytes); - _decorationImage = SKImage.FromBitmap(SKBitmap.Decode(memoryStream)); - _decorationImageScale = scale; - } - - /// - [ComVisible(false)] - public void AddDecorationImage(SKImage image, int scale) - { - _decorationImage = image; - _decorationImageScale = scale; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/BarcodeException.cs b/MuPDF.NET/Barcode/Encoders/BarcodeException.cs deleted file mode 100644 index 39a374de..00000000 --- a/MuPDF.NET/Barcode/Encoders/BarcodeException.cs +++ /dev/null @@ -1,33 +0,0 @@ - -using System; -using System.Text; - -namespace BarcodeWriter.Core -{ - /// - /// Represents errors that occur during BarcodeWriter.Core class library execution. - /// - /// - /// The following example demonstrates usage of class. - /// - /// - public class BarcodeException : Exception - { - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The exception message that describes the error. - public BarcodeException(string message) : base(message) - { - } - - /// - /// Initializes a new instance of the class with a specified error message and inner exception. - /// - /// The exception message that describes the error. - /// The Inner exception - public BarcodeException(string message, Exception internalException) : base(message, internalException) - { - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/BarcodeHorizontalAlignment.cs b/MuPDF.NET/Barcode/Encoders/BarcodeHorizontalAlignment.cs deleted file mode 100644 index 1573b847..00000000 --- a/MuPDF.NET/Barcode/Encoders/BarcodeHorizontalAlignment.cs +++ /dev/null @@ -1,24 +0,0 @@ - -namespace BarcodeWriter.Core -{ - /// - /// Describes options for horizontal alignment of the barcode within the target rectangle. - /// - public enum BarcodeHorizontalAlignment - { - /// - /// (0) Align the barcode to the left edge of the target rectangle. - /// - Left = 0, - - /// - /// (1) Horizontally center the barcode in the target rectangle. - /// - Center = 1, - - /// - /// (2) Align the barcode to the right edge of the target rectangle. - /// - Right = 2 - } -} diff --git a/MuPDF.NET/Barcode/Encoders/BarcodeVerticalAlignment.cs b/MuPDF.NET/Barcode/Encoders/BarcodeVerticalAlignment.cs deleted file mode 100644 index 891edc7d..00000000 --- a/MuPDF.NET/Barcode/Encoders/BarcodeVerticalAlignment.cs +++ /dev/null @@ -1,24 +0,0 @@ - -namespace BarcodeWriter.Core -{ - /// - /// Describes options for vertical alignment of the barcode within the target rectangle. - /// - public enum BarcodeVerticalAlignment - { - /// - /// (0) Align the barcode to the top edge of the target rectangle. - /// - Top = 0, - - /// - /// (1) Vertically center the barcode in the target rectangle. - /// - Middle = 1, - - /// - /// (2) Align the barcode to the bottom edge of the target rectangle. - /// - Bottom = 2 - } -} diff --git a/MuPDF.NET/Barcode/Encoders/CaptionAlignment.cs b/MuPDF.NET/Barcode/Encoders/CaptionAlignment.cs deleted file mode 100644 index 91844e37..00000000 --- a/MuPDF.NET/Barcode/Encoders/CaptionAlignment.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace BarcodeWriter.Core -{ - /// - /// Describes options for barcode caption alignment. - /// - public enum CaptionAlignment - { - /// - /// (0) Automatic alignment. - /// - Auto = 0, - - /// - /// (1) Align caption text to the left. - /// - Left = 1, - - /// - /// (2) Align caption text to the center. - /// - Center = 2, - - /// - /// (3) Align caption text to the right. - /// - Right = 3 - } -} diff --git a/MuPDF.NET/Barcode/Encoders/CaptionPosition.cs b/MuPDF.NET/Barcode/Encoders/CaptionPosition.cs deleted file mode 100644 index c07071e6..00000000 --- a/MuPDF.NET/Barcode/Encoders/CaptionPosition.cs +++ /dev/null @@ -1,29 +0,0 @@ - -namespace BarcodeWriter.Core -{ - /// - /// Describes options for barcode caption position. - /// - public enum CaptionPosition - { - /// - /// (0) Caption above the barcode. - /// - Above = 0, - - /// - /// (1) Caption below the barcode. - /// - Below = 1, - - /// - /// (2) Caption before the barcode (at left). - /// - Before = 2, - - /// - /// (3) Caption after the barcode (at right). - /// - After= 3, - } -} diff --git a/MuPDF.NET/Barcode/Encoders/CodabarChecksumAlgorithm.cs b/MuPDF.NET/Barcode/Encoders/CodabarChecksumAlgorithm.cs deleted file mode 100644 index cab745b4..00000000 --- a/MuPDF.NET/Barcode/Encoders/CodabarChecksumAlgorithm.cs +++ /dev/null @@ -1,40 +0,0 @@ - -namespace BarcodeWriter.Core -{ - /// - /// Describes all supported Codabar checksum algorithms. - /// - /// - /// Note that there is no checksum defined as part of the Codabar standard, but some industries - /// (libraries, for example) have adopted their own checksum standards. BarcodeWriter.Core - /// implements two such standards. - /// - public enum CodabarChecksumAlgorithm - { - /// - /// (0) Modulo 9 checksum algorithm. - /// - /// Many libraries use the following system which includes 13 digits - /// plus a checksum; - /// - /// - /// Digit 1 indicates the type of barcode: 2 = patron, 3 = item (book) - /// - /// - /// Digits 2-5 identify the institution - /// - /// - /// The next 6 digits (00010 586) identify the individual patron or item - /// - /// - /// Digit 14 is the checksum - /// - /// - Modulo9 = 0, - - /// - /// (1) AIIM check digit calculation algorithm. This standard is recommended by AIIM. - /// - AiimCheckDigit = 1, - } -} diff --git a/MuPDF.NET/Barcode/Encoders/CodabarSpecialSymbol.cs b/MuPDF.NET/Barcode/Encoders/CodabarSpecialSymbol.cs deleted file mode 100644 index 8f7788bc..00000000 --- a/MuPDF.NET/Barcode/Encoders/CodabarSpecialSymbol.cs +++ /dev/null @@ -1,31 +0,0 @@ - -namespace BarcodeWriter.Core -{ - /// - /// Describes special symbols of the Codabar symbology. - /// This symbols can be set as Codabar start or termination symbols. - /// Additional data can be encoded by the choice of start and termination characters. - /// - public enum CodabarSpecialSymbol - { - /// - /// (0) The A symbol. - /// - A = 0, - - /// - /// (1) The B symbol. - /// - B = 1, - - /// - /// (2) The C symbol. - /// - C = 2, - - /// - /// (3) The D symbol - /// - D = 3 - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Code128Alphabet.cs b/MuPDF.NET/Barcode/Encoders/Code128Alphabet.cs deleted file mode 100644 index 92fcbf3d..00000000 --- a/MuPDF.NET/Barcode/Encoders/Code128Alphabet.cs +++ /dev/null @@ -1,36 +0,0 @@ - -namespace BarcodeWriter.Core -{ - /// - /// Describes alphabet options for Code 128 symbology. - /// - public enum Code128Alphabet - { - /// - /// (0) The alphabet to use selected automatically. - /// Different parts of a barcode can be encoded with different alphabets. - /// This alphabet type allows all ASCII table to be encoded with - /// minimum output barcode width. - /// - Auto = 0, - - /// - /// (1) The A alphabet. Allows only character from NUL (ASCII 0) to - /// '_' (ASCII 95) to be encoded. - /// - A = 1, - - /// - /// (2) The B alphabet. Allows only character from SPACE (ASCII 32) to - /// DEL (ASCII 127) to be encoded. - /// - B = 2, - - /// - /// (3) The C alphabet. Allows only numeric values having even length to - /// be encoded. This alphabet allows most efficient encoding - /// of numeric values having even length. - /// - C = 3 - } -} diff --git a/MuPDF.NET/Barcode/Encoders/DataMatrixCompactionMode.cs b/MuPDF.NET/Barcode/Encoders/DataMatrixCompactionMode.cs deleted file mode 100644 index 134f9d56..00000000 --- a/MuPDF.NET/Barcode/Encoders/DataMatrixCompactionMode.cs +++ /dev/null @@ -1,57 +0,0 @@ - -using System; -using System.Text; - -namespace BarcodeWriter.Core -{ - /// - /// Describes all possible compaction (encoding) modes for DataMatrix symbology. - /// - public enum DataMatrixCompactionMode - { - /// - /// (-1) Library will choose best compaction mode (aim is to produce shortest encoded byte string). - /// - Auto = -1, - - /// - /// (0) ASCII compaction mode. Sort of 'general purpose' mode. One encoded symbol - /// produced from: 1/2 of source symbol for ASCII characters with code from 128 to 255, - /// 1 source symbol for ASCII characters with code from 0 to 127, - /// 2 source symbols for ASCII digits. - /// - Ascii = 0, - - /// - /// (1) C40 compaction mode. Best for upper-case alphanumeric strings. One encoded - /// symbol produced from 1 and 1/2 of source symbols. - /// - C40 = 1, - - /// - /// (2) TEXT compaction mode. Best for lower-case alphanumeric strings. One encoded - /// symbol produced from 1 and 1/2 of source symbols. - /// - Text = 2, - - /// - /// (3) X12 compaction mode. Useful for encoding symbols from - /// " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\r*>" range. One encoded - /// symbol produced from 1 and 1/2 of source symbols. - /// - X12 = 3, - - /// - /// (4) EDIFACT compaction mode. Best for ASCII charcters with codes - /// from 32 to 94. One encoded symbol produced from 1 and 1/3 of - /// source symbols. - /// - Edifact = 4, - - /// - /// (5) BINARY (or BASE256) compaction mode. Can encoded all ASCII symbols. - /// One encoded symbol produced from one source symbol. - /// - Binary = 5, - } -} diff --git a/MuPDF.NET/Barcode/Encoders/DataMatrixSize.cs b/MuPDF.NET/Barcode/Encoders/DataMatrixSize.cs deleted file mode 100644 index 9ad4a293..00000000 --- a/MuPDF.NET/Barcode/Encoders/DataMatrixSize.cs +++ /dev/null @@ -1,172 +0,0 @@ - -using System; -using System.Text; - -namespace BarcodeWriter.Core -{ - /// - /// Describes all possible matrix sizes for DataMatrix symbology. - /// - public enum DataMatrixSize - { - /// - /// (0) Library will choose smallest possible square matrix size. - /// - AutoSquareSize = 0, - - /// - /// (1) Library will choose smallest possible matrix size. - /// - AutoSize, - - /// - /// (2) 8 rows x 18 columns. 1 data region. - /// - Size8x18, - - /// - /// (3) 8 rows x 32 columns. 2 data regions. - /// - Size8x32, - - /// - /// (4) 10 rows x 10 columns. 1 data region. - /// - Size10x10, - - /// - /// (5) 12 rows x 12 columns. 1 data region. - /// - Size12x12, - - /// - /// (6) 12 rows x 26 columns. 1 data region. - /// - Size12x26, - - /// - /// (7) 12 rows x 36 columns. 2 vertical data regions. - /// - Size12x36, - - /// - /// (8) 14 rows x 14 columns. 1 data region. - /// - Size14x14, - - /// - /// (9) 16 rows x 16 columns. 1 data region. - /// - Size16x16, - - /// - /// (10) 16 rows x 36 columns. 2 vertical data regions. - /// - Size16x36, - - /// - /// (11) 16 rows x 48 columns. 2 vertical data regions. - /// - Size16x48, - - /// - /// (12) 18 rows x 18 columns. 1 data region. - /// - Size18x18, - - /// - /// (13) 20 rows x 20 columns. 1 data region. - /// - Size20x20, - - /// - /// (14) 22 rows x 22 columns. 1 data region. - /// - Size22x22, - - /// - /// (15) 24 rows x 24 columns. 1 data region. - /// - Size24x24, - - /// - /// (16) 26 rows x 26 columns. 1 data region. - /// - Size26x26, - - /// - /// (17) 32 rows x 32 columns. 2 x 2 data regions. - /// - Size32x32, - - /// - /// (18) 36 rows x 36 columns. 2 x 2 data regions. - /// - Size36x36, - - /// - /// (19) 40 rows x 40 columns. 2 x 2 data regions. - /// - Size40x40, - - /// - /// (20) 44 rows x 44 columns. 2 x 2 data regions. - /// - Size44x44, - - /// - /// (21) 48 rows x 48 columns. 2 x 2 data regions. - /// - Size48x48, - - /// - /// (22) 52 rows x 52 columns. 2 x 2 data regions. - /// - Size52x52, - - /// - /// (23) 64 rows x 64 columns. 4 x 4 data regions. - /// - Size64x64, - - /// - /// (24) 72 rows x 72 columns. 4 x 4 data regions. - /// - Size72x72, - - /// - /// (25) 80 rows x 80 columns. 4 x 4 data regions. - /// - Size80x80, - - /// - /// (26) 88 rows x 88 columns. 4 x 4 data regions. - /// - Size88x88, - - /// - /// (27) 96 rows x 96 columns. 4 x 4 data regions. - /// - Size96x96, - - /// - /// (28) 104 rows x 104 columns. 4 x 4 data regions. - /// - Size104x104, - - /// - /// (29) 120 rows x 120 columns. 6 x 6 data regions. - /// - Size120x120, - - /// - /// (30) 132 rows x 132 columns. 6 x 6 data regions. - /// - Size132x132, - - /// - /// (31) 144 rows x 144 columns. 6 x 6 data regions. - /// - Size144x144, - } -} diff --git a/MuPDF.NET/Barcode/Encoders/IBarCodeContainer.cs b/MuPDF.NET/Barcode/Encoders/IBarCodeContainer.cs deleted file mode 100644 index d38934b8..00000000 --- a/MuPDF.NET/Barcode/Encoders/IBarCodeContainer.cs +++ /dev/null @@ -1,24 +0,0 @@ - -using System; -using System.Text; - -namespace BarcodeWriter.Core -{ - /// - /// Base interface for all barcode containers (controls and such). - /// - public interface IBarCodeContainer - { - /// - /// Gets or sets the horizontal alignment of the barcode within the container. - /// - /// The horizontal alignment of the barcode within the container. - BarcodeHorizontalAlignment HorizontalAlignment { get; set; } - - /// - /// Gets or sets the vertical alignment of the barcode within the container. - /// - /// The vertical alignment of the barcode within the container. - BarcodeVerticalAlignment VerticalAlignment { get; set; } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/IBarcode.cs b/MuPDF.NET/Barcode/Encoders/IBarcode.cs deleted file mode 100644 index e2205365..00000000 --- a/MuPDF.NET/Barcode/Encoders/IBarcode.cs +++ /dev/null @@ -1,706 +0,0 @@ -using System; -using System.Text; -using System.Runtime.InteropServices; -using System.IO; -using SkiaSharp; - -namespace BarcodeWriter.Core -{ - /// - /// Delegate for the callback method called from . - /// - /// Index of page being processed. - /// Array of Image objects to draw on PDF page. - /// Array of upper-left points of images (in document units). - /// Name for new PDF layer to place images on. If null or empty, no new layer will be created. - public delegate void DrawImagesToPDFCallback(int pageIndex, out SKImage[] images, out SKPoint[] points, out string documentLayerName); - - /// - /// Base interface for all barcode classes. - /// - //[Guid(Barcode.InterfaceID)] - public interface IBarcodeEncoder - { - /// - /// Gets or sets the additional barcode caption text to draw. - /// - /// The additional barcode caption text to draw. - string AdditionalCaption { get; set; } - - /// - /// Gets or sets a value indicating whether checksum should be added to barcode. - /// - /// true if checksum should be added to barcode; otherwise, false. - bool AddChecksum { get; set; } - - /// - /// Gets or sets a value indicating whether checksum should be added to barcode caption. - /// - /// - /// True if checksum should be added to barcode caption; otherwise, false. - /// - bool AddChecksumToCaption { get; set; } - - /// - /// Gets or sets the barcode caption (by default this is encoded value). - /// - /// The barcode caption. - string Caption { get; set; } - - /// - /// Gets or sets a value indicating whether to draw the barcode encoded value. - /// - /// true if to draw the barcode encoded value; otherwise, false. - bool DrawCaption { get; set; } - - /// - /// Gets or sets a value indicating whether to draw the barcode encoded value for 2D barcodes. - /// - /// - /// True if to draw the barcode encoded value for 2D barcodes; otherwise, false. - /// - bool DrawCaptionFor2DBarcodes { get; set; } - - /// - /// Gets or sets a value indicating whether to draw quite zones when - /// barcode type supposes such zones. - /// - bool DrawQuietZones { get; set; } - - /// - /// Gets or sets the barcode symbology type. - /// - /// The barcode symbology type. - SymbologyType Symbology { get; set; } - - /// - /// Gets or sets the barcode symbology specific options. - /// - /// The barcode symbology specific options. - SymbologyOptions Options { get; set; } - - /// - /// Gets or sets the barcode value to encode. - /// - /// The barcode value to encode. - string Value { get; set; } - - /// - /// Gets or sets the supplementary barcode value to encode (used - /// with EAN-13, ISBN and UPC-A barcodes). - /// - /// The supplementary barcode value to encode. - string SupplementValue { get; set; } - - /// - /// Gets or sets the position of the additional caption. - /// - /// The additional caption position. - CaptionPosition AdditionalCaptionPosition { get; set; } - - /// - /// Gets or sets text alignment of the additional barcode caption. - /// - /// Caption alignment. - CaptionAlignment AdditionalCaptionAlignment { get; set; } - - /// - /// Gets or sets the barcode rotation angle in degrees. - /// - /// The barcode rotation angle in degrees. - RotationAngle Angle { get; set; } - - /// - /// Gets or sets the horizontal resolution of barcode. - /// - /// The horizontal resolution of barcode. - float ResolutionX { get; set; } - - /// - /// Gets or sets the vertical resolution of barcode. - /// - /// The vertical resolution of barcode. - float ResolutionY { get; set; } - - /// - /// Gets or sets a value indicating whether the component should produce monochrome (1-bit, black and white) images. - /// - /// Note: 1-bit monochrome images can be created only in BMP, PNG and TIFF image formats. - /// true if images should be saved in monochrome format; otherwise, false. - bool ProduceMonochromeImages { get; set; } - - /// - /// Gets or sets the color used to draw the barcode background. - /// - /// The color used to draw the barcode background. - SKColor BackColor { get; set; } - - /// - /// Gets or sets the position of the barcode caption. - /// - /// The barcode caption position. - CaptionPosition CaptionPosition { get; set; } - - /// - /// Gets or sets the alignment of barcode caption text. - /// - /// Caption alignment. - CaptionAlignment CaptionAlignment { get; set; } - - /// - /// Gets or sets the color used to draw the barcode bars and caption(s). - /// - /// The color used to draw the barcode bars and caption(s). - SKColor ForeColor { get; set; } - - /// - /// Gets or sets the barcode margins in pixels. - /// - /// The barcode margins in pixels. - Margins Margins { get; set; } - - /// - /// Gets or sets the font of the additional barcode caption. - /// - /// The additional barcode caption font. - SKFont AdditionalCaptionFont { get; set; } - - /// - /// Gets or sets the height of the barcode bars in pixels. - /// - /// The height of the barcode bars in pixels. - int BarHeight { get; set; } - - /// - /// Gets or sets the font of the barcode caption. - /// - /// The barcode caption font. - SKFont CaptionFont { get; set; } - - /// - /// Gets or sets the width of the narrow bar in pixels. - /// - /// The width of the narrow bar in pixels. - int NarrowBarWidth { get; set; } - - /// - /// Gets or sets the width of the wide bar relative to the narrow bar. - /// - /// The width of the wide bar relative to the narrow bar. - int WideToNarrowRatio { get; set; } - - /// - /// Gets or sets a value indicating whether unused space should be cut when - /// drawing or saving barcode images. Unused space is usually a result - /// of calling one of FitInto methods with size greater then needed - /// to draw barcode. - /// - /// true if unused space should be cut; otherwise, false. - bool CutUnusedSpace { get; set; } - - /// - /// Gets or sets a value indicating whether to check output size so it's - /// not less than barcode size. - /// Use FitInto() method to fit barcode into a given physical size. - /// - /// A value indicating whether to check output size so it's - /// not less than barcode size. - bool PreserveMinReadableSize { get; set; } - - /// - /// Sets whether to generate barcodes with round dots. Works only for QR Code, DataMatrix, - /// and Aztec barcode types. - /// - bool RoundDots { get; set; } - - /// - /// Scale factor for in percents. - /// - int RoundDotsScale { get; set; } - - /// - /// Gets the component version number. - /// - string Version { get; } - - - /// - /// Comma-separated list of profiles to apply to the . - /// Profiles are sets of properties and methods represented as JSON string. - /// Check the source code examples installed with the SDK. - /// - string Profiles { get; set; } - - /// - /// Loads profiles from file. - /// - /// JSON file containing profiles. - void LoadProfiles(string fileName); - - /// - /// Loads profiles from JSON string. - /// - /// JSON string containing profiles. - void LoadProfilesFromString(string jsonString); - - /// - /// Loads profiles from JSON string and automatically applies them. Note that profiles containing - /// detection keywords will be deferred until the extraction. - /// - /// Note, all existing profiles are discarded before loading profiles from the provided string. - /// JSON string containing profiles. - void LoadAndApplyProfiles(string jsonString); - - /// - /// Creates JSON profile will all Barcode properties with current values. - /// - /// Name of profile (without spaces). - /// Output file name. - void CreateProfile(string profileName, string outputFileName); - - /// - /// Creates JSON profile will all Barcode properties with current values. - /// - /// Name of profile (without spaces). - /// JSON string. - string CreateProfile(string profileName); - - /// - /// Validates the value using current symbology rules. - /// - /// The value. - /// true if value is valid (can be encoded); otherwise, false. - bool ValueIsValid(string value); - - /// - /// Validates the value using current symbology rules. - /// - /// The value. - /// Expect the checksum if it's applicable for selected symbology. - /// true if value is valid (can be encoded); otherwise, false. - bool ValueIsValid(string value, bool checksumIsMandatory); - - /// - /// Validates the GS1 value using GS1 rules. - /// - /// The value. - /// - /// True if value is valid (can be encoded); otherwise, false. - /// - bool ValueIsValidGS1(string value); - - /// - /// Gets the value restrictions for the specified symbology. - /// - /// The symbology to get value restrictions for. - /// The value restrictions for the specified symbology. - string GetValueRestrictions(SymbologyType symbology); - - /// - /// Gets the supplementary value restrictions. - /// - /// The supplementary value restrictions. - string GetSupplementaryValueRestrictions(); - - /// - /// Sets the barcode margins in specified units. - /// - /// The left margin value in specified units. - /// The top margin value in specified units. - /// The right margin value in specified units. - /// The bottom margin value in specified units. - /// The unit of measure for margin values. - void SetMargins(float left, float top, float right, float bottom, UnitOfMeasure unit); - - /// - /// Returns the barcode margins in specified units. - /// - /// Receives the left margin value in specified units. - /// Receives the top margin value in specified units. - /// Receives the right margin value in specified units. - /// Receives the bottom margin value in specified units. - /// The unit of measure for retrieved margin values. - void GetMargins(out float left, out float top, out float right, out float bottom, UnitOfMeasure unit); - - /// - /// Returns the left barcode margin value in specified units. - /// - /// The unit of measure for retrieved margin value. - /// - /// The left barcode margin value in specified units. - /// - float GetMarginLeft(UnitOfMeasure unit); - - /// - /// Returns the top barcode margin value in specified units. - /// - /// The unit of measure for retrieved margin value. - /// - /// The top barcode margin value in specified units. - /// - float GetMarginTop(UnitOfMeasure unit); - - /// - /// Returns the right barcode margin value in specified units. - /// - /// The unit of measure for retrieved margin value. - /// - /// The right barcode margin value in specified units. - /// - float GetMarginRight(UnitOfMeasure unit); - - /// - /// Retrieves the bottom barcode margin value in specified units. - /// - /// The unit of measure for retrieved margin value. - /// - /// The bottom barcode margin value in specified units. - /// - float GetMarginBottom(UnitOfMeasure unit); - - /// - /// Sets the height of the barcode bars in specified units. - /// - /// The height of the barcode bars in specified units. - /// The unit of measure for bar height value. - void SetBarHeight(float height, UnitOfMeasure unit); - - /// - /// Returns the height of the barcode bars in specified units. - /// - /// The unit of measure for retrieved bar height value. - /// The height of the barcode bars in specified units. - float GetBarHeight(UnitOfMeasure unit); - - /// - /// Sets the width of the narrow bars in specified units. - /// - /// The width of the narrow bars in specified units. - /// The unit of measure for narrow bar width value. - void SetNarrowBarWidth(float width, UnitOfMeasure unit); - - /// - /// Retrieves the width of the narrow bar in specified units. - /// - /// The unit of measure for retrieved narrow bar width. - /// The width of the narrow bar in specified units. - float GetNarrowBarWidth(UnitOfMeasure unit); - - /// - /// Returns the size in specified units of the smallest rectangle that - /// can accommodate the barcode. - /// - /// The unit of measure for retrieved size. - /// The size in specified units of the smallest rectangle that - /// can accommodate the barcode. - SKSize GetMinimalSize(UnitOfMeasure unit); - - /// - /// Returns the width in specified units of the smallest rectangle that - /// can accommodate the barcode. - /// - /// The unit of measure for retrieved width. - /// - /// The width in specified units of the smallest rectangle that - /// can accommodate the barcode. - /// - float GetMinimalWidth(UnitOfMeasure unit); - - /// - /// Returns the height in specified units of the smallest rectangle that - /// can accommodate the barcode. - /// - /// The unit of measure for retrieved height. - /// - /// The height in specified units of the smallest rectangle that - /// can accommodate the barcode. - /// - float GetMinimalHeight(UnitOfMeasure unit); - - /// - /// IMPORTANT: Call this method AFTER setting the barcode value. - /// Fits the barcode into the area of size specified in pixels. - /// Calling this method will change output size of the barcode. - /// Barcode size will be increased in order to occupy all - /// of the area. - /// - /// The size of the area to fit the barcode into. - void FitInto(SKSize size); - - /// - /// IMPORTANT: Call this method AFTER setting the barcode value. - /// Fits the barcode into the area of size specified in pixels. - /// Calling this method will change output size of the barcode. - /// Barcode size will be increased in order to occupy all - /// of the area. - /// - /// The width of the area. - /// The height of the area. - void FitInto(int width, int height); - - /// - /// IMPORTANT: Call this method AFTER setting the barcode value. - /// Fits the barcode into the area of size specified in units. - /// Calling this method will change output size of the barcode. - /// Barcode size will be increased in order to occupy all - /// of the area. - /// - /// The size of the area to fit the barcode into. - /// The unit of the size. - [ComVisible(false)] - void FitInto(SKSize size, UnitOfMeasure unit); - - /// - /// IMPORTANT: Call this method AFTER setting the barcode value. - /// Fits the barcode into the area of size specified in units. - /// Calling this method will change output size of the barcode. - /// Barcode size will be increased in order to occupy all - /// of the area. - /// - /// The width of the area in specified units. - /// The height of the area in specified units. - /// The unit of measure for area size. - void FitInto(float width, float height, UnitOfMeasure unit); - - /// - /// Reverts any changes to barcode size caused by a call to any of FitInto methods. - /// - void RevertToNormalSize(); - - /// - /// Gets or sets the size of the smallest rectangle in pixels that - /// can accommodate the barcode. - /// - /// The size of the smallest rectangle in pixels that can accommodate the barcode. - SKSize GetMinimalSize(); - - /// - /// Draws the barcode on canvas object. - /// - /// The object to draw the barcode on. - /// The position in pixels of the top left point of the barcode. - [ComVisible(false)] - void Draw(SKCanvas canvas, SKPoint position); - - /// - /// Draws the barcode. - /// - /// The Graphics object to draw the barcode on. - /// The coordinate of leftmost barcode point in specified units. - /// The coordinate of topmost barcode point in specified units. - /// The unit of measure for coordinates. - [ComVisible(false)] - void Draw(SKCanvas canvase, float left, float top, UnitOfMeasure unit); - - /// - /// Gets the object with the barcode. - /// - /// The object with the barcode. - SKBitmap GetImage(); - - /// - /// Saves the barcode image to file. - /// - /// Name of the file to save barcode to. - void SaveImage(string fileName); - - /// - /// Saves the barcode image to file. - /// - /// Name of the file to save barcode to. - /// Format of the barcode image. - [ComVisible(false)] - void SaveImage(string fileName, SKEncodedImageFormat imageFormat); - - /// - /// Saves the barcode image to file. - /// - /// Name of the file to save barcode to. - /// The format of the image (may be different then implied by the file name extension). - /// Size of the area containing image. - /// The image leftmost position within area. - /// The image topmost position within area. - [ComVisible(false)] - void SaveImage(string fileName, SKEncodedImageFormat format, SKSize areaSize, int imageLeft, int imageTop); - - /// - /// Saves the barcode image to specified stream. - /// - /// The stream to save barcode to. - [ComVisible(false)] - void SaveImage(Stream stream); - - /// - /// Saves the barcode image to specified stream. - /// - /// The stream to save barcode to. - /// Format of the barcode image. - [ComVisible(false)] - void SaveImage(Stream stream, SKEncodedImageFormat imageFormat); - - /// - /// Saves the image with barcode into given stream. - /// - /// The stream to save barcode to. - /// The image format of the barcode. - /// Size of the area containing image. - /// The image leftmost position within area. - /// The image topmost position within area. - [ComVisible(false)] - void SaveImage(Stream stream, SKEncodedImageFormat format, SKSize areaSize, int imageLeft, int imageTop); - - /// - /// Returns the barcode image as byte array. - /// - /// The byte array containing barcode image. - byte[] GetImageBytes(); - - /// - /// Returns the barcode image in TIFF format as byte array. - /// - /// The byte array containing barcode image. - byte[] GetImageBytesTIFF(); - - /// - /// Returns the barcode image in PNG format as byte array. - /// - /// The byte array containing barcode image. - byte[] GetImageBytesPNG(); - - /// - /// Returns the barcode image in GIF format as byte array. - /// - /// The byte array containing barcode image. - byte[] GetImageBytesGIF(); - - /// - /// Returns the barcode image in JPEG format as byte array. - /// - /// The byte array containing barcode image. - byte[] GetImageBytesJPG(); - - /// - /// Sets the font of the barcode caption. - /// - /// The font family. - /// The size of the font in pixels. - void SetCaptionFont(string familyName, int size); - - /// - /// Sets the font of the barcode caption font. - /// - /// The font family. - /// The size of the font in pixels. - /// If true, then the text drawn with the font will be bold. - /// If true, then the text drawn with the font will be italic. - /// If true, then the text drawn with the font will be underlined. - /// If true, then the text drawn with the font will be contain line through the middle. - /// A GDI character set to use for this font. - void SetCaptionFont(string familyName, int size, bool bold, bool italic, bool underline, bool strikeout, byte gdiCharSet); - - /// - /// Sets the font of the additional barcode caption. - /// - /// The font family. - /// The size of the font in pixels. - void SetAdditionalCaptionFont(string familyName, int size); - - /// - /// Sets the font of the barcode additional caption. - /// - /// The font family. - /// The size of the font in pixels. - /// If true, then the text drawn with the font will be bold. - /// If true, then the text drawn with the font will be italic. - /// If true, then the text drawn with the font will be underlined. - /// If true, then the text drawn with the font will be contain line through the middle. - /// A GDI character set to use for this font. - void SetAdditionalCaptionFont(string familyName, int size, bool bold, bool italic, bool underline, bool strikeout, byte gdiCharSet); - - /// - /// Draws barcode to image at specified coordinates. - /// - /// Input image file path. - /// Index of the page for multi-page TIFF images. -1 means all pages. - /// X coordinate. - /// Y coordinate. - /// Output image file path. - void DrawToImage(string inputFile, int pageIndex, int x, int y, string outputFile); - - /// - /// Draws barcode to image at specified coordinates. - /// - /// Input stream containing the image to draw the barcode on. - /// Index of the page for multi-page TIFF images. -1 means all pages. - /// X coordinate. - /// Y coordinate. - /// Output stream. - void DrawToImage(Stream inputStream, int pageIndex, int x, int y, Stream outputStream); - - /// - /// Sets the fore color in RGB format. - /// - /// Red color component. - /// Green color component. - /// Blue color component. - void SetForeColorRGB(byte r, byte g, byte b); - - /// - /// Sets the background color in RGB format. - /// - /// Red color component. - /// Green color component. - /// Blue color component. - void SetBackColorRGB(byte R, byte G, byte B); - - /// - /// Sets the gap size between the barcode and caption. To reset the gap to default (1/10 of caption font height) set to float.NaN. - /// - /// Gap size in specified units. - /// The unit of measure for margin values. - /// You should use this method after setting the ResolutionY property. - void SetCustomCaptionGap(float gap, UnitOfMeasure unit); - - /// - /// Replaces macro codes with corresponding ASCII control characters. - /// - /// - /// Use following macros to insert ASCII control characters: {NUL}, {SOH}, {STX}, {ETX}, {EOT}, {ENQ}, {ACK}, {BEL}, {BS}, {TAB}, {LF}, {VT}, {FF}, {CR}, - /// {SO}, {SI}, {DLE}, {DC1}, {DC2}, {DC3}, {DC4}, {NAK}, {SYN}, {ETB}, {CAN}, {EM}, {SUB}, {ESC}, {FS}, {GS}, {RS}, {US}. - /// - /// Input value. - /// Value with processed macros. - string ProcessMacros(string value); - - /// - /// Add decorative image to draw in the center of the barcode. - /// (!) Supported with QR Code only. - /// - /// Note, the embedded image damages the barcode, but the QR Code's error correction algorithm makes it possible - /// to decode the damaged barcode if the damage doesn't exceed 7-30% of barcode square (depending on the error correction level). - /// See and . - /// It's recommended to generate the QR Code with highest error correction level and check the barcode is still decodable after image applying. - /// To read the barcode you can use ByteScout Barcode Reader SDK: https://bytescout.com/products/developer/barcodereadersdk/bytescoutbarcodereadersdk.html - /// - /// Image to add to barcode. - /// Scale of the image square relatively to the barcode square, in percents. - /// Recommended is 15 percents with the highest error correction level. Set -1 to disable the scaling. - void AddDecorationImage(string imageFileName, int scale); - - /// - /// Add decorative image to draw in the center of the barcode. - /// (!) Supported with QR Code only. - /// - /// Note, the embedded image damages the barcode, but the QR Code's error correction algorithm makes it possible - /// to decode the damaged barcode if the damage doesn't exceed 7-30% of barcode square (depending on the error correction level). - /// See and . - /// It's recommended to generate the QR Code with highest error correction level and check the barcode is still decodable after image applying. - /// To read the barcode you can use ByteScout Barcode Reader SDK: https://bytescout.com/products/developer/barcodereadersdk/bytescoutbarcodereadersdk.html - /// - /// Image to add to barcode. - /// Scale of the image square relatively to the barcode square, in percents. - /// Recommended is 15 percents with the highest error correction level. Set -1 to disable the scaling. - [ComVisible(false)] - void AddDecorationImage(SKImage image, int scale); - } -} diff --git a/MuPDF.NET/Barcode/Encoders/IMargins.cs b/MuPDF.NET/Barcode/Encoders/IMargins.cs deleted file mode 100644 index 0d807b34..00000000 --- a/MuPDF.NET/Barcode/Encoders/IMargins.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.ComponentModel; - -namespace BarcodeWriter.Core -{ - /// - /// Interface that describes barcode margins. - /// - interface IMargins - { - /// - /// Occurs when margins get changed. - /// - event EventHandler Changed; - - /// - /// Gets or sets the left margin in pixels. - /// - /// The left margin in pixels. - int Left { get; set; } - - /// - /// Gets or sets the top margin in pixels. - /// - /// The top margin in pixels. - int Top { get; set; } - - /// - /// Gets or sets the right margin in pixels. - /// - /// The right margin in pixels. - int Right { get; set; } - - /// - /// Gets or sets the bottom margin in pixels. - /// - /// The bottom margin in pixels. - int Bottom { get; set; } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/ISymbologyOptions.cs b/MuPDF.NET/Barcode/Encoders/ISymbologyOptions.cs deleted file mode 100644 index c9dc71ce..00000000 --- a/MuPDF.NET/Barcode/Encoders/ISymbologyOptions.cs +++ /dev/null @@ -1,50 +0,0 @@ -using SkiaSharp; -using System; -using System.Drawing; - -namespace BarcodeWriter.Core -{ - interface ISymbologyOptions - { - AztecErrorCorrectionLevel AztecErrorCorrectionLevel { get; set; } - int AztecSymbolSize { get; set; } - CodabarChecksumAlgorithm CodabarChecksumAlgorithm { get; set; } - CodabarSpecialSymbol CodabarStartSymbol { get; set; } - CodabarSpecialSymbol CodabarStopSymbol { get; set; } - Code128Alphabet Code128Alphabet { get; set; } - DataMatrixCompactionMode DataMatrixCompactionMode { get; set; } - AztecCompactionMode AztecCompactionMode { get; set; } - DataMatrixSize DataMatrixSize { get; set; } - bool DrawIntercharacterGap { get; set; } - bool ISBNAutoCaption { get; set; } - string ISBNCaptionTemplate { get; set; } - int PDF417ColumnCount { get; set; } - PDF417CompactionMode PDF417CompactionMode { get; set; } - bool PDF417CreateMacro { get; set; } - PDF417ErrorCorrectionLevel PDF417ErrorCorrectionLevel { get; set; } - int PDF417FileID { get; set; } - bool PDF417LastSegment { get; set; } - int PDF417MinimumColumnCount { get; set; } - int PDF417RowCount { get; set; } - int PDF417SegmentIndex { get; set; } - bool PDF417UseManualSize { get; set; } - QREncodeHint QREncodeHint { get; set; } - QRErrorCorrectionLevel QRErrorCorrectionLevel { get; set; } - int QRVersion { get; set; } - bool ShowStartStop { get; set; } - int SupplementSpace { get; set; } - int GS1ExpandedStackedSegmentsNumber { get; set; } - byte MaxiCodeMode { get; set; } - bool MaxiCodeEnableCustomWidth { get; set; } - MSIChecksumAlgorithm MSIChecksumAlgorithm { get; set; } - bool OnlyHorizontalBearerBar { get; set; } - PZNType PZNType { get; set; } - int TextEncodingCodePage { get; set; } - bool TextEncodingUseUTF8 { get; set; } - - bool PharmaCodeTwoTrack { get; set; } - bool PharmaCodeSupplementaryCode { get;set; } - SKColor PharmaCodeSupplementaryBarColor { get; set; } - bool PharmaCodeMiniature { get; set; } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/ImageComposer.cs b/MuPDF.NET/Barcode/Encoders/ImageComposer.cs deleted file mode 100644 index d0debdc9..00000000 --- a/MuPDF.NET/Barcode/Encoders/ImageComposer.cs +++ /dev/null @@ -1,479 +0,0 @@ -using BarcodeWriter.Core.Internal; -using SkiaSharp; -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -using System.Web; - -namespace BarcodeWriter.Core -{ - /// - /// Represents class that creates a single composite image from several images by placing them - /// in a fixed position or arranging automatically. - /// - [ClassInterface(ClassInterfaceType.AutoDual)] - public class ImageComposer : IDisposable - { - private class ImageData - { - public SKImage Image { get; } - public SKPoint Position { get; } - - public ImageData(SKImage image, SKPoint position) - { - Image = image; - Position = position; - } - } - - // Full list of input images - private readonly List _imageDataList = new List(); - - // Composition mode - public CompositionMode CompositionMode { get; set; } - - /// - /// Gets or sets the gap between images for automatic composition mode. - /// - public int InnerGap { get; set; } - - /// - /// Gets or sets margins for the composite image. - /// - public int Margins { get; set; } - - /// - /// Gets or sets the background color of composite image. - /// - [ComVisible(false)] - public SKColor BackgroundColor { get; set; } = SKColors.White; - - // Result of composition - private SKImage _outputImage = null; - - /// - /// Constructs ImageComposer object. - /// - /// Gap between images in automatic composition mode. Default is 0. - /// Margins for output image. Default is 0. - /// Composition mode. Default is 'CompositionMode.ArrangeHorizontally'. - public ImageComposer(int innerGap = 0, int margins = 0, CompositionMode compositionMode = CompositionMode.ArrangeHorizontally) - { - InnerGap = innerGap; - Margins = margins; - CompositionMode = compositionMode; - } - - /// - /// - /// - public void Dispose() - { - _outputImage?.Dispose(); - _imageDataList.Clear(); - } - - /// - /// Adds an image to the composition. - /// - /// Image file path. - /// Horizontal (left) position for fixed positioning mode (see ). Default is 0. - /// Vertical (top) position for fixed positioning mode (see ). Default is 0. - /// Rotation angle in degrees. Default is 0. - public void AddImage(string fileName, int positionX = 0, int positionY = 0, int rotationAngle = 0) - { - using (SKData data = SKData.Create(fileName)) - { - var image = Rotate(SKImage.FromEncodedData(data), rotationAngle); - _imageDataList.Add(new ImageData(image, new SKPoint(positionX, positionY))); - } - } - - /// - /// Adds an image to the composition. - /// - /// Stream that contains an image file. - /// Horizontal (left) position for fixed positioning mode (see ). Default is 0. - /// Vertical (top) position for fixed positioning mode (see ). Default is 0. - /// Rotation angle in degrees. Default is 0. - [ComVisible(false)] - public void AddImage(Stream stream, int positionX = 0, int positionY = 0, int rotationAngle = 0) - { - using (SKData data = SKData.Create(stream)) - { - var image = Rotate(SKImage.FromEncodedData(data), rotationAngle); - _imageDataList.Add(new ImageData(image, new SKPoint(positionX, positionY))); - } - } - - /// - /// Adds an image to the composition. - /// - /// object to add. - /// Horizontal (left) position for fixed positioning mode (see ). Default is 0. - /// Vertical (top) position for fixed positioning mode (see ). Default is 0. - /// Rotation angle in degrees. Default is 0. - [ComVisible(false)] - public void AddImage(SKImage image, int positionX = 0, int positionY = 0, int rotationAngle = 0) - { - var newImage = Rotate(image, rotationAngle); - _imageDataList.Add(new ImageData(newImage, new SKPoint(positionX, positionY))); - } - - /// - /// Sets the background color of composite image. - /// - /// Red component of the color. From 0 to 255. - /// Green component of the color. From 0 to 255. - /// Blue component of the color. From 0 to 255. - /// Alpha (transparency) component of the color. From 0 to 255. - public void SetBackgroundColor(int red, int green, int blue, int alpha = 255) - { - BackgroundColor = new SKColor((byte)red, (byte)green, (byte)blue, (byte)alpha); - } - - /// - /// Returns composed image. - /// - /// object. - [ComVisible(false)] - public SKImage GetComposedImage() - { - Run(); - return _outputImage; - } - - /// - /// Saves composed image to a file. - /// - /// Output image file path. - public void SaveComposedImage(string fileName) - { - Run(); - - using (var stream = File.OpenWrite(fileName)) - { - SKEncodedImageFormat format = Utils.FormatFromName(fileName); - using (var data = _outputImage.Encode(format, 100)) - { - data.SaveTo(stream); - } - } - } - - /// - /// Saves composed image to a file. - /// - /// Output stream. - [ComVisible(false)] - public void SaveComposedImage(Stream outputStream) - { - SKEncodedImageFormat format = SKEncodedImageFormat.Png; - using (var data = _outputImage.Encode(format, 100)) - { - data.SaveTo(outputStream); - } - } - - /// - /// Saves composed image to a file. - /// - /// Output image file path. - /// to save. - [ComVisible(false)] - public void SaveComposedImage(string fileName, SKEncodedImageFormat imageFormat) - { - using (var stream = File.OpenWrite(fileName)) - { - SKEncodedImageFormat format = SKEncodedImageFormat.Png; - using (var data = _outputImage.Encode(format, 100)) - { - data.SaveTo(stream); - } - } - } - - /// - /// Saves composed image to a file. - /// - /// Output stream. - /// to save. - [ComVisible(false)] - public void SaveComposedImage(Stream outputStream, SKEncodedImageFormat imageFormat) - { - // Encode to PNG - using (var data = _outputImage.Encode(SKEncodedImageFormat.Png, 100)) - { data.SaveTo(outputStream); } - } - - // Runs the composition process. - - private void Run() - { - int width = OutputSizeX(); - int height = OutputSizeY(); - - // Create a surface for drawing - using (var surface = SKSurface.Create(new SKImageInfo(width, height, SKColorType.Bgra8888, SKAlphaType.Premul))) - { - var canvas = surface.Canvas; - - // Clear background - SKColor skBackground = BackgroundColor; - canvas.Clear(skBackground); - - // Draw content - if (CompositionMode == CompositionMode.FixedPosition) - DrawFixed(canvas); - else - DrawAuto(canvas); - - // Flush drawing - canvas.Flush(); - - // Create SKImage from the surface - using (var skImage = surface.Snapshot()) - { - // Optionally, encode to PNG or keep as SKImage - _outputImage = skImage; // store SKImage - } - } - } - - // Draws images using positions set by AddImage() methods on the provided Graphics object - - private void DrawFixed(SKCanvas canvas) - { - foreach (var imageData in _imageDataList) - { - using (var skBitmap = SKBitmap.FromImage(imageData.Image)) - { - canvas.DrawBitmap(skBitmap, new SKPoint(imageData.Position.X, imageData.Position.Y)); - } - } - } - - // Draws images using automatically generated positions on the provided Graphics object - - private void DrawAuto(SKCanvas canvas) - { - SKPoint position = new SKPoint(Margins, Margins); - foreach (var imageData in _imageDataList) - { - using (var skBitmap = SKBitmap.FromImage(imageData.Image)) - { - canvas.DrawBitmap(skBitmap, new SKPoint(imageData.Position.X, imageData.Position.Y)); - UpdatePositionAuto(ref position, imageData.Image); - } - } - } - - // Updates position according to CompositionMode mode. - - private void UpdatePositionAuto(ref SKPoint position, SKImage image) - { - switch (CompositionMode) - { - case CompositionMode.ArrangeHorizontally: - position.X += image.Width + InnerGap; - break; - case CompositionMode.ArrangeVertically: - position.Y += image.Height + InnerGap; - break; - case CompositionMode.FixedPosition: - // no change for parameter as this method used for auto composition only - break; - default: - throw new NotImplementedException("Can not handle this CompositionMode"); - } - } - - // Calculates width of the output image. - - private int OutputSizeX() - { - switch (CompositionMode) - { - case CompositionMode.ArrangeHorizontally: - return HorizontalSizesSum() + 2 * Margins + (_imageDataList.Count - 1) * InnerGap; - case CompositionMode.ArrangeVertically: - return MaxSizeX() + 2 * Margins; - case CompositionMode.FixedPosition: - return (int) Math.Ceiling(MaxRightBottomCorner().X) + 2 * Margins; - default: - throw new NotImplementedException("Can not handle this CompositionMode"); - } - } - - // Calculates height of the output image. - - private int OutputSizeY() - { - switch (CompositionMode) - { - case CompositionMode.ArrangeHorizontally: - return MaxSizeY() + 2 * Margins; - case CompositionMode.ArrangeVertically: - return VerticalSizesSum() + 2 * Margins + (_imageDataList.Count - 1) * InnerGap; - case CompositionMode.FixedPosition: - return (int) Math.Ceiling(MaxRightBottomCorner().Y) + 2 * Margins; - default: - throw new NotImplementedException("Can not handle this Alignment"); - } - } - - // Returns the total width of all images. - - private int MaxSizeX() - { - var max = 0; - - foreach (var imageData in _imageDataList) - if (imageData.Image.Width > max) - max = imageData.Image.Width; - - return max; - } - - // Returns the total height of all images. - - private int MaxSizeY() - { - var max = 0; - - foreach (var imageData in _imageDataList) - if (imageData.Image.Height > max) - max = imageData.Image.Height; - - return max; - } - - // Returns max bottom-right point for the output image. - - private SKPoint MaxRightBottomCorner() - { - /** [] - * [] - * - * - * [] X - this is the result - */ - var max = new SKPoint(0, 0); - - foreach (var imageData in _imageDataList) - { - var imageRightBottomCornerX = imageData.Position.X + imageData.Image.Width; - var imageRightBottomCornerY = imageData.Position.Y + imageData.Image.Height; - - if (imageRightBottomCornerX > max.X) - max.X = imageRightBottomCornerX; - - if (imageRightBottomCornerY > max.Y) - max.Y = imageRightBottomCornerY; - } - - return max; - } - - // Returns width of all images arranged horizontally. - - private int HorizontalSizesSum() - { - var sizesSum = 0; - - foreach (var imageData in _imageDataList) - sizesSum += imageData.Image.Width; - - return sizesSum; - } - - // Returns height of all images arranged vertically. - - private int VerticalSizesSum() - { - var sizesSum = 0; - - foreach (var imageData in _imageDataList) - sizesSum += imageData.Image.Height; - - return sizesSum; - } - - /// - /// Rotates an input image for an angle and produces a resulting image with larger size - /// - /// Input image - /// Angle for rotation (degrees) - /// New resulting image - private SKImage Rotate(SKImage image, float angle) - { - // Calculate the size of the rotated image - var rotatedSize = NewImageRotatedSize(image, angle); // Should return Size or width/height - - var info = new SKImageInfo(rotatedSize.Width, rotatedSize.Height, SKColorType.Rgba8888, SKAlphaType.Premul); - - using (var surface = SKSurface.Create(info)) - { - var canvas = surface.Canvas; - canvas.Clear(SKColors.Transparent); - - // Move rotation point to the center of the new image - canvas.Translate(rotatedSize.Width / 2f, rotatedSize.Height / 2f); - - // Rotate canvas - canvas.RotateDegrees(angle); - - // Draw the original image centered - canvas.Translate(-image.Width / 2f, -image.Height / 2f); - canvas.DrawImage(image, 0, 0); - - // Return the SKImage from surface - canvas.Flush(); - return surface.Snapshot(); - } - } - - // Returns new empty bitmap with the size that is large enough to contain an input image rotated for the specified angle. - - private SKImage NewImageRotatedSize(SKImage image, float angle) - { - // Convert angle to radians - double angleRad = angle * Math.PI / 180; - - int width = image.Width; - int height = image.Height; - - // Calculate new rotated dimensions - int newWidth = (int)Math.Ceiling(Math.Abs(height * Math.Sin(angleRad)) + Math.Abs(width * Math.Cos(angleRad))); - int newHeight = (int)Math.Ceiling(Math.Abs(height * Math.Cos(angleRad)) + Math.Abs(width * Math.Sin(angleRad))); - - // Create a new empty SKBitmap with the rotated size - SKImage rotatedBitmap = SKImage.FromBitmap(new SKBitmap(newWidth, newHeight, image.ColorType, image.AlphaType)); - - return rotatedBitmap; - } - } - - /// - /// Image composition modes for . - /// - public enum CompositionMode - { - /// - /// Automatically - /// - ArrangeHorizontally, - - /// - /// - /// - ArrangeVertically, - - /// - /// - /// - FixedPosition - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/BooklandSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/BooklandSymbology.cs deleted file mode 100644 index 03948b6f..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/BooklandSymbology.cs +++ /dev/null @@ -1,88 +0,0 @@ - -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using Bookland symbology rules. Bookland symbology is - /// used exclusively with books. - /// - class BooklandSymbology : EAN13Symbology - { - /// - /// Initializes a new instance of the class. - /// - public BooklandSymbology() - : base() - { - m_type = TrueSymbologyType.Bookland; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public BooklandSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.Bookland; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "000000000"; - } - - /// - /// Validates the value using Bookland symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 9) - return false; - - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Bookland symbology allows only strings with exactly nine numbers to be encoded. Any value is always prepended with '978'"; - } - - /// - /// Gets the barcode value encoded using Bookland symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - // checksum char ALWAYS added to encoded value and caption - return "978" + Value + getChecksum("978" + Value); - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/CodabarSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/CodabarSymbology.cs deleted file mode 100644 index a405885e..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/CodabarSymbology.cs +++ /dev/null @@ -1,286 +0,0 @@ - -using System; -using System.Drawing; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using Codabar (aka Ames Code/USD-4/NW-7/2 of 7 Code) symbology rules. - /// This symbology used for example in libraries and blood banks. - /// - class CodabarSymbology : SymbologyDrawing - { - private static string m_alphabet = "0123456789-$:/.+"; - - /// - /// Initializes a new instance of the class. - /// - public CodabarSymbology() - : base(TrueSymbologyType.Codabar) - { - // Codabar symbology usually shows start and stop symbols within a caption - // because them can be used for additional data encoding. - Options.ShowStartStop = true; - - // Codabar symbology have no "official" checksum algorithm - AddChecksum = false; - AddChecksumToCaption = false; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public CodabarSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.Codabar) - { - } - - /// - /// Validates the value using Codabar symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Codabar symbology allows only symbols from this string '0123456789-$:/.+' to be encoded.\n" + - "Start and stop symbols are set separately. Please use Options to set start and stop Codabar symbols."; - } - - /// - /// Gets the barcode value encoded using Codabar symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using Codabar symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - - if (Options.ShowStartStop || !forCaption) - sb.Append(Options.CodabarStartSymbol.ToString()); - - sb.Append(Value); - - if (AddChecksum) - { - if ((forCaption && AddChecksumToCaption) || (!forCaption)) - { - char checksumChar = getChecksumChar(Value); - sb.Append(checksumChar); - } - } - - if (Options.ShowStartStop || !forCaption) - sb.Append(Options.CodabarStopSymbol.ToString()); - - return sb.ToString(); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// The encoding pattern for given character. - protected override string getCharPattern(char c) - { - switch (c) - { - case '0': - return "nnnnnww"; - case '1': - return "nnnnwwn"; - case '2': - return "nnnwnnw"; - case '3': - return "wwnnnnn"; - case '4': - return "nnwnnwn"; - case '5': - return "wnnnnwn"; - case '6': - return "nwnnnnw"; - case '7': - return "nwnnwnn"; - case '8': - return "nwwnnnn"; - case '9': - return "wnnwnnn"; - case '-': - return "nnnwwnn"; - case '$': - return "nnwwnnn"; - case ':': - return "wnnnwnw"; - case '/': - return "wnwnnnw"; - case '.': - return "wnwnwnn"; - case '+': - return "nnwnwnw"; - case 'A': - return "nnwwnwn"; - case 'B': - return "nwnwnnw"; - case 'C': - return "nnnwnww"; - case 'D': - return "nnnwwwn"; - } - - return "wwwwwww"; - } - - /// - /// Gets the checksum char. - /// - /// The value. - /// The checksum char. - private char getChecksumChar(string value) - { - // There is no checksum defined as part of the Codabar standard, but - // some industries (libraries, for example) have adopted their - // own checksum standards. - - if (Options.CodabarChecksumAlgorithm == CodabarChecksumAlgorithm.Modulo9) - return getModulo9ChecksumChar(value); - - return getAIIMChecksumChar(value); - } - - /// - /// Gets the modulo9 checksum char. - /// - /// The value to calculate checksum char for. - /// The modulo9 checksum char. - private static char getModulo9ChecksumChar(string value) - { - // Many libraries use the following system which includes 13 digits - // plus a checksum; - // Digit 1 indicates the type of barcode: 2 = patron, 3 = item (book) - // Digits 2-5 identify the institution - // The next 6 digits (00010 586) identify the individual patron or item - // Digit 14 is the checksum - - // For implementation details please take a look at - // https://www.barcodesinc.com/articles/codabar.htm - - // https://en.wikipedia.org/wiki/Codabar - // https://en.wikipedia.org/wiki/Luhn_algorithm - - int evenSum = 0; - int oddSum = 0; - - // The most significant (leftmost) digit position is considered 'odd'. - for (int i = 0; i < value.Length; i++) - { - if (i % 2 == 0) - { - int product = getCharPosition(value[i]) * 2; - if (product < 10) - oddSum += product; - else - oddSum += product - 9; - } - else - evenSum += getCharPosition(value[i]); - } - - int total = oddSum + evenSum; - - int checkCharPos = 0; - if (total != 0) - checkCharPos = (total / 10) * 10 + 10 - total; - - // ... in case the sum of digits ends in 0 then 0 is the check digit. - if (checkCharPos % 10 == 0) - checkCharPos = 0; - - return m_alphabet[checkCharPos]; - } - - /// - /// Gets the AIIM checksum char. - /// - /// The value to calculate checksum char for. - /// The AIIM checksum char. - private char getAIIMChecksumChar(string value) - { - // AIM recommends the following checkdigit calculation - // 1. The sum of all character values is taken, including the - // Start and the Stop characters. - // 2. The data character whose value that when added to the total - // from step one equals a multiple of 16 is the check character. - int total = getSpecialSymbolValue(Options.CodabarStartSymbol); - - for (int i = 0; i < value.Length; i++) - total += getCharPosition(value[i]); - - total += getSpecialSymbolValue(Options.CodabarStopSymbol); - - int checkCharPos = (total / 16) * 16 + 16 - total; - - if ((checkCharPos % 16) == 0) - checkCharPos = 0; - - return m_alphabet[checkCharPos]; - } - - /// - /// Gets the char position within the alphabet. - /// - /// The char to find. - /// - private static int getCharPosition(char c) - { - for (int i = 0; i < m_alphabet.Length; i++) - { - if (m_alphabet[i] == c) - return i; - } - - string message = String.Format("Incorrect character '%c' for Codabar symbology", c); - throw new BarcodeException(message); - } - - /// - /// Gets the special symbol value. - /// - /// The symbol. - /// - private static int getSpecialSymbolValue(CodabarSpecialSymbol symbol) - { - if (symbol == CodabarSpecialSymbol.A) - return 16; - else if (symbol == CodabarSpecialSymbol.B) - return 17; - else if (symbol == CodabarSpecialSymbol.C) - return 18; - - return 19; // D - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/Code128Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/Code128Symbology.cs deleted file mode 100644 index 1eb11177..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/Code128Symbology.cs +++ /dev/null @@ -1,745 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using SkiaSharp; -using System; -using System.Drawing; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Code 128 is a very effective, high-density symbology which permits - /// the encoding of alphanumeric data. Code 128 is a very dense code, - /// used extensively worldwide. - /// - class Code128Symbology : SymbologyDrawing - { - protected const char m_fnc1 = ''; - protected static char m_fnc2 = ''; - protected static char m_fnc3 = ''; - protected static char m_fnc4 = ''; - - protected const char m_shift = ''; - - protected const char m_codeA = ''; - protected const char m_codeB = ''; - protected const char m_codeC = ''; - - protected const char m_startA = ''; - protected const char m_startB = ''; - protected const char m_startC = ''; - - protected string m_numbers = "0123456789"; - protected string m_lowerCase = "abcdefghijklmnopqrstuvwxyz"; - - protected char[] m_aSpecials = { m_fnc3, m_fnc2, m_shift, m_codeC, m_codeB, m_fnc4, m_fnc1, m_startA, m_startB, m_startC }; - protected char[] m_bSpecials = { m_fnc3, m_fnc2, m_shift, m_codeC, m_fnc4, m_codeA, m_fnc1, m_startA, m_startB, m_startC }; - protected char[] m_cSpecials = { m_codeB, m_codeA, m_fnc1, m_startA, m_startB, m_startC }; - - /// - /// Initializes a new instance of the class. - /// - public Code128Symbology() - : base(TrueSymbologyType.Code128) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public Code128Symbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.Code128) - { - } - - /// - /// Validates the value using Code 128 symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - switch (getUsedAlphabet()) - { - case Code128Alphabet.A: - return aValueIsValid(value); - - case Code128Alphabet.B: - return bValueIsValid(value); - - case Code128Alphabet.C: - return cValueIsValid(value); - - case Code128Alphabet.Auto: - default: - return autoValueIsValid(value); - } - } - - protected virtual Code128Alphabet getUsedAlphabet() - { - return Options.Code128Alphabet; - } - - /// - /// Validates the value using Code 128 Alphabet A symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - private static bool aValueIsValid(string value) - { - // Alphabet A allows only character from NUL (ASCII 0) to - // '_' (ASCII 95) to be encoded. - - foreach (int c in value) - { - if (c > 95) - return false; - } - - return true; - } - - /// - /// Validates the value using Code 128 Alphabet B symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - private static bool bValueIsValid(string value) - { - // Alphabet B allows only character from SPACE (ASCII 32) to - // DEL (ASCII 127) to be encoded. - - foreach (int c in value) - { - if (c > 127 || c < 32) - return false; - } - - return true; - } - - /// - /// Validates the value using Code 128 Alphabet C symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - private bool cValueIsValid(string value) - { - // Alphabet C allows only numeric values with even length - - if (value.Length % 2 != 0) - return false; - - foreach (char c in value) - { - if (m_numbers.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Validates the value using Code 128 with auto alphabet selection symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - protected bool autoValueIsValid(string value) - { - return aValueIsValid(value) || bValueIsValid(value) || cValueIsValid(value); - } - - /// - /// Gets the value restrictions description string. - /// - /// The value restrictions description string. - public override string getValueRestrictions() - { - switch (getUsedAlphabet()) - { - case Code128Alphabet.A: - return "Code 128 Alphabet A allows only character from NUL (ASCII 0) to '_' (ASCII 95) to be encoded."; - - case Code128Alphabet.C: - return "Code 128 Alphabet C allows only numeric values with even length."; - - case Code128Alphabet.B: - return "Code 128 Alphabet B allows only character from SPACE (ASCII 32) to DEL (ASCII 127) to be encoded."; - - case Code128Alphabet.Auto: - default: - return "Code 128 with auto alphabet selection allows at most first 128 ASCII symbols to be encoded."; - } - } - - /// - /// Gets the barcode value encoded using Code 128 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using Code 128 symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - - if (!forCaption && getUsedAlphabet() != Code128Alphabet.Auto) - sb.Append(getStartChar()); - - if (forCaption || getUsedAlphabet() != Code128Alphabet.Auto) - sb.Append(Value); - else - { - // we need to encode value using auto alphabet selection - sb.Append(autoEncodeValue(Value)); - } - - // checksum chars are always added to encoded value - // (and NEVER to caption) - - return sb.ToString(); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// The encoding pattern for given character. - protected override string getCharPattern(char c) - { - return null; - } - - /// - /// Gets the start char for the given alphabet. - /// - /// - protected char getStartChar() - { - switch (getUsedAlphabet()) - { - case Code128Alphabet.A: - return m_startA; - - case Code128Alphabet.B: - return m_startB; - - case Code128Alphabet.C: - return m_startC; - - default: - throw new BarcodeException("Incorrect alphabet"); - } - } - - /// - /// Encodes current value using auto alphabet selection. - /// - /// The value to encode. - /// Current value encoded using auto alphabet selection. - protected string autoEncodeValue(string value) - { - StringBuilder sb = new StringBuilder(); - - Code128Alphabet alphabet = selectAlphabet(value, 0); - if (alphabet == Code128Alphabet.C) - sb.Append(m_startC); - else if (alphabet == Code128Alphabet.A) - sb.Append(m_startA); - else - sb.Append(m_startB); - - int pos = 0; - while (pos < value.Length) - { - if (alphabet == Code128Alphabet.C) - { - int numberCount = getNumberCount(value, pos); - int skipCharCount = numberCount - numberCount % 2; - for (int i = 0; i < skipCharCount; i++) - { - sb.Append(value[pos]); - pos++; - } - - if (pos != value.Length) - { - alphabet = selectAlphabet(value, pos); - if (alphabet == Code128Alphabet.A) - sb.Append(m_codeA); - else - sb.Append(m_codeB); - } - } - else if ((alphabet == Code128Alphabet.A || alphabet == Code128Alphabet.B) && getNumberCount(value, pos) >= 4) - { - int numberCount = getNumberCount(value, pos); - if (numberCount % 2 == 1) - { - sb.Append(value[pos]); - pos++; - } - - sb.Append(m_codeC); - alphabet = Code128Alphabet.C; - } - else if ((alphabet == Code128Alphabet.B) && isControlCharacter(value[pos])) - { - if ((pos < value.Length - 2) && isLowerCaseCharacter(value[pos + 1]) && isControlCharacter(value[pos + 2])) - { - sb.Append(m_shift); - sb.Append(value[pos]); - pos++; - } - else - { - sb.Append(m_codeA); - alphabet = Code128Alphabet.A; - } - } - else if (alphabet == Code128Alphabet.A && isLowerCaseCharacter(value[pos])) - { - if ((pos < value.Length - 2) && isControlCharacter(value[pos + 1]) && isLowerCaseCharacter(value[pos + 2])) - { - sb.Append(m_shift); - sb.Append(value[pos]); - pos++; - } - else - { - sb.Append(m_codeB); - alphabet = Code128Alphabet.B; - } - } - else - { - sb.Append(value[pos]); - pos++; - } - } - - return sb.ToString(); - } - - /// - /// Selects the alphabet to encode value from the given position. - /// - /// The value. - /// The start position. - /// The alphabet to encode value from the given position. - protected virtual Code128Alphabet selectAlphabet(string value, int pos) - { - if (getNumberCount(value, pos) >= 4) - return Code128Alphabet.C; - else if (getFirstControlCharPos(value, pos) < getFirstLowerCasePos(value, pos)) - return Code128Alphabet.A; - else - return Code128Alphabet.B; - } - - /// - /// Gets the length of consecutively going numbers. - /// - /// The value. - /// The start position. - /// The length of consecutively going numbers. - protected int getNumberCount(string value, int startPos) - { - int numberCount = 0; - - for (int i = startPos; i < value.Length; i++) - { - if (m_numbers.IndexOf(value[i]) == -1) - break; - else - numberCount++; - } - - return numberCount; - } - - /// - /// Gets the first lower case character posistion. - /// - /// The value. - /// The start position. - /// The first lower case character posistion. - protected int getFirstLowerCasePos(string value, int startPos) - { - for (int i = startPos; i < value.Length; i++) - { - if (isLowerCaseCharacter(value[i])) - return i; - } - - return value.Length; - } - - /// - /// Gets the first control character position. - /// - /// The value. - /// The start position. - /// The first control character position. - protected static int getFirstControlCharPos(string value, int startPos) - { - for (int i = startPos; i < value.Length; i++) - { - if (isControlCharacter(value[i])) - return i; - } - - return value.Length; - } - - /// - /// Determines whether given character is a control character. - /// - /// The character to test. - /// - /// true if given character is a control character; otherwise, false. - /// - protected static bool isControlCharacter(char c) - { - if (c >= '\0' && c < ' ') - return true; - - return false; - } - - /// - /// Determines whether given character is a lower case character. - /// - /// The character to test. - /// - /// true if given character is a lower case character; otherwise, false. - /// - protected bool isLowerCaseCharacter(char c) - { - if (m_lowerCase.IndexOf(c) != -1) - return true; - - return false; - } - - /// - /// Calculates the modulo 103 checksum. - /// - /// The value to calculate checksum for. - /// The checksum. - private static int calculateChecksum(int[] values) - { - int total = values[0]; - for (int i = 1; i < values.Length; i++) - total += values[i] * i; - - return total % 103; - } - - /// - /// Gets the character values array for the given string. - /// - /// The string. - /// The character values array for the given string. - protected virtual int[] getValues(string value) - { - // number of values is less or equal to number of characters in source string - int[] intialValues = new int[value.Length]; - - Code128Alphabet alphabet = Code128Alphabet.Auto; - switch (value[0]) - { - case m_startA: - alphabet = Code128Alphabet.A; - break; - - case m_startB: - default: - alphabet = Code128Alphabet.B; - break; - - case m_startC: - alphabet = Code128Alphabet.C; - break; - } - - intialValues[0] = getCharValue(value[0], alphabet); - bool shiftOccured = false; - - int valuesAdded = 1; - for (int i = valuesAdded; i < value.Length; i++) - { - bool special = false; - switch (value[i]) - { - case m_codeA: - special = true; - intialValues[valuesAdded] = getCharValue(value[i], alphabet); - alphabet = Code128Alphabet.A; - break; - - case m_codeB: - special = true; - intialValues[valuesAdded] = getCharValue(value[i], alphabet); - alphabet = Code128Alphabet.B; - break; - - case m_codeC: - special = true; - intialValues[valuesAdded] = getCharValue(value[i], alphabet); - alphabet = Code128Alphabet.C; - break; - - case m_shift: - special = true; - intialValues[valuesAdded] = getCharValue(value[i], alphabet); - if (alphabet == Code128Alphabet.A) - { - alphabet = Code128Alphabet.B; - shiftOccured = true; - } - else - { - alphabet = Code128Alphabet.A; - shiftOccured = true; - } - break; - } - - if (!special) - { - if (alphabet == Code128Alphabet.C) - { - string s = value.Substring(i, 2); - try - { - intialValues[valuesAdded] = Convert.ToInt16(s); - i++; - } - catch (FormatException) - { - char c = Char.IsDigit(s[0]) ? s[1] : s[0]; - string message = String.Format("Incorrect character '{0}' for C alphabet of Code 128 symbology", c); - throw new BarcodeException(message); - } - } - else - intialValues[valuesAdded] = getCharValue(value[i], alphabet); - - if (shiftOccured) - { - if (alphabet == Code128Alphabet.A) - alphabet = Code128Alphabet.B; - else - alphabet = Code128Alphabet.A; - - shiftOccured = false; - } - } - - valuesAdded++; - } - - if (valuesAdded != value.Length) - { - int[] values = new int[valuesAdded]; - for (int i = 0; i < valuesAdded; i++) - values[i] = intialValues[i]; - - return values; - } - - return intialValues; - } - - /// - /// Gets the character value for the given alphabet. - /// - /// The character. - /// The alphabet. - /// The character value for the given alphabet. - protected int getCharValue(char c, Code128Alphabet alphabet) - { - int value = 0; - - if (alphabet == Code128Alphabet.A) - { - if (c >= ' ' && c <= '_') - value = (int)c - 32; - else if (c >= '\0' && c < ' ') - value = (int)c + 64; - else - { - for (int i = 0; i < m_aSpecials.Length; i++) - { - if (m_aSpecials[i] == c) - { - value += 96 + i; - break; - } - } - } - } - else if (alphabet == Code128Alphabet.B) - { - if (c >= ' ' && (int)c <= 127) - value = (int)c - 32; - else - { - for (int i = 0; i < m_bSpecials.Length; i++) - { - if (m_bSpecials[i] == c) - { - value += 96 + i; - break; - } - } - } - } - else - { - for (int i = 0; i < m_cSpecials.Length; i++) - { - if (m_cSpecials[i] == c) - { - value += 100 + i; - break; - } - } - } - - return value; - } - - /// - /// Gets the encoding pattern for given value. - /// - /// The value to get encoding pattern for. - /// - /// The encoding pattern for given value. - /// - private string getValuePattern(int value) - { - return m_patterns[value]; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - string encoded = GetEncodedValue(false); - int[] values = getValues(encoded); - int checksumCharValue = calculateChecksum(values); - - foreach (int i in values) - { - string pattern = getValuePattern(i); - x = drawPattern(pattern, x, y, NarrowBarWidth, BarHeight); - } - - string checksum = getValuePattern(checksumCharValue); - x = drawPattern(checksum, x, y, NarrowBarWidth, BarHeight); - - x = drawPattern(m_stopPattern, x, y, NarrowBarWidth, BarHeight); - - // draw termination bar - m_rects.Add(new SKRect(x, y, NarrowBarWidth * 2+x, BarHeight+y)); - - x += NarrowBarWidth * 2; - - drawingSize.Width = x; - drawingSize.Height = BarHeight; - return drawingSize; - } - - /// - /// Draws the pattern. - /// - /// The pattern to draw. - /// The start X position. - /// The start Y position. - /// The width. - /// The height. - /// - /// The new X position (start X position + width of the - /// rectangle occupied by pattern. - /// - private int drawPattern(string pattern, int x, int y, int width, int height) - { - foreach (char patternChar in pattern) - { - bool drawBar = (patternChar == '1'); - if (drawBar) - m_rects.Add(new SKRect(x, y, width+x, height+y)); - - x += width; - } - - return x; - } - - - ////////////////////////////////////////////////////////////////////////// - // - // Patterns part - // - ////////////////////////////////////////////////////////////////////////// - - private string[] m_patterns = { - "11011001100", "11001101100", "11001100110", "10010011000", - "10010001100", "10001001100", "10011001000", "10011000100", - "10001100100", "11001001000", "11001000100", "11000100100", - "10110011100", "10011011100", "10011001110", "10111001100", - "10011101100", "10011100110", "11001110010", "11001011100", - "11001001110", "11011100100", "11001110100", "11101101110", - "11101001100", "11100101100", "11100100110", "11101100100", - "11100110100", "11100110010", "11011011000", "11011000110", - "11000110110", "10100011000", "10001011000", "10001000110", - "10110001000", "10001101000", "10001100010", "11010001000", - "11000101000", "11000100010", "10110111000", "10110001110", - "10001101110", "10111011000", "10111000110", "10001110110", - "11101110110", "11010001110", "11000101110", "11011101000", - "11011100010", "11011101110", "11101011000", "11101000110", - "11100010110", "11101101000", "11101100010", "11100011010", - "11101111010", "11001000010", "11110001010", "10100110000", - "10100001100", "10010110000", "10010000110", "10000101100", - "10000100110", "10110010000", "10110000100", "10011010000", - "10011000010", "10000110100", "10000110010", "11000010010", - "11001010000", "11110111010", "11000010100", "10001111010", - "10100111100", "10010111100", "10010011110", "10111100100", - "10011110100", "10011110010", "11110100100", "11110010100", - "11110010010", "11011011110", "11011110110", "11110110110", - "10101111000", "10100011110", "10001011110", "10111101000", - "10111100010", "11110101000", "11110100010", "10111011110", - "10111101110", "11101011110", "11110101110", "11010000100", - "11010010000", "11010011100" - }; - - private string m_stopPattern = "11000111010"; - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/Code39Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/Code39Symbology.cs deleted file mode 100644 index c353174a..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/Code39Symbology.cs +++ /dev/null @@ -1,302 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Drawing; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using Code 39 (aka USD-3, 3 of 9) symbology rules. - /// This symbology used for example by U.S. Government and military, required for DoD applications. - /// - class Code39Symbology : SymbologyDrawing - { - protected static char[] m_alphabet = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', - '/', '+', '%' }; - - private static string m_extendedAlphabet = "%U$A$B$C$D$E$F$G$H$I$J$K$L$M$N$O$P$Q$R$S$T$U$V$W$X$Y$Z%A%B%C%D%E_ /A/B/C/D/E/F/G/H/I/J/K/L_-_./O_0_1_2_3_4_5_6_7_8_9/Z%F%G%H%I%J%V_A_B_C_D_E_F_G_H_I_J_K_L_M_N_O_P_Q_R_S_T_U_V_W_X_Y_Z%K%L%M%N%O%W+A+B+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z%P%Q%R%S%Z"; - private bool m_useExtendedAlphabet; - - /// - /// Initializes a new instance of the class. - /// - public Code39Symbology() - : base(TrueSymbologyType.Code39) - { - init(); - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public Code39Symbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.Code39) - { - } - - /// - /// Initializes this instance. - /// - private void init() - { - // Since Code 39 is self-checking, a check digit normally isn't necessary. - AddChecksum = false; - AddChecksumToCaption = false; - } - - /// - /// Validates the value using Code 39 symbology rules. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// true if value is valid (can be encoded); otherwise, false. - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - foreach (int c in value) - { - if (c > 127) - return false; - } - - // value is valid, but do we need to use extended alphabet? - m_useExtendedAlphabet = false; - - foreach (char c in value) - { - // if character not found within alphabet - // then we need to use extended alphabet - if (getCharPosition(c) == -1) - m_useExtendedAlphabet = true; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// The value restrictions description string. - public override string getValueRestrictions() - { - return "Code 39 symbology allows at most first 128 ASCII symbols to be encoded."; - } - - /// - /// Gets the barcode value encoded using Code 39 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using Code 39 symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - - if (Options.ShowStartStop || !forCaption) - sb.Append("*"); - - string valueAsSimpleChars = getValueAsSimpleChars(); - - if (forCaption) - sb.Append(Value); - else - sb.Append(valueAsSimpleChars); - - if (AddChecksum) - { - if ((forCaption && AddChecksumToCaption) || (!forCaption)) - { - int checksumCharPosition = calculateChecksum(valueAsSimpleChars); - char checksumChar = m_alphabet[checksumCharPosition]; - sb.Append(checksumChar); - } - } - - if (Options.ShowStartStop || !forCaption) - sb.Append("*"); - - return sb.ToString(); - } - - /// - /// Gets the value as simple chars (replaces extended chars with simple chars). - /// - /// The value as simple chars. - public string getValueAsSimpleChars() - { - string value = Value; - if (!m_useExtendedAlphabet) - return value; - - // simple string is at most twice as long as extended - // because each extended character encoded as two simple characters - StringBuilder sb = new StringBuilder(value.Length * 2); - - foreach (char c in Value) - { - int shiftCharPosition = ((int)c) * 2; - char shiftChar = m_extendedAlphabet[shiftCharPosition]; - if (shiftChar != '_') - { - // underscore is a padding character. just skip it. - sb.Append(shiftChar); - } - - sb.Append(m_extendedAlphabet[shiftCharPosition + 1]); - } - - return sb.ToString(); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// The encoding pattern for given character. - protected override string getCharPattern(char c) - { - switch (c) - { - case '0': - return "nnnwwnwnnn"; - case '1': - return "wnnwnnnnwn"; - case '2': - return "nnwwnnnnwn"; - case '3': - return "wnwwnnnnnn"; - case '4': - return "nnnwwnnnwn"; - case '5': - return "wnnwwnnnnn"; - case '6': - return "nnwwwnnnnn"; - case '7': - return "nnnwnnwnwn"; - case '8': - return "wnnwnnwnnn"; - case '9': - return "nnwwnnwnnn"; - case 'A': - return "wnnnnwnnwn"; - case 'B': - return "nnwnnwnnwn"; - case 'C': - return "wnwnnwnnnn"; - case 'D': - return "nnnnwwnnwn"; - case 'E': - return "wnnnwwnnnn"; - case 'F': - return "nnwnwwnnnn"; - case 'G': - return "nnnnnwwnwn"; - case 'H': - return "wnnnnwwnnn"; - case 'I': - return "nnwnnwwnnn"; - case 'J': - return "nnnnwwwnnn"; - case 'K': - return "wnnnnnnwwn"; - case 'L': - return "nnwnnnnwwn"; - case 'M': - return "wnwnnnnwnn"; - case 'N': - return "nnnnwnnwwn"; - case 'O': - return "wnnnwnnwnn"; - case 'P': - return "nnwnwnnwnn"; - case 'Q': - return "nnnnnnwwwn"; - case 'R': - return "wnnnnnwwnn"; - case 'S': - return "nnwnnnwwnn"; - case 'T': - return "nnnnwnwwnn"; - case 'U': - return "wwnnnnnnwn"; - case 'V': - return "nwwnnnnnwn"; - case 'W': - return "wwwnnnnnnn"; - case 'X': - return "nwnnwnnnwn"; - case 'Y': - return "wwnnwnnnnn"; - case 'Z': - return "nwwnwnnnnn"; - case '-': - return "nwnnnnwnwn"; - case '.': - return "wwnnnnwnnn"; - case ' ': - return "nwwnnnwnnn"; - case '$': - return "nwnwnwnnnn"; - case '/': - return "nwnwnnnwnn"; - case '+': - return "nwnnnwnwnn"; - case '%': - return "nnnwnwnwnn"; - case '*': - return "nwnnwnwnnn"; - } - - return "wwwwwwwwww"; - } - - /// - /// Gets the char position within the alphabet. - /// - /// The char to find. - /// - protected static int getCharPosition(char c) - { - for (int i = 0; i < m_alphabet.Length; i++) - { - if (m_alphabet[i] == c) - return i; - } - - return -1; - } - - /// - /// Calculates the modulo 43 checksum of the given value. - /// - /// The value to calculate checksum for. - /// - private static int calculateChecksum(string value) - { - int checksum = 0; - - foreach (char c in value) - { - int checkValue = getCharPosition(c); - if (checkValue == -1) - { - string message = String.Format("Incorrect character '%c' for Code 39 symbology", c); - throw new BarcodeException(message); - } - - checksum += checkValue; - } - - return checksum % 43; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/Code93Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/Code93Symbology.cs deleted file mode 100644 index ab3a78d0..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/Code93Symbology.cs +++ /dev/null @@ -1,589 +0,0 @@ -/************************************************** - * - Copyright (c) 2008 - 2015 Bytescout - * - * -**************************************************/ - -using SkiaSharp; -using System; -using System.Drawing; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using Code 93 (aka USS-93) symbology rules. - /// - class Code93Symbology : SymbologyDrawing - { - - /// - /// full set of 48 characters - /// - private static char[] m_alphabet = { - '0', - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - 'A', - 'B', - 'C', - 'D', - 'E', - 'F', - 'G', - 'H', - 'I', - 'J', - 'K', - 'L', - 'M', - 'N', - 'O', - 'P', - 'Q', - 'R', - 'S', - 'T', - 'U', - 'V', - 'W', - 'X', - 'Y', - 'Z', - '-', - '.', - ' ', - '$', - '/', - '+', - '%', - 'a', //we use it instead of "($)", see getValueAsSimpleChars and ValueIsValid() - 'b', //we use it instead of "(%)", see getValueAsSimpleChars and ValueIsValid() - 'c', //we use it instead of "(/)", see getValueAsSimpleChars and ValueIsValid() - 'd', //we use it instead of "(+)", see getValueAsSimpleChars and ValueIsValid() - }; - - // Code 93 extended characters is similar to Code 39 extended characters table - // DIFFERENCE: it uses up to 4 characters instead of up to 2 characters for extended symbols encoding - // https://en.wikipedia.org/wiki/Code_93#Full_ASCII_Code_93 - private static string[] m_extendedAlphabet = - { - "(%)U", //NUL - "($)A", //SOH - "($)B", //STX - "($)C", //ETX - "($)D", //EOT - "($)E", //ENQ - "($)F", //ACK - "($)G", //BEL - "($)H", //BS - "($)I", //TAB - "($)J", //LF - "($)K", //VT - "($)L", //FF - "($)M", //CR - "($)N", //SO - "($)O", //SI - "($)P", //DLE - "($)Q", //DC1 - "($)R", //DC2 - "($)S", //DC3 - "($)T", //DC4 - "($)U", //NAK - "($)V", //SYN - "($)W", //ETB - "($)X", //CAN - "($)Y", //EM - "($)Z", //SUB - "(%)A", //ESC - "(%)B", //FS - "(%)C", //GS - "(%)D", //RS - "(%)E", //US - " ", //Space - - "(/)A", //! - "(/)B", //" - "(/)C", //# - - "$", //$ It's also valid to use "#D"but it uses an extra char - //"/D", //$ - - "%", //% It's also valid to use "#E"but it uses an extra char - //"/E", //% - - "(/)F", //& - "(/)G", //' - "(/)H", //( - "(/)I", //) - "(/)J", //* - - "+", //+ It's also valid to use "#K"but it uses an extra char - //"/K", //+ - - "(/)L", //, - - "-", //- It's also valid to use "#M" but it uses an extra char - //"/M", //- - - ".", //. It's also valid to use "#N" but it uses an extra char - //"/N", //. - - "/", // / It's also valid to use "#O" but it uses an extra char - //"/O", /// - - "0", //0 It's also valid to use "#P" but it uses an extra char - //"/P", //0 - - "1", //1 It's also valid to use "#Q" but it uses an extra char - //"/Q", //1 - - "2", //2 It's also valid to use "#R" but it uses an extra char - //"/E", //2 - - "3", //3 It's also valid to use "#S" but it uses an extra char - //"/S", //3 - - "4", //4 It's also valid to use "#T" but it uses an extra char - //"/T", //4 - - "5", //5 It's also valid to use "#U" but it uses an extra char - //"/U", //5 - - "6", //6 It's also valid to use "#V" but it uses an extra char - //"/V", //6 - - "7", //7 It's also valid to use "#W" but it uses an extra char - //"/W", //7 - - "8", //8 It's also valid to use "#X" but it uses an extra char - //"/X", //8 - - "9", //9 It's also valid to use "#Y" but it uses an extra char - //"/Y", //9 - - "(/)Z", //: - "(%)F", //; - "(%)G", //< - "(%)H", //; - "(%)I", //; - "(%)J", //; - "(%)V", //@ - "A", //A - "B", //B - "C", //C - "D", //D - "E", //E - "F", //F - "G", //G - "H", //H - "I", //I - "J", //J - "K", //K - "L", //L - "M", //M - "N", //N - "O", //O - "P", //P - "Q", //Q - "R", //R - "S", //S - "T", //T - "U", //U - "V", //V - "W", //W - "X", //X - "Y", //Y - "Z", //Z - "(%)K", //[ - "(%)L", //\ - "(%)M", //] - "(%)N", //^ - "(%)O", //_ - "(%)W", //` - "(+)A", //a - "(+)B", //b - "(+)C", //c - "(+)D", //d - "(+)E", //e - "(+)F", //f - "(+)G", //g - "(+)H", //h - "(+)I", //i - "(+)J", //j - "(+)K", //k - "(+)L", //l - "(+)M", //m - "(+)N", //n - "(+)O", //o - "(+)P", //p - "(+)Q", //q - "(+)R", //r - "(+)S", //s - "(+)T", //t - "(+)U", //u - "(+)V", //v - "(+)W", //w - "(+)X", //x - "(+)Y", //y - "(+)Z", //z - "(%)P", //{ - "(%)Q", // - "(%)R", //} - "(%)S", //~ - "(%)T"//DEL (can also use %T, %X, %Y, %Z) - }; - - private bool m_useExtendedAlphabet; - - /// - /// Initializes a new instance of the class. - /// - public Code93Symbology() - : base(TrueSymbologyType.Code93) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public Code93Symbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.Code93) - { - } - - /// - /// Validates the value using Code 93 symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - foreach (int c in value) - { - if (c > 127) - return false; - } - - // value is valid, but do we need to use extended alphabet? - m_useExtendedAlphabet = false; - - foreach (char c in value) - { - // if character not found within alphabet (and not one of our control characters "abcd"!) - // then we need to use extended alphabet - if (getCharPosition(c) == -1 || "abcd".IndexOf(c)>-1) - m_useExtendedAlphabet = true; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// The value restrictions description string. - public override string getValueRestrictions() - { - return "Code 93 symbology allows at most first 128 ASCII symbols to be encoded."; - } - - /// - /// Gets the barcode value encoded using Code 93 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using Code 93 symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - - if (Options.ShowStartStop || !forCaption) - sb.Append("*"); - - string valueAsSimpleChars = getValueAsSimpleChars(); - - if (forCaption) - sb.Append(Value); - else - sb.Append(valueAsSimpleChars); - - // two checksum chars always added to encoded value - // (and optionally to caption) - - if ((forCaption && AddChecksumToCaption) || (!forCaption)) - { - // calculate C checksum - int checksumCharPosition = calculateCChecksum(valueAsSimpleChars); - char cchecksumChar = m_alphabet[checksumCharPosition]; - sb.Append(cchecksumChar); - - // calculate K checksum (for value AND C checksum char) - checksumCharPosition = calculateKChecksum(valueAsSimpleChars + cchecksumChar); - char kchecksumChar = m_alphabet[checksumCharPosition]; - sb.Append(kchecksumChar); - } - - if (Options.ShowStartStop || !forCaption) - sb.Append("*"); - - return sb.ToString(); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - switch (c) - { - case '0': - return "bsssbsbss"; - case '1': - return "bsbssbsss"; - case '2': - return "bsbsssbss"; - case '3': - return "bsbssssbs"; - case '4': - return "bssbsbsss"; - case '5': - return "bssbssbss"; - case '6': - return "bssbsssbs"; - case '7': - return "bsbsbssss"; - case '8': - return "bsssbssbs"; - case '9': - return "bssssbsbs"; - case 'A': - return "bbsbsbsss"; - case 'B': - return "bbsbssbss"; - case 'C': - return "bbsbsssbs"; - case 'D': - return "bbssbsbss"; - case 'E': - return "bbssbssbs"; - case 'F': - return "bbsssbsbs"; - case 'G': - return "bsbbsbsss"; - case 'H': - return "bsbbssbss"; - case 'I': - return "bsbbsssbs"; - case 'J': - return "bssbbsbss"; - case 'K': - return "bsssbbsbs"; - case 'L': - return "bsbsbbsss"; - case 'M': - return "bsbssbbss"; - case 'N': - return "bsbsssbbs"; - case 'O': - return "bssbsbbss"; - case 'P': - return "bsssbsbbs"; - case 'Q': - return "bbsbbsbss"; - case 'R': - return "bbsbbssbs"; - case 'S': - return "bbsbsbbss"; - case 'T': - return "bbsbssbbs"; - case 'U': - return "bbssbsbbs"; - case 'V': - return "bbssbbsbs"; - case 'W': - return "bsbbsbbss"; - case 'X': - return "bsbbssbbs"; - case 'Y': - return "bssbbsbbs"; - case 'Z': - return "bssbbbsbs"; - case '-': - return "bssbsbbbs"; - case '.': - return "bbbsbsbss"; - case ' ': - return "bbbsbssbs"; - case '$': - return "bbbssbsbs"; - case '/': - return "bsbbsbbbs"; - case '+': - return "bsbbbsbbs"; - case '%': - return "bbsbsbbbs"; - case '*': - return "bsbsbbbbs"; - case 'a': - return "bssbssbbs"; - case 'b': - return "bbbsbbsbs"; - case 'c': - return "bbbsbsbbs"; - case 'd': - return "bssbbssbs"; - } - - return "sssssssss"; - } - - /// - /// Gets the value as simple chars (replaces extended chars with simple chars). - /// - /// The value as simple chars. - public string getValueAsSimpleChars() - { - string value = Value; - if (!m_useExtendedAlphabet) - return value; - - // simple string is at most twice as long as extended - // because each extended character encoded as two simple characters - StringBuilder sb = new StringBuilder(); - - foreach (char c in Value) - { - string extendedCharRepresentation = m_extendedAlphabet[(int)c]; - sb.Append(extendedCharRepresentation); - } - - // now replace special shift commands like ($), (%) etc with a,b,c,d markers that we - // use in our main alphabet internally - // so the bars parts will be properly assigned - - //'a', //we use it instead of "($)" - sb = sb.Replace("($)", "a"); - //'b', //we use it instead of "(%)" - sb = sb.Replace("(%)", "b"); - //'c', //we use it instead of "(/)" - sb = sb.Replace("(/)", "c"); - //'d', //we use it instead of "(+)" - sb = sb.Replace("(+)", "d"); - - - return sb.ToString(); - } - - /// - /// Gets the char position within the alphabet. - /// - /// The char to find. - /// - private static int getCharPosition(char c) - { - int position = -1; - foreach (char s in m_alphabet) - { - position++; - if (c == s) - { - return position; - } - } - return -1; - } - - /// - /// Calculates the C checksum. - /// - /// The value to calculate checksum for. - /// The C checksum. - private static int calculateCChecksum(string value) - { - int weight = 1; - int total = 0; - for (int i = value.Length - 1; i >= 0; i--) - { - total += getCharPosition(value[i]) * weight; - weight++; - - if (weight > 20) - weight = 1; - } - - return total % 47; - } - - /// - /// Calculates the K checksum. - /// - /// The value to calculate checksum for. - /// The K checksum. - private static int calculateKChecksum(string value) - { - int total = 0; - int weight = 1; - for (int i = value.Length - 1; i >= 0; i--) - { - total += getCharPosition(value[i]) * weight; - weight++; - - if (weight > 15) - weight = 1; - } - - return total % 47; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - string value = GetEncodedValue(false); - - foreach (char c in value) - { - string pattern = getCharPattern(c); - foreach (char patternChar in pattern) - { - bool drawBar = (patternChar == 'b'); - - if (drawBar) - m_rects.Add(new SKRect(x, y, NarrowBarWidth+x, BarHeight+y)); - - x += NarrowBarWidth; - } - } - - // draw termination bar - m_rects.Add(new SKRect(x, y, NarrowBarWidth+x, BarHeight+y)); - - x += NarrowBarWidth; - - drawingSize.Width = x; - drawingSize.Height = BarHeight; - return drawingSize; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/DeutschePostIdentcode.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/DeutschePostIdentcode.cs deleted file mode 100644 index a3151d5c..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/DeutschePostIdentcode.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws Deutsche Post Identcode barcodes. - /// - class DeutschePostIdentcodeSymbology : I2of5Symbology - { - /// - /// Initializes a new instance of the class. - /// - public DeutschePostIdentcodeSymbology() - : base() - { - m_type = TrueSymbologyType.DeutschePostIdentcode; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public DeutschePostIdentcodeSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.DeutschePostIdentcode; - } - - /// - /// Validates the value using Deutsche Post Identcode symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Checksum is obligatory or not. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - string cleanValue = getCleanValue(value); - if (cleanValue.Length != 11) - return false; - - return base.ValueIsValid(cleanValue, checksumIsMandatory); - } - - private static string getCleanValue(string value) - { - string cleanValue = value.Replace(" ", ""); - cleanValue = cleanValue.Replace(".", ""); - return cleanValue; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Deutsche Post Identcode symbology allows only numeric values with exactly 11 digits: 2 digits for ID of primary distribution center, 3 digits for Customer ID, and 6 digits for Mailing number to be encoded. Additionally, spaces and dots can be added as separators\n"; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "00.000 000.000"; - } - - /// - /// Gets the barcode value encoded using Deutsche Post Identcode symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - - string cleanValue = getCleanValue(Value); - sb.Append(cleanValue); - - if ((forCaption && AddChecksumToCaption) || (!forCaption)) - { - char checksumChar = getChecksumChar(cleanValue); - sb.Append(checksumChar); - } - - string encoded = sb.ToString(); - - if (forCaption) - { - encoded = encoded.Insert(2, "."); - encoded = encoded.Insert(6, " "); - encoded = encoded.Insert(10, "."); - - if (AddChecksumToCaption) - encoded = encoded.Insert(14, " "); - } - - return encoded; - } - - /// - /// Gets the checksum char. - /// - /// The value. - /// The checksum char. - protected override char getChecksumChar(string value) - { - int sum = 0; - for (int i = value.Length - 1; i >= 0; i--) - { - sum += 4 * getCharPosition(value[i]); - - if (!((i % 2) == 0)) - sum += 5 * getCharPosition(value[i]); - } - - int checkDigitPos = 10 - (sum % 10); - if (checkDigitPos == 10) - checkDigitPos = 0; - - return m_alphabet[checkDigitPos]; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/DeutschePostLeitcode.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/DeutschePostLeitcode.cs deleted file mode 100644 index 71dd299d..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/DeutschePostLeitcode.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws Deutsche Post Leitcode - /// - class DeutschePostLeitcodeSymbology : I2of5Symbology - { - /// - /// Initializes a new instance of the class. - /// - public DeutschePostLeitcodeSymbology() - : base() - { - m_type = TrueSymbologyType.DeutschePostLeitcode; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public DeutschePostLeitcodeSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.DeutschePostLeitcode; - } - - /// - /// Validates the value using Deutsche Post Leitcode symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Checksum is obligatory or not. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - string cleanValue = getCleanValue(value); - if (cleanValue.Length != 13) - return false; - - return base.ValueIsValid(cleanValue, checksumIsMandatory); - } - - private static string getCleanValue(string value) - { - string cleanValue = value.Replace(" ", ""); - cleanValue = cleanValue.Replace(".", ""); - return cleanValue; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Deutsche Post Leitcode symbology allows only numeric values with exactly 13 digits: 5 digits for Postal Code (Postleitzahl, PLZ), 3 digits for Street ID/number, 3 digits for House number, and 2 digits for Product code to be encoded. Additionally, spaces and dots can be added as separators\n"; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "00000.000.000.00"; - } - - /// - /// Gets the barcode value encoded using Deutsche Post Leitcode symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - - string cleanValue = getCleanValue(Value); - sb.Append(cleanValue); - - if ((forCaption && AddChecksumToCaption) || (!forCaption)) - { - char checksumChar = getChecksumChar(cleanValue); - sb.Append(checksumChar); - } - - string encoded = sb.ToString(); - - if (forCaption) - { - encoded = encoded.Insert(5, "."); - encoded = encoded.Insert(9, "."); - encoded = encoded.Insert(13, "."); - - if (AddChecksumToCaption) - encoded = encoded.Insert(16, " "); - } - - return encoded; - } - - /// - /// Gets the checksum char. - /// - /// The value. - /// The checksum char. - protected override char getChecksumChar(string value) - { - int sum = 0; - for (int i = value.Length - 1; i >= 0; i--) - { - sum += 4 * getCharPosition(value[i]); - - if (!((i % 2) == 0)) - sum += 5 * getCharPosition(value[i]); - } - - int checkDigitPos = 10 - (sum % 10); - if (checkDigitPos == 10) - checkDigitPos = 0; - - return m_alphabet[checkDigitPos]; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/DutchKixSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/DutchKixSymbology.cs deleted file mode 100644 index 9c3656ff..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/DutchKixSymbology.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws Dutch KIX barcodes. - /// - class DutchKixSymbology : RoyalMailSymbology - { - /// - /// Initializes a new instance of the class. - /// - public DutchKixSymbology() - : base() - { - m_type = TrueSymbologyType.DutchKix; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public DutchKixSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.DutchKix; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Ductch KIX symbology allows only digits and characters from A to Z to be encoded."; - } - - /// - /// Gets the barcode value encoded using current symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - return Value.ToUpper(); - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN128Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN128Symbology.cs deleted file mode 100644 index 94c6d3d4..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN128Symbology.cs +++ /dev/null @@ -1,452 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using EAN128 symbology. This symbology was developed - /// to provide a worldwide format and standard for exchanging common - /// data between companies. - /// - /// See also: - /// http://www.gs1-128.info/ - /// http://www.barcodeisland.com/uccean128.phtml - /// - class EAN128Symbology : Code128Symbology - { - /// - /// Initializes a new instance of the class. - /// - public EAN128Symbology() - : base() - { - m_type = TrueSymbologyType.EAN128; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public EAN128Symbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.EAN128; - } - - /// - /// Validates the value using EAN 128 symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Checksum is mandatory or not (if applicable). - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length == 0) - return false; - - intList leftBracketPos = new intList(); - intList rightBracketPos = new intList(); - bool validBrackets = GS1ValueChecker.FindBracketPositions(value, leftBracketPos, rightBracketPos); - bool useCheckDigits = (AddChecksum || checksumIsMandatory) && validBrackets && (leftBracketPos.Count == 1); - - return base.autoValueIsValid(getCleanValue(value, useCheckDigits)); - } - - /// - /// Gets or sets the barcode value to encode. - /// - /// The barcode value to encode. - public override string Value - { - get - { - return base.Value; - } - set - { - //if (this is EAN14Symbology) - //{ - // base.Value = value; - //} - //else - { - bool isBracket = false; - for (int i = 0; i < value.Length; i++) - if (value[i] == '(') - { - isBracket = true; - break; - } - if (isBracket) - base.Value = value; - else - base.Value = ApplicationIdentifiers.SelectAIs(value); - } - } - } - - - // there is also GS1ValueChecker.GetStripped() to get the value cleaned from ( and ) - private string getCleanValue(string value, bool useCheckDigits) - { - string[] parts = value.Split(new char[] { '(', ')' }); - StringBuilder sb = new StringBuilder(); - foreach (string s in parts) - { - if (s.Length != 0) - { - sb.Append(s); - - bool containsAlpha = false; - - for (int pos = 0; pos < s.Length; pos++) - { - if (!char.IsDigit(s[pos])) - { - containsAlpha = true; - break; - } - } - - if (useCheckDigits) - { - if (s.Length == 7 && !containsAlpha) - sb.Append(calculateMod10Checksum(s)); - else if (s.Length == 11 && !containsAlpha) - sb.Append(calculateMod10Checksum(s)); - else if (s.Length == 13 && !containsAlpha) - sb.Append(calculateMod10Checksum(s)); - else if (s.Length == 17 && !containsAlpha) - sb.Append(calculateMod10Checksum(s)); - } - } - } - - return sb.ToString(); - } - - private char calculateMod10Checksum(string s) - { - // http://www.gs1.org/barcodes/support/check_digit_calculator - // 1) Multiply value of each position by - // x3 x1 x3 x1 x3 x1 x3 x1 x3 x1 x3 x1 x3 x1 x3 x1 x3 - // 2) Add results together to create sum - // 3) Subtract the sum from nearest equal or higher multiple of ten = Check Digit - - int total = 0; - for (int i = 0; i < s.Length; i++) - { - int value = m_numbers.IndexOf(s[i]); - if (i % 2 != 0) - total += value; - else - total += value * 3; - } - - if (total % 10 == 0) - return '0'; - - int baseValue = total / 10; - baseValue++; - - return m_numbers[(baseValue * 10) - total]; - } - - /// - /// Gets the value restrictions description string. - /// - /// The value restrictions description string. - public override string getValueRestrictions() - { - string restriction = "GS1-128 (UCC/EAN128) allows at most first 128 ASCII symbols to be encoded."; - restriction += "The value can be set in 2 forms: with parenthesis for AI and without."; - restriction += "If a value comes in form like xxxxxxxxxxx then the SDK automatically sets brackets according to GS1 AI (Application Identifiers)."; - restriction += "If a value comes in form like (xx)yyyyyy then the SDK do NOT verifies the value against AI. However you may verify if the value (with parenthesis) is valid or not by using Barcode.ValueIsValidGS1() bool function"; - return restriction; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "1112345617ABCDEF"; - } - - /// - /// Gets the barcode value encoded using EAN 128 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using EAN 128 symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - if (forCaption) - return Value; - - return getValueForEncoding(Value); - } - - protected string getValueForEncoding(string value) - { - StringBuilder sb = new StringBuilder(); - sb.Append(m_startC); - sb.Append(m_fnc1); - - // always start with Alphabet C - Code128Alphabet alphabet = Code128Alphabet.C; - - intList leftBracketPos = new intList(); - intList rightBracketPos = new intList(); - if (GS1ValueChecker.FindBracketPositions(value, leftBracketPos, rightBracketPos)) - { - bool sawNonFixedLength = false; - bool useCheckDigits = AddChecksum; - - for (int i = 0; i < leftBracketPos.Count; i++) - { - // we need to encode value chunk using auto alphabet selection - int start = leftBracketPos[i]; - int nextStart = value.Length; - if (i != (leftBracketPos.Count - 1)) - nextStart = leftBracketPos[i + 1]; - - bool fixedLength = false; - string aiStart = value.Substring(start + 1, 2); - for (int j = 0; j < GS1ValueChecker.FixedLengthAIs.Length; j++) - { - string ai = GS1ValueChecker.FixedLengthAIs[j]; - if (aiStart == ai) - fixedLength = true; - } - - string chunk = value.Substring(start, nextStart - start); - - if (sawNonFixedLength) - { - // should separate variable length chunks with FNC1 - sb.Append(m_fnc1); - sawNonFixedLength = false; - } - - sb.Append(encodeValue(getCleanValue(chunk, useCheckDigits), ref alphabet)); - - if (!fixedLength) - sawNonFixedLength = true; - } - } - - return sb.ToString(); - } - - /// - /// Encodes current value using auto alphabet selection. - /// - /// The value to encode. - /// The alphabet to start encoding with. - /// - /// Current value encoded using auto alphabet selection. - /// - private string encodeValue(string value, ref Code128Alphabet alphabet) - { - StringBuilder sb = new StringBuilder(); - - int pos = 0; - while (pos < value.Length) - { - if (alphabet == Code128Alphabet.C) - { - int numberCount = getNumberCount(value, pos); - int skipCharCount = numberCount - numberCount % 2; - for (int i = 0; i < skipCharCount; i++) - { - sb.Append(value[pos]); - pos++; - } - - if (pos != value.Length) - { - alphabet = selectAlphabet(value, pos); - if (alphabet == Code128Alphabet.A) - sb.Append(m_codeA); - else - sb.Append(m_codeB); - } - } - else if ((alphabet == Code128Alphabet.A || alphabet == Code128Alphabet.B) && getNumberCount(value, pos) >= 4) - { - int numberCount = getNumberCount(value, pos); - if (numberCount % 2 == 1) - { - sb.Append(value[pos]); - pos++; - } - - sb.Append(m_codeC); - alphabet = Code128Alphabet.C; - } - else if ((alphabet == Code128Alphabet.B) && isControlCharacter(value[pos])) - { - if ((pos < value.Length - 2) && isLowerCaseCharacter(value[pos + 1]) && isControlCharacter(value[pos + 2])) - { - sb.Append(m_shift); - sb.Append(value[pos]); - pos++; - } - else - { - sb.Append(m_codeA); - alphabet = Code128Alphabet.A; - } - } - else if (alphabet == Code128Alphabet.A && isLowerCaseCharacter(value[pos])) - { - if ((pos < value.Length - 2) && isControlCharacter(value[pos + 1]) && isLowerCaseCharacter(value[pos + 2])) - { - sb.Append(m_shift); - sb.Append(value[pos]); - pos++; - } - else - { - sb.Append(m_codeB); - alphabet = Code128Alphabet.B; - } - } - else - { - sb.Append(value[pos]); - pos++; - } - } - - return sb.ToString(); - } - - /// - /// Selects the alphabet to encode value from the given position. - /// - /// The value. - /// The start position. - /// The alphabet to encode value from the given position. - protected override Code128Alphabet selectAlphabet(string value, int pos) - { - int numCount = getNumberCount(value, pos); - if (numCount != 0 && numCount % 2 == 0) - return Code128Alphabet.C; - else if (getFirstControlCharPos(value, pos) > getFirstLowerCasePos(value, pos)) - return Code128Alphabet.A; - else - return Code128Alphabet.B; - } - - /// - /// Gets the character values array for the given string. - /// - /// The string. - /// The character values array for the given string. - protected override int[] getValues(string value) - { - // number of values is less or equal to number of characters in source string - int[] intialValues = new int[value.Length]; - - Code128Alphabet alphabet = Code128Alphabet.C; - intialValues[0] = getCharValue(m_startC, alphabet); - intialValues[1] = getCharValue(m_fnc1, alphabet); - int valuesAdded = 1; - - bool shiftOccured = false; - for (int i = valuesAdded; i < value.Length; i++) - { - bool special = false; - switch (value[i]) - { - case m_codeA: - special = true; - intialValues[valuesAdded] = getCharValue(value[i], alphabet); - alphabet = Code128Alphabet.A; - break; - - case m_codeB: - special = true; - intialValues[valuesAdded] = getCharValue(value[i], alphabet); - alphabet = Code128Alphabet.B; - break; - - case m_codeC: - special = true; - intialValues[valuesAdded] = getCharValue(value[i], alphabet); - alphabet = Code128Alphabet.C; - break; - - case m_shift: - special = true; - intialValues[valuesAdded] = getCharValue(value[i], alphabet); - if (alphabet == Code128Alphabet.A) - { - alphabet = Code128Alphabet.B; - shiftOccured = true; - } - else - { - alphabet = Code128Alphabet.A; - shiftOccured = true; - } - break; - - case m_fnc1: - special = true; - intialValues[valuesAdded] = getCharValue(m_fnc1, alphabet); - break; - } - - if (!special) - { - if (alphabet == Code128Alphabet.C) - { - string s = value.Substring(i, 2); - intialValues[valuesAdded] = Convert.ToInt16(s); - i++; - } - else - intialValues[valuesAdded] = getCharValue(value[i], alphabet); - - if (shiftOccured) - { - if (alphabet == Code128Alphabet.A) - alphabet = Code128Alphabet.B; - else - alphabet = Code128Alphabet.A; - - shiftOccured = false; - } - } - - valuesAdded++; - } - - if (valuesAdded != value.Length) - { - int[] values = new int[valuesAdded]; - for (int i = 0; i < valuesAdded; i++) - values[i] = intialValues[i]; - - return values; - } - - return intialValues; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN13Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN13Symbology.cs deleted file mode 100644 index 2e6e78ce..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN13Symbology.cs +++ /dev/null @@ -1,651 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using EAN13 symbology rules. EAN-13, based upon the - /// UPC-A standard, was implemented by the International Article - /// Numbering Association (EAN) in Europe. EAN-13 used with consumer - /// products internationally. - /// - class EAN13Symbology : SymbologyDrawing - { - protected static string m_alphabet = "0123456789"; - - protected int m_leftLeft; - protected int m_leftRight; - protected int m_rightLeft; - protected int m_rightRight; - - protected string m_leftGuardPattern = "101"; - protected string m_centerGuardPattern = "01010"; - protected string m_rightGuardPattern = "101"; - - /// - /// Initializes a new instance of the class. - /// - public EAN13Symbology() - : base(TrueSymbologyType.EAN13) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public EAN13Symbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.EAN13) - { - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "000000000000"; - } - - /// - /// Validates the value using EAN-13 symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Checksum is mandatory or not (if applicable). - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 12 && value.Length != 13) - return false; - - if (value.Length == 12 && checksumIsMandatory) - return false; - - if (value.Length == 13) - { - // user wants to enter value with check digit - // we need to verify that digit - char c = getChecksum(value.Substring(0, 12)); - if (value[12] != c) - return false; - } - - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "EAN-13 symbology expects strings with 12 digits to be encoded. Optionally, user may enter 13th digit. In the latter case the last digit (check digit) will be verified."; - } - - /// - /// Gets the barcode value encoded using EAN-13 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - // checksum char ALWAYS added to encoded value and caption - string s = Value.Substring(0, 12); - return s + getChecksum(s); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - return null; - } - - /// - /// Gets the left-hand odd parity char pattern. - /// - /// The char to retrieve pattern for. - /// The left-hand odd parity char pattern. - protected static string getLeftOddCharPattern(char c) - { - switch (c) - { - case '0': - return "0001101"; - - case '1': - return "0011001"; - - case '2': - return "0010011"; - - case '3': - return "0111101"; - - case '4': - return "0100011"; - - case '5': - return "0110001"; - - case '6': - return "0101111"; - - case '7': - return "0111011"; - - case '8': - return "0110111"; - - case '9': - return "0001011"; - } - - return "0000000"; - } - - /// - /// Gets the left-hand even parity char pattern. - /// - /// The char to retrieve pattern for. - /// The left-hand even parity char pattern. - protected static string getLeftEvenCharPattern(char c) - { - // The "left-hand even" encoding pattern is based on the "left-hand odd" - // encoding pattern. To arrive at the even encoding, work from - // the left encoding and do the following: - // 1) Change all the 1's to 0's and 0's to 1. - // 2) Read the resulting encoding in reverse order (from right - // to left). The result is the "left-hand even" encoding pattern. - - string leftOdd = getLeftOddCharPattern(c); - StringBuilder sb = new StringBuilder(); - for (int i = leftOdd.Length - 1; i >= 0; i--) - { - if (leftOdd[i] == '0') - sb.Append('1'); - else - sb.Append('0'); - } - - return sb.ToString(); - } - - /// - /// Gets the right-hand char pattern. - /// - /// The char to retrieve pattern for. - /// The right-hand char pattern. - protected static string getRightCharPattern(char c) - { - // The "right-hand" encoding pattern is exactly the same as the - // "left-hand odd" encoding pattern, but with 1's changed to 0's, - // and 0's changed to 1's. - - string leftOdd = getLeftOddCharPattern(c); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < leftOdd.Length; i++) - { - if (leftOdd[i] == '0') - sb.Append('1'); - else - sb.Append('0'); - } - - return sb.ToString(); - } - - /// - /// Gets the parity string. - /// - /// The first number. - /// The parity string. - protected virtual string getParityString(char firstNumber) - { - switch (firstNumber) - { - default: - case '0': - return "oooooo"; - - case '1': - return "ooeoee"; - - case '2': - return "ooeeoe"; - - case '3': - return "ooeeeo"; - - case '4': - return "oeooee"; - - case '5': - return "oeeooe"; - - case '6': - return "oeeeoo"; - - case '7': - return "oeoeoe"; - - case '8': - return "oeoeeo"; - - case '9': - return "oeeoeo"; - } - } - - /// - /// Gets the checksum char. - /// - /// The value to calculate checksum for. - /// The checksum char. - protected virtual char getChecksum(string value) - { - int total = 0; - for (int i = 0; i < value.Length; i++) - { - if (i % 2 == 0) - total += getCharPosition(value[i]); - else - total += getCharPosition(value[i]) * 3; - } - - int lastDigit = total % 10; - if (lastDigit == 0) - return '0'; - - return m_alphabet[10 - lastDigit]; - } - - /// - /// Gets the char position within alphabet. - /// - /// The char. - /// The char position within alphabet. - protected static int getCharPosition(char c) - { - int charPos = m_alphabet.IndexOf(c); - if (charPos == -1) - throw new BarcodeException("Incorrect char for EAN-13 symbology"); - - return charPos; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - int height = BarHeight; - int width = NarrowBarWidth; - - SKSize captionSize = calculateCaptionSize(canvas, font); - float guardHeight = height + captionSize.Height / 2; - - // left guard bars - x = (int)drawPattern(m_leftGuardPattern, x, y, width, guardHeight); - - m_leftLeft = x; - x = drawLeftHandPart(x, y); - m_leftRight = x; - - // center guard bars - x = (int)drawPattern(m_centerGuardPattern, x, y, width, guardHeight); - - m_rightLeft = x; - x = drawRightHandPart(x, y); - m_rightRight = x; - - // right guard bars - x = (int)drawPattern(m_rightGuardPattern, x, y, width, guardHeight); - - drawingSize.Width = x; - drawingSize.Height = BarHeight + captionSize.Height / 2; - return drawingSize; - } - - /// - /// Draws left-hand part of the bars. - /// - /// The start X position. - /// The start Y position. - /// - /// The new X position (start X position + the width of the rectangle - /// occupied by right-hand part of barcode bars and gaps). - /// - protected virtual int drawLeftHandPart(int x, int y) - { - string encoded = GetEncodedValue(false); - - // draw second char and 5 left-hand chars (Manufacturer digits) taking parity pattern into account - string parityPattern = getParityString(encoded[0]); - string pattern = null; - for (int i = 0; i < 6; i++) - { - if (parityPattern[i] == 'o') - pattern = getLeftOddCharPattern(encoded[1 + i]); - else - pattern = getLeftEvenCharPattern(encoded[1 + i]); - - x = (int)drawPattern(pattern, x, y, NarrowBarWidth, BarHeight); - } - - return x; - } - - /// - /// Draws right-hand part of the bars. - /// - /// The start X position. - /// The start Y position. - /// - /// The new X position (start X position + the width of the rectangle - /// occupied by right-hand part of barcode bars and gaps). - /// - protected virtual int drawRightHandPart(int x, int y) - { - string encoded = GetEncodedValue(false); - - // draw 5 right-hand (Product Code) digits and checksum char - for (int i = 0; i < 6; i++) - { - string pattern = getRightCharPattern(encoded[7 + i]); - x = (int)drawPattern(pattern, x, y, NarrowBarWidth, BarHeight); - } - - return x; - } - - /// - /// Draws the pattern. - /// - /// The pattern to draw. - /// The start X position. - /// The start Y position. - /// The width. - /// The height. - /// - /// The next horizontal position to draw barcode part at. - /// - protected float drawPattern(string pattern, float x, float y, float width, float height) - { - foreach (char patternChar in pattern) - { - bool drawBar = patternChar == '1'; - - if (drawBar) - m_rects.Add(new SKRect(x, y, width+x, height+y)); - - x += width; - } - - return x; - } - - protected override int occupiedByCaptionBefore(SKCanvas canvas, SKFont font) - { - if (DrawCaption) - { - if (CaptionPosition == CaptionPosition.Below) - { - SKRect bounds = new SKRect(); - string firstChar = Caption.Substring(0, 1); - font.MeasureText(firstChar, out bounds); - return (int)bounds.Width; - } - - if (CaptionPosition == CaptionPosition.Before) - { - return base.occupiedByCaptionBefore(canvas, font); - } - } - - return 0; - } - - protected override SKSize occupiedByCaptionBelow(SKCanvas canvas, SKFont font) - { - SKSize captionSize = new SKSize(); - - if (CaptionMayBeDrawn && CaptionPosition == CaptionPosition.Below) - { - // if caption is drawn below bars then part of the caption - // is drawn inside the bars. so, we should return the size - // of part that is drawn outside the bars only - captionSize = calculateCaptionSize(canvas, font); - int captionGap = Utils.CalculateCaptionGap(font); - captionSize.Height += captionGap; - captionSize.Height /= 2; - } - - return captionSize; - } - - protected virtual string getCaptionBeforePart() - { - if (Caption.Length >= 0) - return Caption.Substring(0, 1); - - return string.Empty; - } - - protected virtual string getCaptionLeftPart() - { - if (Caption.Length >= 1) - return Caption.Substring(1, Math.Min(Caption.Length - 1, 6)); - - return string.Empty; - } - - protected virtual string getCaptionRightPart() - { - if (Caption.Length >= 7) - return Caption.Substring(7); - - return string.Empty; - } - - protected virtual string getCaptionAfterPart() - { - return string.Empty; - } - - protected override void drawCaptionBeforePart(SKCanvas canvas, SKPaint paint, SKFont font, SKPoint position) - { - // When caption is drawn above the bars then it's not split in parts - if (CaptionPosition == CaptionPosition.Below) - { - string firstChar = getCaptionBeforePart(); - if (string.IsNullOrEmpty(firstChar)) - return; - - // Measure width and height - float textWidth = font.MeasureText(firstChar); - var metrics = font.Metrics; - float textHeight = metrics.Descent - metrics.Ascent + metrics.Leading; - - int captionGap = CustomCaptionGap == -1 ? Utils.CalculateCaptionGap(font) : CustomCaptionGap; - int captionTop = System.Math.Max((int)(position.Y + BarHeight + captionGap), 0); - - // Compute rectangle (for reference; X/Y used directly) - SKRect captionRect = new SKRect( - position.X, - captionTop, - position.X + textWidth + 1, - captionTop + textHeight + 1 - ); - - // Compute centered X - float x = position.X + (textWidth / 2); // centered relative to position.X (adjust as needed) - - // Compute baseline Y - float y = captionTop - metrics.Ascent; // top alignment - - canvas.DrawText(firstChar, x, y, font, paint); - } - } - - protected override void drawCaption(SKCanvas canvas, SKPaint paint, SKFont font, SKPoint position) - { - if (CaptionPosition == CaptionPosition.Below) - { - int captionGap = CustomCaptionGap == -1 ? Utils.CalculateCaptionGap(font) : CustomCaptionGap; - int captionTop = (int)(position.Y + BarHeight + captionGap); - - // Compute caption height using font metrics - var metrics = font.Metrics; - float captionHeight = metrics.Descent - metrics.Ascent + metrics.Leading; - - // Draw left group - string leftGroup = getCaptionLeftPart(); - if (!string.IsNullOrEmpty(leftGroup)) - { - float textWidth = font.MeasureText(leftGroup); - float x = position.X + m_leftLeft + ((m_leftRight - m_leftLeft) - textWidth) / 2f; - float y = captionTop - metrics.Ascent; // top alignment - - canvas.DrawText(leftGroup, x, y, font, paint); - } - - // Draw right group - string rightGroup = getCaptionRightPart(); - if (!string.IsNullOrEmpty(rightGroup)) - { - float textWidth = font.MeasureText(rightGroup); - float x = position.X + m_rightLeft + ((m_rightRight - m_rightLeft) - textWidth) / 2f; - float y = captionTop - metrics.Ascent; // top alignment - - canvas.DrawText(rightGroup, x, y, font, paint); - } - } - else - { - // Draw above or default using base method - base.drawCaption(canvas, paint, font, position); - } - } - - protected override void drawCaptionAfterPart(SKCanvas canvas, SKPaint paint, SKFont font, SKPoint position) - { - if (CaptionPosition == CaptionPosition.Below) - { - string lastChar = getCaptionAfterPart(); - if (string.IsNullOrEmpty(lastChar)) - return; - - // Measure width and height - float textWidth = font.MeasureText(lastChar); - var metrics = font.Metrics; - float textHeight = metrics.Descent - metrics.Ascent + metrics.Leading; - - int captionGap = Utils.CalculateCaptionGap(font); - int captionTop = Math.Max((int)(position.Y + BarHeight + captionGap), 0); - - // Compute rectangle (for reference) - SKRect captionRect = new SKRect( - position.X + m_drawingSize.Width, - captionTop, - position.X + m_drawingSize.Width + textWidth + 1, - captionTop + textHeight + 1 - ); - - // Centered X inside rectangle - float x = captionRect.Left + ((captionRect.Width - textWidth) / 2f); - - // Baseline Y (top-aligned) - float y = captionTop - metrics.Ascent; - - canvas.DrawText(lastChar, x, y, font, paint); - } - } - - protected override SKFont getFontForCaption(SKCanvas canvas, CaptionPosition position) - { - if (position == CaptionPosition.Below) - { - // Widths of left and right sections between guard bars - float leftWidth = m_leftRight - m_leftLeft; - float rightWidth = m_rightRight - m_rightLeft; - - float minWidth = rightWidth; - if (leftWidth < rightWidth || rightWidth == 0) - minWidth = leftWidth; - - // Calculate font sizes for left and right groups - string leftGroup = getCaptionLeftPart(); - SKFont leftFont = calculateFont(leftGroup, CaptionFont.Typeface, CaptionFont.Size, minWidth); - - string rightGroup = getCaptionRightPart(); - SKFont rightFont = calculateFont(rightGroup, CaptionFont.Typeface, CaptionFont.Size, minWidth); - - // Return the smaller font size to fit both - if (rightFont.Size < leftFont.Size) - { - leftFont.Dispose(); - return rightFont; - } - - rightFont.Dispose(); - return leftFont; - } - else - { - // For captions above, fallback to base behavior - return base.getFontForCaption(canvas, position); - } - } - - private static SKFont calculateFont(string text, SKTypeface typeface, float startingSize, float maxWidth) - { - if (string.IsNullOrEmpty(text)) - return new SKFont(typeface, startingSize); - - float fontSize = startingSize; - SKFont result = new SKFont(typeface, fontSize); - - float textWidth = result.MeasureText(text); - - // Decrease font size until text fits within maxWidth - while (textWidth > maxWidth && fontSize > 1) - { - result.Dispose(); // dispose previous font - fontSize -= 0.5f; - result = new SKFont(typeface, fontSize); - textWidth = result.MeasureText(text); - } - - return result; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN14Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN14Symbology.cs deleted file mode 100644 index 51e159ee..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN14Symbology.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.Text; -using System.Drawing; - -namespace BarcodeWriter.Core.Internal -{ - class EAN14Symbology : EAN128Symbology - { - /// - /// EAN-14 (also known as DUN-14, SCC-14 or UPC Shipping Container Code) - /// is a 14-digit code used to identify shipping containers. - /// There are two types of Shipping Container Code representation: - /// 1) Using UCC/EAN-128 system (which is based on CODE 128 encoding), - /// with AI set to 01; - /// 2) Using ITF-14 encoding (which is based on Interleaved 2 of 5 symbology). - /// This type is not supported in this class. - /// - /// See also: - /// http://strokescribe.com/en/EAN14.html - /// - public EAN14Symbology() - : base() - { - m_type = TrueSymbologyType.EAN14; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public EAN14Symbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.EAN14; - } - - /// - /// Validates the value using EAN 14 symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// This parameter is not applicable to this symbology (checksum is always mandatory for this barcodes). - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length == 0) - return false; - - string checkstr; - if (value.Substring(0, 4) == "(01)") - checkstr = value.Substring(4); - else if (value.Substring(0, 2) == "01") - checkstr = value.Substring(2); - else - checkstr = value; - - if (checkstr.Length != 14) - return false; - - string alphabet = "0123456789"; - - foreach (char c in checkstr) - { - if (alphabet.IndexOf(c) == -1) - return false; - } - - char checksum = checkstr[13]; - if (checksum != GS1Utils.getGTINChecksum(checkstr.Substring(0, 13))) - return false; - - return true; - } - - /// - /// Gets or sets the barcode value to encode. - /// - /// The barcode value to encode. - public override string Value - { - get - { - return base.Value; - } - set - { - string tempstr; - - if (value.StartsWith("(01)")) - tempstr = value.Substring(4); - else if (value.StartsWith("01")) - tempstr = value.Substring(2); - else - tempstr = value; - - if (tempstr.Length == 13) - base.Value = "(01)" + tempstr + GS1Utils.getGTINChecksum(tempstr); - else - base.Value = "(01)" + tempstr; - } - } - - /// - /// Gets the value restrictions description string. - /// - /// The value restrictions description string. - public override string getValueRestrictions() - { - return "The EAN-14 barcode value must have exactly 14 digits and must not start with a zero."; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "00000000000000"; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN2Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN2Symbology.cs deleted file mode 100644 index 734bcd14..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN2Symbology.cs +++ /dev/null @@ -1,232 +0,0 @@ -using System; -using System.Text; -using System.Drawing; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using EAN-2 symbology rules. - /// For more, see: http://en.wikipedia.org/wiki/EAN_2 - /// - class EAN2Symbology : SymbologyDrawing - { - private static string m_start = "01011"; - private static string m_gap = "01"; - private static string m_alphabet = "0123456789"; - - private static string[] m_lPatterns = { - "0001101", "0011001", "0010011", - "0111101", "0100011", "0110001", - "0101111", "0111011", "0110111", "0001011" - }; - - private static string[] m_gPatterns = { - "0100111", "0110011", "0011011", - "0100001", "0011101", "0111001", - "0000101", "0010001", "0001001", "0010111" - }; - - /// - /// Initializes a new instance of the class. - /// - public EAN2Symbology() - : base(TrueSymbologyType.EAN2) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public EAN2Symbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.EAN2) - { - } - - /// - /// Gets or sets the barcode caption position. - /// - /// The barcode caption position. - public override CaptionPosition CaptionPosition - { - get - { - // EAN2 always draw caption above the barcode - return CaptionPosition.Above; - } - set - { - base.CaptionPosition = value; - } - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "00"; - } - - /// - /// Validates the value using EAN-2 symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 2) - return false; - - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "EAN-2 symbology allows only strings with exactly two numbers to be encoded."; - } - - /// - /// Gets the barcode value encoded using EAN-2 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - if (forCaption && Options.EANDrawQuietZoneIndicator) - return Value + ">"; - - return Value; - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - return null; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - string value = GetEncodedValue(false); - string pattern = valueToPattern(value); - - foreach (char patternChar in pattern) - { - bool drawBar = false; - if (patternChar == '1') - drawBar = true; - - if (drawBar) - m_rects.Add(new SKRect(x, y, NarrowBarWidth+x, BarHeight+y)); - - x += NarrowBarWidth; - } - - drawingSize.Width = x; - drawingSize.Height = BarHeight; - return drawingSize; - } - - private static string valueToPattern(string value) - { - int v = Convert.ToInt32(value); - int parityIndex = v % 4; - - bool firstIsL = false; - bool secondisL = false; - - switch (parityIndex) - { - case 0: - firstIsL = true; - secondisL = true; - break; - - case 1: - firstIsL = true; - secondisL = false; - break; - - case 2: - firstIsL = false; - secondisL = true; - break; - - case 3: - firstIsL = false; - secondisL = false; - break; - } - - StringBuilder sb = new StringBuilder(); - sb.Append(m_start); - if (firstIsL) - sb.Append(getLPattern(v / 10)); - else - sb.Append(getGPattern(v / 10)); - - sb.Append(m_gap); - - if (secondisL) - sb.Append(getLPattern(v % 10)); - else - sb.Append(getGPattern(v % 10)); - - return sb.ToString(); - } - - /// - /// Gets the encoding pattern for given number using L table. - /// - /// The number to retrieve pattern for. - /// - /// The encoding pattern for given number. - /// - private static string getLPattern(int n) - { - return m_lPatterns[n]; - } - - /// - /// Gets the encoding pattern for given number using L table. - /// - /// The number to retrieve pattern for. - /// - /// The encoding pattern for given number. - /// - private static string getGPattern(int n) - { - return m_gPatterns[n]; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN5Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN5Symbology.cs deleted file mode 100644 index 5a9fd647..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN5Symbology.cs +++ /dev/null @@ -1,240 +0,0 @@ -using System; -using System.Text; -using System.Drawing; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using EAN-5 symbology rules. - /// For more, see: http://en.wikipedia.org/wiki/EAN_5 - /// - class EAN5Symbology : SymbologyDrawing - { - private static string m_start = "01011"; - private static string m_gap = "01"; - private static string m_alphabet = "0123456789"; - - private static string[] m_lPatterns = { - "0001101", "0011001", "0010011", "0111101", "0100011", - "0110001", "0101111", "0111011", "0110111", "0001011" - }; - - private static string[] m_gPatterns = { - "0100111", "0110011", "0011011", "0100001", "0011101", - "0111001", "0000101", "0010001", "0001001", "0010111" - }; - - private static string[] m_structures = { - "GGLLL", "GLGLL", "GLLGL", "GLLLG", "LGGLL", - "LLGGL", "LLLGG", "LGLGL", "LGLLG", "LLGLG" - }; - - /// - /// Initializes a new instance of the class. - /// - public EAN5Symbology() - : base(TrueSymbologyType.EAN2) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public EAN5Symbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.EAN5) - { - } - - /// - /// Gets or sets the barcode caption position. - /// - /// The barcode caption position. - public override CaptionPosition CaptionPosition - { - get - { - // EAN5 always draw caption above the barcode - return CaptionPosition.Above; - } - set - { - base.CaptionPosition = value; - } - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "00000"; - } - - /// - /// Validates the value using EAN-2 symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 5) - return false; - - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "EAN-5 symbology allows only strings with exactly five numbers to be encoded."; - } - - /// - /// Gets the barcode value encoded using EAN-5 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - if (forCaption && Options.EANDrawQuietZoneIndicator) - return Value + ">"; - - return Value; - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - return null; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - string value = GetEncodedValue(false); - string pattern = valueToPattern(value); - - foreach (char patternChar in pattern) - { - bool drawBar = false; - if (patternChar == '1') - drawBar = true; - - if (drawBar) - m_rects.Add(new SKRect(x, y, NarrowBarWidth+x, BarHeight+y)); - - x += NarrowBarWidth; - } - - drawingSize.Width = x; - drawingSize.Height = BarHeight; - return drawingSize; - } - - private static string valueToPattern(string value) - { - string structure = getStructure(value); - - StringBuilder sb = new StringBuilder(); - sb.Append(m_start); - - for (int i = 0; i < structure.Length; i++) - { - int digit = (int)(value[i] - '0'); - if (structure[i] == 'L') - sb.Append(getLPattern(digit)); - else - sb.Append(getGPattern(digit)); - - if (i != (structure.Length - 1)) - sb.Append(m_gap); - } - - return sb.ToString(); - } - - /// - /// Gets the structure to use for barcode generation. - /// - /// The value to encode. - /// The structure to use for barcode generation. - private static string getStructure(string value) - { - int checksum = getChecksum(value); - return m_structures[checksum]; - } - - /// - /// Calculates the EAN-5 checksum for a given value. - /// - /// The value. - /// The EAN-5 checksum for a given value. - private static int getChecksum(string value) - { - int sum = 0; - for (int i = 0; i < value.Length; i++) - { - int digit = (int)(value[i] - '0'); - if (i % 2 == 0) - sum += digit * 3; - else - sum += digit * 9; - } - - return sum % 10; - } - - /// - /// Gets the encoding pattern for given number using L table. - /// - /// The number to retrieve pattern for. - /// - /// The encoding pattern for given number. - /// - private static string getLPattern(int n) - { - return m_lPatterns[n]; - } - - /// - /// Gets the encoding pattern for given number using L table. - /// - /// The number to retrieve pattern for. - /// - /// The encoding pattern for given number. - /// - private static string getGPattern(int n) - { - return m_gPatterns[n]; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN8Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN8Symbology.cs deleted file mode 100644 index 149d064f..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/EAN8Symbology.cs +++ /dev/null @@ -1,211 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using System.Drawing; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using EAN8 symbology rules. EAN-8 is a short version - /// of EAN-13 that is intended to be used on packaging which would - /// be otherwise too small to use one of the other versions. Used with - /// consumer products internationally. EAN-8 symbology allows only numeric - /// values to be encoded. - /// - class EAN8Symbology : EAN13Symbology - { - /// - /// Initializes a new instance of the class. - /// - public EAN8Symbology() - : base() - { - m_type = TrueSymbologyType.EAN8; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public EAN8Symbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.EAN8; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "0000000"; - } - - /// - /// Validates the value using EAN-8 symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Checksum is mandatory or not (if applicable). - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 7 && value.Length != 8) - return false; - - if (value.Length == 7 && checksumIsMandatory) - return false; - - if (value.Length == 8) - { - // user wants to enter value with check digit - // we need to verify that digit - char c = getChecksum(value.Substring(0, 7)); - if (value[7] != c) - return false; - } - - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "EAN-8 symbology expects strings with 7 digits to be encoded. Optionally, user may enter 8th digit. In the latter case the last digit (check digit) will be verified."; - } - - /// - /// Gets the barcode value encoded using EAN-13 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - // checksum char ALWAYS added to encoded value and caption - string s = Value.Substring(0, 7); - return s + getChecksum(s); - } - - /// - /// Gets the checksum char. - /// - /// The value to calculate checksum for. - /// The checksum char. - protected override char getChecksum(string value) - { - int total = 0; - for (int i = 0; i < value.Length; i++) - { - if (i % 2 == 0) - total += getCharPosition(value[i]) * 3; - else - total += getCharPosition(value[i]); - } - - int lastDigit = total % 10; - if (lastDigit == 0) - return '0'; - - return m_alphabet[10 - lastDigit]; - } - - /// - /// Draws left-hand part of the bars. - /// - /// The start X position. - /// The start Y position. - /// - /// The new X position (start X position + the width of the rectangle - /// occupied by right-hand part of barcode bars and gaps). - /// - protected override int drawLeftHandPart(int x, int y) - { - string encoded = GetEncodedValue(false); - - for (int i = 0; i < 4; i++) - { - string pattern = getLeftOddCharPattern(encoded[i]); - x = (int)drawPattern(pattern, x, y, NarrowBarWidth, BarHeight); - } - - return x; - } - - /// - /// Draws right-hand part of the bars. - /// - /// The start X position. - /// The start Y position. - /// - /// The new X position (start X position + the width of the rectangle - /// occupied by right-hand part of barcode bars and gaps). - /// - protected override int drawRightHandPart(int x, int y) - { - string encoded = GetEncodedValue(false); - - for (int i = 0; i < 4; i++) - { - string pattern = getRightCharPattern(encoded[4 + i]); - x = (int)drawPattern(pattern, x, y, NarrowBarWidth, BarHeight); - } - - return x; - } - - protected override int occupiedByCaptionBefore(SKCanvas canvas, SKFont font) - { - if (DrawCaption) - { - if (CaptionPosition == CaptionPosition.Before) - { - return base.occupiedByCaptionBefore(canvas, font); - } - } - - return 0; - } - - protected override string getCaptionBeforePart() - { - // EAN-8 does not draw first char - return string.Empty; - } - - protected override string getCaptionLeftPart() - { - return Caption.Substring(0, Math.Min(Caption.Length - 1, 4)); - } - - protected override string getCaptionRightPart() - { - if (Caption.Length >= 5) - return Caption.Substring(4); - - return string.Empty; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarExpandedSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarExpandedSymbology.cs deleted file mode 100644 index d6071602..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarExpandedSymbology.cs +++ /dev/null @@ -1,968 +0,0 @@ -/************************************************** - * - Copyright (c) 2008 - 2012 Bytescout - * - * -**************************************************/ - -using System; -using System.Text; -using System.Globalization; -using System.Text.RegularExpressions; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using GS1 DataBar Expanded symbology rules. - /// - class GS1DataBarExpandedSymbology : SymbologyDrawing - { - private const int m_elements = 4; // number of pairs of elements in a set - - // Characteristics of signs with structure (17,4) - private static int[,] m_signProperties = new int[,] - { - // | value | Gsum | Nodd | Neven | Widest | Widest | Todd | Teven | - // | range to | | | | odd | even | | | - { 347, 0, 12, 5, 7, 2, 87, 4}, - { 1387, 348, 10, 7, 5, 4, 52, 20}, - { 2947, 1388, 8, 9, 4, 5, 30, 52}, - { 3987, 2948, 6, 11, 3, 6, 10, 104}, - { 4191, 3988, 4, 13, 1, 8, 1, 204}, - }; - - // Table of weighted coefficients for calculating the check sum (Math.Pow(3,x)%211) - private static int[][] m_weightCoefficient = new int[][] - { - new int[] { 1, 3, 9, 27, 81, 32, 96, 77}, // right of A1 - new int[] { 20, 60, 180, 118, 143, 7, 21, 63}, // left of A2 - new int[] {189, 145, 13, 39, 117, 140, 209, 205}, // right of A2 - new int[] {193, 157, 49, 147, 19, 57, 171, 91}, // left of B1 - new int[] { 62, 186, 136, 197, 169, 85, 44, 132}, // right of B1 - new int[] {185, 133, 188, 142, 4, 12, 36, 108}, // left of B2 - new int[] {113, 128, 173, 97, 80, 29, 87, 50}, // right of B2 - new int[] {150, 28, 84, 41, 123, 158, 52, 156}, // ... - new int[] { 46, 138, 203, 187, 139, 206, 196, 166}, - new int[] { 76, 17, 51, 153, 37, 111, 122, 155}, - new int[] { 43, 129, 176, 106, 107, 110, 119, 146}, - new int[] { 16, 48, 144, 10, 30, 90, 59, 177}, - new int[] {109, 116, 137, 200, 178, 112, 125, 164}, - new int[] { 70, 210, 208, 202, 184, 130, 179, 115}, - new int[] {134, 191, 151, 31, 93, 68, 204, 190}, - new int[] {148, 22, 66, 198, 172, 94, 71, 2}, - new int[] { 6, 18, 54, 162, 64, 192, 154, 40}, - new int[] {120, 149, 25, 75, 14, 42, 126, 167}, - new int[] { 79, 26, 78, 23, 69, 207, 199, 175}, - new int[] {103, 98, 83, 38, 114, 131, 182, 124}, - new int[] {161, 61, 183, 127, 170, 88, 53, 159}, - new int[] { 55, 165, 73, 8, 24, 72, 5, 15}, - new int[] { 45, 135, 194, 160, 58, 174, 100, 89}, - }; - - // Table with widths of finder pattern elements - private static int[][] m_finderPaternValues = new int[][] - { - new int[] {1, 8, 4, 1, 1}, // A (А1=A, A2 - mirrored represenation with inverted colors) - new int[] {3, 6, 4, 1, 1}, // B - new int[] {3, 4, 6, 1, 1}, // C - new int[] {3, 2, 8, 1, 1}, // D - new int[] {2, 6, 5, 1, 1}, // E - new int[] {2, 2, 9, 1, 1}, // F - }; - - // the order of finding templates - private static int[][] m_finderPaternOrder = new int[][] - { - // | number | order of finding template | - // | segments | 1=A1, -1=A2, 2=B1, -2=B2 и т.д. | - new int[] { 4, 1, -1}, - new int[] { 6, 1, -2, 2}, - new int[] { 8, 1, -3, 2, -4}, - new int[] {10, 1, -5, 2, -4, 3}, - new int[] {12, 1, -5, 2, -4, 4, -6}, - new int[] {14, 1, -5, 2, -4, 5, -6, 6}, - new int[] {16, 1, -1, 2, -2, 3, -3, 4, -4}, - new int[] {18, 1, -1, 2, -2, 3, -3, 4, -5, 5}, - new int[] {20, 1, -1, 2, -2, 3, -3, 4, -5, 6, -6}, - new int[] {22, 1, -1, 2, -2, 3, -3, 4, -5, 5, -6, 6}, - }; - - private static int[] m_guardPattern = new int[] { 1, 1 }; - - // Symbol FNC1 is corresponding to ASCII character 29 () - // Character FNC1 translates into barcode as ]C1 - private static char FNC1 = '\u001D'; - - private static string m_alphanumericChars = GS1Utils.Numbers + GS1Utils.CapitalLetter + "*,-./";// + CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0]; - - // List of codes AI with variable length for substitution in regular expression - private static string[] m_variableLengthAI = new string[] - { - "10","21","22","23[0-9]","240","241","242","250","251","253","254","30","37", - "390[0-9]","391[0-9]","392[0-9]","393[0-9]","400","401","403","420","421","423", - "7002","7004","703[0-9]","8002","8003","8004","8007","8008","8020","8110","9[0-9]" - }; - - private static Coding m_coding = Coding.AlphaNumeric; - private static int m_fixnum = 0; - - public GS1DataBarExpandedSymbology(TrueSymbologyType type) - : base(type) - { - } - - public GS1DataBarExpandedSymbology(SymbologyDrawing prototype, TrueSymbologyType type) - : base(prototype, type) - { - } - - /// - /// Initializes a new instance of the class. - /// - public GS1DataBarExpandedSymbology() - : base(TrueSymbologyType.GS1_DataBar_Expanded) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public GS1DataBarExpandedSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.GS1_DataBar_Expanded) - { - } - - protected static int[][] FinderPaternOrder - { - get { return m_finderPaternOrder; } - } - - protected static int[][] FinderPaternValues - { - get { return m_finderPaternValues; } - } - - protected static int[] GuardPattern - { - get { return m_guardPattern; } - } - - /// - /// Validates the value using GS1 DataBar Expanded symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// This parameter is not applicable to this symbology (checksum is always mandatory for GS1 barcodes). - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - // Maybe we need to analyze all AI? - int index = value.IndexOf("(01)"); - if (index > -1) - { - string gtin = value.Substring(index + 4, 14); - return GS1Utils.IsGTIN(gtin); - } - else - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "GS1 DataBar Expanded symbology allows encoding up to 74 numeric or 41 alphabetic characters of AI Element String data."; - } - - /// - /// Gets the barcode value encoded using GS1 DataBar Expanded symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - if (forCaption) - return Value; - else - { - string result = Value; - string replacement = "$&" + FNC1; - for (int i = 0; i < m_variableLengthAI.Length; i++) - { - string pattern = "\\(" + m_variableLengthAI[i] + "\\)[a-zA-Z0-9]+"; - if (Regex.IsMatch(result, pattern + "\\(")) - result = Regex.Replace(result, pattern, replacement); - } - return result; - } - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - return null; - } - - /// - /// Gets or sets the height of the barcode bars in pixels. - /// - /// The height of the barcode bars in pixels. - public override int BarHeight - { - get - { - return base.BarHeight; - } - set - { - if (value < 34 * NarrowBarWidth) - base.BarHeight = 34 * NarrowBarWidth; - else - base.BarHeight = value; - } - } - - /// - /// Gets or sets the width of the narrow bar in pixels. - /// - /// The width of the narrow bar in pixels. - public override int NarrowBarWidth - { - get - { - return base.NarrowBarWidth; - } - set - { - // saving proportions between BarHeight and NarrowBarWidth - double ratio = BarHeight / base.NarrowBarWidth; - base.NarrowBarWidth = value; - BarHeight = (int)Math.Round(ratio * base.NarrowBarWidth); - } - } - - /// - - /// Check if the characters in the string value starting with the character index - /// and ending with the character index + count - 1 are digits. - /// - /// String for checking. - /// Index of first checked synbol. - /// Number of checked symbols. - /// - /// true if synbols in the given range are digits; otherwise, false. - /// - static private bool IsDigit(string value, int index, int count) - { - if (index + count > value.Length) - throw new BarcodeException("Error of coding of Databar Expanded symbology"); - - for (int i = index; i < index + count; i++) - { - if (!char.IsDigit(value[i]) && value[i] != FNC1) - return false; - } - return true; - } - - static private intList getAIList(string value) - { - intList aiList = new intList(); - bool bracket = false; - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < value.Length; i++) - { - char c = value[i]; - if (value[i] == '(') - { - bracket = true; - } - else if (value[i] == ')') - { - if (bracket) - { - try - { - aiList.Add(int.Parse(sb.ToString())); - } - catch - { - // ignore - } - finally - { - sb = new StringBuilder(); - } - } - bracket = false; - } - else if (bracket) - { - sb.Append(value[i]); - } - } - return aiList; - } - - static private string getEncodingMethod(string value, intList aiList) - { - if (aiList.Count < 1 || aiList[0] != 01) - return "00"; - if (aiList.Count == 2 && aiList[0] == 01 && value[4] == '9') - { - if (aiList[1] == 3103) - return "0100"; - if (aiList[1] == 3202 || aiList[1] == 3203) - return "0101"; - if (aiList[1] >= 3920 && aiList[1] <= 3929) - return "01100"; - if (aiList[1] >= 3930 && aiList[1] <= 3939) - return "01101"; - } - else if (aiList.Count == 3 && aiList[0] == 01 && value[4] == '9') - { - if (aiList[1] >= 3100 && aiList[1] <= 3109 && aiList[2] == 11) - return "0111000"; - if (aiList[1] >= 3200 && aiList[1] <= 3209 && aiList[2] == 11) - return "0111001"; - if (aiList[1] >= 3100 && aiList[1] <= 3109 && aiList[2] == 13) - return "0111010"; - if (aiList[1] >= 3200 && aiList[1] <= 3209 && aiList[2] == 13) - return "0111011"; - if (aiList[1] >= 3100 && aiList[1] <= 3109 && aiList[2] == 15) - return "0111100"; - if (aiList[1] >= 3200 && aiList[1] <= 3209 && aiList[2] == 15) - return "0111101"; - if (aiList[1] >= 3100 && aiList[1] <= 3109 && aiList[2] == 17) - return "0111110"; - if (aiList[1] >= 3200 && aiList[1] <= 3209 && aiList[2] == 17) - return "0111111"; - } - else if (aiList[0] == 01 && value.Length > 18 &&value[18] == '(') - return "1"; - - return "00"; - } - - /// - /// Checks if the characters in the string 'value' starting from the character at 'index' - /// and ending with the character at 'index + count - 1' can be encoded using the - /// alphanumeric digital encoding scheme. - /// - /// The string to check. - /// The index of the first character to check. - /// The number of characters to check. - /// - /// true if the characters in the specified range can be encoded using the alphanumeric digital encoding scheme; otherwise, false. - /// - - static private bool IsAlphaNumeric(string value, int index, int count) - { - if (index >= value.Length) - throw new BarcodeException("Error of coding of Databar Expanded symbology"); - - for (int i = index; (i < index + count) && (i < value.Length); i++) - { - if (m_alphanumericChars.IndexOf(value[i]) < 0) - return false; - } - return true; - } - - static protected string numberCoding(char c1, char c2) - { - // Scheme of number coding, see page 31 of GOST ISO/IEC 24724-2011 - int d1; - if (char.IsDigit(c1)) - d1 = (int)Char.GetNumericValue(c1); //int.Parse(c1.ToString()) - else if (c1 == FNC1) - d1 = 10; - else - throw new BarcodeException("Incorrect character for number coding of Databar Expanded symbology"); - int d2; - if (char.IsDigit(c2)) - d2 = (int)Char.GetNumericValue(c2); - else if (c2 == FNC1) - d2 = 10; - else - throw new BarcodeException("Incorrect character for number coding of Databar Expanded symbology"); - int value = 11 * d1 + d2 + 8; - StringBuilder result = new StringBuilder(Convert.ToString(value, 2)); - while (result.Length < 7) - { - result.Insert(0, "0"); - } - return result.ToString(); - } - - static protected string alphanumericCoding(char c) - { - // Scheme of alphanumeric coding, see page 32 of ISO/IEC 24724-2011 - string symbols = "*,-./"; - if (char.IsDigit(c)) - { - int digit = (int)Char.GetNumericValue(c); //int.Parse(c1.ToString()) - string sDigit = Convert.ToString(digit + 5, 2); - return sDigit.Length == 3 ? "00" + sDigit : "0" + sDigit; - } - else if (char.IsUpper(c)) - { - return Convert.ToString((int)c - 33, 2); - } - else if (c == FNC1) - return "01111"; - else - { - int index = symbols.IndexOf(c); - if (index >= 0) - return Convert.ToString(index + 58, 2); - } - - throw new BarcodeException("Incorrect character for alphanumeric coding of Databar Expanded symbology"); - } - - static protected string isoIec646Coding(char c) - { - // Scheme of ISO/IEC 646 coding, see page 34 of ISO/IEC 24724-2011 - string symbols = "!\"%&'()*+,-./:;<=>?_ "; - if (char.IsDigit(c)) - { - int digit = (int)Char.GetNumericValue(c); //int.Parse(c1.ToString()) - string sDigit = Convert.ToString(digit + 5, 2); - return sDigit.Length == 3 ? "00" + sDigit : "0" + sDigit; - } - else if (char.IsUpper(c)) - { - return Convert.ToString((int)c - 1, 2); - } - else if (char.IsLower(c)) - { - return Convert.ToString((int)c - 7, 2); - } - - else if (c == FNC1) - return "01111"; - else - { - int index = symbols.IndexOf(c); - if (index >= 0) - return Convert.ToString(index + 232, 2); - } - throw new BarcodeException("Incorrect character for alphanumeric coding of Databar Expanded symbology"); - } - - protected enum Coding - { - AlphaNumeric, - IsoIec646, - Number, - } - - static protected Coding LastCoding - { - get { return m_coding; } - } - - static protected int FixNum - { - get { return m_fixnum; } - } - - /// - /// Encoding binary value of symbol in the field of universal data compression. - /// - /// Data for encoding. - /// - /// - /// - static protected string universalDataCompressionField(string value, string prefix, int segmentsNumber) - { - - const string number = "000";// the pointer of setting the scheme of number coding - const string alphanumeric = "0000";// the pointer of setting the scheme of alphanumeric coding - const string iso_iec646 = "00100"; // The pointer of setting the scheme according to ISO/IEC 646 - StringBuilder binaryString = new StringBuilder(prefix); - - // encoding data - int i = 0; - Coding coding = Coding.Number; - while (i < value.Length) - { - switch (coding) - { - case Coding.AlphaNumeric: - if ((value.Length - i > 5) && IsDigit(value, i, 6)) - { - // adding the pointer to the number coding scheme - binaryString.Append(number); - coding = Coding.Number; - } - else if ((value.Length - i > 3) && IsDigit(value, i, value.Length - i)) - { - // adding the pointer to the number coding scheme - binaryString.Append(number); - coding = Coding.Number; - } - else if (m_alphanumericChars.IndexOf(value[i]) >= 0) - { - // applying the alphanumeric coding scheme - binaryString.Append(alphanumericCoding(value[i])); - i++; - } - else - { - // adding pointer to the encoding scheme according to ISO/IEC 646 - binaryString.Append(iso_iec646); - coding = Coding.IsoIec646; - } - break; - case Coding.IsoIec646: - if ((value.Length - i > 3) && IsDigit(value, i, 4) && IsAlphaNumeric(value, i + 4, 10)) - { - // adding the pointer to the number coding scheme - binaryString.Append(number); - coding = Coding.Number; - } - else if ((value.Length - i > 4) && IsAlphaNumeric(value, i, 15)) - { - // adding the pointer to the alphanumeric coding scheme - binaryString.Append(iso_iec646); // it is correct!!! - coding = Coding.AlphaNumeric; - } - else - { - // appying the encoding scheme ISO/IEC 646 - binaryString.Append(isoIec646Coding(value[i])); - i++; - } - break; - case Coding.Number: - default: - if (i + 1 < value.Length) - { - if ((char.IsDigit(value[i]) && char.IsDigit(value[i + 1])) - || (char.IsDigit(value[i]) && value[i + 1] == FNC1) - || (value[i] == FNC1 && char.IsDigit(value[i + 1])) - ) - { - // applying the number coding scheme - binaryString.Append(numberCoding(value[i], value[i + 1])); - i += 2; - } - else - { - // adding the pointer to the alphanumeric coding scheme - binaryString.Append(alphanumeric); - coding = Coding.AlphaNumeric; - } - } - else - { - if (char.IsDigit(value[i])) - { - int remainder = 12 - binaryString.Length % 12; - if (remainder >= 7 || remainder < 4) - { - binaryString.Append(numberCoding(value[i], FNC1)); - } - else if (remainder >= 4) - { - if ((segmentsNumber < 22) && ((binaryString.Length / 12 + 2) % segmentsNumber == 1)) - { - // Expanded Stacked Symbology && last row contain one sign (add second sign) - binaryString.Append(numberCoding(value[i], FNC1)); - } - else - { - int n = (int)Char.GetNumericValue(value[i]) + 1; - StringBuilder s = new StringBuilder(Convert.ToString(n, 2)); - while (s.Length < 4) - { - s.Insert(0, "0"); - } - binaryString.Append(s.ToString()); - } - } - i++; - } - else - { - // adding the pointer of setting alphanumeric codine scheme - binaryString.Append(alphanumeric); - coding = Coding.AlphaNumeric; - } - } - break; - } - } - - m_coding = coding; - m_fixnum = 0; - int lastRemainder = 12 - binaryString.Length % 12; - if (lastRemainder != 12) - { - if (coding == Coding.Number) - { - for (int j = 0; (j < lastRemainder) && (j < 4); j++) - { - binaryString.Append('0'); // adding the pointer of setting alphanumeric codine scheme (alphanumeric = "0000") - m_coding = Coding.AlphaNumeric; - m_fixnum = j + 1;// maaybe need to pass last j to GS1DataBarStackedExpandedSymbology - } - } - lastRemainder = 12 - binaryString.Length % 12; - if (lastRemainder != 12) - { - int length = iso_iec646.Length; - for (int j = 0; j < lastRemainder; j++) - { - int k = j < length ? j : j % length; - binaryString.Append(iso_iec646[k]); - } - } - } - - return binaryString.ToString(); - } - - static protected string dataCompressionField(string value, int[] groups, int[] bits) - { - int sum = 0; - for (int i = 0; i < groups.Length; i++) - sum += groups[i]; - if (value.Length != sum) - throw new BarcodeException("Error of coding of Databar Expanded symbology"); - - int startIndex = 0; - StringBuilder result = new StringBuilder(); - for (int i = 0; i < groups.Length; i++) - { - string s = value.Substring(startIndex, groups[i]); - startIndex += groups[i]; - int gValue = int.Parse(s); - StringBuilder gString = new StringBuilder(Convert.ToString(gValue, 2)); - while (gString.Length < bits[i]) - { - gString.Insert(0, "0"); - } - result.Append(gString.ToString()); - } - return result.ToString(); - } - - /// - /// Gets the binary field of variable-length symbol. - /// - /// The count of signs. - /// - static protected string getVariableLengthSymbolField(int count) - { - // bits of fields of symbols of variable length - // only for encoding methods "1","00","01100","01101" - // the first bit = 0 if the number of signs of symbol is even and 1 if - odd; - // second bit = 0 if the number of signs of symbol <=14 and 1 if >14; - string a = ((count / 12) % 2 == 0 ? "1" : "0"); - string b = (count > 13 * 12 ? "1" : "0"); - return a + b; - } - - static protected string removeBrackets(string value) - { - string s = value.Replace("(", ""); - return s.Replace(")", ""); - } - - protected string getBinaryValue(int segmentsNumber) - { - intList aiList = getAIList(Value); - string code = getEncodingMethod(Value, aiList); - string sValue = GetEncodedValue(false); - - StringBuilder binaryValue = new StringBuilder(); - // indexes in Substring are calculated for sValue like (01)90012345678908(3103)012233(15)991231 - switch (code) - { - case "00": - binaryValue.Append(universalDataCompressionField(removeBrackets(sValue), "0" + code + "00", segmentsNumber)); - string s00 = getVariableLengthSymbolField(binaryValue.Length); - binaryValue[code.Length + 1] = s00[0]; - binaryValue[code.Length + 2] = s00[1]; - break; - case "1": - string compressionField = dataCompressionField(sValue.Substring(4, 13), - new int[] { 1, 3, 3, 3, 3 }, - new int[] { 4, 10, 10, 10, 10 }); - string s = sValue.Substring(18); - binaryValue.Append(universalDataCompressionField(removeBrackets(s), "0" + code + "00" + compressionField, segmentsNumber)); - string s1 = getVariableLengthSymbolField(binaryValue.Length); - binaryValue[code.Length + 1] = s1[0]; - binaryValue[code.Length + 2] = s1[1]; - break; - case "0100": - binaryValue.Append(dataCompressionField(sValue.Substring(5, 12) + sValue.Substring(24), - new int[] { 3, 3, 3, 3, 6 }, - new int[] { 10, 10, 10, 10, 15 })); - binaryValue.Insert(0, code); // value of encoding method - binaryValue.Insert(0, "0"); // missing two-dimensional component - break; - case "0101": - int mass = int.Parse(sValue.Substring(26)); - if (aiList[1] == 3203) - mass += 10000; - binaryValue.Append(dataCompressionField(sValue.Substring(5, 12) + mass.ToString("000000"), - new int[] { 3, 3, 3, 3, 6 }, - new int[] { 10, 10, 10, 10, 15 })); - binaryValue.Insert(0, code); // value of encoding method - binaryValue.Insert(0, "0"); // missing two-dimensional component - break; - case "01100": - int digitCount0 = aiList[1] % 10; - if (digitCount0 > 3) - throw new BarcodeException("Error of coding of Databar Expanded symbology"); - string st = dataCompressionField(sValue.Substring(5, 12) + digitCount0.ToString(), - new int[] { 3, 3, 3, 3, 1 }, - new int[] { 10, 10, 10, 10, 2 }); - binaryValue.Append(universalDataCompressionField(sValue.Substring(24), "0" + code + "00" + st, segmentsNumber)); - string s01100 = getVariableLengthSymbolField(binaryValue.Length); - binaryValue[code.Length + 1] = s01100[0]; - binaryValue[code.Length + 2] = s01100[1]; - break; - case "01101": - int digitCount1 = aiList[1] % 10; - if (digitCount1 > 3) - throw new BarcodeException("Error of coding of Databar Expanded symbology"); - string ss = dataCompressionField(sValue.Substring(5, 12) + digitCount1.ToString() + sValue.Substring(24, 3), - new int[] { 3, 3, 3, 3, 1, 3 }, - new int[] { 10, 10, 10, 10, 2, 10 }); - binaryValue.Append(universalDataCompressionField(sValue.Substring(27), "0" + code + "00" + ss, segmentsNumber)); - string s01101 = getVariableLengthSymbolField(binaryValue.Length); - binaryValue[code.Length + 1] = s01101[0]; - binaryValue[code.Length + 2] = s01101[1]; - break; - case "0111000": - case "0111001": - case "0111010": - case "0111011": - case "0111100": - case "0111101": - case "0111110": - case "0111111": - int digitCount2 = aiList[1] % 10; - - binaryValue.Append(dataCompressionField(sValue.Substring(5, 12) + digitCount2.ToString() + sValue.Substring(25, 5), - new int[] { 3, 3, 3, 3, 6 }, - new int[] { 10, 10, 10, 10, 20 })); - int YY = int.Parse(sValue.Substring(34, 2)); - int MM = int.Parse(sValue.Substring(36, 2)); - int DD = int.Parse(sValue.Substring(38, 2)); - int date = YY * 384 + (MM - 1) * 32 + DD; - string sDate = date.ToString(); - binaryValue.Append(dataCompressionField(sDate, - new int[] { sDate.Length }, - new int[] { 16 })); - binaryValue.Insert(0, code); // value of encoding method - binaryValue.Insert(0, "0"); // missing the two-dimensional component - break; - default: - throw new BarcodeException("Error of coding of Databar Expanded symbology"); - } - if (segmentsNumber < 22) - { - // Expanded Stacked Symbology - if ((binaryValue.Length / 12 + 1) % segmentsNumber == 1) - { - // the last row must contain at least two symbol signs - if ((LastCoding == Coding.Number) || (FixNum > 0 && FixNum < 4)) - switch (FixNum) - { - case 1: - binaryValue.Append("000001000010"); // dataSigns.Add(66); //000001000010 == 33 - break; - case 2: - binaryValue.Append("000010000100"); // dataSigns.Add(132); //000010000100 == 132 - break; - case 3: - binaryValue.Append("000100001000"); // dataSigns.Add(264); //000100001000 == 264 - break; - default: - binaryValue.Append("000000100001"); // dataSigns.Add(33); //000000100001 == 33 - break; - } - else - binaryValue.Append("001000010000"); // dataSigns.Add(528); // 001000010000 == 528 - string sLength = getVariableLengthSymbolField(binaryValue.Length); - binaryValue[code.Length + 1] = sLength[0]; - binaryValue[code.Length + 2] = sLength[1]; - } - } - return binaryValue.ToString(); - } - - static protected int[] signValue(int value) - { - for (int i = 0; i < m_signProperties.GetLength(0); i++) - { - if (value <= m_signProperties[i, 0]) - { - int Gsum = m_signProperties[i, 1]; - int Teven = m_signProperties[i, 7]; - int Vodd = (value - Gsum) / Teven; - int Veven = (value - Gsum) % Teven; - int[] odd = GS1Utils.getRSSwidths(Vodd, m_signProperties[i, 2], m_elements, m_signProperties[i, 4], false); - int[] even = GS1Utils.getRSSwidths(Veven, m_signProperties[i, 3], m_elements, m_signProperties[i, 5], true); - if (odd.Length != m_elements || even.Length != m_elements) - throw new BarcodeException("Incorrect outer sign for GS1 DataBar Expanded symbology"); - int[] result = new int[odd.Length + even.Length]; - for (int j = 0; j < m_elements; j++) - { - result[j * 2] = odd[j]; - result[j * 2 + 1] = even[j]; - } - return result; - } - } - throw new BarcodeException("Incorrect outer sign for GS1 DataBar Expanded symbology"); - } - - static protected int signCheckSum(int[] sign, int[] weights) - { - if (sign.Length != weights.Length) - throw new BarcodeException("Incorrect sign for GS1 DataBar Expanded symbology"); - int sum = 0; - for (int i = 0; i < sign.Length; i++) - { - sum += sign[i] * weights[i]; - } - return sum; - } - - /// - /// Сhecksums. - /// - /// Width of elements of sign. - /// - static protected int checkSum(int[][] value) - { - if (value.Length > 21 || value.Length < 3) - throw new BarcodeException("Incorrect sign for GS1 DataBar Expanded symbology"); - // find index for table of finder patterns sequences - int index = value.Length % 2 == 0 ? (value.Length - 2) / 2 : (value.Length - 3) / 2; - int sum = signCheckSum(value[0], m_weightCoefficient[0]); // the same for all variations - for (int i = 1; i < value.Length; i++) - { - if (value[i].Length != m_elements * 2) - throw new BarcodeException("Incorrect sign for GS1 DataBar Expanded symbology"); - if (i % 2 != 0) - { - // symbol is located to the left of the finder pattern - int k = m_finderPaternOrder[index][(i + 1) / 2 + 1]; // code of finder pattern - int l = k > 0 ? 4 * k - 5 : -4 * k - 3; // index for weighted coefficient table - sum += signCheckSum(value[i], m_weightCoefficient[l]); - } - else - { - // symbols is located at the right of the finder pattern - int k = m_finderPaternOrder[index][i / 2 + 1]; // code of finder pattern - int l = k > 0 ? 4 * k - 4 : -4 * k - 2; // index for table of weighted coefficients - sum += signCheckSum(value[i], m_weightCoefficient[l]); - } - } - return sum % 211; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - int maxSignNumber = 21; - string sValue = GetEncodedValue(false); - - // Find binary value of symbol. - string binaryValue = getBinaryValue(22); - if ((binaryValue.Length > maxSignNumber * 12) || (binaryValue.Length % 12 != 0)) - throw new BarcodeException("Incorrect value for Databar Expanded symbology"); - - // Split into signs of 12 bits and find their decimal value - intList dataSigns = new intList(); - for (int i = 0; i < binaryValue.Length / 12; i++) - { - string binarySign = binaryValue.Substring(i * 12, 12); - dataSigns.Add(Convert.ToInt16(binarySign, 2)); - } - - // find widths of elements of signs - int[][] signs = new int[dataSigns.Count][]; - for (int i = 0; i < dataSigns.Count; i++) - { - signs[i] = signValue(dataSigns[i]); - } - - // find check sign of symbol - int checkSignValue = 211 * (dataSigns.Count - 3) + checkSum(signs); - int[] checkSign = signValue(checkSignValue); - - intList symbol = new intList(); - GS1Utils.addArray(symbol, m_guardPattern, false); - GS1Utils.addArray(symbol, checkSign, false); - GS1Utils.addArray(symbol, m_finderPaternValues[0], false); - GS1Utils.addArray(symbol, signs[0], true); - - // find index for table of finder patterns sequences - int index = signs.Length % 2 == 0 ? (signs.Length - 2) / 2 : (signs.Length - 3) / 2; - for (int i = 1; i < signs.Length; i++) - { - if (i % 2 != 0) - { - // symbols is located at the left of finder pattern - GS1Utils.addArray(symbol, signs[i], false); - int k = m_finderPaternOrder[index][(i + 1) / 2 + 1]; // code of finder pattern - if (k > 0) - GS1Utils.addArray(symbol, m_finderPaternValues[k - 1], false); - else - GS1Utils.addArray(symbol, m_finderPaternValues[-k - 1], true); - } - else - { - // symbol is located to the right of the finder pattern - GS1Utils.addArray(symbol, signs[i], true); - } - } - GS1Utils.addArray(symbol, m_guardPattern, false); - - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - int height = BarHeight; - int width = NarrowBarWidth; - - SKSize captionSize = calculateCaptionSize(canvas, font); - float guardHeight = height + captionSize.Height / 2; - - for (int i = 0; i < symbol.Count; i++) - { - if (i % 2 != 0) - m_rects.Add(new SKRect(x, y, width * symbol[i]+x, guardHeight+y)); - x += width * symbol[i]; - } - - drawingSize.Width = x; - drawingSize.Height = BarHeight + captionSize.Height / 2; - return drawingSize; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarLimitedSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarLimitedSymbology.cs deleted file mode 100644 index dc3bd4a1..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarLimitedSymbology.cs +++ /dev/null @@ -1,315 +0,0 @@ -/************************************************** - * - Copyright (c) 2008 - 2012 Bytescout - * - * -**************************************************/ - -using System; -using System.Text; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using GS1 DataBar Limited symbology rules. - /// This symbology used within the GS1 System for encode a GTIN. - /// - class GS1DataBarLimitedSymbology : SymbologyDrawing - { - private const int m_combinationsNumber = 2013571; - private const int m_elements = 7; // number of pairs of elements in a set - - // Charactestics of the signs with the structure (26,7) - private static int[,] m_signProperties = new int[,] - { - // | value | Gsum | Nodd | Neven | Widest | Widest | Todd | Teven | - // | range to | | | | odd | even | | | - { 183063, 0, 17, 9, 6, 3, 6538, 28}, - { 820063, 183064, 13, 13, 5, 4, 875, 728}, - { 1000775, 820064, 9, 17, 3, 6, 28, 6454}, - { 1491020, 1000776, 15, 11, 5, 4, 2415, 203}, - { 1979844, 1491021, 11, 15, 4, 5, 203, 2408}, - { 1996938, 1979845, 19, 7, 8, 1, 17094, 1}, - { 2013570, 1996939, 7, 19, 1, 8, 1, 16632}, - }; - - // Table with weighted coefficients for calculating the check sum (Math.Pow(3,x)%89) - private static int[,] m_weightCoefficient = new int[,] - { - { 1, 3, 9, 27, 81, 65, 17, 51, 64, 14, 42, 37, 22, 66}, - {20, 60, 2, 6, 18, 54, 73, 41, 34, 13, 39, 28, 84, 74} - }; - - // Table with the order numbers for calculating the width of the bars and spaces of the check sign - private static int[] m_checkSignSeries = new int[] - {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, - 25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,45,52,57, - 63,64,65,66,73,74,75,76,77,78,79,82,126,127,128,129,130,132,141, - 142,143,144,145,146,210,211,212,213,214,215,216,217,220,316,317, - 318,319,320,322,323,326,337}; - - private static int[] m_leftGuardPattern = new int[] { 1, 1 }; - private static int[] m_rightGuardPattern = new int[] { 1, 1, 5 }; - - /// - /// Initializes a new instance of the class. - /// - public GS1DataBarLimitedSymbology() - : base(TrueSymbologyType.GS1_DataBar_Limited) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public GS1DataBarLimitedSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.GS1_DataBar_Limited) - { - } - - /// - /// Validates the value using GS1 DataBar Limited symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// This parameter is not applicable to this symbology (checksum is always mandatory for GS1 barcodes). - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - bool result = GS1Utils.IsGTIN(value); - if (result) - { - // aside from that the value of the indicator digit must be equal to 0 or 1 - int index = value.IndexOf("(01)") == 0 ? 4 : 0; - result = value[index] == '0' || value[index] == '1'; - } - return result; - } - - /// - /// Gets or sets the barcode value to encode. - /// - /// The barcode value to encode. - public override string Value - { - get - { - return base.Value; - } - set - { - if (ValueIsValid(value, false)) - { - int index = value.IndexOf("(01)"); - if (index == 0) - base.Value = value.Substring(4); - else - base.Value = value; - } - else - { - string generic = "Provided value can't be encoded by current symbology.\n"; - throw new BarcodeException(generic + getValueRestrictions()); - } - } - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - // GS1 Databar Limited allows encoding of up to 14 digits of data. Last digit must be checksum and will be verified. - return "GS1 DataBar Limited symbology allows encoding of up to 14 digits of data. Last digit must be checksum and will be verified."; - } - - /// - /// Gets the barcode value encoded using GS1 DataBar Limited symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - return GS1Utils.GetEncodedGTINValue(Value, forCaption); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - return null; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "00000000000000"; - } - - /// - /// Gets or sets the height of the barcode bars in pixels. - /// - /// The height of the barcode bars in pixels. - public override int BarHeight - { - get - { - return base.BarHeight; - } - set - { - if (value < 10 * NarrowBarWidth) - base.BarHeight = 10 * NarrowBarWidth; - else - base.BarHeight = value; - } - } - - /// - /// Gets or sets the width of the narrow bar in pixels. - /// - /// The width of the narrow bar in pixels. - public override int NarrowBarWidth - { - get - { - return base.NarrowBarWidth; - } - set - { - // preserving proportions between BarHeight and NarrowBarWidth - double ratio = BarHeight / base.NarrowBarWidth; - base.NarrowBarWidth = value; - BarHeight = (int)Math.Round(ratio * base.NarrowBarWidth); - } - } - - static protected int[] signValue(int value) - { - for (int i = 0; i < m_signProperties.GetLength(0); i++) - { - if (value <= m_signProperties[i, 0]) - { - int Gsum = m_signProperties[i, 1]; - int Teven = m_signProperties[i, 7]; - int Vodd = (value - Gsum) / Teven; - int Veven = (value - Gsum) % Teven; - int[] odd = GS1Utils.getRSSwidths(Vodd, m_signProperties[i, 2], m_elements, m_signProperties[i, 4], true); - int[] even = GS1Utils.getRSSwidths(Veven, m_signProperties[i, 3], m_elements, m_signProperties[i, 5], false); // false or true ??? - if (odd.Length != m_elements || even.Length != m_elements) - throw new BarcodeException("Incorrect outer sign for GS1 DataBar Limited symbology"); - int[] result = new int[odd.Length + even.Length]; - for (int j = 0; j < m_elements; j++) - { - result[j * 2] = odd[j]; - result[j * 2 + 1] = even[j]; - } - return result; - } - } - throw new BarcodeException("Incorrect outer sign for GS1 DataBar Limited symbology"); - } - - static protected int[] checkSignValue(int value) - { - // see addon C in ISO/IEC 24724-2011 standard - const int combinationsNumber = 21; - int serialNumber = m_checkSignSeries[value]; - int space = (int)(serialNumber / combinationsNumber); - int bar = (int)(serialNumber % combinationsNumber); - - int[] odd = GS1Utils.getRSSwidths(space, 8, 6, 3, true); - int[] even = GS1Utils.getRSSwidths(bar, 8, 6, 3, true); - int[] result = new int[odd.Length + even.Length + 2]; - for (int j = 0; j < odd.Length; j++) - { - result[j * 2] = odd[j]; - result[j * 2 + 1] = even[j]; - } - result[result.Length - 2] = 1; - result[result.Length - 1] = 1; - return result; - } - - /// - /// Сhecksums. - /// - /// Number of sign of symbol. - /// Widths of sign elements. - /// - static protected int checkSum(int n, int[] value) - { - if (value.Length > 14) - throw new BarcodeException("Incorrect sign for GS1 DataBar Limited symbology"); - if (n > 2) - throw new BarcodeException("GS1 DataBar Limited symbology error"); - int sum = 0; - for (int i = 0; i < value.Length; i++) - sum += value[i] * m_weightCoefficient[n, i]; - return sum; - } - - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - string sValue = GetEncodedValue(false); - long value = long.Parse(sValue); - int left = (int)(value / m_combinationsNumber); - int right = (int)(value % m_combinationsNumber); - int[] leftSign = signValue(left); - int[] rightSign = signValue(right); - - int сhecksum = checkSum(0, leftSign); - сhecksum += checkSum(1, rightSign); - сhecksum = сhecksum % 89; - int[] checkSign = checkSignValue(сhecksum); - - intList symbol = new intList(); - GS1Utils.addArray(symbol, m_leftGuardPattern, false); - GS1Utils.addArray(symbol, leftSign, false); - GS1Utils.addArray(symbol, checkSign, false); - GS1Utils.addArray(symbol, rightSign, false); - GS1Utils.addArray(symbol, m_rightGuardPattern, false); - if (symbol.Count != 47) - throw new BarcodeException("GS1 DataBar Limited symbology error"); - - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - int height = BarHeight; - int width = NarrowBarWidth; - - SKSize captionSize = calculateCaptionSize(canvas, font); - float guardHeight = height + captionSize.Height / 2; - - for (int i = 0; i < symbol.Count; i++) - { - if (i % 2 != 0) - m_rects.Add(new SKRect(x, y, width * symbol[i]+x, guardHeight+y)); - x += width * symbol[i]; - } - - drawingSize.Width = x; - drawingSize.Height = BarHeight + captionSize.Height / 2; - return drawingSize; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarOmnidirectionalBasic.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarOmnidirectionalBasic.cs deleted file mode 100644 index d546dfd6..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarOmnidirectionalBasic.cs +++ /dev/null @@ -1,280 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - class GS1DataBarOmnidirectionalBasic : SymbologyDrawing - { - private const int m_CombinationsNumber = 4537077; - private const int m_Vinside = 1597; - private const int m_elements = 4; // number of pairs of elements in a set - - // Characteristics of outer signs with structure (16,4) - private static int[,] m_outer = new int[,] - { - // | value | Gsum | Nodd | Neven | Widest | Widest | Todd | Teven | - // | range to | | | | odd | even | | | - { 160, 0, 12, 4, 8, 1, 161, 1}, - { 960, 161, 10, 6, 6, 3, 80, 10}, - { 2014, 961, 8, 8, 4, 5, 31, 34}, - { 2714, 2015, 6, 10, 3, 6, 10, 70}, - { 2840, 2715, 4, 12, 1, 8, 1, 126} - }; - - // Characteristics of internal signs with structure (15,4) - private static int[,] m_inner = new int[,] - { - // | value | Gsum | Nodd | Neven | Widest | Widest | Todd | Teven | - // | range to | | | | odd | even | | | - { 335, 0, 5, 10, 2, 7, 4, 84}, - { 1035, 336, 7, 8, 4, 5, 20, 35}, - { 1515, 1036, 9, 6, 6, 3, 48, 10}, - { 1596, 1516, 11, 4, 8, 1, 81, 1} - }; - - // Table with weighted coefficients for calculating checksum (Math.Pow(3,x)%79) - private static int[,] m_weightCoefficient = new int[,] - { - { 1, 3, 9, 27, 2, 6, 18, 54}, - { 4, 12, 36, 29, 8, 24, 72, 58}, - {16, 48, 65, 37, 32, 17, 51, 74}, - {64, 34, 23, 69, 49, 68, 46, 59} - }; - - // Table with width of finder pattern elements - private static int[][] m_finderPaternValues = new int[][] - { - new int[] {3, 8, 2, 1, 1}, // 0 - new int[] {3, 5, 5, 1, 1}, // 1 - new int[] {3, 3, 7, 1, 1}, // 2 - new int[] {3, 1, 9, 1, 1}, // 3 - new int[] {2, 7, 4, 1, 1}, // 4 - new int[] {2, 5, 6, 1, 1}, // 5 - new int[] {2, 3, 8, 1, 1}, // 6 - new int[] {1, 5, 7, 1, 1}, // 7 - new int[] {1, 3, 9, 1, 1}, // 8 - }; - - protected static int[] m_guardPattern = new int[] { 1, 1 }; - protected static int[] m_sign1; - protected static int[] m_sign2; - protected static int[] m_sign3; - protected static int[] m_sign4; - protected static int[] m_leftPatternSign; - protected static int[] m_rightPatternSign; - - public GS1DataBarOmnidirectionalBasic(TrueSymbologyType type) - : base(type) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The existing SymbologyDrawing object to use as parameter prototype. - /// The new symbology drawing type. - public GS1DataBarOmnidirectionalBasic(SymbologyDrawing prototype, TrueSymbologyType type) - : base(prototype, type) - { - } - - /// - /// Validates the value using GS1 DataBar Omnidirectional symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// This parameter is not applicable to this symbology (checksum is always mandatory for GS1 barcodes). - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - return GS1Utils.IsGTIN(value); - } - - /// - /// Gets or sets the barcode value to encode. - /// - /// The barcode value to encode. - public override string Value - { - get - { - return base.Value; - } - set - { - if (ValueIsValid(value, false)) - { - int index = value.IndexOf("(01)"); - if (index == 0) - base.Value = value.Substring(4); - else - base.Value = value; - } - else - { - string generic = "Provided value can't be encoded by current symbology.\n"; - throw new BarcodeException(generic + getValueRestrictions()); - } - } - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - // GS1 DataBar Omnidirectional symbology allows encoding of up to 14 digits of data. Last digit must be checksum and will be verified. - return "GS1 DataBar Omnidirectional symbology allows encoding of up to 14 digits of data. Last digit must be checksum and will be verified."; - } - - /// - /// Gets the barcode value encoded using GS1 DataBar Omnidirectional symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - return GS1Utils.GetEncodedGTINValue(Value, forCaption); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - return null; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "00000000000000"; - } - - /// - /// Gets the checksum char. - /// - /// The value to calculate checksum for. - /// The checksum char. - protected virtual char getChecksum(string value) - { - return GS1Utils.getGTINChecksum(value); - } - - static protected int[] outerSign(int value) - { - for (int i = 0; i < m_outer.GetLength(0); i++) - { - if (value <= m_outer[i, 0]) - { - int Gsum = m_outer[i, 1]; - int Teven = m_outer[i, 7]; - int Vodd = (value - Gsum) / Teven; - int Veven = (value - Gsum) % Teven; - int[] odd = GS1Utils.getRSSwidths(Vodd, m_outer[i, 2], m_elements, m_outer[i, 4], true); - int[] even = GS1Utils.getRSSwidths(Veven, m_outer[i, 3], m_elements, m_outer[i, 5], false); - if (odd.Length != m_elements || even.Length != m_elements) - throw new BarcodeException("Incorrect outer sign for GS1 DataBar Omnidirectional symbology"); - int[] result = new int[odd.Length + even.Length]; - for (int j = 0; j < m_elements; j++) - { - result[j * 2] = odd[j]; - result[j * 2 + 1] = even[j]; - } - return result; - } - } - throw new BarcodeException("Incorrect outer sign for GS1 DataBar Omnidirectional symbology"); - } - - static protected int[] innerSign(int value) - { - for (int i = 0; i < m_inner.GetLength(0); i++) - { - if (value <= m_inner[i, 0]) - { - int Gsum = m_inner[i, 1]; - int Todd = m_inner[i, 6]; - int Veven = (value - Gsum) / Todd; - int Vodd = (value - Gsum) % Todd; - int[] odd = GS1Utils.getRSSwidths(Vodd, m_inner[i, 2], 4, m_inner[i, 4], false); - int[] even = GS1Utils.getRSSwidths(Veven, m_inner[i, 3], 4, m_inner[i, 5], true); - if (odd.Length != 4 || even.Length != 4) - throw new BarcodeException("Incorrect inner sign for GS1 DataBar Omnidirectional symbology"); - int[] result = new int[odd.Length + even.Length]; - for (int j = 0; j < 4; j++) - { - result[j * 2] = odd[j]; - result[j * 2 + 1] = even[j]; - } - return result; - } - } - throw new BarcodeException("Incorrect inner sign for GS1 DataBar Omnidirectional symbology"); - } - - /// - /// Сhecksums. - /// - /// Number of sign symbol. - /// Widths of sign elements. - /// - static protected int checkSum(int n, int[] value) - { - if (value.Length > 8) - throw new BarcodeException("Incorrect sign for GS1 DataBar Omnidirectional symbology"); - if (n > 3) - throw new BarcodeException("GS1 DataBar Omnidirectional symbology error"); - int sum = 0; - for (int i = 0; i < value.Length; i++) - sum += value[i] * m_weightCoefficient[n, i]; - return sum; - } - - static protected void createSegments(string sValue) - { - long value = long.Parse(sValue); - int left = (int)(value / m_CombinationsNumber); - int right = (int)(value % m_CombinationsNumber); - int data1 = left / m_Vinside; - int data2 = left % m_Vinside; - int data3 = right / m_Vinside; - int data4 = right % m_Vinside; - - m_sign1 = outerSign(data1); - m_sign2 = innerSign(data2); - m_sign3 = outerSign(data3); - m_sign4 = innerSign(data4); - - int сhecksum = checkSum(0, m_sign1); - сhecksum += checkSum(1, m_sign2); - сhecksum += checkSum(2, m_sign3); - сhecksum += checkSum(3, m_sign4); - сhecksum = сhecksum % 79; - if (сhecksum >= 8) - сhecksum += 1; - if (сhecksum >= 72) - сhecksum += 1; - - int Cleft = сhecksum / 9; - int Cright = сhecksum % 9; - m_leftPatternSign = m_finderPaternValues[Cleft]; - m_rightPatternSign = m_finderPaternValues[Cright]; - - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarOmnidirectionalSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarOmnidirectionalSymbology.cs deleted file mode 100644 index 7ad9f382..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarOmnidirectionalSymbology.cs +++ /dev/null @@ -1,141 +0,0 @@ -/************************************************** - * - Copyright (c) 2008 - 2012 Bytescout - * - * -**************************************************/ - -using System; -using System.Text; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using GS1 DataBar Omnidirectional (aka RSS-14) symbology rules. - /// This symbology used within the GS1 System for encode a GTIN. - /// - class GS1DataBarOmnidirectionalSymbology : GS1DataBarOmnidirectionalBasic - { - public GS1DataBarOmnidirectionalSymbology(TrueSymbologyType type) - : base(type) - { - } - - public GS1DataBarOmnidirectionalSymbology(SymbologyDrawing prototype, TrueSymbologyType type) - : base(prototype, type) - { - } - - /// - /// Initializes a new instance of the class. - /// - public GS1DataBarOmnidirectionalSymbology() - : base(TrueSymbologyType.GS1_DataBar_Omnidirectional) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public GS1DataBarOmnidirectionalSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.GS1_DataBar_Omnidirectional) - { - } - - /// - /// Gets or sets the height of the barcode bars in pixels. - /// - /// The height of the barcode bars in pixels. - public override int BarHeight - { - get - { - return base.BarHeight; - } - set - { - if (value < 33 * NarrowBarWidth) - base.BarHeight = 33 * NarrowBarWidth; - else - base.BarHeight = value; - } - } - - /// - /// Gets or sets the width of the narrow bar in pixels. - /// - /// The width of the narrow bar in pixels. - public override int NarrowBarWidth - { - get - { - return base.NarrowBarWidth; - } - set - { - // preservring the aspect ration and proportions between BarHeight and NarrowBarWidth - double ratio = BarHeight / base.NarrowBarWidth; - base.NarrowBarWidth = value; - BarHeight = (int)Math.Round(ratio * base.NarrowBarWidth); - } - } - - protected int BaseBarHeight - { - set - { - base.BarHeight = value; - } - } - - protected int BaseNarrowBarWidth - { - set - { - base.NarrowBarWidth = value; - } - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - string sValue = GetEncodedValue(false); - createSegments(sValue); - - intList symbol = new intList(); - GS1Utils.addArray(symbol, m_guardPattern, false); - GS1Utils.addArray(symbol, m_sign1, false); - GS1Utils.addArray(symbol, m_leftPatternSign, false); - GS1Utils.addArray(symbol, m_sign2, true); - GS1Utils.addArray(symbol, m_sign4, false); - GS1Utils.addArray(symbol, m_rightPatternSign, true); - GS1Utils.addArray(symbol, m_sign3, true); - GS1Utils.addArray(symbol, m_guardPattern, false); - if (symbol.Count != 46) - throw new BarcodeException("GS1 DataBar Omnidirectional symbology error"); - - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - int height = BarHeight; - int width = NarrowBarWidth; - - SKSize captionSize = calculateCaptionSize(canvas, font); - float guardHeight = height + captionSize.Height / 2; - - for (int i = 0; i < symbol.Count; i++) - { - if (i % 2 != 0) - m_rects.Add(new SKRect(x, y, width * symbol[i]+x, guardHeight+y)); - x += width * symbol[i]; - } - - drawingSize.Width = x; - drawingSize.Height = BarHeight + captionSize.Height / 2; - return drawingSize; - } - - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarStackedExpandedSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarStackedExpandedSymbology.cs deleted file mode 100644 index 006b22d2..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarStackedExpandedSymbology.cs +++ /dev/null @@ -1,333 +0,0 @@ -/************************************************** - * - Copyright (c) 2008 - 2012 Bytescout - * - * -**************************************************/ - -using System; -using System.Text; -using System.Globalization; -using System.Text.RegularExpressions; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - class GS1DataBarStackedExpandedSymbology : GS1DataBarExpandedSymbology - { - /// - /// Initializes a new instance of the class. - /// - public GS1DataBarStackedExpandedSymbology() - : base(TrueSymbologyType.GS1_DataBar_Expanded_Stacked) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public GS1DataBarStackedExpandedSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.GS1_DataBar_Expanded_Stacked) - { - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "GS1 DataBar Expanded Stacked symbology allows encoding up to 74 numeric or 41 alphabetic characters of AI Element String data."; - } - - /// - /// Gets or sets the number of segments in line. - /// - /// The number of segments in line. - public int SegmentsNumber - { - get { return Options.GS1ExpandedStackedSegmentsNumber; } - set { Options.GS1ExpandedStackedSegmentsNumber = value; } - } - - private void addModulesForSign(intList modules, int[] array, bool firstSpace, bool flip) - { - if (flip) - for (int i = array.Length - 1; i >= 0; i--) - { - int module; - if (firstSpace) - module = ((array.Length - 1 - i) % 2 == 0) ? 0 : 1; - else - module = ((array.Length - 1 - i) % 2 == 0) ? 1 : 0; - for (int j = 0; j < array[i]; j++) - modules.Add(module); - } - else - for (int i = 0; i < array.Length; i++) - { - int module; - if (firstSpace) - module = (i % 2 == 0) ? 0 : 1; - else - module = (i % 2 == 0) ? 1 : 0; - for (int j = 0; j < array[i]; j++) - modules.Add(module); - } - } - - private void addModulesForFinderPattern(intList modules, int[] array, bool firstSpace, bool flip) - { - if (flip) - for (int i = array.Length - 1; i >= 0; i--) - { - int module; - if (firstSpace) - module = ((array.Length - 1 - i) % 2 == 0) ? 0 : 1; - else - module = ((array.Length - 1 - i) % 2 == 0) ? 1 : 0; - - if (module == 0) - { - for (int j = 0; j < array[i]; j++) - modules.Add(module); - } - else - { - for (int j = 0; j < array[i]; j++) - modules.Add((modules[modules.Count - 1] == 1) ? 0 : 1); - } - } - else - for (int i = 0; i < array.Length; i++) - { - int module; - if (firstSpace) - module = (i % 2 == 0) ? 0 : 1; - else - module = (i % 2 == 0) ? 1 : 0; - if (module == 0) - { - for (int j = 0; j < array[i]; j++) - modules.Add(module); - } - else - { - for (int j = 0; j < array[i]; j++) - modules.Add((modules[modules.Count - 1] == 1) ? 0 : 1); - } - } - } - - private void drowSymbolLine(intList symbol, int x, int y, int width, int height, bool firstSpace, bool flip) - { - if (flip) - for (int i = symbol.Count - 1; i >= 0; i--) - { - if (((symbol.Count - 1 - i) % 2 != 0) && firstSpace) - m_rects.Add(new SKRect(x, y, width * symbol[i]+x, height+y)); - else if (((symbol.Count - 1 - i) % 2 == 0) && !firstSpace) - m_rects.Add(new SKRect(x, y, width * symbol[i]+x, height+y)); - x += width * symbol[i]; - } - else - for (int i = 0; i < symbol.Count; i++) - { - if ((i % 2 != 0) && firstSpace) - m_rects.Add(new SKRect(x, y, width * symbol[i]+x, height+y)); - else if ((i % 2 == 0) && !firstSpace) - m_rects.Add(new SKRect(x, y, width * symbol[i]+x, height+y)); - x += width * symbol[i]; - } - } - - private void drowSeparatorLine(intList symbol, int x, int y, int width, int height, bool flip) - { - if (flip) - for (int i = symbol.Count - 1; i >= 0; i--) - { - if (symbol[i] == 1) - m_rects.Add(new SKRect(x, y, width+x, height+y)); - x += width; - } - else - for (int i = 0; i < symbol.Count; i++) - { - if (symbol[i] == 1) - m_rects.Add(new SKRect(x, y, width+x, height+y)); - x += width; - } - } - - // drawing the middle row of the separator pattern - private void drowSeparatorLine(int length, int x, int y, int width, int height) - { - for (int i = 0; i < length; i++) - { - if ((i % 2 != 0) && (i > 3) && (i < length - 4)) - m_rects.Add(new SKRect(x, y, width+x, height+y)); - x += width; - } - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - int maxSignNumber = 21; - string sValue = GetEncodedValue(false); - - // Find binary value of symbol - string binaryValue = getBinaryValue(SegmentsNumber); - if ((binaryValue.Length > maxSignNumber * 12) || (binaryValue.Length % 12 != 0)) - throw new BarcodeException("Incorrect value for Databar Expanded symbology"); - - // Split into signs of 12 bits and find their decimal value - intList dataSigns = new intList(); - for (int i = 0; i < binaryValue.Length / 12; i++) - { - string binarySign = binaryValue.Substring(i * 12, 12); - dataSigns.Add(Convert.ToInt16(binarySign, 2)); - } - - // Find widths of sign elements - int[][] signs_temp = new int[dataSigns.Count][]; - for (int i = 0; i < dataSigns.Count; i++) - { - signs_temp[i] = signValue(dataSigns[i]); - } - - // Find check sign of symbol - int checkSignValue = 211 * (dataSigns.Count - 3) + checkSum(signs_temp); - int[][] signs = new int[dataSigns.Count + 1][]; - signs[0] = signValue(checkSignValue); - for (int i = 0; i < dataSigns.Count; i++) - { - signs[i + 1] = signs_temp[i]; - } - - // find index for table of finder patterns sequences - int paternIndex = (signs.Length - 1) % 2 == 0 ? (signs.Length - 2) / 2 : (signs.Length - 3) / 2; - - intList symbol = new intList(); - int rowCount = (int)Math.Ceiling((double)signs.Length / SegmentsNumber); // number of rows - int index = 0; - bool inversion = (SegmentsNumber > 2) && ((SegmentsNumber / 2) % 2 == 0); - - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - int height = BarHeight; - int width = NarrowBarWidth; - SKSize captionSize = calculateCaptionSize(canvas, font); - - for (int k = 0; k < rowCount; k++) - { - bool oddRow = k % 2 != 0; - bool rowFlip = (oddRow && inversion); - - symbol = new intList(); - int finderPaternCount = 0; - intList separatorModules = new intList(); - GS1Utils.addArray(symbol, GuardPattern, oddRow); - addModulesForSign(separatorModules, GuardPattern, !oddRow, false); - for (int j = 0; j < SegmentsNumber; j++) - { - if (index < signs.Length) - { - if (index % 2 == 0) - { - // symbol is on the left side of finder pattern - addModulesForSign(separatorModules, - signs[index], - inversion ? (symbol.Count % 2 != 0) : (symbol.Count % 2 != 0) ^ oddRow, - false); - GS1Utils.addArray(symbol, signs[index], false); - int patern = FinderPaternOrder[paternIndex][(index + 1) / 2 + 1]; // code of finder pattern - if (patern > 0) - { - addModulesForFinderPattern(separatorModules, - FinderPaternValues[patern - 1], - inversion ? (symbol.Count % 2 != 0) : (symbol.Count % 2 != 0) ^ oddRow, - false); - GS1Utils.addArray(symbol, FinderPaternValues[patern - 1], false); - finderPaternCount++; - } - else - { - addModulesForFinderPattern(separatorModules, - FinderPaternValues[-patern - 1], - inversion ? (symbol.Count % 2 != 0) : (symbol.Count % 2 != 0) ^ oddRow, - true); - GS1Utils.addArray(symbol, FinderPaternValues[-patern - 1], true); - finderPaternCount++; - } - } - else - { - // symbol is on the right side of finder pattern - addModulesForSign(separatorModules, - signs[index], - inversion ? (symbol.Count % 2 != 0) : (symbol.Count % 2 != 0) ^ oddRow, - true); - GS1Utils.addArray(symbol, signs[index], true); - } - - index++; - } - else - break; - } - GS1Utils.addArray(symbol, GuardPattern, oddRow); - addModulesForSign(separatorModules, GuardPattern, !oddRow, false); - for (int i = 0; i < 4; i++) - { - separatorModules[i] = 0; - separatorModules[separatorModules.Count - 1 - i] = 0; - } - - if ((k == rowCount - 1) && (finderPaternCount % 2 != 0) && rowFlip) - { - // if the last row should be displayed mirrored - // but also has an odd number of finder patterns, then ... - separatorModules.Insert(0,0); - symbol[0]++; - oddRow = false; - rowFlip = false; - } - - if (k > 0) - { - // draw the bottom row of the separator pattern - x = 0; - drowSeparatorLine(separatorModules, x, y, width, width, rowFlip); - y += width; - } - - // draw the k-th row of the symbol - x = 0; - drowSymbolLine(symbol, x, y, width, height, !oddRow, rowFlip); - y += height; - - if (k < rowCount - 1) - { - // draw top and middle rows of the separator pattern - x = 0; - drowSeparatorLine(separatorModules, x, y, width, width, rowFlip); - y += width; - - x = 0; - drowSeparatorLine(separatorModules.Count, x, y, width, width); - y += width; - } - } - int segmentsNumber = SegmentsNumber > dataSigns.Count + 1 ? dataSigns.Count + 1 : SegmentsNumber; - drawingSize.Width = segmentsNumber * 17 * NarrowBarWidth - + (int)Math.Ceiling((double)segmentsNumber / 2) * 15 * NarrowBarWidth - + NarrowBarWidth * 4; - drawingSize.Height = y + captionSize.Height / 2; - return drawingSize; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarStackedOmnidirectionalSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarStackedOmnidirectionalSymbology.cs deleted file mode 100644 index a9a56d62..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarStackedOmnidirectionalSymbology.cs +++ /dev/null @@ -1,273 +0,0 @@ -using System; -using System.Text; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using GS1 DataBar Stacked Omnidirectional symbology rules. - /// This symbology used within the GS1 System for encode a GTIN. - /// - class GS1DataBarStackedOmnidirectionalSymbology : GS1DataBarOmnidirectionalBasic - { - /// - /// Initializes a new instance of the class. - /// - public GS1DataBarStackedOmnidirectionalSymbology() - : base(TrueSymbologyType.GS1_DataBar_Stacked_Omnidirectional) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public GS1DataBarStackedOmnidirectionalSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.GS1_DataBar_Stacked_Omnidirectional) - { - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "GS1 DataBar Stacked Omnidirectional symbology allows encoding of up to 14 digits of data. Last digit must be checksum and will be verified."; - } - - /// - /// Gets or sets the height of the barcode bars in pixels. - /// - /// The height of the barcode bars in pixels. - public override int BarHeight - { - get - { - return base.BarHeight; - } - set - { - if (value < 69 * NarrowBarWidth) - base.BarHeight = 69 * NarrowBarWidth; - else - base.BarHeight = value; - } - } - - /// - /// Gets or sets the width of the narrow bar in pixels. - /// - /// The width of the narrow bar in pixels. - public override int NarrowBarWidth - { - get - { - return base.NarrowBarWidth; - } - set - { - // Preserving the proportions between BarHeight and NarrowBarWidth - double ratio = BarHeight / base.NarrowBarWidth; - base.NarrowBarWidth = value; - BarHeight = (int)Math.Round(ratio * base.NarrowBarWidth); - } - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - string sValue = GetEncodedValue(false); - createSegments(sValue); - - intList upperLine = new intList(); - intList lowerLine = new intList(); - GS1Utils.addArray(upperLine, m_guardPattern, false); - GS1Utils.addArray(upperLine, m_sign1, false); - GS1Utils.addArray(upperLine, m_leftPatternSign, false); - GS1Utils.addArray(upperLine, m_sign2, true); - GS1Utils.addArray(upperLine, m_guardPattern, false); - GS1Utils.addArray(lowerLine, m_guardPattern, false); - GS1Utils.addArray(lowerLine, m_sign4, false); - GS1Utils.addArray(lowerLine, m_rightPatternSign, true); - GS1Utils.addArray(lowerLine, m_sign3, true); - GS1Utils.addArray(lowerLine, m_guardPattern, false); - if (upperLine.Count != 25 || lowerLine.Count != 25) - throw new BarcodeException("GS1 DataBar Stacked symbology error"); - - intList upperModules = new intList(); - for (int i = 0; i < upperLine.Count; i++) - { - int module = 0; - if (i % 2 != 0) - module = 1; - for (int j = 0; j < upperLine[i]; j++) - upperModules.Add(module); - } - intList lowerModules = new intList(); - for (int i = 0; i < lowerLine.Count; i++) - { - int module = 1; - if (i % 2 != 0) - module = 0; - for (int j = 0; j < lowerLine[i]; j++) - lowerModules.Add(module); - } - if (upperModules.Count != 50 || lowerModules.Count != 50) - throw new BarcodeException("GS1 DataBar Stacked symbology error"); - - // Forming the upper row of the template - the separator lines - intList upperSeparatorModules = new intList(); - for (int i = 4; i < 18; i++) - { - upperSeparatorModules.Add((upperModules[i] == 1) ? 0 : 1); - } - for (int i = 18; i < 31; i++) - { - if (upperModules[i] == 1) - upperSeparatorModules.Add(0); - else - upperSeparatorModules.Add((upperSeparatorModules[upperSeparatorModules.Count - 1] == 1) ? 0 : 1); - } - for (int i = 31; i < upperModules.Count - 4; i++) - { - upperSeparatorModules.Add((upperModules[i] == 1) ? 0 : 1); - } - - // forming the lower row of the template - the separator lines - intList lowerSeparatorModules = new intList(); - for (int i = 4; i < 18; i++) - { - lowerSeparatorModules.Add((lowerModules[i] == 1) ? 0 : 1); - } - if ((m_rightPatternSign[0] == 3) && (m_rightPatternSign[1] == 1) && (m_rightPatternSign[2] == 9)) - { - // if the search pattern == 3, then when i == 32, we insert a dark module, the rest of the modules are light - for (int i = 18; i < 31; i++) - { - if (i == 32) - lowerSeparatorModules.Add(1); - else - lowerSeparatorModules.Add(0); - } - } - else - { - for (int i = 18; i < 31; i++) - { - if (lowerModules[i] == 1) - lowerSeparatorModules.Add(0); - else - lowerSeparatorModules.Add((lowerSeparatorModules[lowerSeparatorModules.Count - 1] == 1) ? 0 : 1); - } - } - for (int i = 31; i < lowerModules.Count - 4; i++) - { - lowerSeparatorModules.Add((lowerModules[i] == 1) ? 0 : 1); - } - - intList upperSeparatorLine = new intList(); - int moduleColor = upperSeparatorModules[0]; - int counter = 1; - for (int i = 1; i < upperSeparatorModules.Count; i++) - { - if (upperSeparatorModules[i] == moduleColor) - counter++; - else - { - upperSeparatorLine.Add(counter); - moduleColor = upperSeparatorModules[i]; - counter = 1; - } - } - upperSeparatorLine.Add(counter); - intList lowerSeparatorLine = new intList(); - moduleColor = lowerSeparatorModules[0]; - counter = 1; - for (int i = 1; i < lowerSeparatorModules.Count; i++) - { - if (lowerSeparatorModules[i] == moduleColor) - counter++; - else - { - lowerSeparatorLine.Add(counter); - moduleColor = lowerSeparatorModules[i]; - counter = 1; - } - } - lowerSeparatorLine.Add(counter); - - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - int width = NarrowBarWidth; - SKSize captionSize = calculateCaptionSize(canvas, font); - - int height = (int)Math.Round((BarHeight - NarrowBarWidth * 3) / 2.0); - for (int i = 0; i < upperLine.Count; i++) - { - if (i % 2 != 0) - m_rects.Add(new SKRect(x, y, width * upperLine[i]+x, height+y)); - x += width * upperLine[i]; - } - x = 4 * width; - y += height; - height = width; - for (int i = 0; i < upperSeparatorLine.Count; i++) - { - if (upperSeparatorModules[0] == 1) - { - if (i % 2 == 0) - m_rects.Add(new SKRect(x, y, width * upperSeparatorLine[i]+x, height+y)); - } - else - { - if (i % 2 != 0) - m_rects.Add(new SKRect(x, y, width * upperSeparatorLine[i]+x, height+y)); - } - - x += width * upperSeparatorLine[i]; - } - x = 4 * width; - y += height; - for (int i = 0; i < 42; i++) - { - // Middle row of the template - the separator lines consists of alternating light and dark modules - if (i % 2 != 0) - m_rects.Add(new SKRect(x, y, width+x, height+y)); - x += width; - } - x = 4 * width; - y += height; - for (int i = 0; i < lowerSeparatorLine.Count; i++) - { - if (lowerSeparatorModules[0] == 1) - { - if (i % 2 == 0) - m_rects.Add(new SKRect(x, y, width * lowerSeparatorLine[i]+x, height+y)); - } - else - { - if (i % 2 != 0) - m_rects.Add(new SKRect(x, y, width * lowerSeparatorLine[i]+x, height+y)); - } - x += width * lowerSeparatorLine[i]; - } - x = 0; - y += height; - height = (int)Math.Round((BarHeight - NarrowBarWidth * 3) / 2.0); - for (int i = 0; i < lowerLine.Count; i++) - { - if (i % 2 == 0) - m_rects.Add(new SKRect(x, y, width * lowerLine[i]+x, height+y)); - x += width * lowerLine[i]; - } - - drawingSize.Width = x; - drawingSize.Height = BarHeight + captionSize.Height / 2; - return drawingSize; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarStackedSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarStackedSymbology.cs deleted file mode 100644 index 33f4e164..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarStackedSymbology.cs +++ /dev/null @@ -1,187 +0,0 @@ -using System; -using System.Text; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using GS1 DataBar Stacked symbology rules. - /// This symbology used within the GS1 System for encode a GTIN. - /// - class GS1DataBarStackedSymbology : GS1DataBarOmnidirectionalBasic - { - /// - /// Initializes a new instance of the class. - /// - public GS1DataBarStackedSymbology() - : base(TrueSymbologyType.GS1_DataBar_Stacked) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public GS1DataBarStackedSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.GS1_DataBar_Stacked) - { - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "GS1 DataBar Stacked symbology allows encoding of up to 14 digits of data. Last digit must be checksum and will be verified."; - } - - /// - /// Gets or sets the height of the barcode bars in pixels. - /// - /// The height of the barcode bars in pixels. - public override int BarHeight - { - get - { - return base.BarHeight; - } - set - { - if (value != 13 * NarrowBarWidth) - { - base.BarHeight = value; - base.NarrowBarWidth = (int)Math.Round(value / 13.0); - } - } - } - - /// - /// Gets or sets the width of the narrow bar in pixels. - /// - /// The width of the narrow bar in pixels. - public override int NarrowBarWidth - { - get - { - return base.NarrowBarWidth; - } - set - { - if (value * 13 != BarHeight) - { - base.NarrowBarWidth = value; - base.BarHeight = value * 13; - } - } - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - string sValue = GetEncodedValue(false); - createSegments(sValue); - - intList upperLine = new intList(); - intList lowerLine = new intList(); - GS1Utils.addArray(upperLine, m_guardPattern, false); - GS1Utils.addArray(upperLine, m_sign1, false); - GS1Utils.addArray(upperLine, m_leftPatternSign, false); - GS1Utils.addArray(upperLine, m_sign2, true); - GS1Utils.addArray(upperLine, m_guardPattern, false); - GS1Utils.addArray(lowerLine, m_guardPattern, false); - GS1Utils.addArray(lowerLine, m_sign4, false); - GS1Utils.addArray(lowerLine, m_rightPatternSign, true); - GS1Utils.addArray(lowerLine, m_sign3, true); - GS1Utils.addArray(lowerLine, m_guardPattern, false); - if (upperLine.Count != 25 || lowerLine.Count != 25) - throw new BarcodeException("GS1 DataBar Stacked symbology error"); - - intList upperModules = new intList(); - for (int i = 0; i < upperLine.Count; i++) - { - int module = 0; - if (i % 2 != 0) - module = 1; - for (int j = 0; j < upperLine[i]; j++) - upperModules.Add(module); - } - intList lowerModules = new intList(); - for (int i = 0; i < lowerLine.Count; i++) - { - int module = 1; - if (i % 2 != 0) - module = 0; - for (int j = 0; j < lowerLine[i]; j++) - lowerModules.Add(module); - } - if (upperModules.Count != 50 || lowerModules.Count != 50) - throw new BarcodeException("GS1 DataBar Stacked symbology error"); - - - intList separatorModules = new intList(); - separatorModules.Add(0); - for (int i = 4; i < upperModules.Count-4; i++) // where separator template starts and ends is not described - { // it may end with space - if (upperModules[i] == lowerModules[i]) - separatorModules.Add((upperModules[i] == 1) ? 0 : 1); - else - separatorModules.Add((separatorModules[separatorModules.Count-1] == 1) ? 0 : 1); - - } - intList separatorLine = new intList(); - int moduleColor = 0; - int counter = 1; - for (int i = 1; i < separatorModules.Count; i++) - { - if (separatorModules[i] == moduleColor) - counter++; - else - { - separatorLine.Add(counter); - moduleColor = separatorModules[i]; - counter = 1; - } - } - separatorLine.Add(counter); - - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - int width = NarrowBarWidth; - SKSize captionSize = calculateCaptionSize(canvas, font); - - int height = 5 * width; - for (int i = 0; i < upperLine.Count; i++) - { - if (i % 2 != 0) - m_rects.Add(new SKRect(x, y, width * upperLine[i]+x, height+y)); - x += width * upperLine[i]; - } - x = 3 * width; // where separator template starts and ends is not described in the specification - y += height; - height = width; - for (int i = 0; i < separatorLine.Count; i++) - { - if (i % 2 != 0) - m_rects.Add(new SKRect(x, y, width * separatorLine[i]+x, height+y)); - x += width * separatorLine[i]; - } - x = 0; - y += height; - height = 7 * width; - for (int i = 0; i < lowerLine.Count; i++) - { - if (i % 2 == 0) - m_rects.Add(new SKRect(x, y, width * lowerLine[i]+x, height+y)); - x += width * lowerLine[i]; - } - - drawingSize.Width = x; - drawingSize.Height = 13 * NarrowBarWidth + captionSize.Height / 2; - return drawingSize; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarTruncatedSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarTruncatedSymbology.cs deleted file mode 100644 index 7e88604f..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1DataBarTruncatedSymbology.cs +++ /dev/null @@ -1,67 +0,0 @@ -/************************************************** - * - Copyright (c) 2008 - 2012 Bytescout - * - * -**************************************************/ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using GS1 DataBar Truncated symbology rules. - /// This symbology used within the GS1 System for encode a GTIN. - /// - class GS1DataBarTruncatedSymbology : GS1DataBarOmnidirectionalSymbology - { - /// - /// Initializes a new instance of the class. - /// - public GS1DataBarTruncatedSymbology() - : base(TrueSymbologyType.GS1_DataBar_Truncated) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public GS1DataBarTruncatedSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.GS1_DataBar_Truncated) - { - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "GS1 DataBar Truncated symbology allows encoding of up to 14 digits of data. Last digit must be checksum and will be verified."; - } - - /// - /// Gets or sets the height of the barcode bars in pixels. - /// - /// The height of the barcode bars in pixels. - public override int BarHeight - { - get - { - return base.BarHeight; - } - set - { - if (value > 33 * NarrowBarWidth) - base.BaseBarHeight = 33 * NarrowBarWidth; - else if (value < 13 * NarrowBarWidth) - base.BaseBarHeight = 13 * NarrowBarWidth; - else - base.BaseBarHeight = value; - } - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1Utils.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1Utils.cs deleted file mode 100644 index 6a94bafc..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/GS1Utils.cs +++ /dev/null @@ -1,209 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - internal class GS1Utils - { - static private string m_numbers = "0123456789"; - static private string m_capitalLetter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - static private string m_gtinAlphabet = m_numbers; - - /// - /// Function returns the number of combinations r selected from n - /// Combinations = n! / ((n - r)! * r!) - /// (based on the C code from the ISO/IEC 24724-2011 standard) - /// - static internal int combins(int n, int r) - { - int maxDenom, minDenom; - - if (n - r > r) - { - minDenom = r; - maxDenom = n - r; - } - else - { - minDenom = n - r; - maxDenom = r; - } - int val = 1; - int j = 1; - for (int i = n; i > maxDenom; i--) - { - val *= i; - if (j <= minDenom) - { - val /= j; - j++; - } - } - for (; j <= minDenom; j++) - { - val /= j; - } - return (val); - } - - /// - /// Function for generating sizes (widths) of GS1 DataBar elements - /// (based on C code from the ISO/IEC 24724-2011 standard) - /// - /// The input value. - /// The number of modules. - /// The number of pairs of elements in the set (for GS1DataBarOmnidirectional = 4). - /// The maximum width of an element in modules. - /// if set to true, skip templates that do not have elements with a width of one module. - /// The sizes (widths) of the elements. - - static internal int[] getRSSwidths(int val, int n, int elements, int maxWidth, bool noNarrow) - { - int elmWidth; - int subVal; - int narrowMask = 0; - int[] widths = new int[elements]; - for (int bar = 0; bar < elements - 1; bar++) - { - narrowMask |= (1 << bar); - for (elmWidth = 1; ; elmWidth++) - { - // get all combinations - subVal = combins(n - elmWidth - 1, elements - bar - 2); - // excluding combinations where there are no elements of width one module - if ((!noNarrow) && (narrowMask == 0) && - (n - elmWidth - (elements - bar - 1) >= elements - bar - 1)) - { - subVal -= combins(n - elmWidth - (elements - bar), elements - bar - 2); - } - // excluding combinations where the element width is greater than maxVal - if (elements - bar - 1 > 1) - { - int lessVal = 0; - for (int mxwElement = n - elmWidth - (elements - bar - 2); - mxwElement > maxWidth; - mxwElement--) - { - lessVal += combins(n - elmWidth - mxwElement - 1, elements - bar - 3); - } - subVal -= lessVal * (elements - 1 - bar); - } - else if (n - elmWidth > maxWidth) - { - subVal--; - } - val -= subVal; - if (val < 0) break; - narrowMask &= ~(1 << bar); - } - val += subVal; - n -= elmWidth; - widths[bar] = elmWidth; - } - widths[elements - 1] = n; - return widths; - } - - static private int getCharPosition(char c) - { - int charPos = m_gtinAlphabet.IndexOf(c); - if (charPos == -1) - throw new BarcodeException("Incorrect char in GTIN"); - - return charPos; - } - - /// - /// Gets the GTIN checksum. - /// - /// The value to calculate checksum for. - /// The checksum char. - static internal char getGTINChecksum(string value) - { - int sum = 0; - int valueLength = value.Length - 1; - for (int i = 0; i < value.Length; i++) - { - if (i % 2 == 0) - sum += getCharPosition(value[valueLength - i]) * 3; - else - sum += getCharPosition(value[valueLength - i]); - } - - int lastDigit = (int)(Math.Ceiling(sum / 10.0) * 10) - sum; - - return m_gtinAlphabet[lastDigit]; - } - - static internal bool IsGTIN(string value) - { - int index = value.IndexOf("(01)"); - string gtin; - if (index == 0) - gtin = value.Substring(4); - else - gtin = value; - - if (gtin.Length == 0 || gtin.Length > 14) // GTIN-14(GTIN-13,GTIN-12,GTIN-8) - return false; - - foreach (char c in gtin) - { - if (m_gtinAlphabet.IndexOf(c) == -1) - return false; - } - - // user wants to enter value with check digit - // we need to verify that digit - char checksum = getGTINChecksum(gtin.Substring(0, gtin.Length - 1)); - if (gtin[gtin.Length - 1] != checksum) - return false; - - return true; - } - - static internal string GetEncodedGTINValue(string value, bool forCaption) - { - StringBuilder sb = new StringBuilder(value); - while (sb.ToString().Length < 14) - sb.Insert(0, new char[] { '0' }); - string s = sb.ToString(); - if (forCaption) - { - string AI = "(01)"; // for DataBar Omnidirectional Application Identifier = 01 - return AI + s; - } - else - { - string lf = "0"; //linkage flag == 0 if GS1 DataBar symbol stands alone (=> for DataBar Omnidirectional linkage flag == 0) - // == 1 if 2D Composite Component and its separator pattern are printed above the GS1 DataBar symbol - return lf + s.Substring(0, s.Length - 1); // remove the checksum - } - } - - static internal string Numbers - { - get { return m_numbers; } - } - - static internal string CapitalLetter - { - get { return m_capitalLetter; } - } - - static internal void addArray(intList list, int[] array, bool flip) - { - if (flip) - for (int i = array.Length - 1; i >= 0; i--) - { - list.Add(array[i]); - } - else - for (int i = 0; i < array.Length; i++) - { - list.Add(array[i]); - } - } - - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/GTIN12Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/GTIN12Symbology.cs deleted file mode 100644 index d73ff61c..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/GTIN12Symbology.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using GTIN-12 symbology rules. GTIN-12 is UPC-A with 12 digits. - /// - class GTIN12Symbology : UPCASymbology - { - public GTIN12Symbology() - { - m_type = TrueSymbologyType.GTIN12; - } - - public GTIN12Symbology(SymbologyDrawing prototype) : base(prototype) - { - m_type = TrueSymbologyType.GTIN12; - } - - public override string getValueRestrictions() - { - return "GTIN-12 (UPC-A) symbology expects strings with 11 digits to be encoded. Optionally, user may enter 12th digit. In the latter case the last digit (check digit) will be verified."; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/GTIN13Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/GTIN13Symbology.cs deleted file mode 100644 index d096ed18..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/GTIN13Symbology.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace BarcodeWriter.Core.Internal -{ - class GTIN13Symbology : EAN13Symbology - { - public GTIN13Symbology() - { - m_type = TrueSymbologyType.GTIN13; - } - - public GTIN13Symbology(SymbologyDrawing prototype) : base(prototype) - { - m_type = TrueSymbologyType.GTIN13; - } - - public override string getValueRestrictions() - { - return "GTIN-13 (EAN-13) symbology expects strings with 12 digits to be encoded. Optionally, user may enter 13th digit. In the latter case the last digit (check digit) will be verified."; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/GTIN14Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/GTIN14Symbology.cs deleted file mode 100644 index 8b7fa70d..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/GTIN14Symbology.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace BarcodeWriter.Core.Internal -{ - class GTIN14Symbology : ITF14Symbology - { - public GTIN14Symbology() - { - m_type = TrueSymbologyType.GTIN14; - } - - public GTIN14Symbology(SymbologyDrawing prototype) : base(prototype) - { - m_type = TrueSymbologyType.GTIN14; - } - - public override string getValueRestrictions() - { - return "GTIN-14 (ITF-14) symbology expects strings with 13 digits to be encoded. Optionally, user may enter 14th digit. In the latter case the last digit (check digit) will be verified."; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/GTIN8Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/GTIN8Symbology.cs deleted file mode 100644 index e9514e18..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/GTIN8Symbology.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace BarcodeWriter.Core.Internal -{ - class GTIN8Symbology : EAN8Symbology - { - public GTIN8Symbology() - { - m_type = TrueSymbologyType.GTIN8; - } - - public GTIN8Symbology(SymbologyDrawing prototype) : base(prototype) - { - m_type = TrueSymbologyType.GTIN8; - } - - public override string getValueRestrictions() - { - return "GTIN-8 (EAN-8) symbology expects strings with 7 digits to be encoded. Optionally, user may enter 8th digit. In the latter case the last digit (check digit) will be verified."; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/I2of5Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/I2of5Symbology.cs deleted file mode 100644 index 97dccaa5..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/I2of5Symbology.cs +++ /dev/null @@ -1,351 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using SkiaSharp; -using System; -using System.Drawing; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using Interleaved 2 of 5 symbology rules. - /// This symbology is used primarily in the distribution and warehouse industry. - /// - class I2of5Symbology : SymbologyDrawing - { - protected static string m_alphabet = "0123456789"; - - /// - /// Initializes a new instance of the class. - /// - public I2of5Symbology() - : base(TrueSymbologyType.I2of5) - { - // there is no characters corresponding to Interleaved 2 of 5 - // start and stop symbols. - Options.ShowStartStop = false; - AddChecksum = false; - AddChecksumToCaption = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public I2of5Symbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.I2of5) - { - } - - - - /// - /// Validates the value using Interleaved 2 of 5 symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Checksum is obligatory or not. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length == 0) - return true; - - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - // #1: if AddChecksum = true - // EVEN number: checking if last digit is valid checksum - // ODD number: calc and add checksum at the end (to make it EVEN number of digits) - - // #2: if AddChecksum = false - // EVEN number: doing nothing and encoding as is - // ODD number: add padding zero (at the beginning) to make it EVEN number of digits - - if (AddChecksum) - { - // if even number of characters - if (value.Length % 2 == 0) - { - // check if the last character is valid checksum - char curChecksumChar = value[value.Length - 1]; - // get the checksum for the value (except the very last char - char ethalonChecksumChar = getChecksumChar(value.Substring(0, value.Length - 1)); - if (curChecksumChar != ethalonChecksumChar) - return false; - } - else if (checksumIsMandatory) - { - return false; - } - } - else if (checksumIsMandatory) - { - return (value.Length % 2 == 0); - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Interleaved 2 of 5 symbology allows only numeric values to be encoded.\r\n" + - "Total value length must be:\r\n\r\n" + - "EVEN if checksum is not to be added (but the checksum will be checked if it is correct)\r\n" + - "ODD if checksum is to be added.\r\n\r\n" + - "when AddChecksum = false: even number of digits are encoded as is, odd number of characters is padded with zero at the beginning"; - } - - /// - /// Gets the barcode value encoded using Interleaved 2 of 5 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - - // #1: if AddChecksum = true - // EVEN number: checking if last digit is valid checksum - // ODD number: calc and add checksum at the end (to make it EVEN number of digits) - - // #2: if AddChecksum = false - // EVEN number: doing nothing and encoding as is - // ODD number: add padding zero (at the beginning) to make it EVEN number of digits - - if (AddChecksum) - { - sb.Append(Value); - - // append zero if value + checksum char produce string - // with odd length - if (Value.Length % 2 == 0) - { - ; // the valid checksum is valided in ValueIsValid() already - } - else // we have ODD number of digits - { - // so we should calculate and add the checksum - if ((forCaption && AddChecksumToCaption) || (!forCaption)) - { - char checksumChar = getChecksumChar(Value); - sb.Append(checksumChar); - } - } - } - else // if AddChecksum == false - { - // if we are not adding the checksum then we are padding - // with zeros to have EVEN number of characters! - - // append zero if value has odd length - if (Value.Length % 2 != 0) - sb.Append("0"); - - sb.Append(Value); - } - - return sb.ToString(); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - switch (c) - { - case '0': - return "nnwwn"; - case '1': - return "wnnnw"; - case '2': - return "nwnnw"; - case '3': - return "wwnnn"; - case '4': - return "nnwnw"; - case '5': - return "wnwnn"; - case '6': - return "nwwnn"; - case '7': - return "nnnww"; - case '8': - return "wnnwn"; - case '9': - return "nwnwn"; - } - - return "wwwww"; - } - - /// - /// Gets the checksum char. - /// - /// The value. - /// The checksum char. - protected virtual char getChecksumChar(string value) - { - int evenSum = 0; - int oddSum = 0; - - for (int i = 0; i < value.Length; i++) - { - if (i % 2 == 0) - evenSum += getCharPosition(value[i]); - else - oddSum += getCharPosition(value[i]); - } - - int total = 0; - if (value.Length % 2 == 1) - total = evenSum * 3 + oddSum; - else - total = oddSum * 3 + evenSum; - - int checkDigitPos = (total / 10) * 10 + 10 - total; - if (checkDigitPos % 10 == 0) - checkDigitPos = 0; - - return m_alphabet[checkDigitPos]; - } - - /// - /// Gets the char position within the alphabet. - /// - /// The char to find. - /// - protected static int getCharPosition(char c) - { - for (int i = 0; i < m_alphabet.Length; i++) - { - if (m_alphabet[i] == c) - return i; - } - - string message = String.Format("Incorrect character '%c' for Interleaved 2 of 5 symbology", c); - throw new BarcodeException(message); - } - - protected override SKSize buildBars(SKCanvas canvase, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - string value = GetEncodedValue(false); - if (value.Length % 2 != 0) - throw new BarcodeException("Incorrect value length."); - - x += drawStartCode(x, y); - - int widthNarrow = NarrowBarWidth; - int widthWide = NarrowBarWidth * WideToNarrowRatio; - int height = BarHeight; - - for (int i = 0; i < (value.Length - 1); i += 2) - { - string barsPattern = getCharPattern(value[i]); - string gapsPattern = getCharPattern(value[i + 1]); - - for (int patternPos = 0; patternPos < barsPattern.Length; patternPos++) - { - int barWidth = widthNarrow; - if (barsPattern[patternPos] == 'w') - barWidth = widthWide; - - int gapWidth = widthNarrow; - if (gapsPattern[patternPos] == 'w') - gapWidth = widthWide; - - m_rects.Add(new SKRect(x, y, barWidth+x, height+y)); - - x += barWidth; - x += gapWidth; - } - } - - x += drawStopCode(x, y); - - drawingSize.Width = x; - drawingSize.Height = BarHeight; - return drawingSize; - } - - /// - /// Draws the start code. - /// - /// The start X position. - /// The start Y position. - /// - /// The width of the rectangle occupied by barcode start code. - /// - protected int drawStartCode(int x, int y) - { - // The start code consists of two narrow bars and two narrow spaces - - int widthNarrow = NarrowBarWidth; - int height = BarHeight; - int widthOccupied = 0; - - for (int i = 0; i < 2; i++) - { - m_rects.Add(new SKRect(x + widthOccupied, y, widthNarrow+ x + widthOccupied, height+y)); - widthOccupied += widthNarrow; - widthOccupied += widthNarrow; - } - - return widthOccupied; - } - - /// - /// Draws the start code. - /// - /// The start X position. - /// The start Y position. - /// - /// The width of the rectangle occupied by barcode start code. - /// - protected int drawStopCode(int x, int y) - { - // the stop code consists of a wide bar, narrow space, and a narrow bar. - - int widthNarrow = NarrowBarWidth; - int widthWide = NarrowBarWidth * WideToNarrowRatio; - int height = BarHeight; - int widthOccupied = 0; - - m_rects.Add(new SKRect(x + widthOccupied, y, widthWide+ x + widthOccupied, height+y)); - widthOccupied += widthWide; - widthOccupied += widthNarrow; - - m_rects.Add(new SKRect(x + widthOccupied, y, widthNarrow + x + widthOccupied, height+y)); - widthOccupied += widthNarrow; - - return widthOccupied; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/ISBNSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/ISBNSymbology.cs deleted file mode 100644 index c50af8cb..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/ISBNSymbology.cs +++ /dev/null @@ -1,227 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using System.Drawing; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws EAN-13 barcodes for ISBN numbers. - /// - class ISBNSymbology : EAN13Symbology - { - private static string m_template10 = "#-#######-#-#"; - private static string m_template13 = "###-#-####-####-#"; - private string m_currentTemplate = string.Empty; - - /// - /// Initializes a new instance of the class. - /// - public ISBNSymbology() - : base() - { - m_type = TrueSymbologyType.ISBN; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public ISBNSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.ISBN; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "978000000000"; - } - - /// - /// Validates the value using ISBN symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - string realValue = getRealValue(value, ref m_currentTemplate); - if (realValue.Length != 9 && realValue.Length != 10 && realValue.Length != 12 && realValue.Length != 13) - return false; - - if (realValue.Length == 12 || realValue.Length == 13) - { - string prefix = realValue.Substring(0, 3); - if (prefix != "978" && prefix != "979") - return false; - } - - foreach (char c in realValue) - { - if (m_alphabet.IndexOf(c) == -1 && c != 'x' && c != 'X') - return false; - } - - return true; - } - - public string GetAutoCaption() - { - string realValue = getRealValue(Value).ToUpper(); - - if (realValue.Length == 9) - realValue += getISBNCheckDigit(realValue); - - string template; - if (Options.ISBNCaptionTemplate != string.Empty) - template = Options.ISBNCaptionTemplate; - else if (m_currentTemplate != string.Empty) - template = m_currentTemplate; - else if (realValue.Length == 10) - template = m_template10; - else - template = m_template13; - - StringBuilder sb = new StringBuilder(); - int index = 0; - for (int i = 0; i < template.Length; i++) - { - if (template[i] == '-') - sb.Append('-'); - else if (index - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "ISBN symbology allows only strings with valid ISBN (9, 10, 12 or 13 numbers and possibly dashes or spaces between them) to be encoded."; - } - - /// - /// Gets the barcode value encoded using JAN-13 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - string realValue = getRealValue(Value); - if (realValue.Length == 10 || realValue.Length == 13) - realValue = realValue.Remove(realValue.Length - 1, 1); - - if (realValue.Length == 9) - realValue = "978" + realValue; - - // checksum char ALWAYS added to encoded value and caption - return realValue + getChecksum(realValue); - } - - ///// - ///// Gets or sets the ISBN caption template (e.g. #-#######-#-#). - ///// - ///// The ISBN caption template. - //public string ISBNCaptionTemplate - //{ - // get { return m_ISBNCaptionTemplate; } - // set { m_ISBNCaptionTemplate = value; } - //} - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/ITF14Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/ITF14Symbology.cs deleted file mode 100644 index ee12d849..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/ITF14Symbology.cs +++ /dev/null @@ -1,135 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using SkiaSharp; -using System; -using System.Drawing; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using ITF-14 (GTIN-14, UCC-14) symbology rules. - /// This symbology is generally used on a packaging step of products. - /// The ITF-14 always encodes 14 digits. - /// - class ITF14Symbology : I2of5Symbology - { - /// - /// Initializes a new instance of the class. - /// - public ITF14Symbology() - : base() - { - m_type = TrueSymbologyType.ITF14; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public ITF14Symbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.ITF14; - } - - public override string getValueRestrictions() - { - return "ITF-14 symbology expects strings with 13 digits to be encoded. Optionally, user may enter 14th digit. In the latter case the last digit (check digit) will be verified."; - } - - /// - /// Validates the value using ITF-14 symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Checksum is mandatory or not (if applicable). - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 14 && value.Length != 13) - return false; - - return base.ValueIsValid(value, checksumIsMandatory); - } - - /// - /// Gets the barcode value encoded using ITF-14 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - if (Value.Length == 13) - { - return Value + getChecksumChar(Value); - } - return Value; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - int BearerBarsWidth = 4 * NarrowBarWidth; - int QuietZone = 10 * NarrowBarWidth; - SKSize drawingSize = new SKSize(); - int x = BearerBarsWidth + QuietZone; // 0; - int y = BearerBarsWidth; // 0; - - string value = GetEncodedValue(false); - if (value.Length % 2 != 0) - throw new BarcodeException("Incorrect value length."); - - x += drawStartCode(x, y); - - int widthNarrow = NarrowBarWidth; - int widthWide = NarrowBarWidth * WideToNarrowRatio; - int height = BarHeight; - - for (int i = 0; i < (value.Length - 1); i += 2) - { - string barsPattern = getCharPattern(value[i]); - string gapsPattern = getCharPattern(value[i + 1]); - - for (int patternPos = 0; patternPos < barsPattern.Length; patternPos++) - { - int barWidth = widthNarrow; - if (barsPattern[patternPos] == 'w') - barWidth = widthWide; - - int gapWidth = widthNarrow; - if (gapsPattern[patternPos] == 'w') - gapWidth = widthWide; - - m_rects.Add(new SKRect(x, y, barWidth+x, height+y)); - - x += barWidth; - x += gapWidth; - } - } - - x += drawStopCode(x, y); - - m_rects.Add(new SKRect(BearerBarsWidth, 0, x + QuietZone, BearerBarsWidth)); - m_rects.Add(new SKRect(BearerBarsWidth, height + BearerBarsWidth, x + QuietZone, BearerBarsWidth+ height + BearerBarsWidth)); - if (!Options.OnlyHorizontalBearerBar) - { - m_rects.Add(new SKRect(0, 0, BearerBarsWidth, height + BearerBarsWidth * 2)); - m_rects.Add(new SKRect(x + QuietZone, 0, BearerBarsWidth, height + BearerBarsWidth * 2)); - } - drawingSize.Width = x + BearerBarsWidth + QuietZone; - drawingSize.Height = BarHeight + BearerBarsWidth * 2; - return drawingSize; - } - - - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/IntelligentMailSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/IntelligentMailSymbology.cs deleted file mode 100644 index d6b79d2e..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/IntelligentMailSymbology.cs +++ /dev/null @@ -1,466 +0,0 @@ -using System; -using System.Text; -using System.Drawing; -using System.Diagnostics; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using Intelligent Mail Barcode symbology. - /// This symbology is used in the USPS mailstream. It is also known as - /// the USPS OneCode Solution or USPS 4-State Customer Barcode (abbreviated - /// 4CB, 4-CB, or USPS4CB) - /// - class IntelligentMailSymbology : SymbologyDrawing - { - private static string m_alphabet = "0123456789"; - private static int[] m_Table1 = createCodewordToCharTable(true); - private static int[] m_Table2 = createCodewordToCharTable(false); - - // bar position -> (descender char, bit), (ascender char, bit) - int[] m_barPositions = new int[] - { - 7, 2, 4, 3, 1, 10, 0, 0, 9, 12, 2, 8, 5, 5, 6, 11, 8, 9, 3, 1, // 5 - 0, 1, 5, 12, 2, 5, 1, 8, 4, 4, 9, 11, 6, 3, 8, 10, 3, 9, 7, 6, // 10 - 5, 11, 1, 4, 8, 5, 2, 12, 9, 10, 0, 2, 7, 1, 6, 7, 3, 6, 4, 9, // 15 - 0, 3, 8, 6, 6, 4, 2, 7, 1, 1, 9, 9, 7, 10, 5, 2, 4, 0, 3, 8, // 20 - 6, 2, 0, 4, 8, 11, 1, 0, 9, 8, 3, 12, 2, 6, 7, 7, 5, 1, 4, 10, // 25 - 1, 12, 6, 9, 7, 3, 8, 0, 5, 8, 9, 7, 4, 6, 2, 10, 3, 4, 0, 5, // 30 - 8, 4, 5, 7, 7, 11, 1, 9, 6, 0, 9, 6, 0, 6, 4, 8, 2, 1, 3, 2, // 35 - 5, 9, 8, 12, 4, 11, 6, 1, 9, 5, 7, 4, 3, 3, 1, 2, 0, 7, 2, 0, // 40 - 1, 3, 4, 1, 6, 10, 3, 5, 8, 7, 9, 4, 2, 11, 5, 6, 0, 8, 7, 12, // 45 - 4, 2, 8, 1, 5, 10, 3, 0, 9, 3, 0, 9, 6, 5, 2, 4, 7, 8, 1, 7, // 50 - 5, 0, 4, 5, 2, 3, 0, 10, 6, 12, 9, 2, 3, 11, 1, 6, 8, 8, 7, 9, // 55 - 5, 4, 0, 11, 1, 5, 2, 2, 9, 1, 4, 12, 8, 3, 6, 6, 7, 0, 3, 7, // 60 - 4, 7, 7, 5, 0, 12, 1, 11, 2, 9, 9, 0, 6, 8, 5, 3, 3, 10, 8, 2 // 65 - }; - - /// - /// Initializes a new instance of the class. - /// - public IntelligentMailSymbology() - : base(TrueSymbologyType.IntelligentMail) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public IntelligentMailSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.IntelligentMail) - { - } - - /// - /// Validates the value using Intelligent Mail symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - string clean = getCleanValue(value); - if (clean.Length != 20 && clean.Length != 25 && clean.Length != 29 && clean.Length != 31) - return false; - - foreach (char c in clean) - { - if (!char.IsDigit(c)) - return false; - } - - char secondDigit = clean[1]; - if ("01234".IndexOf(secondDigit) == -1) - return false; - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Intelligent Mail symbology allows encoding of up to 31 digits of data. " + - "Tracking code should be exactly 20 digits long and Routing code may be 0, 5, 9 or 11 digits long. " + - "Spaces and dots are allowed as separators."; - } - - /// - /// Gets the barcode value encoded using Intelligent Mail symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - if (forCaption) - return Value; - - return encodeValue(getCleanValue(Value)); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - throw new NotImplementedException(); - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - string value = GetEncodedValue(false); - int fullBarHeight = Math.Max((BarHeight / 3) * 3, 3); - int ascenderHeight = (fullBarHeight / 3) * 2; - int descenderHeight = ascenderHeight; - int trackerHeight = fullBarHeight / 3; - - int gapWidth = Math.Max(NarrowBarWidth / 2, 1); - int width = NarrowBarWidth; - - foreach (char c in value) - { - if (c == 'A') - { - // ascender - m_rects.Add(new SKRect(x, y, width+x, ascenderHeight+y)); - } - else if (c == 'D') - { - // descender - m_rects.Add(new SKRect(x, y + trackerHeight, width+x, descenderHeight+ y + trackerHeight)); - } - else if (c == 'T') - { - // tracker - m_rects.Add(new SKRect(x, y + trackerHeight, width+x, trackerHeight+ y + trackerHeight)); - } - else if (c == 'F') - { - // full - m_rects.Add(new SKRect(x, y, width+x, fullBarHeight+y)); - } - - - x += width + NarrowBarWidth; - } - - drawingSize.Width = x; - drawingSize.Height = fullBarHeight; - return drawingSize; - } - - private string getCleanValue(string value) - { - StringBuilder sb = new StringBuilder(value.Length); - foreach (char c in value) - { - if (c != ' ' && c != '.') - sb.Append(c); - } - - return sb.ToString(); - } - - private string encodeValue(string value) - { - // Tracking Code | Routing Code - // [ 01234567890123456789 ] | [ ] or [ 012345 ] or [ 0123456789 ] or [ 012345678901 ] - - // Tracking Code consist of: - // Barcode Identifier - // Service Type Identifier - // Mailer Identifier - // Serial Number - - long routing = getRoutingCode(value); - - BigInteger binary = processTrackingCode(routing, value); - byte[] binaryData = binary.getBytes(); - if (binaryData.Length < 13) - { - byte[] temp = new byte[13]; - Buffer.BlockCopy(binaryData, 0, temp, 13 - binaryData.Length, binaryData.Length); - binaryData = temp; - } - - ushort crc = calculateCrc(binaryData); - - BigInteger[] codewords = getCodewords(binary); - postprocessCodewords(codewords, crc); - - int[] characters = getCharacters(codewords); - postprocessCharacters(characters, crc); - - return charactersToBars(characters); - } - - private long getRoutingCode(string value) - { - string routingStr = value.Substring(20); - long routing = 0; - switch (routingStr.Length) - { - case 0: - break; - - case 5: - routing = long.Parse(routingStr) + 1; - break; - - case 9: - routing = long.Parse(routingStr) + 100000 + 1; - break; - - case 11: - routing = long.Parse(routingStr) + 1000000000 + 100000 + 1; - break; - } - - return routing; - } - - private BigInteger processTrackingCode(long routingCode, string value) - { - BigInteger binary = new BigInteger(routingCode); - - binary *= 10; - binary += m_alphabet.IndexOf(value[0]); - - binary *= 5; - binary += m_alphabet.IndexOf(value[1]); - - for (int i = 0; i < 18; i++) - { - binary *= 10; - binary += m_alphabet.IndexOf(value[2 + i]); - } - - return binary; - } - - /// - /// Calculates the CRC. - /// - /// The byte array is a 13 byte array - /// holding 102 bits which are right justified - ie: the leftmost 2 bits - /// of the first byte do not hold data and must be set to zero. - /// 11 bit Frame Check Sequence (right justified) - private ushort calculateCrc(byte[] buffer) - { - ushort GeneratorPolynomial = 0x0F35; - ushort FrameCheckSequence = 0x07FF; - int bufIndex = 0; - - // Do most significant byte skipping the 2 most significant bits - ushort Data = (ushort)(buffer[bufIndex++] << 5); - - for (int Bit = 2; Bit < 8; Bit++) - { - if (((FrameCheckSequence ^ Data) & 0x400) != 0) - FrameCheckSequence = (ushort)((FrameCheckSequence << 1) ^ GeneratorPolynomial); - else - FrameCheckSequence = (ushort)(FrameCheckSequence << 1); - - FrameCheckSequence &= 0x7FF; - Data <<= 1; - } - - // Do rest of the bytes - for (int ByteIndex = 1; ByteIndex < 13; ByteIndex++) - { - Data = (ushort)(buffer[bufIndex++] << 3); - for (int Bit = 0; Bit < 8; Bit++) - { - if (((FrameCheckSequence ^ Data) & 0x0400) != 0) - FrameCheckSequence = (ushort)((FrameCheckSequence << 1) ^ GeneratorPolynomial); - else - FrameCheckSequence = (ushort)(FrameCheckSequence << 1); - - FrameCheckSequence &= 0x7FF; - Data <<= 1; - } - } - - return FrameCheckSequence; - } - - private BigInteger[] getCodewords(BigInteger binary) - { - BigInteger[] codewords = new BigInteger[10]; - - codewords[9] = binary % 636; // J - binary /= 636; - - // I - B - for (int i = 8; i >= 1; i--) - { - codewords[i] = binary % 1365; - binary /= 1365; - } - - Debug.Assert(binary >= 0 && binary <= 658); - codewords[0] = binary; // A - - return codewords; - } - - private void postprocessCodewords(BigInteger[] codewords, ushort crc) - { - codewords[9] *= 2; - - if ((crc & (1 << 9)) != 0) - { - // 10th bit is set - codewords[0] += 659; - } - } - - private static ushort ReverseUnsignedShort(ushort Input) - { - ushort Reverse = 0; - for (int Index = 0; Index < 16; Index++) - { - Reverse <<= 1; - Reverse |= (ushort)(Input & 1); - Input >>= 1; - } - - return Reverse; - } - - private static int[] createCodewordToCharTable(bool createTable1) - { - int[] table = null; - int N; - if (createTable1) - { - table = new int[1287]; - N = 5; - } - else - { - table = new int[78]; - N = 2; - } - - // Count up to 2^13 - 1 and find all those values that have N bits on - int LUT_LowerIndex = 0; - int LUT_UpperIndex = table.Length - 1; - for (int Count = 0; Count < 8192; Count++) - { - int BitCount = 0; - for (int BitIndex = 0; BitIndex < 13; BitIndex++) - { - if ((Count & (1 << BitIndex)) != 0) - BitCount++; - } - - // If we don't have the right number of bits on, go on to the next value - if (BitCount != N) - continue; - - // If the reverse is less than count, we have already visited this pair before - int Reverse = ReverseUnsignedShort((ushort)Count) >> 3; - if (Reverse < Count) - continue; - - // If Count is symmetric, place it at the first free slot from the end of the - // list. Otherwise, place it at the first free slot from the beginning of the - // list AND place Reverse at the next free slot from the beginning of the list. - if (Count == Reverse) - { - table[LUT_UpperIndex] = Count; - LUT_UpperIndex -= 1; - } - else - { - table[LUT_LowerIndex] = Count; - LUT_LowerIndex += 1; - - table[LUT_LowerIndex] = Reverse; - LUT_LowerIndex += 1; - } - } - - // Make sure the lower and upper parts of the table meet properly - if (LUT_LowerIndex != (LUT_UpperIndex + 1)) - return null; - - return table; - } - - private int[] getCharacters(BigInteger[] codewords) - { - int[] chars = new int[codewords.Length]; - for (int i = 0; i < codewords.Length; i++) - { - int c = codewords[i].IntValue(); - if (c >= 0 && c <= 1286) - chars[i] = m_Table1[c]; - else if (c > 1286 && c <= 1364) - chars[i] = m_Table2[c - 1287]; - else - throw new InvalidOperationException(); - } - - return chars; - } - - private void postprocessCharacters(int[] characters, ushort crc) - { - for (int bitPos = 0; bitPos < 10; bitPos++) - { - if ((crc & (1 << bitPos)) != 0) - characters[bitPos] = ~characters[bitPos] & 8191; - } - } - - private string charactersToBars(int[] characters) - { - StringBuilder bars = new StringBuilder(65); - for (int i = 1; i <= 65; i++) - { - int index = (i - 1) * 4; - int descenderChar = m_barPositions[index++]; - int descenderBit = m_barPositions[index++]; - int ascenderChar = m_barPositions[index++]; - int ascenderBit = m_barPositions[index++]; - - bool haveDescender = ((characters[descenderChar] & (1 << (descenderBit ))) != 0); - bool haveAscender = ((characters[ascenderChar] & (1 << (ascenderBit ))) != 0); - - if (haveAscender && haveDescender) - bars.Append('F'); - else if (haveAscender) - bars.Append('A'); - else if (haveDescender) - bars.Append('D'); - else - bars.Append('T'); - } - - return bars.ToString(); - } - } -} - - \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/JAN13Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/JAN13Symbology.cs deleted file mode 100644 index 05f56018..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/JAN13Symbology.cs +++ /dev/null @@ -1,94 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using JAN13 (aka JAN Codes) symbology rules. JAN-13 - /// symbology is used mostly in Japan. - /// - class JAN13Symbology : EAN13Symbology - { - /// - /// Initializes a new instance of the class. - /// - public JAN13Symbology() - : base() - { - m_type = TrueSymbologyType.JAN13; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public JAN13Symbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.JAN13; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "0000000000"; - } - - /// - /// Validates the value using JAN-13 symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 10) - return false; - - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "JAN-13 symbology allows only strings with exactly ten numbers to be encoded. Any value is always prepended with '49'"; - } - - /// - /// Gets the barcode value encoded using JAN-13 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - // checksum char ALWAYS added to encoded value and caption - return "49" + Value + getChecksum("49" + Value); - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/MSISymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/MSISymbology.cs deleted file mode 100644 index fdb5765b..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/MSISymbology.cs +++ /dev/null @@ -1,229 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using SkiaSharp; -using System; -using System.Drawing; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using MSI symbology rules. - /// This symbology is used primarily for inventory control, - /// marking storage containers and shelves in warehouse environments. - /// - /// See also: - /// http://en.wikipedia.org/wiki/MSI_Barcode - /// http://www.crifan.com/files/doc/docbook/symbology_plessey/release/html/symbology_plessey.html - /// - class MSISymbology : SymbologyDrawing - { - protected static string m_alphabet = "0123456789"; - protected static string m_startCode = "110"; - protected static string m_stopCode = "1001"; - - /// - /// Initializes a new instance of the class. - /// - public MSISymbology() - : base(TrueSymbologyType.Plessey) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public MSISymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.Plessey) - { - } - - /// - /// Validates the value using MSI symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "MSI symbology allows only numeric values to be encoded."; - } - - /// - /// Gets the barcode value encoded using MSI symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - - if (forCaption) - { - sb.Append(Value); - } - else - { - sb.Append(Value); - sb.Append(getChecksum(Value)); - } - - return sb.ToString(); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - switch (c) - { - case '0': - return "100100100100"; - case '1': - return "100100100110"; - case '2': - return "100100110100"; - case '3': - return "100100110110"; - case '4': - return "100110100100"; - case '5': - return "100110100110"; - case '6': - return "100110110100"; - case '7': - return "100110110110"; - case '8': - return "110100100100"; - case '9': - return "110100100110"; - } - return ""; - } - - protected string getModulo10CheckDigit(string value) - { - // Luhn algorithm (http://en.wikipedia.org/wiki/Luhn_algorithm) - int sum = 0; - for (int i = 0; i < value.Length; i++) - { - byte d = byte.Parse(value[i].ToString()); - if ((value.Length - i) % 2 != 0) // Digits are numbered from right to left - { - if (d > 4) - sum += d * 2 - 9; - else - sum += d * 2; - } - else - sum += d; - } - int checkDigit = 10 - sum % 10; - return checkDigit.ToString(); - } - - protected string getModulo11CheckDigit(string value) - { - int sum = 0; - int weight = 2; - for (int i = value.Length-1; i > -1; i--) - { - sum += byte.Parse(value[i].ToString()) * weight; - weight++; - // The IBM algorithm is uses 2..7, the NCR algorithm is uses 2..9 - if (weight > 7) - weight = 2; - } - - int checkDigit = 11 - sum % 11; - return checkDigit.ToString(); - } - - /// - /// Gets the checksum. - /// - /// The value. - /// The checksum. - protected string getChecksum(string value) - { - switch (Options.MSIChecksumAlgorithm) - { - case MSIChecksumAlgorithm.Modulo10: - return getModulo10CheckDigit(value); - case MSIChecksumAlgorithm.Modulo11: - return getModulo11CheckDigit(value); - case MSIChecksumAlgorithm.Modulo1010: - string checkDigit10 = getModulo10CheckDigit(value); - return checkDigit10 + getModulo10CheckDigit(value + checkDigit10); - case MSIChecksumAlgorithm.Modulo1110: - string checkDigit11 = getModulo11CheckDigit(value); - return checkDigit11 + getModulo10CheckDigit(value + checkDigit11); - case MSIChecksumAlgorithm.NoCheckDigit: - default: - return string.Empty; - } - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - string label = GetEncodedValue(false); - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < label.Length; i++) - { - sb.Append(getCharPattern(label[i])); - } - string value = m_startCode + sb.ToString() + m_stopCode; - - int width = NarrowBarWidth; - int height = BarHeight; - for (int i = 0; i < value.Length; i++) - { - if (value[i] == '1') - m_rects.Add(new SKRect(x, y, width+x, height+y)); - x += width; - } - - drawingSize.Width = x; - drawingSize.Height = height; - return drawingSize; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/NumlySymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/NumlySymbology.cs deleted file mode 100644 index 35d7da1f..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/NumlySymbology.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws Numly barcodes. - /// - class NumlySymbology : Code39Symbology - { - /// - /// Initializes a new instance of the class. - /// - public NumlySymbology() - : base() - { - m_type = TrueSymbologyType.Numly; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public NumlySymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.Numly; - } - - /// - /// Validates the value using Numly symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - string cleanValue = getCleanValue(value); - if (cleanValue.Length != 19) - return false; - - return base.ValueIsValid(cleanValue, checksumIsMandatory); - } - - private static string getCleanValue(string value) - { - return value.Replace("-", ""); - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Numly symbology allows only numeric values with exactly 19 digits to be encoded. Additionally, dashes can be added as separators\n"; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "00000-000000-000000-00"; - } - - /// - /// Gets the barcode value encoded using Numly symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - string encoded = getCleanValue(Value); - - if (forCaption) - { - encoded = encoded.Insert(5, "-"); - encoded = encoded.Insert(12, "-"); - encoded = encoded.Insert(19, "-"); - - encoded = "ESN " + encoded; - } - else - encoded = "*" + encoded + "*"; - - return encoded; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/OPCSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/OPCSymbology.cs deleted file mode 100644 index ce16f791..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/OPCSymbology.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws OPC (Optical Product Code) barcodes - /// - class OPCSymbology : I2of5Symbology - { - /// - /// Initializes a new instance of the class. - /// - public OPCSymbology() - : base() - { - m_type = TrueSymbologyType.OpticalProduct; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public OPCSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.OpticalProduct; - } - - /// - /// Validates the value using Optical Code symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Checksum is mandatory or not (if applicable). - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 9) - return false; - - return base.ValueIsValid(value, checksumIsMandatory); - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Optical Product Code symbology allows only numeric values with exactly 9 digits: 5 digits for Manufacturer Identification Number assigned by the Optical Product Code Council, Inc., 4 digits for Item Identification Number assigned and controlled by the optical manufacturer to be encoded.\n"; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "000000000"; - } - - /// - /// Gets the barcode value encoded using Interleaved 2 of 5 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - sb.Append(Value); - - char checksumChar = getChecksumChar(Value); - sb.Append(checksumChar); - - return sb.ToString(); - } - - /// - /// Gets the checksum char. - /// - /// The value. - /// The checksum char. - protected override char getChecksumChar(string value) - { - int sum = 0; - - for (int i = 0; i < value.Length; i++) - { - int weight = 0; - if (i % 2 == 0) - weight = 2 * getCharPosition(value[i]); - else - weight = getCharPosition(value[i]); - - if (weight < 10) - sum += weight; - else - sum += weight / 10 + weight % 10; - } - - int checkDigitPos = 10 - (sum % 10); - if (checkDigitPos == 10) - checkDigitPos = 0; - - return m_alphabet[checkDigitPos]; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/PZNSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/PZNSymbology.cs deleted file mode 100644 index 4d78a53a..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/PZNSymbology.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws PZN (Pharma Zentral Nummer) barcodes. - /// - class PZNSymbology : Code39Symbology - { - /// - /// Initializes a new instance of the class. - /// - public PZNSymbology() - : base() - { - m_type = TrueSymbologyType.PZN; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public PZNSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.PZN; - } - - /// - /// Validates the value using PZN symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (Options.PZNType == PZNType.PZN7 && value.Length != 6) - return false; - if (Options.PZNType == PZNType.PZN8 && value.Length != 6 && value.Length != 7) - return false; - - return base.ValueIsValid(value, checksumIsMandatory); - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - if (Options.PZNType == PZNType.PZN7) - return "PZN symbology allows only numeric values with exactly 6 digits to be encoded\n"; - else - return "PZN symbology allows only numeric values with exactly 6 or 7 digits to be encoded\n"; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "000000"; - } - - /// - /// Gets the barcode value encoded using Numly symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - - if (forCaption) - sb.Append("PZN - "); - - if (!forCaption) - sb.Append("*-"); - - if (Options.PZNType == PZNType.PZN8 && Value.Length == 6) - sb.Append("0"); - - sb.Append(Value); - sb.Append(getChecksumChar(Value)); - - if (!forCaption) - sb.Append("*"); - - return sb.ToString(); - } - - /// - /// Gets the checksum char. - /// - /// The value. - /// The checksum char. - protected static char getChecksumChar(string value) - { - int sum = 0; - int shift = 8 - value.Length; - for (int i = 0; i < value.Length; i++) - sum += (i + shift) * getCharPosition(value[i]); - - int checkDigitPos = sum % 11; - if (checkDigitPos == 11) - checkDigitPos = 0; - - return m_alphabet[checkDigitPos]; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/PharmaCodeSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/PharmaCodeSymbology.cs deleted file mode 100644 index 5e451627..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/PharmaCodeSymbology.cs +++ /dev/null @@ -1,297 +0,0 @@ -using SkiaSharp; -using System; -using System.Drawing; -using System.Globalization; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using Pharma Code symbology rules. - /// - class PharmaCodeSymbology : SymbologyDrawing - { - /// - /// Initializes a new instance of the class. - /// - public PharmaCodeSymbology() - : base(TrueSymbologyType.PharmaCode) - { - m_type = TrueSymbologyType.PharmaCode; - Init(); - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public PharmaCodeSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.PharmaCode) - { - m_type = TrueSymbologyType.PharmaCode; - Init(); - } - - private void Init() - { - // there is no characters corresponding to PharmaCodeSymbology - // start and stop symbols. - Options.ShowStartStop = false; - AddChecksum = false; - AddChecksumToCaption = false; - base.DrawCaption = false; - } - - /// - /// Validates the value using Pharma Code symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - int val; - if (!int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out val)) - return false; - - if(Options.PharmaCodeTwoTrack) - return val >= 1 && val <= 64570080; - else - return val >= 1 && val <= 131070; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Pharma Code symbology allows only decimal number values to be encoded. Diapason from 1 to 131070 - for OneTrack barcode, from 1 to 64570080 for TwoTrack barcode."; - } - - /// - /// Gets the barcode value encoded using Pharma Code symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - - if (forCaption) - { - sb.Append(Value); - } - else - { - //parse to integer - int val; - if (!int.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out val)) - return string.Empty; - - if (Options.PharmaCodeTwoTrack) - { - //transform to 3 - sb.Append(To3(val)); - } - else - { - //add 1 - val += 1; - - //transform to binary - sb.Append(Convert.ToString(val, 2)); - - //remove first 1 - sb.Remove(0, 1); - } - - if (Options.PharmaCodeSupplementaryCode) - sb.Append('s'); - } - - return sb.ToString(); - } - - string To3(int v) - { - var sb = new StringBuilder(); - - while (v > 0) - { - var d = v % 3; - sb.Insert(0, d); - switch (d) - { - case 0: v = (v - 3) / 3; break; - case 1: v = (v - 1) / 3; break; - case 2: v = (v - 2) / 3; break; - } - } - - return sb.ToString(); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - if (Options.PharmaCodeTwoTrack) - { - switch (c) - { - case '0': - return "30"; - case '1': - return "10"; - case '2': - return "20"; - case 's': - return "s"; - } - } - else - { - switch (c) - { - case '0': - return "n0"; - case '1': - return "w0"; - case 's': - return "s"; - } - } - - return ":o]"; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - InitSizes(); - - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - string value = GetEncodedValue(false); - - bool drawBar = true; - - foreach (char ch in value) - { - string pattern = getCharPattern(ch); - foreach (char patternChar in pattern) - { - int width = 0; - int height = BarHeight; - y = 0; - switch (patternChar) - { - case 'w': width = b; break; - case 'n': width = a; break; - case '0': width = c; break; - case '1': width = a; y = BarHeight / 2; height = BarHeight - BarHeight / 2; break; - case '2': width = a; y = 0; height = BarHeight / 2; break; - case '3': width = a; break; - case 's': width = z; x += d - c; break; - } - - if (drawBar) - m_rects.Add(new SKRect(x, y, width+x, height+y)); - - x += width; - drawBar = !drawBar; - } - } - - drawingSize.Width = x; - drawingSize.Height = BarHeight; - return drawingSize; - } - - private int a; - private int b; - private int c; - private int d; - private int e; - private int z; - - private void InitSizes() - { - var dpi = BarcodeResolution.Width; - - if (Options.PharmaCodeTwoTrack) - { - a = Mm2Pixels(1.0f, dpi); - c = Mm2Pixels(1.0f, dpi); - e = Mm2Pixels(8.0f, dpi); - } - else - { - if (Options.PharmaCodeMiniature) - { - a = Mm2Pixels(0.35f, dpi); - b = a * 3; - c = Mm2Pixels(0.65f, dpi); - d = Mm2Pixels(1.00f, dpi); - e = Mm2Pixels(6.00f, dpi); - z = Mm2Pixels(1.00f, dpi); - } - else - { - a = Mm2Pixels(0.5f, dpi); - b = a * 3; - c = Mm2Pixels(1.00f, dpi); - d = Mm2Pixels(1.50f, dpi); - e = Mm2Pixels(8.00f, dpi); - z = Mm2Pixels(1.50f, dpi); - } - } - - BarHeight = e; - } - - static int Mm2Pixels(double mm, float dpi) - { - var inches = mm * 0.03937f;//mm -> inches - return (int)Math.Ceiling(dpi * inches);//inches -> pixels - } - - protected override void drawBars(SKCanvas canvas, SKPaint paint, SKPoint position) - { - for (int i = 0; i < m_rects.Count; i++) - { - var r = (SKRect)m_rects[i]; - - // Change color for last bar if PharmaCodeSupplementaryCode is enabled - if (i == m_rects.Count - 1 && Options.PharmaCodeSupplementaryCode) - paint.Color = Options.PharmaCodeSupplementaryBarColor; - else - paint.Color = paint.Color; - - // Draw filled rectangle - SKRect rect = new SKRect( - r.Left + position.X, - r.Top + position.Y, - r.Right + position.X, - r.Bottom + position.Y - ); - - canvas.DrawRect(rect, paint); - } - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/PlanetSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/PlanetSymbology.cs deleted file mode 100644 index 0c07442e..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/PlanetSymbology.cs +++ /dev/null @@ -1,124 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using System.Drawing; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using PLANET symbology. PLANET barcode is used by - /// the United States Postal Service to identify and track pieces of - /// mail during delivery - the Post Office's "CONFIRM" services. - /// - class PlanetSymbology : PostnetSymbology - { - /// - /// Initializes a new instance of the class. - /// - public PlanetSymbology() - : base() - { - m_type = TrueSymbologyType.Planet; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public PlanetSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.Planet; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "000000000000"; - } - - /// - /// Validates the value using PLANET symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 12 && value.Length != 14) - return false; - - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "The PLANET symbology allows only strings with 12 or 14 digits to be encoded."; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - int tallHeight = BarHeight; - int shortHeight = BarHeight / WideToNarrowRatio; - int gapWidth = Math.Max(NarrowBarWidth / 2, 1); - int width = NarrowBarWidth; - - m_rects.Add(new SKRect(x, y, width+x, tallHeight+y)); - - x += width; - x += gapWidth; - - string value = GetEncodedValue(false); - foreach (char c in value) - { - string pattern = getCharPattern(c); - foreach (char patternChar in pattern) - { - if (patternChar == '1') - m_rects.Add(new SKRect(x, y + tallHeight - shortHeight, width+x, shortHeight+y)); - else - m_rects.Add(new SKRect(x, y, width+x, tallHeight+y)); - - x += width; - x += gapWidth; - } - } - - m_rects.Add(new SKRect(x, y, width+x, tallHeight+y)); - x += width; - - drawingSize.Width = x; - drawingSize.Height = BarHeight; - return drawingSize; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/PlesseySymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/PlesseySymbology.cs deleted file mode 100644 index efe99bb6..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/PlesseySymbology.cs +++ /dev/null @@ -1,228 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using SkiaSharp; -using System; -using System.Drawing; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using Plessey Code symbology rules. - /// This symbology is used primarily in libraries and for retail grocery shelf marking. - /// - /// See also: - /// http://en.wikipedia.org/wiki/Plessey_Code - /// http://www.adams1.com/plessy.html - /// - class PlesseySymbology : SymbologyDrawing - { - protected static string m_alphabet = "0123456789ABCDEF"; - protected static int m_crc8poly = 0xE9; // generator polinomial g(x) = x^8 + x^7 + x^6 + x^5 + x^3 + 1 - protected static string m_forwardStartCode = "1101"; - protected static string m_reverseStartCode = "0011"; - - private CRC8 crc = new CRC8(m_crc8poly); - - /// - /// Initializes a new instance of the class. - /// - public PlesseySymbology() - : base(TrueSymbologyType.Plessey) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public PlesseySymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.Plessey) - { - } - - /// - /// Validates the value using Plessey Code symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Plessey Code symbology allows only hexadecimal number values to be encoded."; - } - - /// - /// Gets the barcode value encoded using Plessey Code symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - - if (forCaption) - { - sb.Append(Value); - } - else - { - for (int i = 0; i < Value.Length; i++) - { - sb.Append(getCharPattern(Value[i])); - } - } - - return sb.ToString(); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - switch (c) - { - case '0': - return "0000"; - case '1': - return "1000"; - case '2': - return "0100"; - case '3': - return "1100"; - case '4': - return "0010"; - case '5': - return "1010"; - case '6': - return "0110"; - case '7': - return "1110"; - case '8': - return "0001"; - case '9': - return "1001"; - case 'A': - return "0101"; - case 'B': - return "1101"; - case 'C': - return "0011"; - case 'D': - return "1011"; - case 'E': - return "0111"; - case 'F': - return "1111"; - } - - return "0000"; - } - - /// - /// Gets the checksum. - /// - /// The value. - /// The checksum. - protected string getChecksum(string value) - { - // CRC 8, Poly E9 (0x1E9) - - if (value.Length % 8 != 0) - value = "0000" + value; - int length = value.Length / 8; - - byte[] msg = new byte[length]; - for (int i = 0; i < length; i++) - { - msg[i] = Convert.ToByte(value.Substring(i * 8, 8), 2); - } - byte checksum = crc.Checksum(msg); - string checksum_bin = Convert.ToString(checksum, 2); - while (checksum_bin.Length < 8) - checksum_bin = checksum_bin.Insert(0, "0"); - - return checksum_bin; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - string label = GetEncodedValue(false); - if (label.Length % 4 != 0) - throw new BarcodeException("Incorrect value length."); - string value = m_forwardStartCode + label + getChecksum(label); - - int width0 = NarrowBarWidth; - int pitchWidth = width0 * 5; - int width1 = (int)Math.Round(pitchWidth * 0.54); - int height = BarHeight; - - // left margin - x += width0; - // Forward start code + Label + Check code - for (int i = 0; i < value.Length; i++) - { - int barWidth = width0; - if (value[i] == '1') - barWidth = width1; - m_rects.Add(new SKRect(x, y, barWidth+x, height+y)); - x += pitchWidth; - } - // Termination bar - m_rects.Add(new SKRect(x, y, pitchWidth+x, height+y)); - x += pitchWidth; - // Reverse start code - for (int i = 0; i < m_reverseStartCode.Length; i++) - { - int barWidth = width0; - if (m_reverseStartCode[i] == '1') - barWidth = width1; - x += pitchWidth; - m_rects.Add(new SKRect(x - barWidth, y, x, y+height)); - } - // right margin - x += width0; - - drawingSize.Width = x; - drawingSize.Height = height; - return drawingSize; - } - } -} - diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/PostnetSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/PostnetSymbology.cs deleted file mode 100644 index 595bd608..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/PostnetSymbology.cs +++ /dev/null @@ -1,223 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using System.Drawing; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using Postnet symbology. PostNet was developed by the - /// United States Postal Service (USPS) to allow faster sorting and - /// routing of mail. Postnet is often printed on envelopes and business - /// return mail. - /// - class PostnetSymbology : SymbologyDrawing - { - protected static string m_alphabet = "0123456789"; - - /// - /// Initializes a new instance of the class. - /// - public PostnetSymbology() - : base(TrueSymbologyType.Postnet) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public PostnetSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.Postnet) - { - } - - /// - /// Gets a value indicating whether this symbology can not have - /// a caption drawn. - /// - public override bool PreventCaptionDrawing - { - get - { - return true; - } - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "00000"; - } - - /// - /// Validates the value using Postnet symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 5 && value.Length != 9 && value.Length != 11) - return false; - - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Postnet symbology allows only strings with five, nine or eleven digits to be encoded."; - } - - /// - /// Gets the barcode value encoded using Postnet symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - // caption is never drawn - // checksum char ALWAYS added to encoded value - return Value + getChecksum(Value); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - switch (c) - { - case 's': // start|stop - return "1"; - - case '0': - return "11000"; - - case '1': - return "00011"; - - case '2': - return "00101"; - - case '3': - return "00110"; - - case '4': - return "01001"; - - case '5': - return "01010"; - - case '6': - return "01100"; - - case '7': - return "10001"; - - case '8': - return "10010"; - - case '9': - return "10100"; - } - - throw new BarcodeException("Incorrect symbol for Postnet symbology"); - } - - /// - /// Gets the checksum char. - /// - /// The value to calculate checksum for. - /// The checksum char. - private static char getChecksum(string value) - { - int total = 0; - for (int i = 0; i < value.Length; i++) - total += getCharPosition(value[i]); - - int lastDigit = total % 10; - if (lastDigit == 0) - return '0'; - - return m_alphabet[10 - lastDigit]; - } - - /// - /// Gets the char position within alphabet. - /// - /// The char. - /// The char position within alphabet. - private static int getCharPosition(char c) - { - int charPos = m_alphabet.IndexOf(c); - if (charPos == -1) - throw new BarcodeException("Incorrect char for Postnet symbology"); - - return charPos; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - int tallHeight = BarHeight; - int shortHeight = BarHeight / WideToNarrowRatio; - int gapWidth = Math.Max(NarrowBarWidth / 2, 1); - int width = NarrowBarWidth; - - string value = "s" + GetEncodedValue(false) + "s"; - foreach (char c in value) - { - string pattern = getCharPattern(c); - foreach (char patternChar in pattern) - { - if (patternChar == '0') - m_rects.Add(new SKRect(x, y + tallHeight - shortHeight, x+width, y + tallHeight)); - else - m_rects.Add(new SKRect(x, y, width+x, tallHeight+y)); - - x += width; - x += gapWidth; - } - } - - drawingSize.Width = x; - drawingSize.Height = BarHeight; - return drawingSize; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/RoyalMailSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/RoyalMailSymbology.cs deleted file mode 100644 index 0de7db06..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/RoyalMailSymbology.cs +++ /dev/null @@ -1,222 +0,0 @@ -using System; -using System.Text; -using System.Drawing; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws Royal Mail 4-State barcodes. - /// See also: - /// http://en.wikipedia.org/wiki/RM4SCC - /// http://www.morovia.com/education/symbology/royalmail.asp - /// - class RoyalMailSymbology : SymbologyDrawing - { - protected const string m_alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - protected static string[] m_royalValues = { - "11", "12", "13", "14", "15", "10", "21", "22", "23", "24", - "25", "20", "31", "32", "33", "34", "35", "30", "41", "42", - "43", "44", "45", "40", "51", "52", "53", "54", "55", "50", - "01", "02", "03", "04", "05", "00" - }; - - /* 0 = Full, 1 = Ascender, 2 = Descender, 3 = Tracker */ - protected static string[] m_royalTable = { - "3300", "3210", "3201", "2310", "2301", "2211", "3120", "3030", - "3021", "2130", "2121", "2031", "3102", "3012", "3003", "2112", - "2103", "2013", "1320", "1230", "1221", "0330", "0321", "0231", - "1302", "1212", "1203", "0312", "0303", "0213", "1122", "1032", - "1023", "0132", "0123", "0033"}; - - /// - /// Initializes a new instance of the class. - /// - public RoyalMailSymbology() - : base(TrueSymbologyType.RoyalMail) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public RoyalMailSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.RoyalMail) - { - } - - /// - /// Validates the value using current symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - string upper = value.ToUpper(); - - foreach (char c in upper) - { - if (getCharPosition(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the char position within the alphabet. - /// - /// The char to find. - /// - protected static int getCharPosition(char c) - { - return m_alphabet.IndexOf(c); - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Royal Mail 4-State symbology allows only digits and characters from A to Z to be encoded."; - } - - /// - /// Gets the barcode value encoded using current symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - - if (!forCaption) - sb.Append("("); - - string upper = Value.ToUpper(); - sb.Append(upper); - - if (AddChecksum) - { - if ((forCaption && AddChecksumToCaption) || (!forCaption)) - { - int checksumCharPosition = calculateChecksum(upper); - char checksumChar = m_alphabet[checksumCharPosition]; - sb.Append(checksumChar); - } - } - - if (!forCaption) - sb.Append(")"); - - return sb.ToString(); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// The encoding pattern for given character. - protected override string getCharPattern(char c) - { - if (c == '(') - { - // start symbol - return "1"; - } - else if (c == ')') - { - // stop symbol - return "0"; - } - - int pos = getCharPosition(c); - return m_royalTable[pos]; - } - - /// - /// Calculates the checksum of the given value. - /// - /// The value to calculate checksum for. - /// - private static int calculateChecksum(string value) - { - int top = 0; - int bottom = 0; - foreach (char c in value) - { - int pos = getCharPosition(c); - string royalValue = m_royalValues[pos]; - int checksumValue = int.Parse(royalValue); - - top += checksumValue / 10; - bottom += checksumValue % 10; - } - - // Calculate the check digit - int row = (top % 6) - 1; - int column = (bottom % 6) - 1; - - if (row == -1) - row = 5; - - if (column == -1) - column = 5; - - return (6 * row) + column; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - int height = BarHeight; - int width = NarrowBarWidth; - - string encoded = GetEncodedValue(false); - - int fullHeight = height; - int ascenderHeight = Math.Max(6 * height / 10, 1); - int descenderHeight = ascenderHeight; - int trackerHeight = Math.Max(ascenderHeight / 3, 1); - int gapWidth = Math.Max(NarrowBarWidth / 2, 1); - - foreach (char c in encoded) - { - string pattern = getCharPattern(c); - foreach (char patternChar in pattern) - { - // 0 = Full, 1 = Ascender, 2 = Descender, 3 = Tracker - if (patternChar == '0') - m_rects.Add(new SKRect(x, y, width+x, fullHeight+y)); - else if (patternChar == '1') - m_rects.Add(new SKRect(x, y, x+width, y+ascenderHeight)); - else if (patternChar == '2') - m_rects.Add(new SKRect(x, y + ascenderHeight - trackerHeight, x+width, y + ascenderHeight - trackerHeight+descenderHeight)); - else - m_rects.Add(new SKRect(x, y + ascenderHeight - trackerHeight, x + width, y + ascenderHeight)); - - x += width; - x += gapWidth; - } - } - - drawingSize.Width = x; - drawingSize.Height = BarHeight; - return drawingSize; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/SingaporePostSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/SingaporePostSymbology.cs deleted file mode 100644 index 295c0314..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/SingaporePostSymbology.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws Singapore 4-State Postal Code barcodes. - /// - class SingaporePostSymbology : RoyalMailSymbology - { - /// - /// Initializes a new instance of the class. - /// - public SingaporePostSymbology() : base() - { - m_type = TrueSymbologyType.SingaporePostalCode; - } - - public SingaporePostSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.SingaporePostalCode; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Singapore 4-State Postal Code symbology allows only digits and characters from A to Z to be encoded."; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/SwissPostParcelSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/SwissPostParcelSymbology.cs deleted file mode 100644 index f88a2ad6..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/SwissPostParcelSymbology.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws Swiss Post Parcel barcodes. - /// - class SwissPostParcelsymbology : Code128Symbology - { - /// - /// Initializes a new instance of the class. - /// - public SwissPostParcelsymbology() - : base() - { - m_type = TrueSymbologyType.SwissPostParcel; - } - - public SwissPostParcelsymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.SwissPostParcel; - } - - /// - /// Validates the value using Code 128 symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (getCleanValue(value).Length < 18) - return false; - - return base.ValueIsValid(getCleanValue(value), checksumIsMandatory); - } - - private static string getCleanValue(string value) - { - return value.Replace(".", ""); - } - - /// - /// Gets the value restrictions description string. - /// - /// The value restrictions description string. - public override string getValueRestrictions() - { - return "Swiss Post Parcel symbology allows only numeric values with exactly 18 digits: 2 digits for Swiss Post reference, 8 digits for Franking license number, and 8 digits for Item number to be encoded. Additionally, dots could be added as separators.\n"; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "00.00.000000.00000000"; - } - - /// - /// Gets the barcode value encoded using Code 128 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using Code 128 symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - StringBuilder sb = new StringBuilder(); - - if (!forCaption) - sb.Append(getStartChar()); - - sb.Append(getCleanValue(Value)); - - string encoded = sb.ToString(); - - if (forCaption) - { - encoded = encoded.Insert(2, "."); - encoded = encoded.Insert(5, "."); - encoded = encoded.Insert(12, "."); - } - - return encoded; - } - - protected override Code128Alphabet getUsedAlphabet() - { - return Code128Alphabet.C; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/TelepenAlphabet.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/TelepenAlphabet.cs deleted file mode 100644 index ebf8b29e..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/TelepenAlphabet.cs +++ /dev/null @@ -1,33 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -namespace BarcodeWriter.Core -{ - /// - /// Describes alphabet options for Telepen symbology. - /// - public enum TelepenAlphabet - { - /// - /// (0) The alphabet to use ASCII chars (encoded as string) - /// All characters are encoded as ASCII characters. - /// - ASCII = 0, - - /// - /// (1) The Numeric alphabet. Works when input value contains numbers only and makes smaller size barcodes by packing each number - /// Input string should contain numbers only. - /// - Numeric = 1, - - /// - /// (2) Mixed alphabet. Automatically selects appropriate alphabet (ASCII or Numeric) for different parts of barcode. - /// WARNING: Not supported by some hardware barcode readers manufacturers - /// - Auto = 2 - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/TelepenSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/TelepenSymbology.cs deleted file mode 100644 index 8e5e329a..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/TelepenSymbology.cs +++ /dev/null @@ -1,471 +0,0 @@ -using System; -using System.Text; -using System.Collections; -using System.Drawing; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using Telepen symbology. - /// This symbology is used in many countries and - /// very widely in the UK. Most Universities and other academic libraries use - /// Telepen, as do many public libraries. Other users include the motor - /// industry, Ministry of Defence and innumerable well-known organisations - /// for many different applications. - /// - /// http://www.codeproject.com/KB/graphics/BarcodeLibrary.aspx - class TelepenSymbology : SymbologyDrawing - { - private const int START1 = 128; - private const int STOP1 = 129; - - private const int START2 = 130; - private const int STOP2 = 131; - - private const int START3 = 132; - private const int STOP3 = 133; - - private const int MAX_CONTROL_CHAR = 31; // max control char so we allow to encode as [charid] control characters from 0 to 31 only - - private static string[] m_charPatterns = new string[] - { - "1110111011101110", "1011101110111010", "1110001110111010", - "1010111011101110", "1110101110111010", "1011100011101110", - "1000100011101110", "1010101110111010", "1110111000111010", - "1011101011101110", "1110001011101110", "1010111000111010", - "1110101011101110", "1010001000111010", "1000101000111010", - "1010101011101110", "1110111010111010", "1011101110001110", - "1110001110001110", "1010111010111010", "1110101110001110", - "1011100010111010", "1000100010111010", "1010101110001110", - "1110100010001110", "1011101010111010", "1110001010111010", - "1010100010001110", "1110101010111010", "1010001010001110", - "1000101010001110", "1010101010111010", "1110111011100010", - "1011101110101110", "1110001110101110", "1010111011100010", - "1110101110101110", "1011100011100010", "1000100011100010", - "1010101110101110", "1110111000101110", "1011101011100010", - "1110001011100010", "1010111000101110", "1110101011100010", - "1010001000101110", "1000101000101110", "1010101011100010", - "1110111010101110", "1011101000100010", "1110001000100010", - "1010111010101110", "1110101000100010", "1011100010101110", - "1000100010101110", "1010101000100010", "1110100010100010", - "1011101010101110", "1110001010101110", "1010100010100010", - "1110101010101110", "1010001010100010", "1000101010100010", - "1010101010101110", "1110111011101010", "1011101110111000", - "1110001110111000", "1010111011101010", "1110101110111000", - "1011100011101010", "1000100011101010", "1010101110111000", - "1110111000111000", "1011101011101010", "1110001011101010", - "1010111000111000", "1110101011101010", "1010001000111000", - "1000101000111000", "1010101011101010", "1110111010111000", - "1011101110001010", "1110001110001010", "1010111010111000", - "1110101110001010", "1011100010111000", "1000100010111000", - "1010101110001010", "1110100010001010", "1011101010111000", - "1110001010111000", "1010100010001010", "1110101010111000", - "1010001010001010", "1000101010001010", "1010101010111000", - "1110111010001000", "1011101110101010", "1110001110101010", - "1010111010001000", "1110101110101010", "1011100010001000", - "1000100010001000", "1010101110101010", "1110111000101010", - "1011101010001000", "1110001010001000", "1010111000101010", - "1110101010001000", "1010001000101010", "1000101000101010", - "1010101010001000", "1110111010101010", "1011101000101000", - "1110001000101000", "1010111010101010", "1110101000101000", - "1011100010101010", "1000100010101010", "1010101000101000", - "1110100010101000", "1011101010101010", "1110001010101010", - "1010100010101000", "1110101010101010", "1010001010101000", - "1000101010101000", "1010101010101010", // 127 - "1010101010111000", // START1 - "1110001010101010", // STOP1 - "1010101011101000", // START2 - "1110100010101010", // STOP2 - "1010101110101000", // START3 - "1110101000101010" // STOP3 - }; - - private int m_startCode; - private int m_stopCode; - private int m_switchModeCharPos; - private bool m_isOnlyNumeric = false; - private string m_trailingControlChars = ""; // holds string with trailing control characters if any - - /// - /// Initializes a new instance of the class. - /// - public TelepenSymbology() - : base(TrueSymbologyType.Telepen) - { - } - - protected virtual TelepenAlphabet getUsedAlphabet() - { - return Options.TelepenAlphabet; - } - - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public TelepenSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.Telepen) - { - } - - /// - /// Validates the value using Telepen symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - foreach (char c in value) - { - byte b = (byte)c; - if (b > 127) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Telepen symbology allows only ASCII chars to be encoded."; - } - - /// - /// Gets the barcode value encoded using Telepen symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - if (forCaption) - return Value; - - return encodeValue(); - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// - /// The encoding pattern for given character. - /// - protected override string getCharPattern(char c) - { - if (c >= 0 && c <= 127) - return m_charPatterns[(int)c]; - - throw new BarcodeException("Incorrect symbol for Telepen symbology"); - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - string value = GetEncodedValue(false); - foreach (char c in value) - { - if (c == '1') - m_rects.Add(new SKRect(x, y, x+NarrowBarWidth, y+BarHeight)); - - x += NarrowBarWidth; - } - - drawingSize.Width = x; - drawingSize.Height = BarHeight; - return drawingSize; - } - - private string encodeValue() - { - string bcValue = ""; - - bcValue = Value; - // replace all [9] and similar to control character codes - //bcValue = preProcessControlCharacters(Value); - // support for codes in square brackets has been disabled as we can pass control characters inside string if needed - - // cut out leading control character codes if any - bcValue = cutTrailingControlCharacters(bcValue); - - // analyze the value and determine how we should encode the value and which modes should use - planEncodingSequence(bcValue); - - StringBuilder sb = new StringBuilder(); - //sb.Append(m_charPatterns[m_startCode]); - sb.Append(m_charPatterns[START1]); - - int checkSum = 0; - - // encode trailing control characters if any - if (m_trailingControlChars.Length > 0) - { - encodeASCII(m_trailingControlChars, sb, ref checkSum); - } - - - switch (m_startCode) - { - case START2: - // numeric --> ascii - - // adding DLE character to indicate we start numeric mode - if (!m_isOnlyNumeric) - { - // we insert switch only if we have numbers and ascii mixed - // encodeSwitchMode(sb, ref checkSum); - } - - encodeNumeric(bcValue.Substring(0, m_switchModeCharPos), sb, ref checkSum); - - // if we have ASCII remaining then add switch character and add and encode ascii data - if (m_switchModeCharPos < bcValue.Length) - { - encodeSwitchMode(sb, ref checkSum); - encodeASCII(bcValue.Substring(m_switchModeCharPos), sb, ref checkSum); - } - break; - - case START3: - //ascii --> numeric - - encodeASCII(bcValue.Substring(0, m_switchModeCharPos), sb, ref checkSum); - // insert DLE to indicate we switch to numeric value - encodeSwitchMode(sb, ref checkSum); - encodeNumeric(bcValue.Substring(m_switchModeCharPos), sb, ref checkSum); - break; - - default: - //full ascii - encodeASCII(bcValue, sb, ref checkSum); - break; - } - - checkSum = 127 - (checkSum % 127); - sb.Append(m_charPatterns[checkSum]); - - m_stopCode = STOP1; - sb.Append(m_charPatterns[m_stopCode]); - - return sb.ToString(); - } - - private void planEncodingSequence(string Value) - { - // use full ASCII by default - m_startCode = START1; - m_stopCode = STOP1; - - m_switchModeCharPos = Value.Length; - - // we assume we have auto mode, we will check to know if we have numeric only mode or ascii or mixed (auto) - m_isOnlyNumeric = false; - - if (Options.TelepenAlphabet == TelepenAlphabet.ASCII) { - // do nothing as full ASCII is set by default - } - else if (Options.TelepenAlphabet == TelepenAlphabet.Numeric){ - // Numeric only mode due to only numbers being present - m_startCode = START2; - m_stopCode = STOP2; - - // If the data consists of an odd number of digits - // and it is not acceptable to add a leading zero, insert a DLE character before the last digit and - // encode that as a normal ASCII character. - if ((Value.Length % 2) > 0) - m_switchModeCharPos = Value.Length - 1; - - m_isOnlyNumeric = CheckIfNumericValuesOnly(Value); - - if (Value.Length> 0 && !m_isOnlyNumeric) - { - throw new BarcodeException("You can use numeric values (0..9) while using TelepenAlphabet.Numeric alphabet for Telepen symbology"); - } - - - } - else if (Options.TelepenAlphabet == TelepenAlphabet.Auto) - { - - int leadingNumerics = 0; - foreach (char c in Value) - { - if (!Char.IsNumber(c)) - break; - - leadingNumerics++; - } - - if (leadingNumerics == Value.Length) - { - m_isOnlyNumeric = true; // we have numbers only - - // Numeric only mode due to only numbers being present - m_startCode = START2; - m_stopCode = STOP2; - - // If the data consists of an odd number of digits - // and it is not acceptable to add a leading zero, insert a DLE character before the last digit and - // encode that as a normal ASCII character. - if ((Value.Length % 2) > 0) - m_switchModeCharPos = Value.Length - 1; - } - else - { - // there not only numbers in value - - int trailingNumerics = 0; - for (int i = Value.Length - 1; i >= 0; i--) - { - if (!Char.IsNumber(Value[i])) - break; - - trailingNumerics++; - } - - if ( - (leadingNumerics >= 4 || trailingNumerics >= 4) - ) - { - // hybrid mode will be used - if (leadingNumerics > trailingNumerics) - { - // start in numeric switching to ascii - m_startCode = START2; - m_stopCode = STOP2; - - if ((leadingNumerics % 2) == 1) - m_switchModeCharPos = leadingNumerics - 1; - else - m_switchModeCharPos = leadingNumerics; - } - else - { - //start in ascii switching to numeric - m_startCode = START3; - m_stopCode = STOP3; - - if ((trailingNumerics % 2) == 1) - m_switchModeCharPos = Value.Length - trailingNumerics + 1; - else - m_switchModeCharPos = Value.Length - trailingNumerics; - } - } - } - - } - } - /// - /// Checks the whole value if it contains only numeric values (0 to 9) - /// - /// - /// - private bool CheckIfNumericValuesOnly(string Value) - { - int count = 0; - foreach (char c in Value) - { - if (!Char.IsNumber(c)) - return false; - - count++; - } - - return count > 0; - } - - /// - /// Cuts leading control characters into separate string m_trailingControlChars - /// - /// input string - /// returns number of leading control characters (leading only!!) - private string cutTrailingControlCharacters(string input) { - int count = 0; - m_trailingControlChars = ""; - StringBuilder sb = new StringBuilder(); - foreach (char c in input) - { - if (c > 31) - break; - - sb.Append(c); - count++; - } - m_trailingControlChars = sb.ToString(); - if (count > 0) - return input.Substring(count, input.Length - count); - else - return input; - } - - private void encodeNumeric(string input, StringBuilder output, ref int checkSum) - { - if (input.Length % 2 > 0) - throw new InvalidOperationException("Numeric encoding attempted on odd number of characters"); - - // now encode numeric values by pairs (and shifted with +27 - // so we get double density as we encode "10" number as 37 ASCII character - for (int j = 0; j < input.Length; j += 2) - { - string cS = input.Substring(j, 2); - int c = Int32.Parse(cS) + 27; - output.Append(m_charPatterns[c]); - checkSum += c; - } - } - - - /// - /// Replaces control characters given inside square brackets - /// i.e. to insert control character 10 we can use [10] - /// - /// - /// - private string preProcessControlCharacters(string input) { - if (input.IndexOf('[') == -1 || input.IndexOf(']') == -1) - return input; // we don't have both [ and [ so we exit - - for (int i = 0; i <= 31; i++) { - string pattern = String.Format("[{0}]", i); - input = input.Replace(pattern, String.Format("{0}", (char)i)); - } - - - - return input; - } - - private void encodeSwitchMode(StringBuilder output, ref int checkSum) - { - // ASCII code DLE (16) is used to switch modes - checkSum += 16; - output.Append(m_charPatterns[16]); - } - - private void encodeASCII(string input, StringBuilder output, ref int checkSum) - { - foreach (char c in input) - { - int i = (int)c; - output.Append(m_charPatterns[i]); - checkSum += i; - } - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/UPCASymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/UPCASymbology.cs deleted file mode 100644 index ffae5b8a..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/UPCASymbology.cs +++ /dev/null @@ -1,160 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using SkiaSharp; -using System; -using System.Drawing; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using UPC-A symbology rules. UPC stands for Universal Product - /// Code. This code is typically used to record point of sale transactions - /// for consumer goods throughout the grocery industry. - /// - class UPCASymbology : EAN13Symbology - { - /// - /// Initializes a new instance of the class. - /// - public UPCASymbology() - : base() - { - m_type = TrueSymbologyType.UPCA; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public UPCASymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.UPCA; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "00000000000"; - } - - /// - /// Validates the value using UPC-A symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Checksum is mandatory or not (if applicable). - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 11 && value.Length != 12) - return false; - - if (value.Length == 11 && checksumIsMandatory) - return false; - - if (value.Length == 12) - { - // user wants to enter value with check digit - // we need to verify that digit - char c = getChecksum("0" + value.Substring(0, 11)); - if (value[11] != c) - return false; - } - - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "UPC-A symbology expects strings with 11 digits to be encoded. Optionally, user may enter 12th digit. In the latter case the last digit (check digit) will be verified."; - } - - /// - /// Gets the barcode value encoded using UPC-A symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - // checksum char ALWAYS added to encoded value and caption - string s = Value.Substring(0, 11); - char checksumChar = getChecksum("0" + s); - - if (forCaption) - return s + checksumChar; - - // encoded value differs from printed by leading zero - return "0" + s + checksumChar; - } - - protected override int occupiedByCaptionAfter(SKCanvas canvas, SKFont font) - { - if (DrawCaption) - { - if (CaptionPosition == CaptionPosition.Below) - { - string lastChar = Caption.Substring(Caption.Length - 1, 1); - float width = font.MeasureText(lastChar); - return (int)Math.Ceiling(width); - } - - if (CaptionPosition == CaptionPosition.After) - { - return base.occupiedByCaptionAfter(canvas, font); - } - } - - return 0; - } - - protected override string getCaptionLeftPart() - { - if (Caption.Length >= 1) - return Caption.Substring(1, Math.Min(Caption.Length - 1, 5)); - - return string.Empty; - } - - protected override string getCaptionRightPart() - { - if (Caption.Length >= 6) - return Caption.Substring(6, Math.Min(Caption.Length - 7, 5)); - - return string.Empty; - } - - protected override string getCaptionAfterPart() - { - if (Caption.Length >= 0) - return Caption.Substring(Caption.Length - 1, 1); - - return string.Empty; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/UPCESymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/UPCESymbology.cs deleted file mode 100644 index e2266793..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/UPCESymbology.cs +++ /dev/null @@ -1,269 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using System.Drawing; - -namespace BarcodeWriter.Core.Internal -{ - class UPCESymbology : UPCASymbology - { - /// - /// Initializes a new instance of the class. - /// - public UPCESymbology() - : base() - { - m_type = TrueSymbologyType.UPCE; - m_rightGuardPattern = "1"; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public UPCESymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.UPCE; - m_rightGuardPattern = "1"; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "0000000"; - } - - /// - /// Validates the value using UPC-E symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Checksum is mandatory or not (if applicable). - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 7 && value.Length != 8) - return false; - - if (value.Length == 7 && checksumIsMandatory) - return false; - - if (value.Length == 8) - { - // user wants to enter value with check digit - // we need to verify that digit - char c = getChecksum(value.Substring(0, 7)); - if (value[7] != c) - return false; - } - - if (value[0] != '0' && value[0] != '1') - return false; - - foreach (char c in value) - { - if (m_alphabet.IndexOf(c) == -1) - return false; - } - - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "UPC-E symbology expects strings with 7 digits to be encoded. First digit must be 0 or 1. Optionally, user may enter 8th digit. In the latter case the last digit (check digit) will be verified."; - } - - /// - /// Gets the barcode value encoded using UPC-E symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - // checksum char ALWAYS added to caption and NOT added to encoded value - string s = Value.Substring(0, 7); - - if (forCaption) - { - char checksumChar = getChecksum(s); - return s + checksumChar; - } - - return s; - } - - /// - /// Draws right-hand part of the bars. - /// - /// The start X position. - /// The start Y position. - /// - /// The new X position (start X position + the width of the rectangle - /// occupied by right-hand part of barcode bars and gaps). - /// - protected override int drawRightHandPart(int x, int y) - { - // UPC-E symbology does not contain right-hand part. - return x; - } - - /// - /// Gets the checksum char. - /// - /// The value to calculate checksum for. - /// The checksum char. - protected override char getChecksum(string value) - { - string upcaValue = toUPCA(value.Substring(1)); - return base.getChecksum("0" + value[0] + upcaValue); - } - - /// - /// Converts UPC-E value to the UPC-A equivalent. - /// - /// The UPC-E value. - /// The UPC-A equivalent. - private static string toUPCA(string value) - { - char lastChar = value[value.Length - 1]; - switch (lastChar) - { - case '0': - case '1': - case '2': - // if UPC-E code ends in 0, 1, or 2: The UPC-A code is - // determined by taking the first two digits of the - // UPC-E code, taking the last digit of the UPC-E code, - // adding four 0 digits, and then adding characters 3 - // through 5 from the UPC-E code. - return value.Substring(0, 2) + lastChar + "0000" + value.Substring(2, 3); - - case '3': - // if UPC-E code ends in 3: The UPC-A code is determined - // by taking the first three digits of the UPC-E code, - // adding five 0 digits, then adding characters 4 and 5 - // from the UPC-E code. - return value.Substring(0, 3) + "00000" + value.Substring(3, 2); - - case '4': - // if UPC-E code ends in 4: The UPC-A code is determined - // by taking the first four digits of the UPC-E code, - // adding five 0 digits, then adding the fifth character - // from the UPC-E code. - return value.Substring(0, 4) + "00000" + value.Substring(4, 1); - - default: - // if UPC-E code ends in 5, 6, 7, 8, or 9: The UPC-A - // code is determined by taking the first five digits of - // the UPC-E code, adding four 0 digits, then adding the - // last character from the UPC-E code. - return value.Substring(0, 5) + "0000" + lastChar; - } - } - - /// - /// Gets the parity string. - /// - /// The first number. - /// The parity string. - protected override string getParityString(char firstNumber) - { - string encoded = GetEncodedValue(true); - char checkSumChar = encoded[encoded.Length - 1]; - string pattern = null; - switch (checkSumChar) - { - default: - case '0': - pattern = "eeeooo"; - break; - - case '1': - pattern = "eeoeoo"; - break; - - case '2': - pattern = "eeooeo"; - break; - - case '3': - pattern = "eeoooe"; - break; - - case '4': - pattern = "eoeeoo"; - break; - - case '5': - pattern = "eooeeo"; - break; - - case '6': - pattern = "eoooee"; - break; - - case '7': - pattern = "eoeoeo"; - break; - - case '8': - pattern = "eoeooe"; - break; - - case '9': - pattern = "eooeoe"; - break; - } - - if (firstNumber == '1') - { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < pattern.Length; i++) - { - if (pattern[i] == 'e') - sb.Append('o'); - else - sb.Append('e'); - } - - pattern = sb.ToString(); - } - - return pattern; - } - - protected override string getCaptionLeftPart() - { - if (Caption.Length >= 1) - return Caption.Substring(1, Math.Min(Caption.Length - 1, 6)); - - return string.Empty; - } - - protected override string getCaptionRightPart() - { - return string.Empty; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/USPSSackLabelSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/USPSSackLabelSymbology.cs deleted file mode 100644 index 05908aef..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/USPSSackLabelSymbology.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws USPS Sack Label barcodes. - /// - class USPSSackLabelSymbology : I2of5Symbology - { - /// - /// Initializes a new instance of the class. - /// - public USPSSackLabelSymbology() : base() - { - m_type = TrueSymbologyType.USPSSackLabel; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public USPSSackLabelSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.USPSSackLabel; - } - - /// - /// Validates the value using USPS Sack Label symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Checksum is mandatory or not (if applicable). - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 8) - return false; - - return base.ValueIsValid(value, checksumIsMandatory); - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "USPS Sack Label symbology allows only numeric values with exactly 8 digits: 5-digit Zip Code (the sack destination) and a 3-digit content identifier number(CIN) to be encoded.\n"; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "00000000"; - } - - /// - /// Gets the barcode value encoded using USPS Sack Label symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - return Value; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/1D/USPSTrayLabelSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/1D/USPSTrayLabelSymbology.cs deleted file mode 100644 index 7adb1219..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/1D/USPSTrayLabelSymbology.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws USPS Tray Label barcodes. - /// - class USPSTrayLabelSymbology : I2of5Symbology - { - /// - /// Initializes a new instance of the class. - /// - public USPSTrayLabelSymbology() : base() - { - m_type = TrueSymbologyType.USPSTrayLabel; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public USPSTrayLabelSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.USPSTrayLabel; - } - - /// - /// Validates the value using USPS Tray Label symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Checksum is mandatory or not (if applicable). - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (value.Length != 10) - return false; - - return base.ValueIsValid(value, checksumIsMandatory); - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "USPS Tray Label symbology allows only numeric values with exactly 10 digits: 5-digit Zip Code (the sack destination), a 3-digit content identifier number(CIN), and a 2-digit USPS processing code to be encoded.\n"; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "0000000000"; - } - - /// - /// Gets the barcode value encoded using USPS Sack Label symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using current symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - return Value; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/AztecSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/AztecSymbology.cs deleted file mode 100644 index ea9d0eb1..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/AztecSymbology.cs +++ /dev/null @@ -1,2054 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using System.Drawing; -using System.Collections; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using Aztec Code symbology. An Aztec code barcode is - /// used by Deutsche Bahn, Trenitalia and by Swiss Federal Railways for - /// tickets sold online and printed out by customers. The Aztec Code - /// has been selected by the airline industry (IATA's BCBP standard) - /// for the electronic boarding passes. - /// - class AztecSymbology : SymbologyDrawing2D - { - private int m_symbolHeight; - private int m_symbolWidth; - private byte[][] m_encodedSymbolData; - - /// - /// Initializes a new instance of the class. - /// - public AztecSymbology() - : base(TrueSymbologyType.Aztec) - { - } - - public AztecSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.Aztec) - { - } - - /// - /// Validates the value using Aztec Code symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Aztec Code symbology allows a maximum data size of 3832 numeric or 3067 alphabetic characters or 1914 bytes.\n"; - } - - /// - /// Gets the barcode value encoded using Aztec Code symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using Aztec Code symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - if (forCaption) - return Value; - - return ""; - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// The encoding pattern for given character. - protected override string getCharPattern(char c) - { - return ""; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - - encodeString(Value); - if (m_encodedSymbolData == null) - return drawingSize; - - int cellWidth = NarrowBarWidth; - drawingSize.Width = m_symbolWidth * cellWidth; - drawingSize.Height = m_symbolHeight * cellWidth; - - for (int y = 0; y < m_symbolHeight; y++) - { - for (int x = 0; x < m_symbolWidth; x++) - { - if (m_encodedSymbolData[y][x] == '1') - m_rects.Add(new SKRect(x * cellWidth, y * cellWidth, x * cellWidth+cellWidth, y * cellWidth+cellWidth)); - } - } - - return drawingSize; - } - - private static void removeValueAfter(int pos, intList charmap, intList typemap) - { - charmap.RemoveAt(pos + 1); - typemap.RemoveAt(pos + 1); - } - - private static void appendBits(byteList dest, string bits) - { - for (int i = 0; i < bits.Length; i++) - dest.Add((byte)bits[i]); - } - - private static void conditionalAppend(bool condition, byteList binaryString) - { - if (condition) - appendBits(binaryString, "1"); - else - appendBits(binaryString, "0"); - } - - private byteList convertToBinary(byte[] source) - { - if (source.Length <= 2) - { - if (source.Length == 0) - source = new byte[] { 0, 0 }; - else - source = new byte[] { source[0], 0 }; - } - - intList charmap = new intList(); - intList typemap = new intList(); - fillMappings(source, charmap, typemap); - - tryDoubleCharacterEncoding(charmap, typemap); - - int[][] blockmap = new int[][] { new int[source.Length], new int[source.Length] }; - buildBlockMap(ref blockmap, typemap, charmap); - - // we don't want to shift an initial capital letter - if (typemap[0] == 65) - typemap[0] = 1; - - resolveProblemChars(charmap, typemap); - - byteList binaryString = buildBinaryString(charmap, typemap); - if (binaryString.Count > 14970) - throw new BarcodeException("Input value too long or too many extended ASCII characters"); - - return binaryString; - } - - private static byteList buildBinaryString(intList charmap, intList typemap) - { - byteList binaryString = new byteList(); - int currentTable = UPPER; // start with UPPER table - int nextTable = currentTable; - int lastTable = currentTable; - - for (int i = 0; i < charmap.Count; i++) - { - nextTable = currentTable; - if (typemap[i] != currentTable) - { - // Change table - if (currentTable == BINARY) - { - // If ending binary mode the current table is the same as when entering binary mode - currentTable = lastTable; - nextTable = lastTable; - } - - if (typemap[i] > 64) - nextTable = dealWithShiftCharacter(typemap[i], currentTable, binaryString); - else - nextTable = dealWithLatchCharacter(typemap, i, currentTable, binaryString, ref lastTable); - } - - // Add data to the binary string - currentTable = nextTable; - - int chartype = typemap[i]; - if (chartype > 64) - chartype -= 64; - - switch (chartype) - { - case UPPER: - case LOWER: - case MIXED: - case PUNC: - appendBits(binaryString, c_hexBit[charmap[i]]); - break; - - case DIGIT: - appendBits(binaryString, c_pentBit[charmap[i]]); - break; - - case BINARY: - conditionalAppend((charmap[i] & 0x80) != 0, binaryString); - conditionalAppend((charmap[i] & 0x40) != 0, binaryString); - conditionalAppend((charmap[i] & 0x20) != 0, binaryString); - conditionalAppend((charmap[i] & 0x10) != 0, binaryString); - conditionalAppend((charmap[i] & 0x08) != 0, binaryString); - conditionalAppend((charmap[i] & 0x04) != 0, binaryString); - conditionalAppend((charmap[i] & 0x02) != 0, binaryString); - conditionalAppend((charmap[i] & 0x01) != 0, binaryString); - break; - } - } - - return binaryString; - } - - private static int dealWithLatchCharacter(intList typemap, int typePos, int currentTable, byteList binaryString, ref int lastTable) - { - int nextTable = currentTable; - - switch (typemap[typePos]) - { - case UPPER: // To UPPER - switch (currentTable) - { - case LOWER: // ML UL - appendBits(binaryString, c_hexBit[29]); - appendBits(binaryString, c_hexBit[29]); - nextTable = UPPER; - break; - case MIXED: // UL - appendBits(binaryString, c_hexBit[29]); - nextTable = UPPER; - break; - case PUNC: // UL - appendBits(binaryString, c_hexBit[31]); - nextTable = UPPER; - break; - case DIGIT: // UL - appendBits(binaryString, c_pentBit[14]); - nextTable = UPPER; - break; - } - break; - case LOWER: // To LOWER - switch (currentTable) - { - case UPPER: // LL - appendBits(binaryString, c_hexBit[28]); - nextTable = LOWER; - break; - case MIXED: // LL - appendBits(binaryString, c_hexBit[28]); - nextTable = LOWER; - break; - case PUNC: // UL LL - appendBits(binaryString, c_hexBit[31]); - appendBits(binaryString, c_hexBit[28]); - nextTable = LOWER; - break; - case DIGIT: // UL LL - appendBits(binaryString, c_pentBit[14]); - appendBits(binaryString, c_hexBit[28]); - nextTable = LOWER; - break; - } - break; - case MIXED: // To MIXED - switch (currentTable) - { - case UPPER: // ML - appendBits(binaryString, c_hexBit[29]); - nextTable = MIXED; - break; - case LOWER: // ML - appendBits(binaryString, c_hexBit[29]); - nextTable = MIXED; - break; - case PUNC: // UL ML - appendBits(binaryString, c_hexBit[31]); - appendBits(binaryString, c_hexBit[29]); - nextTable = MIXED; - break; - case DIGIT: // UL ML - appendBits(binaryString, c_pentBit[14]); - appendBits(binaryString, c_hexBit[29]); - nextTable = MIXED; - break; - } - break; - case PUNC: // To PUNC - switch (currentTable) - { - case UPPER: // ML PL - appendBits(binaryString, c_hexBit[29]); - appendBits(binaryString, c_hexBit[30]); - nextTable = PUNC; - break; - case LOWER: // ML PL - appendBits(binaryString, c_hexBit[29]); - appendBits(binaryString, c_hexBit[30]); - nextTable = PUNC; - break; - case MIXED: // PL - appendBits(binaryString, c_hexBit[30]); - nextTable = PUNC; - break; - case DIGIT: // UL ML PL - appendBits(binaryString, c_pentBit[14]); - appendBits(binaryString, c_hexBit[29]); - appendBits(binaryString, c_hexBit[30]); - nextTable = PUNC; - break; - } - break; - case DIGIT: // To DIGIT - switch (currentTable) - { - case UPPER: // DL - appendBits(binaryString, c_hexBit[30]); - nextTable = DIGIT; - break; - case LOWER: // DL - appendBits(binaryString, c_hexBit[30]); - nextTable = DIGIT; - break; - case MIXED: // UL DL - appendBits(binaryString, c_hexBit[29]); - appendBits(binaryString, c_hexBit[30]); - nextTable = DIGIT; - break; - case PUNC: // UL DL - appendBits(binaryString, c_hexBit[31]); - appendBits(binaryString, c_hexBit[30]); - nextTable = DIGIT; - break; - } - break; - case BINARY: // To BINARY - lastTable = currentTable; - switch (currentTable) - { - case UPPER: // BS - appendBits(binaryString, c_hexBit[31]); - nextTable = BINARY; - break; - case LOWER: // BS - appendBits(binaryString, c_hexBit[31]); - nextTable = BINARY; - break; - case MIXED: // BS - appendBits(binaryString, c_hexBit[31]); - nextTable = BINARY; - break; - case PUNC: // UL BS - appendBits(binaryString, c_hexBit[31]); - appendBits(binaryString, c_hexBit[31]); - nextTable = BINARY; - break; - case DIGIT: // UL BS - appendBits(binaryString, c_pentBit[14]); - appendBits(binaryString, c_hexBit[31]); - nextTable = BINARY; - break; - } - - int bytes = 0; - do - { - bytes++; - if ((typePos + bytes - 1) == typemap.Count - 1) - break; - - } while (typemap[typePos + (bytes - 1)] == BINARY); - - if (bytes > 2079) - throw new BarcodeException("Input value too long or too many extended ASCII characters"); - - if (bytes > 31) - { - // Put 00000 followed by 11-bit number of bytes less 31 - int adjusted = bytes - 31; - appendBits(binaryString, "00000"); - - conditionalAppend((adjusted & 0x400) != 0, binaryString); - conditionalAppend((adjusted & 0x200) != 0, binaryString); - conditionalAppend((adjusted & 0x100) != 0, binaryString); - conditionalAppend((adjusted & 0x80) != 0, binaryString); - conditionalAppend((adjusted & 0x40) != 0, binaryString); - conditionalAppend((adjusted & 0x20) != 0, binaryString); - conditionalAppend((adjusted & 0x10) != 0, binaryString); - conditionalAppend((adjusted & 0x08) != 0, binaryString); - conditionalAppend((adjusted & 0x04) != 0, binaryString); - conditionalAppend((adjusted & 0x02) != 0, binaryString); - conditionalAppend((adjusted & 0x01) != 0, binaryString); - } - else - { - // Put 5-bit number of bytes - conditionalAppend((bytes & 0x10) != 0, binaryString); - conditionalAppend((bytes & 0x08) != 0, binaryString); - conditionalAppend((bytes & 0x04) != 0, binaryString); - conditionalAppend((bytes & 0x02) != 0, binaryString); - conditionalAppend((bytes & 0x01) != 0, binaryString); - } - - break; - } - - return nextTable; - } - - private static int dealWithShiftCharacter(int character, int currentTable, byteList binaryString) - { - int nextTable = currentTable; - switch (character) - { - case (64 + UPPER): // To UPPER - switch (currentTable) - { - case LOWER: // US - appendBits(binaryString, c_hexBit[28]); - break; - case MIXED: // UL - appendBits(binaryString, c_hexBit[29]); - nextTable = UPPER; - break; - case PUNC: // UL - appendBits(binaryString, c_hexBit[31]); - nextTable = UPPER; - break; - case DIGIT: // US - appendBits(binaryString, c_pentBit[15]); - break; - } - break; - - case (64 + LOWER): // To LOWER - switch (currentTable) - { - case UPPER: // LL - appendBits(binaryString, c_hexBit[28]); - nextTable = LOWER; - break; - case MIXED: // LL - appendBits(binaryString, c_hexBit[28]); - nextTable = LOWER; - break; - case PUNC: // UL LL - appendBits(binaryString, c_hexBit[31]); - appendBits(binaryString, c_hexBit[28]); - nextTable = LOWER; - break; - case DIGIT: // UL LL - appendBits(binaryString, c_pentBit[14]); - appendBits(binaryString, c_hexBit[28]); - nextTable = LOWER; - break; - } - break; - - case (64 + MIXED): // To MIXED - switch (currentTable) - { - case UPPER: // ML - appendBits(binaryString, c_hexBit[29]); - nextTable = MIXED; - break; - case LOWER: // ML - appendBits(binaryString, c_hexBit[29]); - nextTable = MIXED; - break; - case PUNC: // UL ML - appendBits(binaryString, c_hexBit[31]); - appendBits(binaryString, c_hexBit[29]); - nextTable = MIXED; - break; - case DIGIT: // UL ML - appendBits(binaryString, c_pentBit[14]); - appendBits(binaryString, c_hexBit[29]); - nextTable = MIXED; - break; - } - break; - - case (64 + PUNC): // To PUNC - switch (currentTable) - { - case UPPER: // PS - appendBits(binaryString, c_hexBit[0]); - break; - case LOWER: // PS - appendBits(binaryString, c_hexBit[0]); - break; - case MIXED: // PS - appendBits(binaryString, c_hexBit[0]); - break; - case DIGIT: // PS - appendBits(binaryString, c_pentBit[0]); - break; - } - break; - - case (64 + DIGIT): // To DIGIT - switch (currentTable) - { - case UPPER: // DL - appendBits(binaryString, c_hexBit[30]); - nextTable = DIGIT; - break; - case LOWER: // DL - appendBits(binaryString, c_hexBit[30]); - nextTable = DIGIT; - break; - case MIXED: // UL DL - appendBits(binaryString, c_hexBit[29]); - appendBits(binaryString, c_hexBit[30]); - nextTable = DIGIT; - break; - case PUNC: // UL DL - appendBits(binaryString, c_hexBit[31]); - appendBits(binaryString, c_hexBit[30]); - nextTable = DIGIT; - break; - } - break; - } - - return nextTable; - } - - private static int buildBlockMap(ref int[][] blockmap, intList typemap, intList charmap) - { - int blocks = 0; - blockmap[0][0] = typemap[0]; - blockmap[1][0] = 1; - - for (int i = 1; i < charmap.Count; i++) - { - if (typemap[i] == typemap[i - 1]) - blockmap[1][blocks]++; - else - { - blocks++; - blockmap[0][blocks] = typemap[i]; - blockmap[1][blocks] = 1; - } - } - - if ((blockmap[0][0] & 1) != 0) - blockmap[0][0] = 1; - - if ((blockmap[0][0] & 2) != 0) - blockmap[0][0] = 2; - - if ((blockmap[0][0] & 4) != 0) - blockmap[0][0] = 4; - - if ((blockmap[0][0] & 8) != 0) - blockmap[0][0] = 8; - - if (blocks > 1) - { - // look for adjacent blocks which can use the same table (left to right search) - for (int i = 1; i < blocks; i++) - { - if ((blockmap[0][i] & blockmap[0][i - 1]) != 0) - blockmap[0][i] = (blockmap[0][i] & blockmap[0][i - 1]); - } - - if ((blockmap[0][blocks - 1] & 1) != 0) - blockmap[0][blocks - 1] = 1; - - if ((blockmap[0][blocks - 1] & 2) != 0) - blockmap[0][blocks - 1] = 2; - - if ((blockmap[0][blocks - 1] & 4) != 0) - blockmap[0][blocks - 1] = 4; - - if ((blockmap[0][blocks - 1] & 8) != 0) - blockmap[0][blocks - 1] = 8; - - // look for adjacent blocks which can use the same table (right to left search) - for (int i = blocks - 1; i > 0; i--) - { - if ((blockmap[0][i] & blockmap[0][i + 1]) != 0) - blockmap[0][i] = (blockmap[0][i] & blockmap[0][i + 1]); - } - - // determine the encoding table for characters which do not fit with adjacent blocks - for (int i = 1; i < blocks; i++) - { - if ((blockmap[0][i] & 8) != 0) - blockmap[0][i] = 8; - - if ((blockmap[0][i] & 4) != 0) - blockmap[0][i] = 4; - - if ((blockmap[0][i] & 2) != 0) - blockmap[0][i] = 2; - - if ((blockmap[0][i] & 1) != 0) - blockmap[0][i] = 1; - } - - tryToCombineBlocks(ref blockmap, ref blocks); - } - - // Put the adjusted block data back into typemap - int addition = 0; - for (int i = 0; i <= blocks; i++) - { - if ((blockmap[1][i] < 3) && (blockmap[0][i] != 32)) - { - // Shift character(s) needed - for (int k = 0; k < blockmap[1][i]; k++) - typemap[addition + k] = blockmap[0][i] + 64; - } - else - { - // Latch character (or byte mode) needed - for (int k = 0; k < blockmap[1][i]; k++) - typemap[addition + k] = blockmap[0][i]; - } - - addition += blockmap[1][i]; - } - - return blocks; - } - - /// - /// Tries to combine blocks of the same type. - /// - /// The blockmap. - /// The blocks. - private static void tryToCombineBlocks(ref int[][] blockmap, ref int blocks) - { - int i = 0; - do - { - if (blockmap[0][i] == blockmap[0][i + 1]) - { - blockmap[1][i] += blockmap[1][i + 1]; - for (int j = i + 1; j < blocks; j++) - { - blockmap[0][j] = blockmap[0][j + 1]; - blockmap[1][j] = blockmap[1][j + 1]; - } - blocks--; - } - else - i++; - - } while (i < blocks); - } - - private static void resolveProblemChars(intList charmap, intList typemap) - { - // try to resolve problem characters (those that appear in - // different tables with different values) - - for (int i = 0; i < charmap.Count; i++) - { - if (charmap[i] >= 300) - { - int table = typemap[i]; - if (table > 64) - table -= 64; - - switch (charmap[i]) - { - case 300: - // Carriage Return - switch (table) - { - case PUNC: - charmap[i] = 1; - break; - - case MIXED: - charmap[i] = 14; - break; - } - break; - - case 301: - // Comma - switch (table) - { - case PUNC: - charmap[i] = 17; - break; - - case DIGIT: - charmap[i] = 12; - break; - } - break; - - case 302: - // Full Stop - switch (table) - { - case PUNC: - charmap[i] = 19; - break; - - case DIGIT: - charmap[i] = 13; - break; - } - break; - } - } - } - } - - private static void tryDoubleCharacterEncoding(intList charmap, intList typemap) - { - // Look for double character encoding possibilities - int i = 0; - do - { - if (((charmap[i] == 300) && (charmap[i + 1] == 11)) - && ((typemap[i] == PUNC) && (typemap[i + 1] == PUNC))) - { - // CR LF combination - charmap[i] = 2; - typemap[i] = PUNC; - removeValueAfter(i, charmap, typemap); - } - - if (((charmap[i] == 302) && (charmap[i + 1] == 1)) && - ((typemap[i] == 24) && (typemap[i + 1] == 23))) - { - // . SP combination - charmap[i] = 3; - typemap[i] = PUNC; - removeValueAfter(i, charmap, typemap); - } - - if (((charmap[i] == 301) && (charmap[i + 1] == 1)) && - ((typemap[i] == 24) && (typemap[i + 1] == 23))) - { - // , SP combination - charmap[i] = 4; - typemap[i] = PUNC; - removeValueAfter(i, charmap, typemap); - } - - if (((charmap[i] == 21) && (charmap[i + 1] == 1)) && - ((typemap[i] == PUNC) && (typemap[i + 1] == 23))) - { - // : SP combination - charmap[i] = 5; - typemap[i] = PUNC; - removeValueAfter(i, charmap, typemap); - } - - i++; - } while (i < charmap.Count - 1); - } - - private void fillMappings(byte[] source, intList charmap, intList typemap) - { - // is binary mode indicator - // once we have switched to binary mode we should not switch back - // for example if you pass "привет, мир!" ("privet, mir!" in Russian which is "hello, world!") text then it should be encoded in entire binary mode - bool isBinaryMode = false || - // or check if we should force BINARY compaction mode - Options.AztecCompactionMode == AztecCompactionMode.Binary; - - for (int pos = 0; pos < source.Length; pos++) - { - if (isBinaryMode || // once we enabled binary mode we should use it for further encoding without switching back - source[pos] > 127) - { - charmap.Add(source[pos]); - typemap.Add(BINARY); - isBinaryMode = true; - } - else - { - charmap.Add(c_symbolChar[source[pos]]); - typemap.Add(c_codeSet[source[pos]]); - } - } - } - - private void encodeString(string source) - { - m_encodedSymbolData = null; - m_symbolHeight = 0; - m_symbolWidth = 0; - - // get encoding used in options - Encoding tmpEncoding = Options.Encoding; - - /* - // detect Unicode and auto switch to UTF-8 ? - - bool UnicodeSymbolFound = !Utils.IsLatinISOEncodingOnly(Value); - - // if we are not with encoding UTF8 - // then we are forcing to use UTF8 - if (UnicodeSymbolFound && tmpEncoding != Encoding.UTF8) - { - // force encoding to UTF8 - tmpEncoding = Encoding.UTF8; - } - */ - - byteList binaryString = convertToBinary(tmpEncoding.GetBytes(source)); - byteList adjustedString = new byteList(); - - bool buildCompactSymbol = false; - int layerCount = 0; - int maxDataLength = 0; - int codewordSize = 0; - int adjustedPos = 0; - - if (Options.AztecSymbolSize == 0) - codewordSize = detectSize(ref buildCompactSymbol, ref layerCount, binaryString, adjustedString, ref maxDataLength, out adjustedPos); - else - codewordSize = validateSize(ref buildCompactSymbol, ref layerCount, binaryString, adjustedString, ref maxDataLength, out adjustedPos); - - int adjustedLength = adjustedString.Count; - int dataBlockCount = adjustedLength / codewordSize; - int eccBlockCount = c_codewordsPerSymbol[layerCount - 1] - dataBlockCount; - if (buildCompactSymbol) - eccBlockCount = c_codewordsPerCompactSymbol[layerCount - 1] - dataBlockCount; - - splitIntoCodewords(codewordSize, dataBlockCount, eccBlockCount, adjustedString); - - // Invert the data so that actual data is on the outside and reed-solomon on the inside - byte[] bitPattern = new byte[20045]; - for (int i = 0; i < 20045; i++) - bitPattern[i] = (byte)'0'; - - int totalBits = (dataBlockCount + eccBlockCount) * codewordSize; - for (int i = 0; i < totalBits; i++) - bitPattern[i] = adjustedString[totalBits - i - 1]; - - byte[] descriptor = buildSymbolDescriptor(buildCompactSymbol, layerCount, dataBlockCount); - - // Merge descriptor with the rest of the symbol - for (int i = 0; i < 40; i++) - { - if (buildCompactSymbol) - bitPattern[2000 + i - 2] = descriptor[i]; - else - bitPattern[20000 + i - 2] = descriptor[i]; - } - - plotEncodedData(buildCompactSymbol, layerCount, bitPattern); - } - - private void plotEncodedData(bool buildCompactSymbol, int layerCount, byte[] bitPattern) - { - // Plot all of the data into the symbol in predefined spiral pattern - if (buildCompactSymbol) - { - m_symbolHeight = 27 - (2 * c_compactOffsets[layerCount - 1]); - m_symbolWidth = m_symbolHeight; - } - else - { - m_symbolHeight = 151 - (2 * c_offsets[layerCount - 1]); - m_symbolWidth = m_symbolHeight; - } - - m_encodedSymbolData = new byte[m_symbolHeight][]; - for (int i = 0; i < m_symbolHeight; i++) - { - m_encodedSymbolData[i] = new byte[m_symbolWidth]; - for (int j = 0; j < m_symbolWidth; j++) - m_encodedSymbolData[i][j] = (byte)'0'; - } - - if (buildCompactSymbol) - { - int offset = c_compactOffsets[layerCount - 1]; - for (int y = offset; y < (27 - offset); y++) - { - for (int x = offset; x < (27 - offset); x++) - { - if (c_compactSymbolMap[(y * 27) + x] == 1) - m_encodedSymbolData[y - offset][x - offset] = (byte)'1'; - - if (c_compactSymbolMap[(y * 27) + x] >= 2) - { - if (bitPattern[c_compactSymbolMap[(y * 27) + x] - 2] == '1') - m_encodedSymbolData[y - offset][x - offset] = (byte)'1'; - } - } - } - } - else - { - int offset = c_offsets[layerCount - 1]; - for (int y = offset; y < (151 - offset); y++) - { - for (int x = offset; x < (151 - offset); x++) - { - if (c_symbolMap[(y * 151) + x] == 1) - m_encodedSymbolData[y - offset][x - offset] = (byte)'1'; - - if (c_symbolMap[(y * 151) + x] >= 2) - { - if (bitPattern[c_symbolMap[(y * 151) + x] - 2] == '1') - m_encodedSymbolData[y - offset][x - offset] = (byte)'1'; - } - } - } - } - - //string[] data = new string[m_symbolHeight]; - //for (int i = 0; i < m_symbolHeight; i++) - // data[i] = Encoding.Default.GetString(m_encodedSymbolData[i], 0, m_encodedSymbolData[i].Length); - - //using (System.IO.TextWriter tw = new System.IO.StreamWriter("d:\\downloads\\sharp.txt")) - //{ - // for (int i = 0; i < m_symbolHeight; i++) - // tw.WriteLine(data[i]); - //} - } - - private static byte[] buildSymbolDescriptor(bool buildCompactSymbol, int layerCount, int dataBlockCount) - { - byte[] descriptor = new byte[42]; - byte[] descriptorData = new byte[4]; - - if (buildCompactSymbol) - fillCompactSymbolDescriptor(layerCount, dataBlockCount, descriptor); - else - fillSymbolDescriptor(layerCount, dataBlockCount, descriptor); - - // Split into 4-bit codewords - for (int i = 0; i < 4; i++) - { - if (descriptor[i * 4] == '1') - descriptorData[i] += 8; - - if (descriptor[(i * 4) + 1] == '1') - descriptorData[i] += 4; - - if (descriptor[(i * 4) + 2] == '1') - descriptorData[i] += 2; - - if (descriptor[(i * 4) + 3] == '1') - descriptorData[i] += 1; - } - - addECCToDescriptor(buildCompactSymbol, descriptor, descriptorData); - return descriptor; - } - - private static void addECCToDescriptor(bool buildCompactSymbol, byte[] descriptor, byte[] descriptorData) - { - // Add Reed-Solomon error correction with Galois field GF(16) and - // prime modulus x^4 + x + 1 (section 7.2.3) - - ReedSolomon rs = new ReedSolomon(); - rs.Init(0x13); - if (buildCompactSymbol) - { - rs.InitCode(5, 1); - byte[] descriptorECC = null; - rs.Encode(2, descriptorData, out descriptorECC); - for (int i = 0; i < 5; i++) - { - if ((descriptorECC[4 - i] & 0x08) != 0) - descriptor[(i * 4) + 8] = (byte)'1'; - else - descriptor[(i * 4) + 8] = (byte)'0'; - - if ((descriptorECC[4 - i] & 0x04) != 0) - descriptor[(i * 4) + 9] = (byte)'1'; - else - descriptor[(i * 4) + 9] = (byte)'0'; - - if ((descriptorECC[4 - i] & 0x02) != 0) - descriptor[(i * 4) + 10] = (byte)'1'; - else - descriptor[(i * 4) + 10] = (byte)'0'; - - if ((descriptorECC[4 - i] & 0x01) != 0) - descriptor[(i * 4) + 11] = (byte)'1'; - else - descriptor[(i * 4) + 11] = (byte)'0'; - } - } - else - { - rs.InitCode(6, 1); - byte[] descriptorECC = null; - rs.Encode(4, descriptorData, out descriptorECC); - for (int i = 0; i < 6; i++) - { - if ((descriptorECC[5 - i] & 0x08) != 0) - descriptor[(i * 4) + 16] = (byte)'1'; - else - descriptor[(i * 4) + 16] = (byte)'0'; - - if ((descriptorECC[5 - i] & 0x04) != 0) - descriptor[(i * 4) + 17] = (byte)'1'; - else - descriptor[(i * 4) + 17] = (byte)'0'; - - if ((descriptorECC[5 - i] & 0x02) != 0) - descriptor[(i * 4) + 18] = (byte)'1'; - else - descriptor[(i * 4) + 18] = (byte)'0'; - - if ((descriptorECC[5 - i] & 0x01) != 0) - descriptor[(i * 4) + 19] = (byte)'1'; - else - descriptor[(i * 4) + 19] = (byte)'0'; - } - } - } - - private static void fillSymbolDescriptor(int layerCount, int dataBlockCount, byte[] descriptor) - { - // The first 5 bits represent the number of layers minus 1 - if (((layerCount - 1) & 0x10) != 0) - descriptor[0] = (byte)'1'; - else - descriptor[0] = (byte)'0'; - - if (((layerCount - 1) & 0x08) != 0) - descriptor[1] = (byte)'1'; - else - descriptor[1] = (byte)'0'; - - if (((layerCount - 1) & 0x04) != 0) - descriptor[2] = (byte)'1'; - else - descriptor[2] = (byte)'0'; - - if (((layerCount - 1) & 0x02) != 0) - descriptor[3] = (byte)'1'; - else - descriptor[3] = (byte)'0'; - - if (((layerCount - 1) & 0x01) != 0) - descriptor[4] = (byte)'1'; - else - descriptor[4] = (byte)'0'; - - // The next 11 bits represent the number of data blocks minus 1 - if (((dataBlockCount - 1) & 0x400) != 0) - descriptor[5] = (byte)'1'; - else - descriptor[5] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x200) != 0) - descriptor[6] = (byte)'1'; - else - descriptor[6] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x100) != 0) - descriptor[7] = (byte)'1'; - else - descriptor[7] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x80) != 0) - descriptor[8] = (byte)'1'; - else - descriptor[8] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x40) != 0) - descriptor[9] = (byte)'1'; - else - descriptor[9] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x20) != 0) - descriptor[10] = (byte)'1'; - else - descriptor[10] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x10) != 0) - descriptor[11] = (byte)'1'; - else - descriptor[11] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x08) != 0) - descriptor[12] = (byte)'1'; - else - descriptor[12] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x04) != 0) - descriptor[13] = (byte)'1'; - else - descriptor[13] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x02) != 0) - descriptor[14] = (byte)'1'; - else - descriptor[14] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x01) != 0) - descriptor[15] = (byte)'1'; - else - descriptor[15] = (byte)'0'; - } - - private static void fillCompactSymbolDescriptor(int layerCount, int dataBlockCount, byte[] descriptor) - { - // The first 2 bits represent the number of layers minus 1 - if (((layerCount - 1) & 0x02) != 0) - descriptor[0] = (byte)'1'; - else - descriptor[0] = (byte)'0'; - - if (((layerCount - 1) & 0x01) != 0) - descriptor[1] = (byte)'1'; - else - descriptor[1] = (byte)'0'; - - // The next 6 bits represent the number of data blocks minus 1 - if (((dataBlockCount - 1) & 0x20) != 0) - descriptor[2] = (byte)'1'; - else - descriptor[2] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x10) != 0) - descriptor[3] = (byte)'1'; - else - descriptor[3] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x08) != 0) - descriptor[4] = (byte)'1'; - else - descriptor[4] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x04) != 0) - descriptor[5] = (byte)'1'; - else - descriptor[5] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x02) != 0) - descriptor[6] = (byte)'1'; - else - descriptor[6] = (byte)'0'; - - if (((dataBlockCount - 1) & 0x01) != 0) - descriptor[7] = (byte)'1'; - else - descriptor[7] = (byte)'0'; - } - - // Split into codewords and calculate Reed-Solomon error correction codes - private static void splitIntoCodewords(int codewordSize, int dataBlockCount, int eccBlockCount, byteList adjustedString) - { - switch (codewordSize) - { - case 6: - splitIntoCodewords6(codewordSize, dataBlockCount, eccBlockCount, adjustedString); - break; - - case 8: - splitIntoCodewords8(codewordSize, dataBlockCount, eccBlockCount, adjustedString); - break; - - case 10: - splitIntoCodewords10(codewordSize, dataBlockCount, eccBlockCount, adjustedString); - break; - - case 12: - splitIntoCodewords12(codewordSize, dataBlockCount, eccBlockCount, adjustedString); - break; - } - } - - private static void splitIntoCodewords12(int codewordSize, int dataBlockCount, int eccBlockCount, byteList adjustedString) - { - uint[] dataPart = new uint[dataBlockCount + 3]; - - for (int i = 0; i < dataBlockCount; i++) - { - if (adjustedString[i * codewordSize] == '1') - dataPart[i] += 2048; - - if (adjustedString[i * codewordSize + 1] == '1') - dataPart[i] += 1024; - - if (adjustedString[i * codewordSize + 2] == '1') - dataPart[i] += 512; - - if (adjustedString[i * codewordSize + 3] == '1') - dataPart[i] += 256; - - if (adjustedString[i * codewordSize + 4] == '1') - dataPart[i] += 128; - - if (adjustedString[i * codewordSize + 5] == '1') - dataPart[i] += 64; - - if (adjustedString[i * codewordSize + 6] == '1') - dataPart[i] += 32; - - if (adjustedString[i * codewordSize + 7] == '1') - dataPart[i] += 16; - - if (adjustedString[i * codewordSize + 8] == '1') - dataPart[i] += 8; - - if (adjustedString[i * codewordSize + 9] == '1') - dataPart[i] += 4; - - if (adjustedString[i * codewordSize + 10] == '1') - dataPart[i] += 2; - - if (adjustedString[i * codewordSize + 11] == '1') - dataPart[i] += 1; - } - - ReedSolomon rs12 = new ReedSolomon(); - rs12.Init(0x1069); - rs12.InitCode(eccBlockCount, 1); - uint[] eccPart = null; - rs12.EncodeLong(dataBlockCount, dataPart, out eccPart); - - for (int i = eccBlockCount - 1; i >= 0; i--) - { - conditionalAppend((eccPart[i] & 0x800) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x400) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x200) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x100) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x80) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x40) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x20) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x10) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x08) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x04) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x02) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x01) != 0, adjustedString); - } - } - - private static void splitIntoCodewords10(int codewordSize, int dataBlockCount, int eccBlockCount, byteList adjustedString) - { - uint[] dataPart = new uint[dataBlockCount + 3]; - - for (int i = 0; i < dataBlockCount; i++) - { - if (adjustedString[i * codewordSize] == '1') - dataPart[i] += 512; - - if (adjustedString[i * codewordSize + 1] == '1') - dataPart[i] += 256; - - if (adjustedString[i * codewordSize + 2] == '1') - dataPart[i] += 128; - - if (adjustedString[i * codewordSize + 3] == '1') - dataPart[i] += 64; - - if (adjustedString[i * codewordSize + 4] == '1') - dataPart[i] += 32; - - if (adjustedString[i * codewordSize + 5] == '1') - dataPart[i] += 16; - - if (adjustedString[i * codewordSize + 6] == '1') - dataPart[i] += 8; - - if (adjustedString[i * codewordSize + 7] == '1') - dataPart[i] += 4; - - if (adjustedString[i * codewordSize + 8] == '1') - dataPart[i] += 2; - - if (adjustedString[i * codewordSize + 9] == '1') - dataPart[i] += 1; - } - - ReedSolomon rs10 = new ReedSolomon(); - rs10.Init(0x409); - rs10.InitCode(eccBlockCount, 1); - uint[] eccPart = null; - rs10.EncodeLong(dataBlockCount, dataPart, out eccPart); - - for (int i = eccBlockCount - 1; i >= 0; i--) - { - conditionalAppend((eccPart[i] & 0x200) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x100) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x80) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x40) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x20) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x10) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x08) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x04) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x02) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x01) != 0, adjustedString); - } - } - - private static void splitIntoCodewords8(int codewordSize, int dataBlockCount, int eccBlockCount, byteList adjustedString) - { - uint[] dataPart = new uint[dataBlockCount + 3]; - - for (int i = 0; i < dataBlockCount; i++) - { - if (adjustedString[i * codewordSize] == '1') - dataPart[i] += 128; - - if (adjustedString[i * codewordSize + 1] == '1') - dataPart[i] += 64; - - if (adjustedString[i * codewordSize + 2] == '1') - dataPart[i] += 32; - - if (adjustedString[i * codewordSize + 3] == '1') - dataPart[i] += 16; - - if (adjustedString[i * codewordSize + 4] == '1') - dataPart[i] += 8; - - if (adjustedString[i * codewordSize + 5] == '1') - dataPart[i] += 4; - - if (adjustedString[i * codewordSize + 6] == '1') - dataPart[i] += 2; - - if (adjustedString[i * codewordSize + 7] == '1') - dataPart[i] += 1; - } - - ReedSolomon rs8 = new ReedSolomon(); - rs8.Init(0x12d); - rs8.InitCode(eccBlockCount, 1); - uint[] eccPart = null; - rs8.EncodeLong(dataBlockCount, dataPart, out eccPart); - - for (int i = eccBlockCount - 1; i >= 0; i--) - { - conditionalAppend((eccPart[i] & 0x80) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x40) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x20) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x10) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x08) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x04) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x02) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x01) != 0, adjustedString); - } - } - - private static void splitIntoCodewords6(int codewordSize, int dataBlockCount, int eccBlockCount, byteList adjustedString) - { - uint[] dataPart = new uint[dataBlockCount + 3]; - - for (int i = 0; i < dataBlockCount; i++) - { - if (adjustedString[i * codewordSize] == '1') - dataPart[i] += 32; - - if (adjustedString[i * codewordSize + 1] == '1') - dataPart[i] += 16; - - if (adjustedString[i * codewordSize + 2] == '1') - dataPart[i] += 8; - - if (adjustedString[i * codewordSize + 3] == '1') - dataPart[i] += 4; - - if (adjustedString[i * codewordSize + 4] == '1') - dataPart[i] += 2; - - if (adjustedString[i * codewordSize + 5] == '1') - dataPart[i] += 1; - } - - ReedSolomon rs6 = new ReedSolomon(); - rs6.Init(0x43); - rs6.InitCode(eccBlockCount, 1); - uint[] eccPart = null; - rs6.EncodeLong(dataBlockCount, dataPart, out eccPart); - - for (int i = eccBlockCount - 1; i >= 0; i--) - { - conditionalAppend((eccPart[i] & 0x20) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x10) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x08) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x04) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x02) != 0, adjustedString); - conditionalAppend((eccPart[i] & 0x01) != 0, adjustedString); - } - } - - private int validateSize(ref bool buildCompactSymbol, ref int layerCount, byteList binaryString, byteList adjustedString, ref int maxDataLength, out int adjustedPos) - { - int codewordLength = 0; - // The size of the symbol has been specified by the user - - if ((Options.AztecSymbolSize >= 1) && (Options.AztecSymbolSize <= 4)) - { - buildCompactSymbol = true; - layerCount = Options.AztecSymbolSize; - } - if ((Options.AztecSymbolSize >= 5) && (Options.AztecSymbolSize <= 36)) - { - buildCompactSymbol = false; - layerCount = Options.AztecSymbolSize - 4; - } - - if ((Options.AztecSymbolSize < 0) || (Options.AztecSymbolSize > 36)) - throw new BarcodeException("Invalid Aztec Code symbol size. Size should be in [0..36] range."); - - // Determine codeword length - Table 3 - if ((layerCount >= 0) && (layerCount <= 2)) - codewordLength = 6; - - if ((layerCount >= 3) && (layerCount <= 8)) - codewordLength = 8; - - if ((layerCount >= 9) && (layerCount <= 22)) - codewordLength = 10; - - if (layerCount >= 23) - codewordLength = 12; - - int binaryPos = 0; - - adjustedString.Clear(); - adjustedPos = 0; - - do - { - if ((adjustedPos + 1) % codewordLength == 0) - { - /* Last bit of codeword */ - bool done = false; - int count = 0; - - /* Discover how many '1's in current codeword */ - for (int t = 0; t < (codewordLength - 1); t++) - { - if (getDefaulted(binaryString, (binaryPos - (codewordLength - 1)) + t) == (byte)'1') - count++; - } - - if (count == (codewordLength - 1)) - { - ensureSize(adjustedString, adjustedPos); - adjustedString[adjustedPos] = (byte)'0'; - adjustedPos++; - done = true; - } - - if (count == 0) - { - ensureSize(adjustedString, adjustedPos); - adjustedString[adjustedPos] = (byte)'1'; - adjustedPos++; - done = true; - } - - if (!done) - { - ensureSize(adjustedString, adjustedPos); - adjustedString[adjustedPos] = getDefaulted(binaryString, binaryPos); - adjustedPos++; - binaryPos++; - } - } - - ensureSize(adjustedString, adjustedPos); - adjustedString[adjustedPos] = getDefaulted(binaryString, binaryPos); - adjustedPos++; - binaryPos++; - } while (binaryPos < binaryString.Count); - - trimRight(adjustedString); - - int remainder = adjustedString.Count % codewordLength; - int padbits = codewordLength - remainder; - if (padbits == codewordLength) - padbits = 0; - - for (int i = 0; i < padbits; i++) - adjustedString.Add((byte)'1'); - - int oneCount = 0; - for (int i = (adjustedString.Count - codewordLength); i < adjustedString.Count; i++) - { - if (adjustedString[i] == (byte)'1') - oneCount++; - } - - if (oneCount == codewordLength) - adjustedString[adjustedString.Count - 1] = (byte)'0'; - - // Check if the data actually fits into the selected symbol size - if (buildCompactSymbol) - maxDataLength = codewordLength * (c_codewordsPerCompactSymbol[layerCount - 1] - 3); - else - maxDataLength = codewordLength * (c_codewordsPerSymbol[layerCount - 1] - 3); - - if (adjustedString.Count > maxDataLength) - throw new BarcodeException("Input value too long for specified Aztec Code symbol size"); - - return codewordLength; - } - - private int detectSize(ref bool buildCompactSymbol, ref int layerCount, byteList binaryString, byteList adjustedString, ref int maxDataLength, out int adjustedPos) - { - int codewordLength = 0; - do - { - buildCompactSymbol = false; - layerCount = 0; - int sizeToCheck = Math.Max(adjustedString.Count, binaryString.Count); - - switch (getErrorLevelToUse()) - { - // For each level of error correction work out the smallest symbol which the data will fit in - case AztecErrorCorrectionLevel.Level1: - for (int i = 32; i > 0; i--) - { - if (sizeToCheck < c_ecl10DataSizes[i - 1]) - { - layerCount = i; - buildCompactSymbol = false; - maxDataLength = c_ecl10DataSizes[i - 1]; - } - } - - for (int i = 4; i > 0; i--) - { - if (sizeToCheck < c_eclCompact10DataSizes[i - 1]) - { - layerCount = i; - buildCompactSymbol = true; - maxDataLength = c_eclCompact10DataSizes[i - 1]; - } - } - break; - - case AztecErrorCorrectionLevel.Level2: - for (int i = 32; i > 0; i--) - { - if (sizeToCheck < c_ecl23DataSizes[i - 1]) - { - layerCount = i; - buildCompactSymbol = false; - maxDataLength = c_ecl23DataSizes[i - 1]; - } - } - - for (int i = 4; i > 0; i--) - { - if (sizeToCheck < c_eclCompact23DataSizes[i - 1]) - { - layerCount = i; - buildCompactSymbol = true; - maxDataLength = c_eclCompact23DataSizes[i - 1]; - } - } - break; - - case AztecErrorCorrectionLevel.Level3: - for (int i = 32; i > 0; i--) - { - if (sizeToCheck < c_ecl36DataSizes[i - 1]) - { - layerCount = i; - buildCompactSymbol = false; - maxDataLength = c_ecl36DataSizes[i - 1]; - } - } - - for (int i = 4; i > 0; i--) - { - if (sizeToCheck < c_eclCompact36DataSizes[i - 1]) - { - layerCount = i; - buildCompactSymbol = true; - maxDataLength = c_eclCompact36DataSizes[i - 1]; - } - } - break; - - case AztecErrorCorrectionLevel.Level4: - for (int i = 32; i > 0; i--) - { - if (sizeToCheck < c_ecl50DataSizes[i - 1]) - { - layerCount = i; - buildCompactSymbol = false; - maxDataLength = c_ecl50DataSizes[i - 1]; - } - } - - for (int i = 4; i > 0; i--) - { - if (sizeToCheck < c_eclCompact50DataSizes[i - 1]) - { - layerCount = i; - buildCompactSymbol = true; - maxDataLength = c_eclCompact50DataSizes[i - 1]; - } - } - break; - } - - if (layerCount == 0) - throw new BarcodeException("Input too long (too many bits for selected error correction level"); - - // Determine codeword length - Table 3 - codewordLength = 6; - - if ((layerCount >= 3) && (layerCount <= 8)) - codewordLength = 8; - - if ((layerCount >= 9) && (layerCount <= 22)) - codewordLength = 10; - - if (layerCount >= 23) - codewordLength = 12; - - int binaryPos = 0; - - adjustedString.Clear(); - adjustedPos = 0; - - do - { - if ((adjustedPos + 1) % codewordLength == 0) - { - /* Last bit of codeword */ - bool done = false; - int count = 0; - - /* Discover how many '1's in current codeword */ - for (int t = 0; t < (codewordLength - 1); t++) - { - if (getDefaulted(binaryString, (binaryPos - (codewordLength - 1)) + t) == (byte)'1') - count++; - } - - if (count == (codewordLength - 1)) - { - ensureSize(adjustedString, adjustedPos); - adjustedString[adjustedPos] = (byte)'0'; - adjustedPos++; - done = true; - } - - if (count == 0) - { - ensureSize(adjustedString, adjustedPos); - adjustedString[adjustedPos] = (byte)'1'; - adjustedPos++; - done = true; - } - - if (!done) - { - ensureSize(adjustedString, adjustedPos); - adjustedString[adjustedPos] = getDefaulted(binaryString, binaryPos); - adjustedPos++; - binaryPos++; - } - } - - ensureSize(adjustedString, adjustedPos); - adjustedString[adjustedPos] = getDefaulted(binaryString, binaryPos); - adjustedPos++; - binaryPos++; - } while (binaryPos < binaryString.Count); - - trimRight(adjustedString); - - int remainder = adjustedString.Count % codewordLength; - int padbits = codewordLength - remainder; - if (padbits == codewordLength) - padbits = 0; - - for (int i = 0; i < padbits; i++) - adjustedString.Add((byte)'1'); - - int oneCount = 0; - for (int i = (adjustedString.Count - codewordLength); i < adjustedString.Count; i++) - { - if (adjustedString[i] == (byte)'1') - oneCount++; - } - - if (oneCount == codewordLength) - adjustedString[adjustedString.Count - 1] = (byte)'0'; - - } while (adjustedString.Count > maxDataLength); - - return codewordLength; - } - - private static void ensureSize(byteList list, int pos) - { - while (list.Count <= pos) - list.Add((byte)'0'); - } - - private static byte getDefaulted(byteList list, int pos) - { - if (list.Count > pos) - return list[pos]; - - return 0; - } - - private static void trimRight(byteList list) - { - while (list[list.Count - 1] == 0) - list.RemoveAt(list.Count - 1); - } - - private AztecErrorCorrectionLevel getErrorLevelToUse() - { - AztecErrorCorrectionLevel errorLevelToUse = Options.AztecErrorCorrectionLevel; - if (errorLevelToUse == AztecErrorCorrectionLevel.Auto) - errorLevelToUse = AztecErrorCorrectionLevel.Level2; - - return errorLevelToUse; - } - - - ////////////////////////////////////////////////////////////////////////// - // - // Data part - // - ////////////////////////////////////////////////////////////////////////// - - private const int UPPER = 1; - private const int LOWER = 2; - private const int MIXED = 4; - private const int PUNC = 8; - private const int DIGIT = 16; - private const int BINARY = 32; - - // 22801 - private static int[] c_symbolMap = - { - 19969,19968,18851,18853,18855,18857,18859,18861,18863,18865,18867,0,18869,18871,18873,18875,18877,18879,18881,18883,18885,18887,18889,18891,18893,18895,18897,0,18899,18901,18903,18905,18907,18909,18911,18913,18915,18917,18919,18921,18923,18925,18927,0,18929,18931,18933,18935,18937,18939,18941,18943,18945,18947,18949,18951,18953,18955,18957,0,18959,18961,18963,18965,18967,18969,18971,18973,18975,18977,18979,18981,18983,18985,18987,0,18989,18991,18993,18995,18997,18999,19001,19003,19005,19007,19009,19011,19013,19015,19017,0,19019,19021,19023,19025,19027,19029,19031,19033,19035,19037,19039,19041,19043,19045,19047,0,19049,19051,19053,19055,19057,19059,19061,19063,19065,19067,19069,19071,19073,19075,19077,0,19079,19081,19083,19085,19087,19089,19091,19093,19095,19097,19099,19101,19103,19105,19107,0,19109,19111,19113,19115,19117,19119,19121,19123,19125,19127,19129, - 19967,19966,18850,18852,18854,18856,18858,18860,18862,18864,18866,1,18868,18870,18872,18874,18876,18878,18880,18882,18884,18886,18888,18890,18892,18894,18896,1,18898,18900,18902,18904,18906,18908,18910,18912,18914,18916,18918,18920,18922,18924,18926,1,18928,18930,18932,18934,18936,18938,18940,18942,18944,18946,18948,18950,18952,18954,18956,1,18958,18960,18962,18964,18966,18968,18970,18972,18974,18976,18978,18980,18982,18984,18986,1,18988,18990,18992,18994,18996,18998,19000,19002,19004,19006,19008,19010,19012,19014,19016,1,19018,19020,19022,19024,19026,19028,19030,19032,19034,19036,19038,19040,19042,19044,19046,1,19048,19050,19052,19054,19056,19058,19060,19062,19064,19066,19068,19070,19072,19074,19076,1,19078,19080,19082,19084,19086,19088,19090,19092,19094,19096,19098,19100,19102,19104,19106,1,19108,19110,19112,19114,19116,19118,19120,19122,19124,19126,19128, - 19965,19964,18849,18848,17763,17765,17767,17769,17771,17773,17775,0,17777,17779,17781,17783,17785,17787,17789,17791,17793,17795,17797,17799,17801,17803,17805,0,17807,17809,17811,17813,17815,17817,17819,17821,17823,17825,17827,17829,17831,17833,17835,0,17837,17839,17841,17843,17845,17847,17849,17851,17853,17855,17857,17859,17861,17863,17865,0,17867,17869,17871,17873,17875,17877,17879,17881,17883,17885,17887,17889,17891,17893,17895,0,17897,17899,17901,17903,17905,17907,17909,17911,17913,17915,17917,17919,17921,17923,17925,0,17927,17929,17931,17933,17935,17937,17939,17941,17943,17945,17947,17949,17951,17953,17955,0,17957,17959,17961,17963,17965,17967,17969,17971,17973,17975,17977,17979,17981,17983,17985,0,17987,17989,17991,17993,17995,17997,17999,18001,18003,18005,18007,18009,18011,18013,18015,0,18017,18019,18021,18023,18025,18027,18029,18031,18033,19130,19131, - 19963,19962,18847,18846,17762,17764,17766,17768,17770,17772,17774,1,17776,17778,17780,17782,17784,17786,17788,17790,17792,17794,17796,17798,17800,17802,17804,1,17806,17808,17810,17812,17814,17816,17818,17820,17822,17824,17826,17828,17830,17832,17834,1,17836,17838,17840,17842,17844,17846,17848,17850,17852,17854,17856,17858,17860,17862,17864,1,17866,17868,17870,17872,17874,17876,17878,17880,17882,17884,17886,17888,17890,17892,17894,1,17896,17898,17900,17902,17904,17906,17908,17910,17912,17914,17916,17918,17920,17922,17924,1,17926,17928,17930,17932,17934,17936,17938,17940,17942,17944,17946,17948,17950,17952,17954,1,17956,17958,17960,17962,17964,17966,17968,17970,17972,17974,17976,17978,17980,17982,17984,1,17986,17988,17990,17992,17994,17996,17998,18000,18002,18004,18006,18008,18010,18012,18014,1,18016,18018,18020,18022,18024,18026,18028,18030,18032,19132,19133, - 19961,19960,18845,18844,17761,17760,16707,16709,16711,16713,16715,0,16717,16719,16721,16723,16725,16727,16729,16731,16733,16735,16737,16739,16741,16743,16745,0,16747,16749,16751,16753,16755,16757,16759,16761,16763,16765,16767,16769,16771,16773,16775,0,16777,16779,16781,16783,16785,16787,16789,16791,16793,16795,16797,16799,16801,16803,16805,0,16807,16809,16811,16813,16815,16817,16819,16821,16823,16825,16827,16829,16831,16833,16835,0,16837,16839,16841,16843,16845,16847,16849,16851,16853,16855,16857,16859,16861,16863,16865,0,16867,16869,16871,16873,16875,16877,16879,16881,16883,16885,16887,16889,16891,16893,16895,0,16897,16899,16901,16903,16905,16907,16909,16911,16913,16915,16917,16919,16921,16923,16925,0,16927,16929,16931,16933,16935,16937,16939,16941,16943,16945,16947,16949,16951,16953,16955,0,16957,16959,16961,16963,16965,16967,16969,18034,18035,19134,19135, - 19959,19958,18843,18842,17759,17758,16706,16708,16710,16712,16714,1,16716,16718,16720,16722,16724,16726,16728,16730,16732,16734,16736,16738,16740,16742,16744,1,16746,16748,16750,16752,16754,16756,16758,16760,16762,16764,16766,16768,16770,16772,16774,1,16776,16778,16780,16782,16784,16786,16788,16790,16792,16794,16796,16798,16800,16802,16804,1,16806,16808,16810,16812,16814,16816,16818,16820,16822,16824,16826,16828,16830,16832,16834,1,16836,16838,16840,16842,16844,16846,16848,16850,16852,16854,16856,16858,16860,16862,16864,1,16866,16868,16870,16872,16874,16876,16878,16880,16882,16884,16886,16888,16890,16892,16894,1,16896,16898,16900,16902,16904,16906,16908,16910,16912,16914,16916,16918,16920,16922,16924,1,16926,16928,16930,16932,16934,16936,16938,16940,16942,16944,16946,16948,16950,16952,16954,1,16956,16958,16960,16962,16964,16966,16968,18036,18037,19136,19137, - 19957,19956,18841,18840,17757,17756,16705,16704,15683,15685,15687,0,15689,15691,15693,15695,15697,15699,15701,15703,15705,15707,15709,15711,15713,15715,15717,0,15719,15721,15723,15725,15727,15729,15731,15733,15735,15737,15739,15741,15743,15745,15747,0,15749,15751,15753,15755,15757,15759,15761,15763,15765,15767,15769,15771,15773,15775,15777,0,15779,15781,15783,15785,15787,15789,15791,15793,15795,15797,15799,15801,15803,15805,15807,0,15809,15811,15813,15815,15817,15819,15821,15823,15825,15827,15829,15831,15833,15835,15837,0,15839,15841,15843,15845,15847,15849,15851,15853,15855,15857,15859,15861,15863,15865,15867,0,15869,15871,15873,15875,15877,15879,15881,15883,15885,15887,15889,15891,15893,15895,15897,0,15899,15901,15903,15905,15907,15909,15911,15913,15915,15917,15919,15921,15923,15925,15927,0,15929,15931,15933,15935,15937,16970,16971,18038,18039,19138,19139, - 19955,19954,18839,18838,17755,17754,16703,16702,15682,15684,15686,1,15688,15690,15692,15694,15696,15698,15700,15702,15704,15706,15708,15710,15712,15714,15716,1,15718,15720,15722,15724,15726,15728,15730,15732,15734,15736,15738,15740,15742,15744,15746,1,15748,15750,15752,15754,15756,15758,15760,15762,15764,15766,15768,15770,15772,15774,15776,1,15778,15780,15782,15784,15786,15788,15790,15792,15794,15796,15798,15800,15802,15804,15806,1,15808,15810,15812,15814,15816,15818,15820,15822,15824,15826,15828,15830,15832,15834,15836,1,15838,15840,15842,15844,15846,15848,15850,15852,15854,15856,15858,15860,15862,15864,15866,1,15868,15870,15872,15874,15876,15878,15880,15882,15884,15886,15888,15890,15892,15894,15896,1,15898,15900,15902,15904,15906,15908,15910,15912,15914,15916,15918,15920,15922,15924,15926,1,15928,15930,15932,15934,15936,16972,16973,18040,18041,19140,19141, - 19953,19952,18837,18836,17753,17752,16701,16700,15681,15680,14691,0,14693,14695,14697,14699,14701,14703,14705,14707,14709,14711,14713,14715,14717,14719,14721,0,14723,14725,14727,14729,14731,14733,14735,14737,14739,14741,14743,14745,14747,14749,14751,0,14753,14755,14757,14759,14761,14763,14765,14767,14769,14771,14773,14775,14777,14779,14781,0,14783,14785,14787,14789,14791,14793,14795,14797,14799,14801,14803,14805,14807,14809,14811,0,14813,14815,14817,14819,14821,14823,14825,14827,14829,14831,14833,14835,14837,14839,14841,0,14843,14845,14847,14849,14851,14853,14855,14857,14859,14861,14863,14865,14867,14869,14871,0,14873,14875,14877,14879,14881,14883,14885,14887,14889,14891,14893,14895,14897,14899,14901,0,14903,14905,14907,14909,14911,14913,14915,14917,14919,14921,14923,14925,14927,14929,14931,0,14933,14935,14937,15938,15939,16974,16975,18042,18043,19142,19143, - 19951,19950,18835,18834,17751,17750,16699,16698,15679,15678,14690,1,14692,14694,14696,14698,14700,14702,14704,14706,14708,14710,14712,14714,14716,14718,14720,1,14722,14724,14726,14728,14730,14732,14734,14736,14738,14740,14742,14744,14746,14748,14750,1,14752,14754,14756,14758,14760,14762,14764,14766,14768,14770,14772,14774,14776,14778,14780,1,14782,14784,14786,14788,14790,14792,14794,14796,14798,14800,14802,14804,14806,14808,14810,1,14812,14814,14816,14818,14820,14822,14824,14826,14828,14830,14832,14834,14836,14838,14840,1,14842,14844,14846,14848,14850,14852,14854,14856,14858,14860,14862,14864,14866,14868,14870,1,14872,14874,14876,14878,14880,14882,14884,14886,14888,14890,14892,14894,14896,14898,14900,1,14902,14904,14906,14908,14910,14912,14914,14916,14918,14920,14922,14924,14926,14928,14930,1,14932,14934,14936,15940,15941,16976,16977,18044,18045,19144,19145, - 19949,19948,18833,18832,17749,17748,16697,16696,15677,15676,14689,0,14688,13731,13733,13735,13737,13739,13741,13743,13745,13747,13749,13751,13753,13755,13757,0,13759,13761,13763,13765,13767,13769,13771,13773,13775,13777,13779,13781,13783,13785,13787,0,13789,13791,13793,13795,13797,13799,13801,13803,13805,13807,13809,13811,13813,13815,13817,0,13819,13821,13823,13825,13827,13829,13831,13833,13835,13837,13839,13841,13843,13845,13847,0,13849,13851,13853,13855,13857,13859,13861,13863,13865,13867,13869,13871,13873,13875,13877,0,13879,13881,13883,13885,13887,13889,13891,13893,13895,13897,13899,13901,13903,13905,13907,0,13909,13911,13913,13915,13917,13919,13921,13923,13925,13927,13929,13931,13933,13935,13937,0,13939,13941,13943,13945,13947,13949,13951,13953,13955,13957,13959,13961,13963,13965,13967,0,13969,14938,14939,15942,15943,16978,16979,18046,18047,19146,19147, - 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, - 19947,19946,18831,18830,17747,17746,16695,16694,15675,15674,14687,0,14686,13730,13732,13734,13736,13738,13740,13742,13744,13746,13748,13750,13752,13754,13756,0,13758,13760,13762,13764,13766,13768,13770,13772,13774,13776,13778,13780,13782,13784,13786,0,13788,13790,13792,13794,13796,13798,13800,13802,13804,13806,13808,13810,13812,13814,13816,0,13818,13820,13822,13824,13826,13828,13830,13832,13834,13836,13838,13840,13842,13844,13846,0,13848,13850,13852,13854,13856,13858,13860,13862,13864,13866,13868,13870,13872,13874,13876,0,13878,13880,13882,13884,13886,13888,13890,13892,13894,13896,13898,13900,13902,13904,13906,0,13908,13910,13912,13914,13916,13918,13920,13922,13924,13926,13928,13930,13932,13934,13936,0,13938,13940,13942,13944,13946,13948,13950,13952,13954,13956,13958,13960,13962,13964,13966,0,13968,14940,14941,15944,15945,16980,16981,18048,18049,19148,19149, - 19945,19944,18829,18828,17745,17744,16693,16692,15673,15672,14685,1,14684,13729,13728,12803,12805,12807,12809,12811,12813,12815,12817,12819,12821,12823,12825,1,12827,12829,12831,12833,12835,12837,12839,12841,12843,12845,12847,12849,12851,12853,12855,1,12857,12859,12861,12863,12865,12867,12869,12871,12873,12875,12877,12879,12881,12883,12885,1,12887,12889,12891,12893,12895,12897,12899,12901,12903,12905,12907,12909,12911,12913,12915,1,12917,12919,12921,12923,12925,12927,12929,12931,12933,12935,12937,12939,12941,12943,12945,1,12947,12949,12951,12953,12955,12957,12959,12961,12963,12965,12967,12969,12971,12973,12975,1,12977,12979,12981,12983,12985,12987,12989,12991,12993,12995,12997,12999,13001,13003,13005,1,13007,13009,13011,13013,13015,13017,13019,13021,13023,13025,13027,13029,13031,13033,13970,1,13971,14942,14943,15946,15947,16982,16983,18050,18051,19150,19151, - 19943,19942,18827,18826,17743,17742,16691,16690,15671,15670,14683,0,14682,13727,13726,12802,12804,12806,12808,12810,12812,12814,12816,12818,12820,12822,12824,0,12826,12828,12830,12832,12834,12836,12838,12840,12842,12844,12846,12848,12850,12852,12854,0,12856,12858,12860,12862,12864,12866,12868,12870,12872,12874,12876,12878,12880,12882,12884,0,12886,12888,12890,12892,12894,12896,12898,12900,12902,12904,12906,12908,12910,12912,12914,0,12916,12918,12920,12922,12924,12926,12928,12930,12932,12934,12936,12938,12940,12942,12944,0,12946,12948,12950,12952,12954,12956,12958,12960,12962,12964,12966,12968,12970,12972,12974,0,12976,12978,12980,12982,12984,12986,12988,12990,12992,12994,12996,12998,13000,13002,13004,0,13006,13008,13010,13012,13014,13016,13018,13020,13022,13024,13026,13028,13030,13032,13972,0,13973,14944,14945,15948,15949,16984,16985,18052,18053,19152,19153, - 19941,19940,18825,18824,17741,17740,16689,16688,15669,15668,14681,1,14680,13725,13724,12801,12800,11907,11909,11911,11913,11915,11917,11919,11921,11923,11925,1,11927,11929,11931,11933,11935,11937,11939,11941,11943,11945,11947,11949,11951,11953,11955,1,11957,11959,11961,11963,11965,11967,11969,11971,11973,11975,11977,11979,11981,11983,11985,1,11987,11989,11991,11993,11995,11997,11999,12001,12003,12005,12007,12009,12011,12013,12015,1,12017,12019,12021,12023,12025,12027,12029,12031,12033,12035,12037,12039,12041,12043,12045,1,12047,12049,12051,12053,12055,12057,12059,12061,12063,12065,12067,12069,12071,12073,12075,1,12077,12079,12081,12083,12085,12087,12089,12091,12093,12095,12097,12099,12101,12103,12105,1,12107,12109,12111,12113,12115,12117,12119,12121,12123,12125,12127,12129,13034,13035,13974,1,13975,14946,14947,15950,15951,16986,16987,18054,18055,19154,19155, - 19939,19938,18823,18822,17739,17738,16687,16686,15667,15666,14679,0,14678,13723,13722,12799,12798,11906,11908,11910,11912,11914,11916,11918,11920,11922,11924,0,11926,11928,11930,11932,11934,11936,11938,11940,11942,11944,11946,11948,11950,11952,11954,0,11956,11958,11960,11962,11964,11966,11968,11970,11972,11974,11976,11978,11980,11982,11984,0,11986,11988,11990,11992,11994,11996,11998,12000,12002,12004,12006,12008,12010,12012,12014,0,12016,12018,12020,12022,12024,12026,12028,12030,12032,12034,12036,12038,12040,12042,12044,0,12046,12048,12050,12052,12054,12056,12058,12060,12062,12064,12066,12068,12070,12072,12074,0,12076,12078,12080,12082,12084,12086,12088,12090,12092,12094,12096,12098,12100,12102,12104,0,12106,12108,12110,12112,12114,12116,12118,12120,12122,12124,12126,12128,13036,13037,13976,0,13977,14948,14949,15952,15953,16988,16989,18056,18057,19156,19157, - 19937,19936,18821,18820,17737,17736,16685,16684,15665,15664,14677,1,14676,13721,13720,12797,12796,11905,11904,11043,11045,11047,11049,11051,11053,11055,11057,1,11059,11061,11063,11065,11067,11069,11071,11073,11075,11077,11079,11081,11083,11085,11087,1,11089,11091,11093,11095,11097,11099,11101,11103,11105,11107,11109,11111,11113,11115,11117,1,11119,11121,11123,11125,11127,11129,11131,11133,11135,11137,11139,11141,11143,11145,11147,1,11149,11151,11153,11155,11157,11159,11161,11163,11165,11167,11169,11171,11173,11175,11177,1,11179,11181,11183,11185,11187,11189,11191,11193,11195,11197,11199,11201,11203,11205,11207,1,11209,11211,11213,11215,11217,11219,11221,11223,11225,11227,11229,11231,11233,11235,11237,1,11239,11241,11243,11245,11247,11249,11251,11253,11255,11257,12130,12131,13038,13039,13978,1,13979,14950,14951,15954,15955,16990,16991,18058,18059,19158,19159, - 19935,19934,18819,18818,17735,17734,16683,16682,15663,15662,14675,0,14674,13719,13718,12795,12794,11903,11902,11042,11044,11046,11048,11050,11052,11054,11056,0,11058,11060,11062,11064,11066,11068,11070,11072,11074,11076,11078,11080,11082,11084,11086,0,11088,11090,11092,11094,11096,11098,11100,11102,11104,11106,11108,11110,11112,11114,11116,0,11118,11120,11122,11124,11126,11128,11130,11132,11134,11136,11138,11140,11142,11144,11146,0,11148,11150,11152,11154,11156,11158,11160,11162,11164,11166,11168,11170,11172,11174,11176,0,11178,11180,11182,11184,11186,11188,11190,11192,11194,11196,11198,11200,11202,11204,11206,0,11208,11210,11212,11214,11216,11218,11220,11222,11224,11226,11228,11230,11232,11234,11236,0,11238,11240,11242,11244,11246,11248,11250,11252,11254,11256,12132,12133,13040,13041,13980,0,13981,14952,14953,15956,15957,16992,16993,18060,18061,19160,19161, - 19933,19932,18817,18816,17733,17732,16681,16680,15661,15660,14673,1,14672,13717,13716,12793,12792,11901,11900,11041,11040,10211,10213,10215,10217,10219,10221,1,10223,10225,10227,10229,10231,10233,10235,10237,10239,10241,10243,10245,10247,10249,10251,1,10253,10255,10257,10259,10261,10263,10265,10267,10269,10271,10273,10275,10277,10279,10281,1,10283,10285,10287,10289,10291,10293,10295,10297,10299,10301,10303,10305,10307,10309,10311,1,10313,10315,10317,10319,10321,10323,10325,10327,10329,10331,10333,10335,10337,10339,10341,1,10343,10345,10347,10349,10351,10353,10355,10357,10359,10361,10363,10365,10367,10369,10371,1,10373,10375,10377,10379,10381,10383,10385,10387,10389,10391,10393,10395,10397,10399,10401,1,10403,10405,10407,10409,10411,10413,10415,10417,11258,11259,12134,12135,13042,13043,13982,1,13983,14954,14955,15958,15959,16994,16995,18062,18063,19162,19163, - 19931,19930,18815,18814,17731,17730,16679,16678,15659,15658,14671,0,14670,13715,13714,12791,12790,11899,11898,11039,11038,10210,10212,10214,10216,10218,10220,0,10222,10224,10226,10228,10230,10232,10234,10236,10238,10240,10242,10244,10246,10248,10250,0,10252,10254,10256,10258,10260,10262,10264,10266,10268,10270,10272,10274,10276,10278,10280,0,10282,10284,10286,10288,10290,10292,10294,10296,10298,10300,10302,10304,10306,10308,10310,0,10312,10314,10316,10318,10320,10322,10324,10326,10328,10330,10332,10334,10336,10338,10340,0,10342,10344,10346,10348,10350,10352,10354,10356,10358,10360,10362,10364,10366,10368,10370,0,10372,10374,10376,10378,10380,10382,10384,10386,10388,10390,10392,10394,10396,10398,10400,0,10402,10404,10406,10408,10410,10412,10414,10416,11260,11261,12136,12137,13044,13045,13984,0,13985,14956,14957,15960,15961,16996,16997,18064,18065,19164,19165, - 19929,19928,18813,18812,17729,17728,16677,16676,15657,15656,14669,1,14668,13713,13712,12789,12788,11897,11896,11037,11036,10209,10208,9411,9413,9415,9417,1,9419,9421,9423,9425,9427,9429,9431,9433,9435,9437,9439,9441,9443,9445,9447,1,9449,9451,9453,9455,9457,9459,9461,9463,9465,9467,9469,9471,9473,9475,9477,1,9479,9481,9483,9485,9487,9489,9491,9493,9495,9497,9499,9501,9503,9505,9507,1,9509,9511,9513,9515,9517,9519,9521,9523,9525,9527,9529,9531,9533,9535,9537,1,9539,9541,9543,9545,9547,9549,9551,9553,9555,9557,9559,9561,9563,9565,9567,1,9569,9571,9573,9575,9577,9579,9581,9583,9585,9587,9589,9591,9593,9595,9597,1,9599,9601,9603,9605,9607,9609,10418,10419,11262,11263,12138,12139,13046,13047,13986,1,13987,14958,14959,15962,15963,16998,16999,18066,18067,19166,19167, - 19927,19926,18811,18810,17727,17726,16675,16674,15655,15654,14667,0,14666,13711,13710,12787,12786,11895,11894,11035,11034,10207,10206,9410,9412,9414,9416,0,9418,9420,9422,9424,9426,9428,9430,9432,9434,9436,9438,9440,9442,9444,9446,0,9448,9450,9452,9454,9456,9458,9460,9462,9464,9466,9468,9470,9472,9474,9476,0,9478,9480,9482,9484,9486,9488,9490,9492,9494,9496,9498,9500,9502,9504,9506,0,9508,9510,9512,9514,9516,9518,9520,9522,9524,9526,9528,9530,9532,9534,9536,0,9538,9540,9542,9544,9546,9548,9550,9552,9554,9556,9558,9560,9562,9564,9566,0,9568,9570,9572,9574,9576,9578,9580,9582,9584,9586,9588,9590,9592,9594,9596,0,9598,9600,9602,9604,9606,9608,10420,10421,11264,11265,12140,12141,13048,13049,13988,0,13989,14960,14961,15964,15965,17000,17001,18068,18069,19168,19169, - 19925,19924,18809,18808,17725,17724,16673,16672,15653,15652,14665,1,14664,13709,13708,12785,12784,11893,11892,11033,11032,10205,10204,9409,9408,8643,8645,1,8647,8649,8651,8653,8655,8657,8659,8661,8663,8665,8667,8669,8671,8673,8675,1,8677,8679,8681,8683,8685,8687,8689,8691,8693,8695,8697,8699,8701,8703,8705,1,8707,8709,8711,8713,8715,8717,8719,8721,8723,8725,8727,8729,8731,8733,8735,1,8737,8739,8741,8743,8745,8747,8749,8751,8753,8755,8757,8759,8761,8763,8765,1,8767,8769,8771,8773,8775,8777,8779,8781,8783,8785,8787,8789,8791,8793,8795,1,8797,8799,8801,8803,8805,8807,8809,8811,8813,8815,8817,8819,8821,8823,8825,1,8827,8829,8831,8833,9610,9611,10422,10423,11266,11267,12142,12143,13050,13051,13990,1,13991,14962,14963,15966,15967,17002,17003,18070,18071,19170,19171, - 19923,19922,18807,18806,17723,17722,16671,16670,15651,15650,14663,0,14662,13707,13706,12783,12782,11891,11890,11031,11030,10203,10202,9407,9406,8642,8644,0,8646,8648,8650,8652,8654,8656,8658,8660,8662,8664,8666,8668,8670,8672,8674,0,8676,8678,8680,8682,8684,8686,8688,8690,8692,8694,8696,8698,8700,8702,8704,0,8706,8708,8710,8712,8714,8716,8718,8720,8722,8724,8726,8728,8730,8732,8734,0,8736,8738,8740,8742,8744,8746,8748,8750,8752,8754,8756,8758,8760,8762,8764,0,8766,8768,8770,8772,8774,8776,8778,8780,8782,8784,8786,8788,8790,8792,8794,0,8796,8798,8800,8802,8804,8806,8808,8810,8812,8814,8816,8818,8820,8822,8824,0,8826,8828,8830,8832,9612,9613,10424,10425,11268,11269,12144,12145,13052,13053,13992,0,13993,14964,14965,15968,15969,17004,17005,18072,18073,19172,19173, - 19921,19920,18805,18804,17721,17720,16669,16668,15649,15648,14661,1,14660,13705,13704,12781,12780,11889,11888,11029,11028,10201,10200,9405,9404,8641,8640,1,7907,7909,7911,7913,7915,7917,7919,7921,7923,7925,7927,7929,7931,7933,7935,1,7937,7939,7941,7943,7945,7947,7949,7951,7953,7955,7957,7959,7961,7963,7965,1,7967,7969,7971,7973,7975,7977,7979,7981,7983,7985,7987,7989,7991,7993,7995,1,7997,7999,8001,8003,8005,8007,8009,8011,8013,8015,8017,8019,8021,8023,8025,1,8027,8029,8031,8033,8035,8037,8039,8041,8043,8045,8047,8049,8051,8053,8055,1,8057,8059,8061,8063,8065,8067,8069,8071,8073,8075,8077,8079,8081,8083,8085,1,8087,8089,8834,8835,9614,9615,10426,10427,11270,11271,12146,12147,13054,13055,13994,1,13995,14966,14967,15970,15971,17006,17007,18074,18075,19174,19175, - 19919,19918,18803,18802,17719,17718,16667,16666,15647,15646,14659,0,14658,13703,13702,12779,12778,11887,11886,11027,11026,10199,10198,9403,9402,8639,8638,0,7906,7908,7910,7912,7914,7916,7918,7920,7922,7924,7926,7928,7930,7932,7934,0,7936,7938,7940,7942,7944,7946,7948,7950,7952,7954,7956,7958,7960,7962,7964,0,7966,7968,7970,7972,7974,7976,7978,7980,7982,7984,7986,7988,7990,7992,7994,0,7996,7998,8000,8002,8004,8006,8008,8010,8012,8014,8016,8018,8020,8022,8024,0,8026,8028,8030,8032,8034,8036,8038,8040,8042,8044,8046,8048,8050,8052,8054,0,8056,8058,8060,8062,8064,8066,8068,8070,8072,8074,8076,8078,8080,8082,8084,0,8086,8088,8836,8837,9616,9617,10428,10429,11272,11273,12148,12149,13056,13057,13996,0,13997,14968,14969,15972,15973,17008,17009,18076,18077,19176,19177, - 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, - 19917,19916,18801,18800,17717,17716,16665,16664,15645,15644,14657,0,14656,13701,13700,12777,12776,11885,11884,11025,11024,10197,10196,9401,9400,8637,8636,0,7905,7904,7203,7205,7207,7209,7211,7213,7215,7217,7219,7221,7223,7225,7227,0,7229,7231,7233,7235,7237,7239,7241,7243,7245,7247,7249,7251,7253,7255,7257,0,7259,7261,7263,7265,7267,7269,7271,7273,7275,7277,7279,7281,7283,7285,7287,0,7289,7291,7293,7295,7297,7299,7301,7303,7305,7307,7309,7311,7313,7315,7317,0,7319,7321,7323,7325,7327,7329,7331,7333,7335,7337,7339,7341,7343,7345,7347,0,7349,7351,7353,7355,7357,7359,7361,7363,7365,7367,7369,7371,7373,7375,7377,0,8090,8091,8838,8839,9618,9619,10430,10431,11274,11275,12150,12151,13058,13059,13998,0,13999,14970,14971,15974,15975,17010,17011,18078,18079,19178,19179, - 19915,19914,18799,18798,17715,17714,16663,16662,15643,15642,14655,1,14654,13699,13698,12775,12774,11883,11882,11023,11022,10195,10194,9399,9398,8635,8634,1,7903,7902,7202,7204,7206,7208,7210,7212,7214,7216,7218,7220,7222,7224,7226,1,7228,7230,7232,7234,7236,7238,7240,7242,7244,7246,7248,7250,7252,7254,7256,1,7258,7260,7262,7264,7266,7268,7270,7272,7274,7276,7278,7280,7282,7284,7286,1,7288,7290,7292,7294,7296,7298,7300,7302,7304,7306,7308,7310,7312,7314,7316,1,7318,7320,7322,7324,7326,7328,7330,7332,7334,7336,7338,7340,7342,7344,7346,1,7348,7350,7352,7354,7356,7358,7360,7362,7364,7366,7368,7370,7372,7374,7376,1,8092,8093,8840,8841,9620,9621,10432,10433,11276,11277,12152,12153,13060,13061,14000,1,14001,14972,14973,15976,15977,17012,17013,18080,18081,19180,19181, - 19913,19912,18797,18796,17713,17712,16661,16660,15641,15640,14653,0,14652,13697,13696,12773,12772,11881,11880,11021,11020,10193,10192,9397,9396,8633,8632,0,7901,7900,7201,7200,6531,6533,6535,6537,6539,6541,6543,6545,6547,6549,6551,0,6553,6555,6557,6559,6561,6563,6565,6567,6569,6571,6573,6575,6577,6579,6581,0,6583,6585,6587,6589,6591,6593,6595,6597,6599,6601,6603,6605,6607,6609,6611,0,6613,6615,6617,6619,6621,6623,6625,6627,6629,6631,6633,6635,6637,6639,6641,0,6643,6645,6647,6649,6651,6653,6655,6657,6659,6661,6663,6665,6667,6669,6671,0,6673,6675,6677,6679,6681,6683,6685,6687,6689,6691,6693,6695,6697,7378,7379,0,8094,8095,8842,8843,9622,9623,10434,10435,11278,11279,12154,12155,13062,13063,14002,0,14003,14974,14975,15978,15979,17014,17015,18082,18083,19182,19183, - 19911,19910,18795,18794,17711,17710,16659,16658,15639,15638,14651,1,14650,13695,13694,12771,12770,11879,11878,11019,11018,10191,10190,9395,9394,8631,8630,1,7899,7898,7199,7198,6530,6532,6534,6536,6538,6540,6542,6544,6546,6548,6550,1,6552,6554,6556,6558,6560,6562,6564,6566,6568,6570,6572,6574,6576,6578,6580,1,6582,6584,6586,6588,6590,6592,6594,6596,6598,6600,6602,6604,6606,6608,6610,1,6612,6614,6616,6618,6620,6622,6624,6626,6628,6630,6632,6634,6636,6638,6640,1,6642,6644,6646,6648,6650,6652,6654,6656,6658,6660,6662,6664,6666,6668,6670,1,6672,6674,6676,6678,6680,6682,6684,6686,6688,6690,6692,6694,6696,7380,7381,1,8096,8097,8844,8845,9624,9625,10436,10437,11280,11281,12156,12157,13064,13065,14004,1,14005,14976,14977,15980,15981,17016,17017,18084,18085,19184,19185, - 19909,19908,18793,18792,17709,17708,16657,16656,15637,15636,14649,0,14648,13693,13692,12769,12768,11877,11876,11017,11016,10189,10188,9393,9392,8629,8628,0,7897,7896,7197,7196,6529,6528,5891,5893,5895,5897,5899,5901,5903,5905,5907,0,5909,5911,5913,5915,5917,5919,5921,5923,5925,5927,5929,5931,5933,5935,5937,0,5939,5941,5943,5945,5947,5949,5951,5953,5955,5957,5959,5961,5963,5965,5967,0,5969,5971,5973,5975,5977,5979,5981,5983,5985,5987,5989,5991,5993,5995,5997,0,5999,6001,6003,6005,6007,6009,6011,6013,6015,6017,6019,6021,6023,6025,6027,0,6029,6031,6033,6035,6037,6039,6041,6043,6045,6047,6049,6698,6699,7382,7383,0,8098,8099,8846,8847,9626,9627,10438,10439,11282,11283,12158,12159,13066,13067,14006,0,14007,14978,14979,15982,15983,17018,17019,18086,18087,19186,19187, - 19907,19906,18791,18790,17707,17706,16655,16654,15635,15634,14647,1,14646,13691,13690,12767,12766,11875,11874,11015,11014,10187,10186,9391,9390,8627,8626,1,7895,7894,7195,7194,6527,6526,5890,5892,5894,5896,5898,5900,5902,5904,5906,1,5908,5910,5912,5914,5916,5918,5920,5922,5924,5926,5928,5930,5932,5934,5936,1,5938,5940,5942,5944,5946,5948,5950,5952,5954,5956,5958,5960,5962,5964,5966,1,5968,5970,5972,5974,5976,5978,5980,5982,5984,5986,5988,5990,5992,5994,5996,1,5998,6000,6002,6004,6006,6008,6010,6012,6014,6016,6018,6020,6022,6024,6026,1,6028,6030,6032,6034,6036,6038,6040,6042,6044,6046,6048,6700,6701,7384,7385,1,8100,8101,8848,8849,9628,9629,10440,10441,11284,11285,12160,12161,13068,13069,14008,1,14009,14980,14981,15984,15985,17020,17021,18088,18089,19188,19189, - 19905,19904,18789,18788,17705,17704,16653,16652,15633,15632,14645,0,14644,13689,13688,12765,12764,11873,11872,11013,11012,10185,10184,9389,9388,8625,8624,0,7893,7892,7193,7192,6525,6524,5889,5888,5283,5285,5287,5289,5291,5293,5295,0,5297,5299,5301,5303,5305,5307,5309,5311,5313,5315,5317,5319,5321,5323,5325,0,5327,5329,5331,5333,5335,5337,5339,5341,5343,5345,5347,5349,5351,5353,5355,0,5357,5359,5361,5363,5365,5367,5369,5371,5373,5375,5377,5379,5381,5383,5385,0,5387,5389,5391,5393,5395,5397,5399,5401,5403,5405,5407,5409,5411,5413,5415,0,5417,5419,5421,5423,5425,5427,5429,5431,5433,6050,6051,6702,6703,7386,7387,0,8102,8103,8850,8851,9630,9631,10442,10443,11286,11287,12162,12163,13070,13071,14010,0,14011,14982,14983,15986,15987,17022,17023,18090,18091,19190,19191, - 19903,19902,18787,18786,17703,17702,16651,16650,15631,15630,14643,1,14642,13687,13686,12763,12762,11871,11870,11011,11010,10183,10182,9387,9386,8623,8622,1,7891,7890,7191,7190,6523,6522,5887,5886,5282,5284,5286,5288,5290,5292,5294,1,5296,5298,5300,5302,5304,5306,5308,5310,5312,5314,5316,5318,5320,5322,5324,1,5326,5328,5330,5332,5334,5336,5338,5340,5342,5344,5346,5348,5350,5352,5354,1,5356,5358,5360,5362,5364,5366,5368,5370,5372,5374,5376,5378,5380,5382,5384,1,5386,5388,5390,5392,5394,5396,5398,5400,5402,5404,5406,5408,5410,5412,5414,1,5416,5418,5420,5422,5424,5426,5428,5430,5432,6052,6053,6704,6705,7388,7389,1,8104,8105,8852,8853,9632,9633,10444,10445,11288,11289,12164,12165,13072,13073,14012,1,14013,14984,14985,15988,15989,17024,17025,18092,18093,19192,19193, - 19901,19900,18785,18784,17701,17700,16649,16648,15629,15628,14641,0,14640,13685,13684,12761,12760,11869,11868,11009,11008,10181,10180,9385,9384,8621,8620,0,7889,7888,7189,7188,6521,6520,5885,5884,5281,5280,4707,4709,4711,4713,4715,0,4717,4719,4721,4723,4725,4727,4729,4731,4733,4735,4737,4739,4741,4743,4745,0,4747,4749,4751,4753,4755,4757,4759,4761,4763,4765,4767,4769,4771,4773,4775,0,4777,4779,4781,4783,4785,4787,4789,4791,4793,4795,4797,4799,4801,4803,4805,0,4807,4809,4811,4813,4815,4817,4819,4821,4823,4825,4827,4829,4831,4833,4835,0,4837,4839,4841,4843,4845,4847,4849,5434,5435,6054,6055,6706,6707,7390,7391,0,8106,8107,8854,8855,9634,9635,10446,10447,11290,11291,12166,12167,13074,13075,14014,0,14015,14986,14987,15990,15991,17026,17027,18094,18095,19194,19195, - 19899,19898,18783,18782,17699,17698,16647,16646,15627,15626,14639,1,14638,13683,13682,12759,12758,11867,11866,11007,11006,10179,10178,9383,9382,8619,8618,1,7887,7886,7187,7186,6519,6518,5883,5882,5279,5278,4706,4708,4710,4712,4714,1,4716,4718,4720,4722,4724,4726,4728,4730,4732,4734,4736,4738,4740,4742,4744,1,4746,4748,4750,4752,4754,4756,4758,4760,4762,4764,4766,4768,4770,4772,4774,1,4776,4778,4780,4782,4784,4786,4788,4790,4792,4794,4796,4798,4800,4802,4804,1,4806,4808,4810,4812,4814,4816,4818,4820,4822,4824,4826,4828,4830,4832,4834,1,4836,4838,4840,4842,4844,4846,4848,5436,5437,6056,6057,6708,6709,7392,7393,1,8108,8109,8856,8857,9636,9637,10448,10449,11292,11293,12168,12169,13076,13077,14016,1,14017,14988,14989,15992,15993,17028,17029,18096,18097,19196,19197, - 19897,19896,18781,18780,17697,17696,16645,16644,15625,15624,14637,0,14636,13681,13680,12757,12756,11865,11864,11005,11004,10177,10176,9381,9380,8617,8616,0,7885,7884,7185,7184,6517,6516,5881,5880,5277,5276,4705,4704,4163,4165,4167,0,4169,4171,4173,4175,4177,4179,4181,4183,4185,4187,4189,4191,4193,4195,4197,0,4199,4201,4203,4205,4207,4209,4211,4213,4215,4217,4219,4221,4223,4225,4227,0,4229,4231,4233,4235,4237,4239,4241,4243,4245,4247,4249,4251,4253,4255,4257,0,4259,4261,4263,4265,4267,4269,4271,4273,4275,4277,4279,4281,4283,4285,4287,0,4289,4291,4293,4295,4297,4850,4851,5438,5439,6058,6059,6710,6711,7394,7395,0,8110,8111,8858,8859,9638,9639,10450,10451,11294,11295,12170,12171,13078,13079,14018,0,14019,14990,14991,15994,15995,17030,17031,18098,18099,19198,19199, - 19895,19894,18779,18778,17695,17694,16643,16642,15623,15622,14635,1,14634,13679,13678,12755,12754,11863,11862,11003,11002,10175,10174,9379,9378,8615,8614,1,7883,7882,7183,7182,6515,6514,5879,5878,5275,5274,4703,4702,4162,4164,4166,1,4168,4170,4172,4174,4176,4178,4180,4182,4184,4186,4188,4190,4192,4194,4196,1,4198,4200,4202,4204,4206,4208,4210,4212,4214,4216,4218,4220,4222,4224,4226,1,4228,4230,4232,4234,4236,4238,4240,4242,4244,4246,4248,4250,4252,4254,4256,1,4258,4260,4262,4264,4266,4268,4270,4272,4274,4276,4278,4280,4282,4284,4286,1,4288,4290,4292,4294,4296,4852,4853,5440,5441,6060,6061,6712,6713,7396,7397,1,8112,8113,8860,8861,9640,9641,10452,10453,11296,11297,12172,12173,13080,13081,14020,1,14021,14992,14993,15996,15997,17032,17033,18100,18101,19200,19201, - 19893,19892,18777,18776,17693,17692,16641,16640,15621,15620,14633,0,14632,13677,13676,12753,12752,11861,11860,11001,11000,10173,10172,9377,9376,8613,8612,0,7881,7880,7181,7180,6513,6512,5877,5876,5273,5272,4701,4700,4161,4160,3651,0,3653,3655,3657,3659,3661,3663,3665,3667,3669,3671,3673,3675,3677,3679,3681,0,3683,3685,3687,3689,3691,3693,3695,3697,3699,3701,3703,3705,3707,3709,3711,0,3713,3715,3717,3719,3721,3723,3725,3727,3729,3731,3733,3735,3737,3739,3741,0,3743,3745,3747,3749,3751,3753,3755,3757,3759,3761,3763,3765,3767,3769,3771,0,3773,3775,3777,4298,4299,4854,4855,5442,5443,6062,6063,6714,6715,7398,7399,0,8114,8115,8862,8863,9642,9643,10454,10455,11298,11299,12174,12175,13082,13083,14022,0,14023,14994,14995,15998,15999,17034,17035,18102,18103,19202,19203, - 19891,19890,18775,18774,17691,17690,16639,16638,15619,15618,14631,1,14630,13675,13674,12751,12750,11859,11858,10999,10998,10171,10170,9375,9374,8611,8610,1,7879,7878,7179,7178,6511,6510,5875,5874,5271,5270,4699,4698,4159,4158,3650,1,3652,3654,3656,3658,3660,3662,3664,3666,3668,3670,3672,3674,3676,3678,3680,1,3682,3684,3686,3688,3690,3692,3694,3696,3698,3700,3702,3704,3706,3708,3710,1,3712,3714,3716,3718,3720,3722,3724,3726,3728,3730,3732,3734,3736,3738,3740,1,3742,3744,3746,3748,3750,3752,3754,3756,3758,3760,3762,3764,3766,3768,3770,1,3772,3774,3776,4300,4301,4856,4857,5444,5445,6064,6065,6716,6717,7400,7401,1,8116,8117,8864,8865,9644,9645,10456,10457,11300,11301,12176,12177,13084,13085,14024,1,14025,14996,14997,16000,16001,17036,17037,18104,18105,19204,19205, - 19889,19888,18773,18772,17689,17688,16637,16636,15617,15616,14629,0,14628,13673,13672,12749,12748,11857,11856,10997,10996,10169,10168,9373,9372,8609,8608,0,7877,7876,7177,7176,6509,6508,5873,5872,5269,5268,4697,4696,4157,4156,3649,0,3648,3171,3173,3175,3177,3179,3181,3183,3185,3187,3189,3191,3193,3195,3197,0,3199,3201,3203,3205,3207,3209,3211,3213,3215,3217,3219,3221,3223,3225,3227,0,3229,3231,3233,3235,3237,3239,3241,3243,3245,3247,3249,3251,3253,3255,3257,0,3259,3261,3263,3265,3267,3269,3271,3273,3275,3277,3279,3281,3283,3285,3287,0,3289,3778,3779,4302,4303,4858,4859,5446,5447,6066,6067,6718,6719,7402,7403,0,8118,8119,8866,8867,9646,9647,10458,10459,11302,11303,12178,12179,13086,13087,14026,0,14027,14998,14999,16002,16003,17038,17039,18106,18107,19206,19207, - 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, - 19887,19886,18771,18770,17687,17686,16635,16634,15615,15614,14627,0,14626,13671,13670,12747,12746,11855,11854,10995,10994,10167,10166,9371,9370,8607,8606,0,7875,7874,7175,7174,6507,6506,5871,5870,5267,5266,4695,4694,4155,4154,3647,0,3646,3170,3172,3174,3176,3178,3180,3182,3184,3186,3188,3190,3192,3194,3196,0,3198,3200,3202,3204,3206,3208,3210,3212,3214,3216,3218,3220,3222,3224,3226,0,3228,3230,3232,3234,3236,3238,3240,3242,3244,3246,3248,3250,3252,3254,3256,0,3258,3260,3262,3264,3266,3268,3270,3272,3274,3276,3278,3280,3282,3284,3286,0,3288,3780,3781,4304,4305,4860,4861,5448,5449,6068,6069,6720,6721,7404,7405,0,8120,8121,8868,8869,9648,9649,10460,10461,11304,11305,12180,12181,13088,13089,14028,0,14029,15000,15001,16004,16005,17040,17041,18108,18109,19208,19209, - 19885,19884,18769,18768,17685,17684,16633,16632,15613,15612,14625,1,14624,13669,13668,12745,12744,11853,11852,10993,10992,10165,10164,9369,9368,8605,8604,1,7873,7872,7173,7172,6505,6504,5869,5868,5265,5264,4693,4692,4153,4152,3645,1,3644,3169,3168,2723,2725,2727,2729,2731,2733,2735,2737,2739,2741,2743,2745,1,2747,2749,2751,2753,2755,2757,2759,2761,2763,2765,2767,2769,2771,2773,2775,1,2777,2779,2781,2783,2785,2787,2789,2791,2793,2795,2797,2799,2801,2803,2805,1,2807,2809,2811,2813,2815,2817,2819,2821,2823,2825,2827,2829,2831,2833,3290,1,3291,3782,3783,4306,4307,4862,4863,5450,5451,6070,6071,6722,6723,7406,7407,1,8122,8123,8870,8871,9650,9651,10462,10463,11306,11307,12182,12183,13090,13091,14030,1,14031,15002,15003,16006,16007,17042,17043,18110,18111,19210,19211, - 19883,19882,18767,18766,17683,17682,16631,16630,15611,15610,14623,0,14622,13667,13666,12743,12742,11851,11850,10991,10990,10163,10162,9367,9366,8603,8602,0,7871,7870,7171,7170,6503,6502,5867,5866,5263,5262,4691,4690,4151,4150,3643,0,3642,3167,3166,2722,2724,2726,2728,2730,2732,2734,2736,2738,2740,2742,2744,0,2746,2748,2750,2752,2754,2756,2758,2760,2762,2764,2766,2768,2770,2772,2774,0,2776,2778,2780,2782,2784,2786,2788,2790,2792,2794,2796,2798,2800,2802,2804,0,2806,2808,2810,2812,2814,2816,2818,2820,2822,2824,2826,2828,2830,2832,3292,0,3293,3784,3785,4308,4309,4864,4865,5452,5453,6072,6073,6724,6725,7408,7409,0,8124,8125,8872,8873,9652,9653,10464,10465,11308,11309,12184,12185,13092,13093,14032,0,14033,15004,15005,16008,16009,17044,17045,18112,18113,19212,19213, - 19881,19880,18765,18764,17681,17680,16629,16628,15609,15608,14621,1,14620,13665,13664,12741,12740,11849,11848,10989,10988,10161,10160,9365,9364,8601,8600,1,7869,7868,7169,7168,6501,6500,5865,5864,5261,5260,4689,4688,4149,4148,3641,1,3640,3165,3164,2721,2720,2307,2309,2311,2313,2315,2317,2319,2321,2323,2325,1,2327,2329,2331,2333,2335,2337,2339,2341,2343,2345,2347,2349,2351,2353,2355,1,2357,2359,2361,2363,2365,2367,2369,2371,2373,2375,2377,2379,2381,2383,2385,1,2387,2389,2391,2393,2395,2397,2399,2401,2403,2405,2407,2409,2834,2835,3294,1,3295,3786,3787,4310,4311,4866,4867,5454,5455,6074,6075,6726,6727,7410,7411,1,8126,8127,8874,8875,9654,9655,10466,10467,11310,11311,12186,12187,13094,13095,14034,1,14035,15006,15007,16010,16011,17046,17047,18114,18115,19214,19215, - 19879,19878,18763,18762,17679,17678,16627,16626,15607,15606,14619,0,14618,13663,13662,12739,12738,11847,11846,10987,10986,10159,10158,9363,9362,8599,8598,0,7867,7866,7167,7166,6499,6498,5863,5862,5259,5258,4687,4686,4147,4146,3639,0,3638,3163,3162,2719,2718,2306,2308,2310,2312,2314,2316,2318,2320,2322,2324,0,2326,2328,2330,2332,2334,2336,2338,2340,2342,2344,2346,2348,2350,2352,2354,0,2356,2358,2360,2362,2364,2366,2368,2370,2372,2374,2376,2378,2380,2382,2384,0,2386,2388,2390,2392,2394,2396,2398,2400,2402,2404,2406,2408,2836,2837,3296,0,3297,3788,3789,4312,4313,4868,4869,5456,5457,6076,6077,6728,6729,7412,7413,0,8128,8129,8876,8877,9656,9657,10468,10469,11312,11313,12188,12189,13096,13097,14036,0,14037,15008,15009,16012,16013,17048,17049,18116,18117,19216,19217, - 19877,19876,18761,18760,17677,17676,16625,16624,15605,15604,14617,1,14616,13661,13660,12737,12736,11845,11844,10985,10984,10157,10156,9361,9360,8597,8596,1,7865,7864,7165,7164,6497,6496,5861,5860,5257,5256,4685,4684,4145,4144,3637,1,3636,3161,3160,2717,2716,2305,2304,1923,1925,1927,1929,1931,1933,1935,1937,1,1939,1941,1943,1945,1947,1949,1951,1953,1955,1957,1959,1961,1963,1965,1967,1,1969,1971,1973,1975,1977,1979,1981,1983,1985,1987,1989,1991,1993,1995,1997,1,1999,2001,2003,2005,2007,2009,2011,2013,2015,2017,2410,2411,2838,2839,3298,1,3299,3790,3791,4314,4315,4870,4871,5458,5459,6078,6079,6730,6731,7414,7415,1,8130,8131,8878,8879,9658,9659,10470,10471,11314,11315,12190,12191,13098,13099,14038,1,14039,15010,15011,16014,16015,17050,17051,18118,18119,19218,19219, - 19875,19874,18759,18758,17675,17674,16623,16622,15603,15602,14615,0,14614,13659,13658,12735,12734,11843,11842,10983,10982,10155,10154,9359,9358,8595,8594,0,7863,7862,7163,7162,6495,6494,5859,5858,5255,5254,4683,4682,4143,4142,3635,0,3634,3159,3158,2715,2714,2303,2302,1922,1924,1926,1928,1930,1932,1934,1936,0,1938,1940,1942,1944,1946,1948,1950,1952,1954,1956,1958,1960,1962,1964,1966,0,1968,1970,1972,1974,1976,1978,1980,1982,1984,1986,1988,1990,1992,1994,1996,0,1998,2000,2002,2004,2006,2008,2010,2012,2014,2016,2412,2413,2840,2841,3300,0,3301,3792,3793,4316,4317,4872,4873,5460,5461,6080,6081,6732,6733,7416,7417,0,8132,8133,8880,8881,9660,9661,10472,10473,11316,11317,12192,12193,13100,13101,14040,0,14041,15012,15013,16016,16017,17052,17053,18120,18121,19220,19221, - 19873,19872,18757,18756,17673,17672,16621,16620,15601,15600,14613,1,14612,13657,13656,12733,12732,11841,11840,10981,10980,10153,10152,9357,9356,8593,8592,1,7861,7860,7161,7160,6493,6492,5857,5856,5253,5252,4681,4680,4141,4140,3633,1,3632,3157,3156,2713,2712,2301,2300,1921,1920,1571,1573,1575,1577,1579,1581,1,1583,1585,1587,1589,1591,1593,1595,1597,1599,1601,1603,1605,1607,1609,1611,1,1613,1615,1617,1619,1621,1623,1625,1627,1629,1631,1633,1635,1637,1639,1641,1,1643,1645,1647,1649,1651,1653,1655,1657,2018,2019,2414,2415,2842,2843,3302,1,3303,3794,3795,4318,4319,4874,4875,5462,5463,6082,6083,6734,6735,7418,7419,1,8134,8135,8882,8883,9662,9663,10474,10475,11318,11319,12194,12195,13102,13103,14042,1,14043,15014,15015,16018,16019,17054,17055,18122,18123,19222,19223, - 19871,19870,18755,18754,17671,17670,16619,16618,15599,15598,14611,0,14610,13655,13654,12731,12730,11839,11838,10979,10978,10151,10150,9355,9354,8591,8590,0,7859,7858,7159,7158,6491,6490,5855,5854,5251,5250,4679,4678,4139,4138,3631,0,3630,3155,3154,2711,2710,2299,2298,1919,1918,1570,1572,1574,1576,1578,1580,0,1582,1584,1586,1588,1590,1592,1594,1596,1598,1600,1602,1604,1606,1608,1610,0,1612,1614,1616,1618,1620,1622,1624,1626,1628,1630,1632,1634,1636,1638,1640,0,1642,1644,1646,1648,1650,1652,1654,1656,2020,2021,2416,2417,2844,2845,3304,0,3305,3796,3797,4320,4321,4876,4877,5464,5465,6084,6085,6736,6737,7420,7421,0,8136,8137,8884,8885,9664,9665,10476,10477,11320,11321,12196,12197,13104,13105,14044,0,14045,15016,15017,16020,16021,17056,17057,18124,18125,19224,19225, - 19869,19868,18753,18752,17669,17668,16617,16616,15597,15596,14609,1,14608,13653,13652,12729,12728,11837,11836,10977,10976,10149,10148,9353,9352,8589,8588,1,7857,7856,7157,7156,6489,6488,5853,5852,5249,5248,4677,4676,4137,4136,3629,1,3628,3153,3152,2709,2708,2297,2296,1917,1916,1569,1568,1251,1253,1255,1257,1,1259,1261,1263,1265,1267,1269,1271,1273,1275,1277,1279,1281,1283,1285,1287,1,1289,1291,1293,1295,1297,1299,1301,1303,1305,1307,1309,1311,1313,1315,1317,1,1319,1321,1323,1325,1327,1329,1658,1659,2022,2023,2418,2419,2846,2847,3306,1,3307,3798,3799,4322,4323,4878,4879,5466,5467,6086,6087,6738,6739,7422,7423,1,8138,8139,8886,8887,9666,9667,10478,10479,11322,11323,12198,12199,13106,13107,14046,1,14047,15018,15019,16022,16023,17058,17059,18126,18127,19226,19227, - 19867,19866,18751,18750,17667,17666,16615,16614,15595,15594,14607,0,14606,13651,13650,12727,12726,11835,11834,10975,10974,10147,10146,9351,9350,8587,8586,0,7855,7854,7155,7154,6487,6486,5851,5850,5247,5246,4675,4674,4135,4134,3627,0,3626,3151,3150,2707,2706,2295,2294,1915,1914,1567,1566,1250,1252,1254,1256,0,1258,1260,1262,1264,1266,1268,1270,1272,1274,1276,1278,1280,1282,1284,1286,0,1288,1290,1292,1294,1296,1298,1300,1302,1304,1306,1308,1310,1312,1314,1316,0,1318,1320,1322,1324,1326,1328,1660,1661,2024,2025,2420,2421,2848,2849,3308,0,3309,3800,3801,4324,4325,4880,4881,5468,5469,6088,6089,6740,6741,7424,7425,0,8140,8141,8888,8889,9668,9669,10480,10481,11324,11325,12200,12201,13108,13109,14048,0,14049,15020,15021,16024,16025,17060,17061,18128,18129,19228,19229, - 19865,19864,18749,18748,17665,17664,16613,16612,15593,15592,14605,1,14604,13649,13648,12725,12724,11833,11832,10973,10972,10145,10144,9349,9348,8585,8584,1,7853,7852,7153,7152,6485,6484,5849,5848,5245,5244,4673,4672,4133,4132,3625,1,3624,3149,3148,2705,2704,2293,2292,1913,1912,1565,1564,1249,1248,963,965,1,967,969,971,973,975,977,979,981,983,985,987,989,991,993,995,1,997,999,1001,1003,1005,1007,1009,1011,1013,1015,1017,1019,1021,1023,1025,1,1027,1029,1031,1033,1330,1331,1662,1663,2026,2027,2422,2423,2850,2851,3310,1,3311,3802,3803,4326,4327,4882,4883,5470,5471,6090,6091,6742,6743,7426,7427,1,8142,8143,8890,8891,9670,9671,10482,10483,11326,11327,12202,12203,13110,13111,14050,1,14051,15022,15023,16026,16027,17062,17063,18130,18131,19230,19231, - 19863,19862,18747,18746,17663,17662,16611,16610,15591,15590,14603,0,14602,13647,13646,12723,12722,11831,11830,10971,10970,10143,10142,9347,9346,8583,8582,0,7851,7850,7151,7150,6483,6482,5847,5846,5243,5242,4671,4670,4131,4130,3623,0,3622,3147,3146,2703,2702,2291,2290,1911,1910,1563,1562,1247,1246,962,964,0,966,968,970,972,974,976,978,980,982,984,986,988,990,992,994,0,996,998,1000,1002,1004,1006,1008,1010,1012,1014,1016,1018,1020,1022,1024,0,1026,1028,1030,1032,1332,1333,1664,1665,2028,2029,2424,2425,2852,2853,3312,0,3313,3804,3805,4328,4329,4884,4885,5472,5473,6092,6093,6744,6745,7428,7429,0,8144,8145,8892,8893,9672,9673,10484,10485,11328,11329,12204,12205,13112,13113,14052,0,14053,15024,15025,16028,16029,17064,17065,18132,18133,19232,19233, - 19861,19860,18745,18744,17661,17660,16609,16608,15589,15588,14601,1,14600,13645,13644,12721,12720,11829,11828,10969,10968,10141,10140,9345,9344,8581,8580,1,7849,7848,7149,7148,6481,6480,5845,5844,5241,5240,4669,4668,4129,4128,3621,1,3620,3145,3144,2701,2700,2289,2288,1909,1908,1561,1560,1245,1244,961,960,1,707,709,711,713,715,717,719,721,723,725,727,729,731,733,735,1,737,739,741,743,745,747,749,751,753,755,757,759,761,763,765,1,767,769,1034,1035,1334,1335,1666,1667,2030,2031,2426,2427,2854,2855,3314,1,3315,3806,3807,4330,4331,4886,4887,5474,5475,6094,6095,6746,6747,7430,7431,1,8146,8147,8894,8895,9674,9675,10486,10487,11330,11331,12206,12207,13114,13115,14054,1,14055,15026,15027,16030,16031,17066,17067,18134,18135,19234,19235, - 19859,19858,18743,18742,17659,17658,16607,16606,15587,15586,14599,0,14598,13643,13642,12719,12718,11827,11826,10967,10966,10139,10138,9343,9342,8579,8578,0,7847,7846,7147,7146,6479,6478,5843,5842,5239,5238,4667,4666,4127,4126,3619,0,3618,3143,3142,2699,2698,2287,2286,1907,1906,1559,1558,1243,1242,959,958,0,706,708,710,712,714,716,718,720,722,724,726,728,730,732,734,0,736,738,740,742,744,746,748,750,752,754,756,758,760,762,764,0,766,768,1036,1037,1336,1337,1668,1669,2032,2033,2428,2429,2856,2857,3316,0,3317,3808,3809,4332,4333,4888,4889,5476,5477,6096,6097,6748,6749,7432,7433,0,8148,8149,8896,8897,9676,9677,10488,10489,11332,11333,12208,12209,13116,13117,14056,0,14057,15028,15029,16032,16033,17068,17069,18136,18137,19236,19237, - 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, - 19857,19856,18741,18740,17657,17656,16605,16604,15585,15584,14597,0,14596,13641,13640,12717,12716,11825,11824,10965,10964,10137,10136,9341,9340,8577,8576,0,7845,7844,7145,7144,6477,6476,5841,5840,5237,5236,4665,4664,4125,4124,3617,0,3616,3141,3140,2697,2696,2285,2284,1905,1904,1557,1556,1241,1240,957,956,0,705,704,483,485,487,489,491,493,495,497,499,501,503,505,507,0,509,511,513,515,517,519,521,523,525,527,529,531,533,535,537,0,770,771,1038,1039,1338,1339,1670,1671,2034,2035,2430,2431,2858,2859,3318,0,3319,3810,3811,4334,4335,4890,4891,5478,5479,6098,6099,6750,6751,7434,7435,0,8150,8151,8898,8899,9678,9679,10490,10491,11334,11335,12210,12211,13118,13119,14058,0,14059,15030,15031,16034,16035,17070,17071,18138,18139,19238,19239, - 19855,19854,18739,18738,17655,17654,16603,16602,15583,15582,14595,1,14594,13639,13638,12715,12714,11823,11822,10963,10962,10135,10134,9339,9338,8575,8574,1,7843,7842,7143,7142,6475,6474,5839,5838,5235,5234,4663,4662,4123,4122,3615,1,3614,3139,3138,2695,2694,2283,2282,1903,1902,1555,1554,1239,1238,955,954,1,703,702,482,484,486,488,490,492,494,496,498,500,502,504,506,1,508,510,512,514,516,518,520,522,524,526,528,530,532,534,536,1,772,773,1040,1041,1340,1341,1672,1673,2036,2037,2432,2433,2860,2861,3320,1,3321,3812,3813,4336,4337,4892,4893,5480,5481,6100,6101,6752,6753,7436,7437,1,8152,8153,8900,8901,9680,9681,10492,10493,11336,11337,12212,12213,13120,13121,14060,1,14061,15032,15033,16036,16037,17072,17073,18140,18141,19240,19241, - 19853,19852,18737,18736,17653,17652,16601,16600,15581,15580,14593,0,14592,13637,13636,12713,12712,11821,11820,10961,10960,10133,10132,9337,9336,8573,8572,0,7841,7840,7141,7140,6473,6472,5837,5836,5233,5232,4661,4660,4121,4120,3613,0,3612,3137,3136,2693,2692,2281,2280,1901,1900,1553,1552,1237,1236,953,952,0,701,700,481,480,291,293,295,297,299,301,303,305,307,309,311,0,313,315,317,319,321,323,325,327,329,331,333,335,337,538,539,0,774,775,1042,1043,1342,1343,1674,1675,2038,2039,2434,2435,2862,2863,3322,0,3323,3814,3815,4338,4339,4894,4895,5482,5483,6102,6103,6754,6755,7438,7439,0,8154,8155,8902,8903,9682,9683,10494,10495,11338,11339,12214,12215,13122,13123,14062,0,14063,15034,15035,16038,16039,17074,17075,18142,18143,19242,19243, - 19851,19850,18735,18734,17651,17650,16599,16598,15579,15578,14591,1,14590,13635,13634,12711,12710,11819,11818,10959,10958,10131,10130,9335,9334,8571,8570,1,7839,7838,7139,7138,6471,6470,5835,5834,5231,5230,4659,4658,4119,4118,3611,1,3610,3135,3134,2691,2690,2279,2278,1899,1898,1551,1550,1235,1234,951,950,1,699,698,479,478,290,292,294,296,298,300,302,304,306,308,310,1,312,314,316,318,320,322,324,326,328,330,332,334,336,540,541,1,776,777,1044,1045,1344,1345,1676,1677,2040,2041,2436,2437,2864,2865,3324,1,3325,3816,3817,4340,4341,4896,4897,5484,5485,6104,6105,6756,6757,7440,7441,1,8156,8157,8904,8905,9684,9685,10496,10497,11340,11341,12216,12217,13124,13125,14064,1,14065,15036,15037,16040,16041,17076,17077,18144,18145,19244,19245, - 19849,19848,18733,18732,17649,17648,16597,16596,15577,15576,14589,0,14588,13633,13632,12709,12708,11817,11816,10957,10956,10129,10128,9333,9332,8569,8568,0,7837,7836,7137,7136,6469,6468,5833,5832,5229,5228,4657,4656,4117,4116,3609,0,3608,3133,3132,2689,2688,2277,2276,1897,1896,1549,1548,1233,1232,949,948,0,697,696,477,476,289,288,131,133,135,137,139,141,143,145,147,0,149,151,153,155,157,159,161,163,165,167,169,338,339,542,543,0,778,779,1046,1047,1346,1347,1678,1679,2042,2043,2438,2439,2866,2867,3326,0,3327,3818,3819,4342,4343,4898,4899,5486,5487,6106,6107,6758,6759,7442,7443,0,8158,8159,8906,8907,9686,9687,10498,10499,11342,11343,12218,12219,13126,13127,14066,0,14067,15038,15039,16042,16043,17078,17079,18146,18147,19246,19247, - 19847,19846,18731,18730,17647,17646,16595,16594,15575,15574,14587,1,14586,13631,13630,12707,12706,11815,11814,10955,10954,10127,10126,9331,9330,8567,8566,1,7835,7834,7135,7134,6467,6466,5831,5830,5227,5226,4655,4654,4115,4114,3607,1,3606,3131,3130,2687,2686,2275,2274,1895,1894,1547,1546,1231,1230,947,946,1,695,694,475,474,287,286,130,132,134,136,138,140,142,144,146,1,148,150,152,154,156,158,160,162,164,166,168,340,341,544,545,1,780,781,1048,1049,1348,1349,1680,1681,2044,2045,2440,2441,2868,2869,3328,1,3329,3820,3821,4344,4345,4900,4901,5488,5489,6108,6109,6760,6761,7444,7445,1,8160,8161,8908,8909,9688,9689,10500,10501,11344,11345,12220,12221,13128,13129,14068,1,14069,15040,15041,16044,16045,17080,17081,18148,18149,19248,19249, - 19845,19844,18729,18728,17645,17644,16593,16592,15573,15572,14585,0,14584,13629,13628,12705,12704,11813,11812,10953,10952,10125,10124,9329,9328,8565,8564,0,7833,7832,7133,7132,6465,6464,5829,5828,5225,5224,4653,4652,4113,4112,3605,0,3604,3129,3128,2685,2684,2273,2272,1893,1892,1545,1544,1229,1228,945,944,0,693,692,473,472,285,284,129,128,3,5,7,9,11,13,15,0,17,19,21,23,25,27,29,31,33,170,171,342,343,546,547,0,782,783,1050,1051,1350,1351,1682,1683,2046,2047,2442,2443,2870,2871,3330,0,3331,3822,3823,4346,4347,4902,4903,5490,5491,6110,6111,6762,6763,7446,7447,0,8162,8163,8910,8911,9690,9691,10502,10503,11346,11347,12222,12223,13130,13131,14070,0,14071,15042,15043,16046,16047,17082,17083,18150,18151,19250,19251, - 19843,19842,18727,18726,17643,17642,16591,16590,15571,15570,14583,1,14582,13627,13626,12703,12702,11811,11810,10951,10950,10123,10122,9327,9326,8563,8562,1,7831,7830,7131,7130,6463,6462,5827,5826,5223,5222,4651,4650,4111,4110,3603,1,3602,3127,3126,2683,2682,2271,2270,1891,1890,1543,1542,1227,1226,943,942,1,691,690,471,470,283,282,127,126,2,4,6,8,10,12,14,1,16,18,20,22,24,26,28,30,32,172,173,344,345,548,549,1,784,785,1052,1053,1352,1353,1684,1685,2048,2049,2444,2445,2872,2873,3332,1,3333,3824,3825,4348,4349,4904,4905,5492,5493,6112,6113,6764,6765,7448,7449,1,8164,8165,8912,8913,9692,9693,10504,10505,11348,11349,12224,12225,13132,13133,14072,1,14073,15044,15045,16048,16049,17084,17085,18152,18153,19252,19253, - 19841,19840,18725,18724,17641,17640,16589,16588,15569,15568,14581,0,14580,13625,13624,12701,12700,11809,11808,10949,10948,10121,10120,9325,9324,8561,8560,0,7829,7828,7129,7128,6461,6460,5825,5824,5221,5220,4649,4648,4109,4108,3601,0,3600,3125,3124,2681,2680,2269,2268,1889,1888,1541,1540,1225,1224,941,940,0,689,688,469,468,281,280,125,124,1,1,20000,20001,20002,20003,20004,0,20005,20006,20007,20008,20009,0,1,34,35,174,175,346,347,550,551,0,786,787,1054,1055,1354,1355,1686,1687,2050,2051,2446,2447,2874,2875,3334,0,3335,3826,3827,4350,4351,4906,4907,5494,5495,6114,6115,6766,6767,7450,7451,0,8166,8167,8914,8915,9694,9695,10506,10507,11350,11351,12226,12227,13134,13135,14074,0,14075,15046,15047,16050,16051,17086,17087,18154,18155,19254,19255, - 19839,19838,18723,18722,17639,17638,16587,16586,15567,15566,14579,1,14578,13623,13622,12699,12698,11807,11806,10947,10946,10119,10118,9323,9322,8559,8558,1,7827,7826,7127,7126,6459,6458,5823,5822,5219,5218,4647,4646,4107,4106,3599,1,3598,3123,3122,2679,2678,2267,2266,1887,1886,1539,1538,1223,1222,939,938,1,687,686,467,466,279,278,123,122,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,36,37,176,177,348,349,552,553,1,788,789,1056,1057,1356,1357,1688,1689,2052,2053,2448,2449,2876,2877,3336,1,3337,3828,3829,4352,4353,4908,4909,5496,5497,6116,6117,6768,6769,7452,7453,1,8168,8169,8916,8917,9696,9697,10508,10509,11352,11353,12228,12229,13136,13137,14076,1,14077,15048,15049,16052,16053,17088,17089,18156,18157,19256,19257, - 19837,19836,18721,18720,17637,17636,16585,16584,15565,15564,14577,0,14576,13621,13620,12697,12696,11805,11804,10945,10944,10117,10116,9321,9320,8557,8556,0,7825,7824,7125,7124,6457,6456,5821,5820,5217,5216,4645,4644,4105,4104,3597,0,3596,3121,3120,2677,2676,2265,2264,1885,1884,1537,1536,1221,1220,937,936,0,685,684,465,464,277,276,121,120,20039,1,0,0,0,0,0,0,0,0,0,0,0,1,20010,38,39,178,179,350,351,554,555,0,790,791,1058,1059,1358,1359,1690,1691,2054,2055,2450,2451,2878,2879,3338,0,3339,3830,3831,4354,4355,4910,4911,5498,5499,6118,6119,6770,6771,7454,7455,0,8170,8171,8918,8919,9698,9699,10510,10511,11354,11355,12230,12231,13138,13139,14078,0,14079,15050,15051,16054,16055,17090,17091,18158,18159,19258,19259, - 19835,19834,18719,18718,17635,17634,16583,16582,15563,15562,14575,1,14574,13619,13618,12695,12694,11803,11802,10943,10942,10115,10114,9319,9318,8555,8554,1,7823,7822,7123,7122,6455,6454,5819,5818,5215,5214,4643,4642,4103,4102,3595,1,3594,3119,3118,2675,2674,2263,2262,1883,1882,1535,1534,1219,1218,935,934,1,683,682,463,462,275,274,119,118,20038,1,0,1,1,1,1,1,1,1,1,1,0,1,20011,40,41,180,181,352,353,556,557,1,792,793,1060,1061,1360,1361,1692,1693,2056,2057,2452,2453,2880,2881,3340,1,3341,3832,3833,4356,4357,4912,4913,5500,5501,6120,6121,6772,6773,7456,7457,1,8172,8173,8920,8921,9700,9701,10512,10513,11356,11357,12232,12233,13140,13141,14080,1,14081,15052,15053,16056,16057,17092,17093,18160,18161,19260,19261, - 19833,19832,18717,18716,17633,17632,16581,16580,15561,15560,14573,0,14572,13617,13616,12693,12692,11801,11800,10941,10940,10113,10112,9317,9316,8553,8552,0,7821,7820,7121,7120,6453,6452,5817,5816,5213,5212,4641,4640,4101,4100,3593,0,3592,3117,3116,2673,2672,2261,2260,1881,1880,1533,1532,1217,1216,933,932,0,681,680,461,460,273,272,117,116,20037,1,0,1,0,0,0,0,0,0,0,1,0,1,20012,42,43,182,183,354,355,558,559,0,794,795,1062,1063,1362,1363,1694,1695,2058,2059,2454,2455,2882,2883,3342,0,3343,3834,3835,4358,4359,4914,4915,5502,5503,6122,6123,6774,6775,7458,7459,0,8174,8175,8922,8923,9702,9703,10514,10515,11358,11359,12234,12235,13142,13143,14082,0,14083,15054,15055,16058,16059,17094,17095,18162,18163,19262,19263, - 19831,19830,18715,18714,17631,17630,16579,16578,15559,15558,14571,1,14570,13615,13614,12691,12690,11799,11798,10939,10938,10111,10110,9315,9314,8551,8550,1,7819,7818,7119,7118,6451,6450,5815,5814,5211,5210,4639,4638,4099,4098,3591,1,3590,3115,3114,2671,2670,2259,2258,1879,1878,1531,1530,1215,1214,931,930,1,679,678,459,458,271,270,115,114,20036,1,0,1,0,1,1,1,1,1,0,1,0,1,20013,44,45,184,185,356,357,560,561,1,796,797,1064,1065,1364,1365,1696,1697,2060,2061,2456,2457,2884,2885,3344,1,3345,3836,3837,4360,4361,4916,4917,5504,5505,6124,6125,6776,6777,7460,7461,1,8176,8177,8924,8925,9704,9705,10516,10517,11360,11361,12236,12237,13144,13145,14084,1,14085,15056,15057,16060,16061,17096,17097,18164,18165,19264,19265, - 19829,19828,18713,18712,17629,17628,16577,16576,15557,15556,14569,0,14568,13613,13612,12689,12688,11797,11796,10937,10936,10109,10108,9313,9312,8549,8548,0,7817,7816,7117,7116,6449,6448,5813,5812,5209,5208,4637,4636,4097,4096,3589,0,3588,3113,3112,2669,2668,2257,2256,1877,1876,1529,1528,1213,1212,929,928,0,677,676,457,456,269,268,113,112,20035,1,0,1,0,1,0,0,0,1,0,1,0,1,20014,46,47,186,187,358,359,562,563,0,798,799,1066,1067,1366,1367,1698,1699,2062,2063,2458,2459,2886,2887,3346,0,3347,3838,3839,4362,4363,4918,4919,5506,5507,6126,6127,6778,6779,7462,7463,0,8178,8179,8926,8927,9706,9707,10518,10519,11362,11363,12238,12239,13146,13147,14086,0,14087,15058,15059,16062,16063,17098,17099,18166,18167,19266,19267, - 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, - 19827,19826,18711,18710,17627,17626,16575,16574,15555,15554,14567,0,14566,13611,13610,12687,12686,11795,11794,10935,10934,10107,10106,9311,9310,8547,8546,0,7815,7814,7115,7114,6447,6446,5811,5810,5207,5206,4635,4634,4095,4094,3587,0,3586,3111,3110,2667,2666,2255,2254,1875,1874,1527,1526,1211,1210,927,926,0,675,674,455,454,267,266,111,110,20034,1,0,1,0,1,0,0,0,1,0,1,0,1,20015,48,49,188,189,360,361,564,565,0,800,801,1068,1069,1368,1369,1700,1701,2064,2065,2460,2461,2888,2889,3348,0,3349,3840,3841,4364,4365,4920,4921,5508,5509,6128,6129,6780,6781,7464,7465,0,8180,8181,8928,8929,9708,9709,10520,10521,11364,11365,12240,12241,13148,13149,14088,0,14089,15060,15061,16064,16065,17100,17101,18168,18169,19268,19269, - 19825,19824,18709,18708,17625,17624,16573,16572,15553,15552,14565,1,14564,13609,13608,12685,12684,11793,11792,10933,10932,10105,10104,9309,9308,8545,8544,1,7813,7812,7113,7112,6445,6444,5809,5808,5205,5204,4633,4632,4093,4092,3585,1,3584,3109,3108,2665,2664,2253,2252,1873,1872,1525,1524,1209,1208,925,924,1,673,672,453,452,265,264,109,108,20033,1,0,1,0,1,1,1,1,1,0,1,0,1,20016,50,51,190,191,362,363,566,567,1,802,803,1070,1071,1370,1371,1702,1703,2066,2067,2462,2463,2890,2891,3350,1,3351,3842,3843,4366,4367,4922,4923,5510,5511,6130,6131,6782,6783,7466,7467,1,8182,8183,8930,8931,9710,9711,10522,10523,11366,11367,12242,12243,13150,13151,14090,1,14091,15062,15063,16066,16067,17102,17103,18170,18171,19270,19271, - 19823,19822,18707,18706,17623,17622,16571,16570,15551,15550,14563,0,14562,13607,13606,12683,12682,11791,11790,10931,10930,10103,10102,9307,9306,8543,8542,0,7811,7810,7111,7110,6443,6442,5807,5806,5203,5202,4631,4630,4091,4090,3583,0,3582,3107,3106,2663,2662,2251,2250,1871,1870,1523,1522,1207,1206,923,922,0,671,670,451,450,263,262,107,106,20032,1,0,1,0,0,0,0,0,0,0,1,0,1,20017,52,53,192,193,364,365,568,569,0,804,805,1072,1073,1372,1373,1704,1705,2068,2069,2464,2465,2892,2893,3352,0,3353,3844,3845,4368,4369,4924,4925,5512,5513,6132,6133,6784,6785,7468,7469,0,8184,8185,8932,8933,9712,9713,10524,10525,11368,11369,12244,12245,13152,13153,14092,0,14093,15064,15065,16068,16069,17104,17105,18172,18173,19272,19273, - 19821,19820,18705,18704,17621,17620,16569,16568,15549,15548,14561,1,14560,13605,13604,12681,12680,11789,11788,10929,10928,10101,10100,9305,9304,8541,8540,1,7809,7808,7109,7108,6441,6440,5805,5804,5201,5200,4629,4628,4089,4088,3581,1,3580,3105,3104,2661,2660,2249,2248,1869,1868,1521,1520,1205,1204,921,920,1,669,668,449,448,261,260,105,104,20031,1,0,1,1,1,1,1,1,1,1,1,0,1,20018,54,55,194,195,366,367,570,571,1,806,807,1074,1075,1374,1375,1706,1707,2070,2071,2466,2467,2894,2895,3354,1,3355,3846,3847,4370,4371,4926,4927,5514,5515,6134,6135,6786,6787,7470,7471,1,8186,8187,8934,8935,9714,9715,10526,10527,11370,11371,12246,12247,13154,13155,14094,1,14095,15066,15067,16070,16071,17106,17107,18174,18175,19274,19275, - 19819,19818,18703,18702,17619,17618,16567,16566,15547,15546,14559,0,14558,13603,13602,12679,12678,11787,11786,10927,10926,10099,10098,9303,9302,8539,8538,0,7807,7806,7107,7106,6439,6438,5803,5802,5199,5198,4627,4626,4087,4086,3579,0,3578,3103,3102,2659,2658,2247,2246,1867,1866,1519,1518,1203,1202,919,918,0,667,666,447,446,259,258,103,102,20030,1,0,0,0,0,0,0,0,0,0,0,0,1,20019,56,57,196,197,368,369,572,573,0,808,809,1076,1077,1376,1377,1708,1709,2072,2073,2468,2469,2896,2897,3356,0,3357,3848,3849,4372,4373,4928,4929,5516,5517,6136,6137,6788,6789,7472,7473,0,8188,8189,8936,8937,9716,9717,10528,10529,11372,11373,12248,12249,13156,13157,14096,0,14097,15068,15069,16072,16073,17108,17109,18176,18177,19276,19277, - 19817,19816,18701,18700,17617,17616,16565,16564,15545,15544,14557,1,14556,13601,13600,12677,12676,11785,11784,10925,10924,10097,10096,9301,9300,8537,8536,1,7805,7804,7105,7104,6437,6436,5801,5800,5197,5196,4625,4624,4085,4084,3577,1,3576,3101,3100,2657,2656,2245,2244,1865,1864,1517,1516,1201,1200,917,916,1,665,664,445,444,257,256,101,100,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,58,59,198,199,370,371,574,575,1,810,811,1078,1079,1378,1379,1710,1711,2074,2075,2470,2471,2898,2899,3358,1,3359,3850,3851,4374,4375,4930,4931,5518,5519,6138,6139,6790,6791,7474,7475,1,8190,8191,8938,8939,9718,9719,10530,10531,11374,11375,12250,12251,13158,13159,14098,1,14099,15070,15071,16074,16075,17110,17111,18178,18179,19278,19279, - 19815,19814,18699,18698,17615,17614,16563,16562,15543,15542,14555,0,14554,13599,13598,12675,12674,11783,11782,10923,10922,10095,10094,9299,9298,8535,8534,0,7803,7802,7103,7102,6435,6434,5799,5798,5195,5194,4623,4622,4083,4082,3575,0,3574,3099,3098,2655,2654,2243,2242,1863,1862,1515,1514,1199,1198,915,914,0,663,662,443,442,255,254,99,98,0,0,20029,20028,20027,20026,20025,0,20024,20023,20022,20021,20020,0,0,60,61,200,201,372,373,576,577,0,812,813,1080,1081,1380,1381,1712,1713,2076,2077,2472,2473,2900,2901,3360,0,3361,3852,3853,4376,4377,4932,4933,5520,5521,6140,6141,6792,6793,7476,7477,0,8192,8193,8940,8941,9720,9721,10532,10533,11376,11377,12252,12253,13160,13161,14100,0,14101,15072,15073,16076,16077,17112,17113,18180,18181,19280,19281, - 19813,19812,18697,18696,17613,17612,16561,16560,15541,15540,14553,1,14552,13597,13596,12673,12672,11781,11780,10921,10920,10093,10092,9297,9296,8533,8532,1,7801,7800,7101,7100,6433,6432,5797,5796,5193,5192,4621,4620,4081,4080,3573,1,3572,3097,3096,2653,2652,2241,2240,1861,1860,1513,1512,1197,1196,913,912,1,661,660,441,440,253,252,96,94,92,90,88,86,84,82,80,1,78,76,74,72,70,68,66,62,63,202,203,374,375,578,579,1,814,815,1082,1083,1382,1383,1714,1715,2078,2079,2474,2475,2902,2903,3362,1,3363,3854,3855,4378,4379,4934,4935,5522,5523,6142,6143,6794,6795,7478,7479,1,8194,8195,8942,8943,9722,9723,10534,10535,11378,11379,12254,12255,13162,13163,14102,1,14103,15074,15075,16078,16079,17114,17115,18182,18183,19282,19283, - 19811,19810,18695,18694,17611,17610,16559,16558,15539,15538,14551,0,14550,13595,13594,12671,12670,11779,11778,10919,10918,10091,10090,9295,9294,8531,8530,0,7799,7798,7099,7098,6431,6430,5795,5794,5191,5190,4619,4618,4079,4078,3571,0,3570,3095,3094,2651,2650,2239,2238,1859,1858,1511,1510,1195,1194,911,910,0,659,658,439,438,251,250,97,95,93,91,89,87,85,83,81,0,79,77,75,73,71,69,67,64,65,204,205,376,377,580,581,0,816,817,1084,1085,1384,1385,1716,1717,2080,2081,2476,2477,2904,2905,3364,0,3365,3856,3857,4380,4381,4936,4937,5524,5525,6144,6145,6796,6797,7480,7481,0,8196,8197,8944,8945,9724,9725,10536,10537,11380,11381,12256,12257,13164,13165,14104,0,14105,15076,15077,16080,16081,17116,17117,18184,18185,19284,19285, - 19809,19808,18693,18692,17609,17608,16557,16556,15537,15536,14549,1,14548,13593,13592,12669,12668,11777,11776,10917,10916,10089,10088,9293,9292,8529,8528,1,7797,7796,7097,7096,6429,6428,5793,5792,5189,5188,4617,4616,4077,4076,3569,1,3568,3093,3092,2649,2648,2237,2236,1857,1856,1509,1508,1193,1192,909,908,1,657,656,437,436,248,246,244,242,240,238,236,234,232,230,228,1,226,224,222,220,218,216,214,212,210,206,207,378,379,582,583,1,818,819,1086,1087,1386,1387,1718,1719,2082,2083,2478,2479,2906,2907,3366,1,3367,3858,3859,4382,4383,4938,4939,5526,5527,6146,6147,6798,6799,7482,7483,1,8198,8199,8946,8947,9726,9727,10538,10539,11382,11383,12258,12259,13166,13167,14106,1,14107,15078,15079,16082,16083,17118,17119,18186,18187,19286,19287, - 19807,19806,18691,18690,17607,17606,16555,16554,15535,15534,14547,0,14546,13591,13590,12667,12666,11775,11774,10915,10914,10087,10086,9291,9290,8527,8526,0,7795,7794,7095,7094,6427,6426,5791,5790,5187,5186,4615,4614,4075,4074,3567,0,3566,3091,3090,2647,2646,2235,2234,1855,1854,1507,1506,1191,1190,907,906,0,655,654,435,434,249,247,245,243,241,239,237,235,233,231,229,0,227,225,223,221,219,217,215,213,211,208,209,380,381,584,585,0,820,821,1088,1089,1388,1389,1720,1721,2084,2085,2480,2481,2908,2909,3368,0,3369,3860,3861,4384,4385,4940,4941,5528,5529,6148,6149,6800,6801,7484,7485,0,8200,8201,8948,8949,9728,9729,10540,10541,11384,11385,12260,12261,13168,13169,14108,0,14109,15080,15081,16084,16085,17120,17121,18188,18189,19288,19289, - 19805,19804,18689,18688,17605,17604,16553,16552,15533,15532,14545,1,14544,13589,13588,12665,12664,11773,11772,10913,10912,10085,10084,9289,9288,8525,8524,1,7793,7792,7093,7092,6425,6424,5789,5788,5185,5184,4613,4612,4073,4072,3565,1,3564,3089,3088,2645,2644,2233,2232,1853,1852,1505,1504,1189,1188,905,904,1,653,652,432,430,428,426,424,422,420,418,416,414,412,410,408,1,406,404,402,400,398,396,394,392,390,388,386,382,383,586,587,1,822,823,1090,1091,1390,1391,1722,1723,2086,2087,2482,2483,2910,2911,3370,1,3371,3862,3863,4386,4387,4942,4943,5530,5531,6150,6151,6802,6803,7486,7487,1,8202,8203,8950,8951,9730,9731,10542,10543,11386,11387,12262,12263,13170,13171,14110,1,14111,15082,15083,16086,16087,17122,17123,18190,18191,19290,19291, - 19803,19802,18687,18686,17603,17602,16551,16550,15531,15530,14543,0,14542,13587,13586,12663,12662,11771,11770,10911,10910,10083,10082,9287,9286,8523,8522,0,7791,7790,7091,7090,6423,6422,5787,5786,5183,5182,4611,4610,4071,4070,3563,0,3562,3087,3086,2643,2642,2231,2230,1851,1850,1503,1502,1187,1186,903,902,0,651,650,433,431,429,427,425,423,421,419,417,415,413,411,409,0,407,405,403,401,399,397,395,393,391,389,387,384,385,588,589,0,824,825,1092,1093,1392,1393,1724,1725,2088,2089,2484,2485,2912,2913,3372,0,3373,3864,3865,4388,4389,4944,4945,5532,5533,6152,6153,6804,6805,7488,7489,0,8204,8205,8952,8953,9732,9733,10544,10545,11388,11389,12264,12265,13172,13173,14112,0,14113,15084,15085,16088,16089,17124,17125,18192,18193,19292,19293, - 19801,19800,18685,18684,17601,17600,16549,16548,15529,15528,14541,1,14540,13585,13584,12661,12660,11769,11768,10909,10908,10081,10080,9285,9284,8521,8520,1,7789,7788,7089,7088,6421,6420,5785,5784,5181,5180,4609,4608,4069,4068,3561,1,3560,3085,3084,2641,2640,2229,2228,1849,1848,1501,1500,1185,1184,901,900,1,648,646,644,642,640,638,636,634,632,630,628,626,624,622,620,1,618,616,614,612,610,608,606,604,602,600,598,596,594,590,591,1,826,827,1094,1095,1394,1395,1726,1727,2090,2091,2486,2487,2914,2915,3374,1,3375,3866,3867,4390,4391,4946,4947,5534,5535,6154,6155,6806,6807,7490,7491,1,8206,8207,8954,8955,9734,9735,10546,10547,11390,11391,12266,12267,13174,13175,14114,1,14115,15086,15087,16090,16091,17126,17127,18194,18195,19294,19295, - 19799,19798,18683,18682,17599,17598,16547,16546,15527,15526,14539,0,14538,13583,13582,12659,12658,11767,11766,10907,10906,10079,10078,9283,9282,8519,8518,0,7787,7786,7087,7086,6419,6418,5783,5782,5179,5178,4607,4606,4067,4066,3559,0,3558,3083,3082,2639,2638,2227,2226,1847,1846,1499,1498,1183,1182,899,898,0,649,647,645,643,641,639,637,635,633,631,629,627,625,623,621,0,619,617,615,613,611,609,607,605,603,601,599,597,595,592,593,0,828,829,1096,1097,1396,1397,1728,1729,2092,2093,2488,2489,2916,2917,3376,0,3377,3868,3869,4392,4393,4948,4949,5536,5537,6156,6157,6808,6809,7492,7493,0,8208,8209,8956,8957,9736,9737,10548,10549,11392,11393,12268,12269,13176,13177,14116,0,14117,15088,15089,16092,16093,17128,17129,18196,18197,19296,19297, - 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, - 19797,19796,18681,18680,17597,17596,16545,16544,15525,15524,14537,0,14536,13581,13580,12657,12656,11765,11764,10905,10904,10077,10076,9281,9280,8517,8516,0,7785,7784,7085,7084,6417,6416,5781,5780,5177,5176,4605,4604,4065,4064,3557,0,3556,3081,3080,2637,2636,2225,2224,1845,1844,1497,1496,1181,1180,896,894,0,892,890,888,886,884,882,880,878,876,874,872,870,868,866,864,0,862,860,858,856,854,852,850,848,846,844,842,840,838,836,834,0,830,831,1098,1099,1398,1399,1730,1731,2094,2095,2490,2491,2918,2919,3378,0,3379,3870,3871,4394,4395,4950,4951,5538,5539,6158,6159,6810,6811,7494,7495,0,8210,8211,8958,8959,9738,9739,10550,10551,11394,11395,12270,12271,13178,13179,14118,0,14119,15090,15091,16094,16095,17130,17131,18198,18199,19298,19299, - 19795,19794,18679,18678,17595,17594,16543,16542,15523,15522,14535,1,14534,13579,13578,12655,12654,11763,11762,10903,10902,10075,10074,9279,9278,8515,8514,1,7783,7782,7083,7082,6415,6414,5779,5778,5175,5174,4603,4602,4063,4062,3555,1,3554,3079,3078,2635,2634,2223,2222,1843,1842,1495,1494,1179,1178,897,895,1,893,891,889,887,885,883,881,879,877,875,873,871,869,867,865,1,863,861,859,857,855,853,851,849,847,845,843,841,839,837,835,1,832,833,1100,1101,1400,1401,1732,1733,2096,2097,2492,2493,2920,2921,3380,1,3381,3872,3873,4396,4397,4952,4953,5540,5541,6160,6161,6812,6813,7496,7497,1,8212,8213,8960,8961,9740,9741,10552,10553,11396,11397,12272,12273,13180,13181,14120,1,14121,15092,15093,16096,16097,17132,17133,18200,18201,19300,19301, - 19793,19792,18677,18676,17593,17592,16541,16540,15521,15520,14533,0,14532,13577,13576,12653,12652,11761,11760,10901,10900,10073,10072,9277,9276,8513,8512,0,7781,7780,7081,7080,6413,6412,5777,5776,5173,5172,4601,4600,4061,4060,3553,0,3552,3077,3076,2633,2632,2221,2220,1841,1840,1493,1492,1176,1174,1172,1170,0,1168,1166,1164,1162,1160,1158,1156,1154,1152,1150,1148,1146,1144,1142,1140,0,1138,1136,1134,1132,1130,1128,1126,1124,1122,1120,1118,1116,1114,1112,1110,0,1108,1106,1102,1103,1402,1403,1734,1735,2098,2099,2494,2495,2922,2923,3382,0,3383,3874,3875,4398,4399,4954,4955,5542,5543,6162,6163,6814,6815,7498,7499,0,8214,8215,8962,8963,9742,9743,10554,10555,11398,11399,12274,12275,13182,13183,14122,0,14123,15094,15095,16098,16099,17134,17135,18202,18203,19302,19303, - 19791,19790,18675,18674,17591,17590,16539,16538,15519,15518,14531,1,14530,13575,13574,12651,12650,11759,11758,10899,10898,10071,10070,9275,9274,8511,8510,1,7779,7778,7079,7078,6411,6410,5775,5774,5171,5170,4599,4598,4059,4058,3551,1,3550,3075,3074,2631,2630,2219,2218,1839,1838,1491,1490,1177,1175,1173,1171,1,1169,1167,1165,1163,1161,1159,1157,1155,1153,1151,1149,1147,1145,1143,1141,1,1139,1137,1135,1133,1131,1129,1127,1125,1123,1121,1119,1117,1115,1113,1111,1,1109,1107,1104,1105,1404,1405,1736,1737,2100,2101,2496,2497,2924,2925,3384,1,3385,3876,3877,4400,4401,4956,4957,5544,5545,6164,6165,6816,6817,7500,7501,1,8216,8217,8964,8965,9744,9745,10556,10557,11400,11401,12276,12277,13184,13185,14124,1,14125,15096,15097,16100,16101,17136,17137,18204,18205,19304,19305, - 19789,19788,18673,18672,17589,17588,16537,16536,15517,15516,14529,0,14528,13573,13572,12649,12648,11757,11756,10897,10896,10069,10068,9273,9272,8509,8508,0,7777,7776,7077,7076,6409,6408,5773,5772,5169,5168,4597,4596,4057,4056,3549,0,3548,3073,3072,2629,2628,2217,2216,1837,1836,1488,1486,1484,1482,1480,1478,0,1476,1474,1472,1470,1468,1466,1464,1462,1460,1458,1456,1454,1452,1450,1448,0,1446,1444,1442,1440,1438,1436,1434,1432,1430,1428,1426,1424,1422,1420,1418,0,1416,1414,1412,1410,1406,1407,1738,1739,2102,2103,2498,2499,2926,2927,3386,0,3387,3878,3879,4402,4403,4958,4959,5546,5547,6166,6167,6818,6819,7502,7503,0,8218,8219,8966,8967,9746,9747,10558,10559,11402,11403,12278,12279,13186,13187,14126,0,14127,15098,15099,16102,16103,17138,17139,18206,18207,19306,19307, - 19787,19786,18671,18670,17587,17586,16535,16534,15515,15514,14527,1,14526,13571,13570,12647,12646,11755,11754,10895,10894,10067,10066,9271,9270,8507,8506,1,7775,7774,7075,7074,6407,6406,5771,5770,5167,5166,4595,4594,4055,4054,3547,1,3546,3071,3070,2627,2626,2215,2214,1835,1834,1489,1487,1485,1483,1481,1479,1,1477,1475,1473,1471,1469,1467,1465,1463,1461,1459,1457,1455,1453,1451,1449,1,1447,1445,1443,1441,1439,1437,1435,1433,1431,1429,1427,1425,1423,1421,1419,1,1417,1415,1413,1411,1408,1409,1740,1741,2104,2105,2500,2501,2928,2929,3388,1,3389,3880,3881,4404,4405,4960,4961,5548,5549,6168,6169,6820,6821,7504,7505,1,8220,8221,8968,8969,9748,9749,10560,10561,11404,11405,12280,12281,13188,13189,14128,1,14129,15100,15101,16104,16105,17140,17141,18208,18209,19308,19309, - 19785,19784,18669,18668,17585,17584,16533,16532,15513,15512,14525,0,14524,13569,13568,12645,12644,11753,11752,10893,10892,10065,10064,9269,9268,8505,8504,0,7773,7772,7073,7072,6405,6404,5769,5768,5165,5164,4593,4592,4053,4052,3545,0,3544,3069,3068,2625,2624,2213,2212,1832,1830,1828,1826,1824,1822,1820,1818,0,1816,1814,1812,1810,1808,1806,1804,1802,1800,1798,1796,1794,1792,1790,1788,0,1786,1784,1782,1780,1778,1776,1774,1772,1770,1768,1766,1764,1762,1760,1758,0,1756,1754,1752,1750,1748,1746,1742,1743,2106,2107,2502,2503,2930,2931,3390,0,3391,3882,3883,4406,4407,4962,4963,5550,5551,6170,6171,6822,6823,7506,7507,0,8222,8223,8970,8971,9750,9751,10562,10563,11406,11407,12282,12283,13190,13191,14130,0,14131,15102,15103,16106,16107,17142,17143,18210,18211,19310,19311, - 19783,19782,18667,18666,17583,17582,16531,16530,15511,15510,14523,1,14522,13567,13566,12643,12642,11751,11750,10891,10890,10063,10062,9267,9266,8503,8502,1,7771,7770,7071,7070,6403,6402,5767,5766,5163,5162,4591,4590,4051,4050,3543,1,3542,3067,3066,2623,2622,2211,2210,1833,1831,1829,1827,1825,1823,1821,1819,1,1817,1815,1813,1811,1809,1807,1805,1803,1801,1799,1797,1795,1793,1791,1789,1,1787,1785,1783,1781,1779,1777,1775,1773,1771,1769,1767,1765,1763,1761,1759,1,1757,1755,1753,1751,1749,1747,1744,1745,2108,2109,2504,2505,2932,2933,3392,1,3393,3884,3885,4408,4409,4964,4965,5552,5553,6172,6173,6824,6825,7508,7509,1,8224,8225,8972,8973,9752,9753,10564,10565,11408,11409,12284,12285,13192,13193,14132,1,14133,15104,15105,16108,16109,17144,17145,18212,18213,19312,19313, - 19781,19780,18665,18664,17581,17580,16529,16528,15509,15508,14521,0,14520,13565,13564,12641,12640,11749,11748,10889,10888,10061,10060,9265,9264,8501,8500,0,7769,7768,7069,7068,6401,6400,5765,5764,5161,5160,4589,4588,4049,4048,3541,0,3540,3065,3064,2621,2620,2208,2206,2204,2202,2200,2198,2196,2194,2192,2190,0,2188,2186,2184,2182,2180,2178,2176,2174,2172,2170,2168,2166,2164,2162,2160,0,2158,2156,2154,2152,2150,2148,2146,2144,2142,2140,2138,2136,2134,2132,2130,0,2128,2126,2124,2122,2120,2118,2116,2114,2110,2111,2506,2507,2934,2935,3394,0,3395,3886,3887,4410,4411,4966,4967,5554,5555,6174,6175,6826,6827,7510,7511,0,8226,8227,8974,8975,9754,9755,10566,10567,11410,11411,12286,12287,13194,13195,14134,0,14135,15106,15107,16110,16111,17146,17147,18214,18215,19314,19315, - 19779,19778,18663,18662,17579,17578,16527,16526,15507,15506,14519,1,14518,13563,13562,12639,12638,11747,11746,10887,10886,10059,10058,9263,9262,8499,8498,1,7767,7766,7067,7066,6399,6398,5763,5762,5159,5158,4587,4586,4047,4046,3539,1,3538,3063,3062,2619,2618,2209,2207,2205,2203,2201,2199,2197,2195,2193,2191,1,2189,2187,2185,2183,2181,2179,2177,2175,2173,2171,2169,2167,2165,2163,2161,1,2159,2157,2155,2153,2151,2149,2147,2145,2143,2141,2139,2137,2135,2133,2131,1,2129,2127,2125,2123,2121,2119,2117,2115,2112,2113,2508,2509,2936,2937,3396,1,3397,3888,3889,4412,4413,4968,4969,5556,5557,6176,6177,6828,6829,7512,7513,1,8228,8229,8976,8977,9756,9757,10568,10569,11412,11413,12288,12289,13196,13197,14136,1,14137,15108,15109,16112,16113,17148,17149,18216,18217,19316,19317, - 19777,19776,18661,18660,17577,17576,16525,16524,15505,15504,14517,0,14516,13561,13560,12637,12636,11745,11744,10885,10884,10057,10056,9261,9260,8497,8496,0,7765,7764,7065,7064,6397,6396,5761,5760,5157,5156,4585,4584,4045,4044,3537,0,3536,3061,3060,2616,2614,2612,2610,2608,2606,2604,2602,2600,2598,2596,2594,0,2592,2590,2588,2586,2584,2582,2580,2578,2576,2574,2572,2570,2568,2566,2564,0,2562,2560,2558,2556,2554,2552,2550,2548,2546,2544,2542,2540,2538,2536,2534,0,2532,2530,2528,2526,2524,2522,2520,2518,2516,2514,2510,2511,2938,2939,3398,0,3399,3890,3891,4414,4415,4970,4971,5558,5559,6178,6179,6830,6831,7514,7515,0,8230,8231,8978,8979,9758,9759,10570,10571,11414,11415,12290,12291,13198,13199,14138,0,14139,15110,15111,16114,16115,17150,17151,18218,18219,19318,19319, - 19775,19774,18659,18658,17575,17574,16523,16522,15503,15502,14515,1,14514,13559,13558,12635,12634,11743,11742,10883,10882,10055,10054,9259,9258,8495,8494,1,7763,7762,7063,7062,6395,6394,5759,5758,5155,5154,4583,4582,4043,4042,3535,1,3534,3059,3058,2617,2615,2613,2611,2609,2607,2605,2603,2601,2599,2597,2595,1,2593,2591,2589,2587,2585,2583,2581,2579,2577,2575,2573,2571,2569,2567,2565,1,2563,2561,2559,2557,2555,2553,2551,2549,2547,2545,2543,2541,2539,2537,2535,1,2533,2531,2529,2527,2525,2523,2521,2519,2517,2515,2512,2513,2940,2941,3400,1,3401,3892,3893,4416,4417,4972,4973,5560,5561,6180,6181,6832,6833,7516,7517,1,8232,8233,8980,8981,9760,9761,10572,10573,11416,11417,12292,12293,13200,13201,14140,1,14141,15112,15113,16116,16117,17152,17153,18220,18221,19320,19321, - 19773,19772,18657,18656,17573,17572,16521,16520,15501,15500,14513,0,14512,13557,13556,12633,12632,11741,11740,10881,10880,10053,10052,9257,9256,8493,8492,0,7761,7760,7061,7060,6393,6392,5757,5756,5153,5152,4581,4580,4041,4040,3533,0,3532,3056,3054,3052,3050,3048,3046,3044,3042,3040,3038,3036,3034,3032,3030,0,3028,3026,3024,3022,3020,3018,3016,3014,3012,3010,3008,3006,3004,3002,3000,0,2998,2996,2994,2992,2990,2988,2986,2984,2982,2980,2978,2976,2974,2972,2970,0,2968,2966,2964,2962,2960,2958,2956,2954,2952,2950,2948,2946,2942,2943,3402,0,3403,3894,3895,4418,4419,4974,4975,5562,5563,6182,6183,6834,6835,7518,7519,0,8234,8235,8982,8983,9762,9763,10574,10575,11418,11419,12294,12295,13202,13203,14142,0,14143,15114,15115,16118,16119,17154,17155,18222,18223,19322,19323, - 19771,19770,18655,18654,17571,17570,16519,16518,15499,15498,14511,1,14510,13555,13554,12631,12630,11739,11738,10879,10878,10051,10050,9255,9254,8491,8490,1,7759,7758,7059,7058,6391,6390,5755,5754,5151,5150,4579,4578,4039,4038,3531,1,3530,3057,3055,3053,3051,3049,3047,3045,3043,3041,3039,3037,3035,3033,3031,1,3029,3027,3025,3023,3021,3019,3017,3015,3013,3011,3009,3007,3005,3003,3001,1,2999,2997,2995,2993,2991,2989,2987,2985,2983,2981,2979,2977,2975,2973,2971,1,2969,2967,2965,2963,2961,2959,2957,2955,2953,2951,2949,2947,2944,2945,3404,1,3405,3896,3897,4420,4421,4976,4977,5564,5565,6184,6185,6836,6837,7520,7521,1,8236,8237,8984,8985,9764,9765,10576,10577,11420,11421,12296,12297,13204,13205,14144,1,14145,15116,15117,16120,16121,17156,17157,18224,18225,19324,19325, - 19769,19768,18653,18652,17569,17568,16517,16516,15497,15496,14509,0,14508,13553,13552,12629,12628,11737,11736,10877,10876,10049,10048,9253,9252,8489,8488,0,7757,7756,7057,7056,6389,6388,5753,5752,5149,5148,4577,4576,4037,4036,3528,0,3526,3524,3522,3520,3518,3516,3514,3512,3510,3508,3506,3504,3502,3500,3498,0,3496,3494,3492,3490,3488,3486,3484,3482,3480,3478,3476,3474,3472,3470,3468,0,3466,3464,3462,3460,3458,3456,3454,3452,3450,3448,3446,3444,3442,3440,3438,0,3436,3434,3432,3430,3428,3426,3424,3422,3420,3418,3416,3414,3412,3410,3406,0,3407,3898,3899,4422,4423,4978,4979,5566,5567,6186,6187,6838,6839,7522,7523,0,8238,8239,8986,8987,9766,9767,10578,10579,11422,11423,12298,12299,13206,13207,14146,0,14147,15118,15119,16122,16123,17158,17159,18226,18227,19326,19327, - 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, - 19767,19766,18651,18650,17567,17566,16515,16514,15495,15494,14507,0,14506,13551,13550,12627,12626,11735,11734,10875,10874,10047,10046,9251,9250,8487,8486,0,7755,7754,7055,7054,6387,6386,5751,5750,5147,5146,4575,4574,4035,4034,3529,0,3527,3525,3523,3521,3519,3517,3515,3513,3511,3509,3507,3505,3503,3501,3499,0,3497,3495,3493,3491,3489,3487,3485,3483,3481,3479,3477,3475,3473,3471,3469,0,3467,3465,3463,3461,3459,3457,3455,3453,3451,3449,3447,3445,3443,3441,3439,0,3437,3435,3433,3431,3429,3427,3425,3423,3421,3419,3417,3415,3413,3411,3408,0,3409,3900,3901,4424,4425,4980,4981,5568,5569,6188,6189,6840,6841,7524,7525,0,8240,8241,8988,8989,9768,9769,10580,10581,11424,11425,12300,12301,13208,13209,14148,0,14149,15120,15121,16124,16125,17160,17161,18228,18229,19328,19329, - 19765,19764,18649,18648,17565,17564,16513,16512,15493,15492,14505,1,14504,13549,13548,12625,12624,11733,11732,10873,10872,10045,10044,9249,9248,8485,8484,1,7753,7752,7053,7052,6385,6384,5749,5748,5145,5144,4573,4572,4032,4030,4028,1,4026,4024,4022,4020,4018,4016,4014,4012,4010,4008,4006,4004,4002,4000,3998,1,3996,3994,3992,3990,3988,3986,3984,3982,3980,3978,3976,3974,3972,3970,3968,1,3966,3964,3962,3960,3958,3956,3954,3952,3950,3948,3946,3944,3942,3940,3938,1,3936,3934,3932,3930,3928,3926,3924,3922,3920,3918,3916,3914,3912,3910,3908,1,3906,3902,3903,4426,4427,4982,4983,5570,5571,6190,6191,6842,6843,7526,7527,1,8242,8243,8990,8991,9770,9771,10582,10583,11426,11427,12302,12303,13210,13211,14150,1,14151,15122,15123,16126,16127,17162,17163,18230,18231,19330,19331, - 19763,19762,18647,18646,17563,17562,16511,16510,15491,15490,14503,0,14502,13547,13546,12623,12622,11731,11730,10871,10870,10043,10042,9247,9246,8483,8482,0,7751,7750,7051,7050,6383,6382,5747,5746,5143,5142,4571,4570,4033,4031,4029,0,4027,4025,4023,4021,4019,4017,4015,4013,4011,4009,4007,4005,4003,4001,3999,0,3997,3995,3993,3991,3989,3987,3985,3983,3981,3979,3977,3975,3973,3971,3969,0,3967,3965,3963,3961,3959,3957,3955,3953,3951,3949,3947,3945,3943,3941,3939,0,3937,3935,3933,3931,3929,3927,3925,3923,3921,3919,3917,3915,3913,3911,3909,0,3907,3904,3905,4428,4429,4984,4985,5572,5573,6192,6193,6844,6845,7528,7529,0,8244,8245,8992,8993,9772,9773,10584,10585,11428,11429,12304,12305,13212,13213,14152,0,14153,15124,15125,16128,16129,17164,17165,18232,18233,19332,19333, - 19761,19760,18645,18644,17561,17560,16509,16508,15489,15488,14501,1,14500,13545,13544,12621,12620,11729,11728,10869,10868,10041,10040,9245,9244,8481,8480,1,7749,7748,7049,7048,6381,6380,5745,5744,5141,5140,4568,4566,4564,4562,4560,1,4558,4556,4554,4552,4550,4548,4546,4544,4542,4540,4538,4536,4534,4532,4530,1,4528,4526,4524,4522,4520,4518,4516,4514,4512,4510,4508,4506,4504,4502,4500,1,4498,4496,4494,4492,4490,4488,4486,4484,4482,4480,4478,4476,4474,4472,4470,1,4468,4466,4464,4462,4460,4458,4456,4454,4452,4450,4448,4446,4444,4442,4440,1,4438,4436,4434,4430,4431,4986,4987,5574,5575,6194,6195,6846,6847,7530,7531,1,8246,8247,8994,8995,9774,9775,10586,10587,11430,11431,12306,12307,13214,13215,14154,1,14155,15126,15127,16130,16131,17166,17167,18234,18235,19334,19335, - 19759,19758,18643,18642,17559,17558,16507,16506,15487,15486,14499,0,14498,13543,13542,12619,12618,11727,11726,10867,10866,10039,10038,9243,9242,8479,8478,0,7747,7746,7047,7046,6379,6378,5743,5742,5139,5138,4569,4567,4565,4563,4561,0,4559,4557,4555,4553,4551,4549,4547,4545,4543,4541,4539,4537,4535,4533,4531,0,4529,4527,4525,4523,4521,4519,4517,4515,4513,4511,4509,4507,4505,4503,4501,0,4499,4497,4495,4493,4491,4489,4487,4485,4483,4481,4479,4477,4475,4473,4471,0,4469,4467,4465,4463,4461,4459,4457,4455,4453,4451,4449,4447,4445,4443,4441,0,4439,4437,4435,4432,4433,4988,4989,5576,5577,6196,6197,6848,6849,7532,7533,0,8248,8249,8996,8997,9776,9777,10588,10589,11432,11433,12308,12309,13216,13217,14156,0,14157,15128,15129,16132,16133,17168,17169,18236,18237,19336,19337, - 19757,19756,18641,18640,17557,17556,16505,16504,15485,15484,14497,1,14496,13541,13540,12617,12616,11725,11724,10865,10864,10037,10036,9241,9240,8477,8476,1,7745,7744,7045,7044,6377,6376,5741,5740,5136,5134,5132,5130,5128,5126,5124,1,5122,5120,5118,5116,5114,5112,5110,5108,5106,5104,5102,5100,5098,5096,5094,1,5092,5090,5088,5086,5084,5082,5080,5078,5076,5074,5072,5070,5068,5066,5064,1,5062,5060,5058,5056,5054,5052,5050,5048,5046,5044,5042,5040,5038,5036,5034,1,5032,5030,5028,5026,5024,5022,5020,5018,5016,5014,5012,5010,5008,5006,5004,1,5002,5000,4998,4996,4994,4990,4991,5578,5579,6198,6199,6850,6851,7534,7535,1,8250,8251,8998,8999,9778,9779,10590,10591,11434,11435,12310,12311,13218,13219,14158,1,14159,15130,15131,16134,16135,17170,17171,18238,18239,19338,19339, - 19755,19754,18639,18638,17555,17554,16503,16502,15483,15482,14495,0,14494,13539,13538,12615,12614,11723,11722,10863,10862,10035,10034,9239,9238,8475,8474,0,7743,7742,7043,7042,6375,6374,5739,5738,5137,5135,5133,5131,5129,5127,5125,0,5123,5121,5119,5117,5115,5113,5111,5109,5107,5105,5103,5101,5099,5097,5095,0,5093,5091,5089,5087,5085,5083,5081,5079,5077,5075,5073,5071,5069,5067,5065,0,5063,5061,5059,5057,5055,5053,5051,5049,5047,5045,5043,5041,5039,5037,5035,0,5033,5031,5029,5027,5025,5023,5021,5019,5017,5015,5013,5011,5009,5007,5005,0,5003,5001,4999,4997,4995,4992,4993,5580,5581,6200,6201,6852,6853,7536,7537,0,8252,8253,9000,9001,9780,9781,10592,10593,11436,11437,12312,12313,13220,13221,14160,0,14161,15132,15133,16136,16137,17172,17173,18240,18241,19340,19341, - 19753,19752,18637,18636,17553,17552,16501,16500,15481,15480,14493,1,14492,13537,13536,12613,12612,11721,11720,10861,10860,10033,10032,9237,9236,8473,8472,1,7741,7740,7041,7040,6373,6372,5736,5734,5732,5730,5728,5726,5724,5722,5720,1,5718,5716,5714,5712,5710,5708,5706,5704,5702,5700,5698,5696,5694,5692,5690,1,5688,5686,5684,5682,5680,5678,5676,5674,5672,5670,5668,5666,5664,5662,5660,1,5658,5656,5654,5652,5650,5648,5646,5644,5642,5640,5638,5636,5634,5632,5630,1,5628,5626,5624,5622,5620,5618,5616,5614,5612,5610,5608,5606,5604,5602,5600,1,5598,5596,5594,5592,5590,5588,5586,5582,5583,6202,6203,6854,6855,7538,7539,1,8254,8255,9002,9003,9782,9783,10594,10595,11438,11439,12314,12315,13222,13223,14162,1,14163,15134,15135,16138,16139,17174,17175,18242,18243,19342,19343, - 19751,19750,18635,18634,17551,17550,16499,16498,15479,15478,14491,0,14490,13535,13534,12611,12610,11719,11718,10859,10858,10031,10030,9235,9234,8471,8470,0,7739,7738,7039,7038,6371,6370,5737,5735,5733,5731,5729,5727,5725,5723,5721,0,5719,5717,5715,5713,5711,5709,5707,5705,5703,5701,5699,5697,5695,5693,5691,0,5689,5687,5685,5683,5681,5679,5677,5675,5673,5671,5669,5667,5665,5663,5661,0,5659,5657,5655,5653,5651,5649,5647,5645,5643,5641,5639,5637,5635,5633,5631,0,5629,5627,5625,5623,5621,5619,5617,5615,5613,5611,5609,5607,5605,5603,5601,0,5599,5597,5595,5593,5591,5589,5587,5584,5585,6204,6205,6856,6857,7540,7541,0,8256,8257,9004,9005,9784,9785,10596,10597,11440,11441,12316,12317,13224,13225,14164,0,14165,15136,15137,16140,16141,17176,17177,18244,18245,19344,19345, - 19749,19748,18633,18632,17549,17548,16497,16496,15477,15476,14489,1,14488,13533,13532,12609,12608,11717,11716,10857,10856,10029,10028,9233,9232,8469,8468,1,7737,7736,7037,7036,6368,6366,6364,6362,6360,6358,6356,6354,6352,6350,6348,1,6346,6344,6342,6340,6338,6336,6334,6332,6330,6328,6326,6324,6322,6320,6318,1,6316,6314,6312,6310,6308,6306,6304,6302,6300,6298,6296,6294,6292,6290,6288,1,6286,6284,6282,6280,6278,6276,6274,6272,6270,6268,6266,6264,6262,6260,6258,1,6256,6254,6252,6250,6248,6246,6244,6242,6240,6238,6236,6234,6232,6230,6228,1,6226,6224,6222,6220,6218,6216,6214,6212,6210,6206,6207,6858,6859,7542,7543,1,8258,8259,9006,9007,9786,9787,10598,10599,11442,11443,12318,12319,13226,13227,14166,1,14167,15138,15139,16142,16143,17178,17179,18246,18247,19346,19347, - 19747,19746,18631,18630,17547,17546,16495,16494,15475,15474,14487,0,14486,13531,13530,12607,12606,11715,11714,10855,10854,10027,10026,9231,9230,8467,8466,0,7735,7734,7035,7034,6369,6367,6365,6363,6361,6359,6357,6355,6353,6351,6349,0,6347,6345,6343,6341,6339,6337,6335,6333,6331,6329,6327,6325,6323,6321,6319,0,6317,6315,6313,6311,6309,6307,6305,6303,6301,6299,6297,6295,6293,6291,6289,0,6287,6285,6283,6281,6279,6277,6275,6273,6271,6269,6267,6265,6263,6261,6259,0,6257,6255,6253,6251,6249,6247,6245,6243,6241,6239,6237,6235,6233,6231,6229,0,6227,6225,6223,6221,6219,6217,6215,6213,6211,6208,6209,6860,6861,7544,7545,0,8260,8261,9008,9009,9788,9789,10600,10601,11444,11445,12320,12321,13228,13229,14168,0,14169,15140,15141,16144,16145,17180,17181,18248,18249,19348,19349, - 19745,19744,18629,18628,17545,17544,16493,16492,15473,15472,14485,1,14484,13529,13528,12605,12604,11713,11712,10853,10852,10025,10024,9229,9228,8465,8464,1,7733,7732,7032,7030,7028,7026,7024,7022,7020,7018,7016,7014,7012,7010,7008,1,7006,7004,7002,7000,6998,6996,6994,6992,6990,6988,6986,6984,6982,6980,6978,1,6976,6974,6972,6970,6968,6966,6964,6962,6960,6958,6956,6954,6952,6950,6948,1,6946,6944,6942,6940,6938,6936,6934,6932,6930,6928,6926,6924,6922,6920,6918,1,6916,6914,6912,6910,6908,6906,6904,6902,6900,6898,6896,6894,6892,6890,6888,1,6886,6884,6882,6880,6878,6876,6874,6872,6870,6868,6866,6862,6863,7546,7547,1,8262,8263,9010,9011,9790,9791,10602,10603,11446,11447,12322,12323,13230,13231,14170,1,14171,15142,15143,16146,16147,17182,17183,18250,18251,19350,19351, - 19743,19742,18627,18626,17543,17542,16491,16490,15471,15470,14483,0,14482,13527,13526,12603,12602,11711,11710,10851,10850,10023,10022,9227,9226,8463,8462,0,7731,7730,7033,7031,7029,7027,7025,7023,7021,7019,7017,7015,7013,7011,7009,0,7007,7005,7003,7001,6999,6997,6995,6993,6991,6989,6987,6985,6983,6981,6979,0,6977,6975,6973,6971,6969,6967,6965,6963,6961,6959,6957,6955,6953,6951,6949,0,6947,6945,6943,6941,6939,6937,6935,6933,6931,6929,6927,6925,6923,6921,6919,0,6917,6915,6913,6911,6909,6907,6905,6903,6901,6899,6897,6895,6893,6891,6889,0,6887,6885,6883,6881,6879,6877,6875,6873,6871,6869,6867,6864,6865,7548,7549,0,8264,8265,9012,9013,9792,9793,10604,10605,11448,11449,12324,12325,13232,13233,14172,0,14173,15144,15145,16148,16149,17184,17185,18252,18253,19352,19353, - 19741,19740,18625,18624,17541,17540,16489,16488,15469,15468,14481,1,14480,13525,13524,12601,12600,11709,11708,10849,10848,10021,10020,9225,9224,8461,8460,1,7728,7726,7724,7722,7720,7718,7716,7714,7712,7710,7708,7706,7704,7702,7700,1,7698,7696,7694,7692,7690,7688,7686,7684,7682,7680,7678,7676,7674,7672,7670,1,7668,7666,7664,7662,7660,7658,7656,7654,7652,7650,7648,7646,7644,7642,7640,1,7638,7636,7634,7632,7630,7628,7626,7624,7622,7620,7618,7616,7614,7612,7610,1,7608,7606,7604,7602,7600,7598,7596,7594,7592,7590,7588,7586,7584,7582,7580,1,7578,7576,7574,7572,7570,7568,7566,7564,7562,7560,7558,7556,7554,7550,7551,1,8266,8267,9014,9015,9794,9795,10606,10607,11450,11451,12326,12327,13234,13235,14174,1,14175,15146,15147,16150,16151,17186,17187,18254,18255,19354,19355, - 19739,19738,18623,18622,17539,17538,16487,16486,15467,15466,14479,0,14478,13523,13522,12599,12598,11707,11706,10847,10846,10019,10018,9223,9222,8459,8458,0,7729,7727,7725,7723,7721,7719,7717,7715,7713,7711,7709,7707,7705,7703,7701,0,7699,7697,7695,7693,7691,7689,7687,7685,7683,7681,7679,7677,7675,7673,7671,0,7669,7667,7665,7663,7661,7659,7657,7655,7653,7651,7649,7647,7645,7643,7641,0,7639,7637,7635,7633,7631,7629,7627,7625,7623,7621,7619,7617,7615,7613,7611,0,7609,7607,7605,7603,7601,7599,7597,7595,7593,7591,7589,7587,7585,7583,7581,0,7579,7577,7575,7573,7571,7569,7567,7565,7563,7561,7559,7557,7555,7552,7553,0,8268,8269,9016,9017,9796,9797,10608,10609,11452,11453,12328,12329,13236,13237,14176,0,14177,15148,15149,16152,16153,17188,17189,18256,18257,19356,19357, - 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, - 19737,19736,18621,18620,17537,17536,16485,16484,15465,15464,14477,0,14476,13521,13520,12597,12596,11705,11704,10845,10844,10017,10016,9221,9220,8456,8454,0,8452,8450,8448,8446,8444,8442,8440,8438,8436,8434,8432,8430,8428,8426,8424,0,8422,8420,8418,8416,8414,8412,8410,8408,8406,8404,8402,8400,8398,8396,8394,0,8392,8390,8388,8386,8384,8382,8380,8378,8376,8374,8372,8370,8368,8366,8364,0,8362,8360,8358,8356,8354,8352,8350,8348,8346,8344,8342,8340,8338,8336,8334,0,8332,8330,8328,8326,8324,8322,8320,8318,8316,8314,8312,8310,8308,8306,8304,0,8302,8300,8298,8296,8294,8292,8290,8288,8286,8284,8282,8280,8278,8276,8274,0,8270,8271,9018,9019,9798,9799,10610,10611,11454,11455,12330,12331,13238,13239,14178,0,14179,15150,15151,16154,16155,17190,17191,18258,18259,19358,19359, - 19735,19734,18619,18618,17535,17534,16483,16482,15463,15462,14475,1,14474,13519,13518,12595,12594,11703,11702,10843,10842,10015,10014,9219,9218,8457,8455,1,8453,8451,8449,8447,8445,8443,8441,8439,8437,8435,8433,8431,8429,8427,8425,1,8423,8421,8419,8417,8415,8413,8411,8409,8407,8405,8403,8401,8399,8397,8395,1,8393,8391,8389,8387,8385,8383,8381,8379,8377,8375,8373,8371,8369,8367,8365,1,8363,8361,8359,8357,8355,8353,8351,8349,8347,8345,8343,8341,8339,8337,8335,1,8333,8331,8329,8327,8325,8323,8321,8319,8317,8315,8313,8311,8309,8307,8305,1,8303,8301,8299,8297,8295,8293,8291,8289,8287,8285,8283,8281,8279,8277,8275,1,8272,8273,9020,9021,9800,9801,10612,10613,11456,11457,12332,12333,13240,13241,14180,1,14181,15152,15153,16156,16157,17192,17193,18260,18261,19360,19361, - 19733,19732,18617,18616,17533,17532,16481,16480,15461,15460,14473,0,14472,13517,13516,12593,12592,11701,11700,10841,10840,10013,10012,9216,9214,9212,9210,0,9208,9206,9204,9202,9200,9198,9196,9194,9192,9190,9188,9186,9184,9182,9180,0,9178,9176,9174,9172,9170,9168,9166,9164,9162,9160,9158,9156,9154,9152,9150,0,9148,9146,9144,9142,9140,9138,9136,9134,9132,9130,9128,9126,9124,9122,9120,0,9118,9116,9114,9112,9110,9108,9106,9104,9102,9100,9098,9096,9094,9092,9090,0,9088,9086,9084,9082,9080,9078,9076,9074,9072,9070,9068,9066,9064,9062,9060,0,9058,9056,9054,9052,9050,9048,9046,9044,9042,9040,9038,9036,9034,9032,9030,0,9028,9026,9022,9023,9802,9803,10614,10615,11458,11459,12334,12335,13242,13243,14182,0,14183,15154,15155,16158,16159,17194,17195,18262,18263,19362,19363, - 19731,19730,18615,18614,17531,17530,16479,16478,15459,15458,14471,1,14470,13515,13514,12591,12590,11699,11698,10839,10838,10011,10010,9217,9215,9213,9211,1,9209,9207,9205,9203,9201,9199,9197,9195,9193,9191,9189,9187,9185,9183,9181,1,9179,9177,9175,9173,9171,9169,9167,9165,9163,9161,9159,9157,9155,9153,9151,1,9149,9147,9145,9143,9141,9139,9137,9135,9133,9131,9129,9127,9125,9123,9121,1,9119,9117,9115,9113,9111,9109,9107,9105,9103,9101,9099,9097,9095,9093,9091,1,9089,9087,9085,9083,9081,9079,9077,9075,9073,9071,9069,9067,9065,9063,9061,1,9059,9057,9055,9053,9051,9049,9047,9045,9043,9041,9039,9037,9035,9033,9031,1,9029,9027,9024,9025,9804,9805,10616,10617,11460,11461,12336,12337,13244,13245,14184,1,14185,15156,15157,16160,16161,17196,17197,18264,18265,19364,19365, - 19729,19728,18613,18612,17529,17528,16477,16476,15457,15456,14469,0,14468,13513,13512,12589,12588,11697,11696,10837,10836,10008,10006,10004,10002,10000,9998,0,9996,9994,9992,9990,9988,9986,9984,9982,9980,9978,9976,9974,9972,9970,9968,0,9966,9964,9962,9960,9958,9956,9954,9952,9950,9948,9946,9944,9942,9940,9938,0,9936,9934,9932,9930,9928,9926,9924,9922,9920,9918,9916,9914,9912,9910,9908,0,9906,9904,9902,9900,9898,9896,9894,9892,9890,9888,9886,9884,9882,9880,9878,0,9876,9874,9872,9870,9868,9866,9864,9862,9860,9858,9856,9854,9852,9850,9848,0,9846,9844,9842,9840,9838,9836,9834,9832,9830,9828,9826,9824,9822,9820,9818,0,9816,9814,9812,9810,9806,9807,10618,10619,11462,11463,12338,12339,13246,13247,14186,0,14187,15158,15159,16162,16163,17198,17199,18266,18267,19366,19367, - 19727,19726,18611,18610,17527,17526,16475,16474,15455,15454,14467,1,14466,13511,13510,12587,12586,11695,11694,10835,10834,10009,10007,10005,10003,10001,9999,1,9997,9995,9993,9991,9989,9987,9985,9983,9981,9979,9977,9975,9973,9971,9969,1,9967,9965,9963,9961,9959,9957,9955,9953,9951,9949,9947,9945,9943,9941,9939,1,9937,9935,9933,9931,9929,9927,9925,9923,9921,9919,9917,9915,9913,9911,9909,1,9907,9905,9903,9901,9899,9897,9895,9893,9891,9889,9887,9885,9883,9881,9879,1,9877,9875,9873,9871,9869,9867,9865,9863,9861,9859,9857,9855,9853,9851,9849,1,9847,9845,9843,9841,9839,9837,9835,9833,9831,9829,9827,9825,9823,9821,9819,1,9817,9815,9813,9811,9808,9809,10620,10621,11464,11465,12340,12341,13248,13249,14188,1,14189,15160,15161,16164,16165,17200,17201,18268,18269,19368,19369, - 19725,19724,18609,18608,17525,17524,16473,16472,15453,15452,14465,0,14464,13509,13508,12585,12584,11693,11692,10832,10830,10828,10826,10824,10822,10820,10818,0,10816,10814,10812,10810,10808,10806,10804,10802,10800,10798,10796,10794,10792,10790,10788,0,10786,10784,10782,10780,10778,10776,10774,10772,10770,10768,10766,10764,10762,10760,10758,0,10756,10754,10752,10750,10748,10746,10744,10742,10740,10738,10736,10734,10732,10730,10728,0,10726,10724,10722,10720,10718,10716,10714,10712,10710,10708,10706,10704,10702,10700,10698,0,10696,10694,10692,10690,10688,10686,10684,10682,10680,10678,10676,10674,10672,10670,10668,0,10666,10664,10662,10660,10658,10656,10654,10652,10650,10648,10646,10644,10642,10640,10638,0,10636,10634,10632,10630,10628,10626,10622,10623,11466,11467,12342,12343,13250,13251,14190,0,14191,15162,15163,16166,16167,17202,17203,18270,18271,19370,19371, - 19723,19722,18607,18606,17523,17522,16471,16470,15451,15450,14463,1,14462,13507,13506,12583,12582,11691,11690,10833,10831,10829,10827,10825,10823,10821,10819,1,10817,10815,10813,10811,10809,10807,10805,10803,10801,10799,10797,10795,10793,10791,10789,1,10787,10785,10783,10781,10779,10777,10775,10773,10771,10769,10767,10765,10763,10761,10759,1,10757,10755,10753,10751,10749,10747,10745,10743,10741,10739,10737,10735,10733,10731,10729,1,10727,10725,10723,10721,10719,10717,10715,10713,10711,10709,10707,10705,10703,10701,10699,1,10697,10695,10693,10691,10689,10687,10685,10683,10681,10679,10677,10675,10673,10671,10669,1,10667,10665,10663,10661,10659,10657,10655,10653,10651,10649,10647,10645,10643,10641,10639,1,10637,10635,10633,10631,10629,10627,10624,10625,11468,11469,12344,12345,13252,13253,14192,1,14193,15164,15165,16168,16169,17204,17205,18272,18273,19372,19373, - 19721,19720,18605,18604,17521,17520,16469,16468,15449,15448,14461,0,14460,13505,13504,12581,12580,11688,11686,11684,11682,11680,11678,11676,11674,11672,11670,0,11668,11666,11664,11662,11660,11658,11656,11654,11652,11650,11648,11646,11644,11642,11640,0,11638,11636,11634,11632,11630,11628,11626,11624,11622,11620,11618,11616,11614,11612,11610,0,11608,11606,11604,11602,11600,11598,11596,11594,11592,11590,11588,11586,11584,11582,11580,0,11578,11576,11574,11572,11570,11568,11566,11564,11562,11560,11558,11556,11554,11552,11550,0,11548,11546,11544,11542,11540,11538,11536,11534,11532,11530,11528,11526,11524,11522,11520,0,11518,11516,11514,11512,11510,11508,11506,11504,11502,11500,11498,11496,11494,11492,11490,0,11488,11486,11484,11482,11480,11478,11476,11474,11470,11471,12346,12347,13254,13255,14194,0,14195,15166,15167,16170,16171,17206,17207,18274,18275,19374,19375, - 19719,19718,18603,18602,17519,17518,16467,16466,15447,15446,14459,1,14458,13503,13502,12579,12578,11689,11687,11685,11683,11681,11679,11677,11675,11673,11671,1,11669,11667,11665,11663,11661,11659,11657,11655,11653,11651,11649,11647,11645,11643,11641,1,11639,11637,11635,11633,11631,11629,11627,11625,11623,11621,11619,11617,11615,11613,11611,1,11609,11607,11605,11603,11601,11599,11597,11595,11593,11591,11589,11587,11585,11583,11581,1,11579,11577,11575,11573,11571,11569,11567,11565,11563,11561,11559,11557,11555,11553,11551,1,11549,11547,11545,11543,11541,11539,11537,11535,11533,11531,11529,11527,11525,11523,11521,1,11519,11517,11515,11513,11511,11509,11507,11505,11503,11501,11499,11497,11495,11493,11491,1,11489,11487,11485,11483,11481,11479,11477,11475,11472,11473,12348,12349,13256,13257,14196,1,14197,15168,15169,16172,16173,17208,17209,18276,18277,19376,19377, - 19717,19716,18601,18600,17517,17516,16465,16464,15445,15444,14457,0,14456,13501,13500,12576,12574,12572,12570,12568,12566,12564,12562,12560,12558,12556,12554,0,12552,12550,12548,12546,12544,12542,12540,12538,12536,12534,12532,12530,12528,12526,12524,0,12522,12520,12518,12516,12514,12512,12510,12508,12506,12504,12502,12500,12498,12496,12494,0,12492,12490,12488,12486,12484,12482,12480,12478,12476,12474,12472,12470,12468,12466,12464,0,12462,12460,12458,12456,12454,12452,12450,12448,12446,12444,12442,12440,12438,12436,12434,0,12432,12430,12428,12426,12424,12422,12420,12418,12416,12414,12412,12410,12408,12406,12404,0,12402,12400,12398,12396,12394,12392,12390,12388,12386,12384,12382,12380,12378,12376,12374,0,12372,12370,12368,12366,12364,12362,12360,12358,12356,12354,12350,12351,13258,13259,14198,0,14199,15170,15171,16174,16175,17210,17211,18278,18279,19378,19379, - 19715,19714,18599,18598,17515,17514,16463,16462,15443,15442,14455,1,14454,13499,13498,12577,12575,12573,12571,12569,12567,12565,12563,12561,12559,12557,12555,1,12553,12551,12549,12547,12545,12543,12541,12539,12537,12535,12533,12531,12529,12527,12525,1,12523,12521,12519,12517,12515,12513,12511,12509,12507,12505,12503,12501,12499,12497,12495,1,12493,12491,12489,12487,12485,12483,12481,12479,12477,12475,12473,12471,12469,12467,12465,1,12463,12461,12459,12457,12455,12453,12451,12449,12447,12445,12443,12441,12439,12437,12435,1,12433,12431,12429,12427,12425,12423,12421,12419,12417,12415,12413,12411,12409,12407,12405,1,12403,12401,12399,12397,12395,12393,12391,12389,12387,12385,12383,12381,12379,12377,12375,1,12373,12371,12369,12367,12365,12363,12361,12359,12357,12355,12352,12353,13260,13261,14200,1,14201,15172,15173,16176,16177,17212,17213,18280,18281,19380,19381, - 19713,19712,18597,18596,17513,17512,16461,16460,15441,15440,14453,0,14452,13496,13494,13492,13490,13488,13486,13484,13482,13480,13478,13476,13474,13472,13470,0,13468,13466,13464,13462,13460,13458,13456,13454,13452,13450,13448,13446,13444,13442,13440,0,13438,13436,13434,13432,13430,13428,13426,13424,13422,13420,13418,13416,13414,13412,13410,0,13408,13406,13404,13402,13400,13398,13396,13394,13392,13390,13388,13386,13384,13382,13380,0,13378,13376,13374,13372,13370,13368,13366,13364,13362,13360,13358,13356,13354,13352,13350,0,13348,13346,13344,13342,13340,13338,13336,13334,13332,13330,13328,13326,13324,13322,13320,0,13318,13316,13314,13312,13310,13308,13306,13304,13302,13300,13298,13296,13294,13292,13290,0,13288,13286,13284,13282,13280,13278,13276,13274,13272,13270,13268,13266,13262,13263,14202,0,14203,15174,15175,16178,16179,17214,17215,18282,18283,19382,19383, - 19711,19710,18595,18594,17511,17510,16459,16458,15439,15438,14451,1,14450,13497,13495,13493,13491,13489,13487,13485,13483,13481,13479,13477,13475,13473,13471,1,13469,13467,13465,13463,13461,13459,13457,13455,13453,13451,13449,13447,13445,13443,13441,1,13439,13437,13435,13433,13431,13429,13427,13425,13423,13421,13419,13417,13415,13413,13411,1,13409,13407,13405,13403,13401,13399,13397,13395,13393,13391,13389,13387,13385,13383,13381,1,13379,13377,13375,13373,13371,13369,13367,13365,13363,13361,13359,13357,13355,13353,13351,1,13349,13347,13345,13343,13341,13339,13337,13335,13333,13331,13329,13327,13325,13323,13321,1,13319,13317,13315,13313,13311,13309,13307,13305,13303,13301,13299,13297,13295,13293,13291,1,13289,13287,13285,13283,13281,13279,13277,13275,13273,13271,13269,13267,13264,13265,14204,1,14205,15176,15177,16180,16181,17216,17217,18284,18285,19384,19385, - 19709,19708,18593,18592,17509,17508,16457,16456,15437,15436,14448,0,14446,14444,14442,14440,14438,14436,14434,14432,14430,14428,14426,14424,14422,14420,14418,0,14416,14414,14412,14410,14408,14406,14404,14402,14400,14398,14396,14394,14392,14390,14388,0,14386,14384,14382,14380,14378,14376,14374,14372,14370,14368,14366,14364,14362,14360,14358,0,14356,14354,14352,14350,14348,14346,14344,14342,14340,14338,14336,14334,14332,14330,14328,0,14326,14324,14322,14320,14318,14316,14314,14312,14310,14308,14306,14304,14302,14300,14298,0,14296,14294,14292,14290,14288,14286,14284,14282,14280,14278,14276,14274,14272,14270,14268,0,14266,14264,14262,14260,14258,14256,14254,14252,14250,14248,14246,14244,14242,14240,14238,0,14236,14234,14232,14230,14228,14226,14224,14222,14220,14218,14216,14214,14212,14210,14206,0,14207,15178,15179,16182,16183,17218,17219,18286,18287,19386,19387, - 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, - 19707,19706,18591,18590,17507,17506,16455,16454,15435,15434,14449,0,14447,14445,14443,14441,14439,14437,14435,14433,14431,14429,14427,14425,14423,14421,14419,0,14417,14415,14413,14411,14409,14407,14405,14403,14401,14399,14397,14395,14393,14391,14389,0,14387,14385,14383,14381,14379,14377,14375,14373,14371,14369,14367,14365,14363,14361,14359,0,14357,14355,14353,14351,14349,14347,14345,14343,14341,14339,14337,14335,14333,14331,14329,0,14327,14325,14323,14321,14319,14317,14315,14313,14311,14309,14307,14305,14303,14301,14299,0,14297,14295,14293,14291,14289,14287,14285,14283,14281,14279,14277,14275,14273,14271,14269,0,14267,14265,14263,14261,14259,14257,14255,14253,14251,14249,14247,14245,14243,14241,14239,0,14237,14235,14233,14231,14229,14227,14225,14223,14221,14219,14217,14215,14213,14211,14208,0,14209,15180,15181,16184,16185,17220,17221,18288,18289,19388,19389, - 19705,19704,18589,18588,17505,17504,16453,16452,15432,15430,15428,1,15426,15424,15422,15420,15418,15416,15414,15412,15410,15408,15406,15404,15402,15400,15398,1,15396,15394,15392,15390,15388,15386,15384,15382,15380,15378,15376,15374,15372,15370,15368,1,15366,15364,15362,15360,15358,15356,15354,15352,15350,15348,15346,15344,15342,15340,15338,1,15336,15334,15332,15330,15328,15326,15324,15322,15320,15318,15316,15314,15312,15310,15308,1,15306,15304,15302,15300,15298,15296,15294,15292,15290,15288,15286,15284,15282,15280,15278,1,15276,15274,15272,15270,15268,15266,15264,15262,15260,15258,15256,15254,15252,15250,15248,1,15246,15244,15242,15240,15238,15236,15234,15232,15230,15228,15226,15224,15222,15220,15218,1,15216,15214,15212,15210,15208,15206,15204,15202,15200,15198,15196,15194,15192,15190,15188,1,15186,15182,15183,16186,16187,17222,17223,18290,18291,19390,19391, - 19703,19702,18587,18586,17503,17502,16451,16450,15433,15431,15429,0,15427,15425,15423,15421,15419,15417,15415,15413,15411,15409,15407,15405,15403,15401,15399,0,15397,15395,15393,15391,15389,15387,15385,15383,15381,15379,15377,15375,15373,15371,15369,0,15367,15365,15363,15361,15359,15357,15355,15353,15351,15349,15347,15345,15343,15341,15339,0,15337,15335,15333,15331,15329,15327,15325,15323,15321,15319,15317,15315,15313,15311,15309,0,15307,15305,15303,15301,15299,15297,15295,15293,15291,15289,15287,15285,15283,15281,15279,0,15277,15275,15273,15271,15269,15267,15265,15263,15261,15259,15257,15255,15253,15251,15249,0,15247,15245,15243,15241,15239,15237,15235,15233,15231,15229,15227,15225,15223,15221,15219,0,15217,15215,15213,15211,15209,15207,15205,15203,15201,15199,15197,15195,15193,15191,15189,0,15187,15184,15185,16188,16189,17224,17225,18292,18293,19392,19393, - 19701,19700,18585,18584,17501,17500,16448,16446,16444,16442,16440,1,16438,16436,16434,16432,16430,16428,16426,16424,16422,16420,16418,16416,16414,16412,16410,1,16408,16406,16404,16402,16400,16398,16396,16394,16392,16390,16388,16386,16384,16382,16380,1,16378,16376,16374,16372,16370,16368,16366,16364,16362,16360,16358,16356,16354,16352,16350,1,16348,16346,16344,16342,16340,16338,16336,16334,16332,16330,16328,16326,16324,16322,16320,1,16318,16316,16314,16312,16310,16308,16306,16304,16302,16300,16298,16296,16294,16292,16290,1,16288,16286,16284,16282,16280,16278,16276,16274,16272,16270,16268,16266,16264,16262,16260,1,16258,16256,16254,16252,16250,16248,16246,16244,16242,16240,16238,16236,16234,16232,16230,1,16228,16226,16224,16222,16220,16218,16216,16214,16212,16210,16208,16206,16204,16202,16200,1,16198,16196,16194,16190,16191,17226,17227,18294,18295,19394,19395, - 19699,19698,18583,18582,17499,17498,16449,16447,16445,16443,16441,0,16439,16437,16435,16433,16431,16429,16427,16425,16423,16421,16419,16417,16415,16413,16411,0,16409,16407,16405,16403,16401,16399,16397,16395,16393,16391,16389,16387,16385,16383,16381,0,16379,16377,16375,16373,16371,16369,16367,16365,16363,16361,16359,16357,16355,16353,16351,0,16349,16347,16345,16343,16341,16339,16337,16335,16333,16331,16329,16327,16325,16323,16321,0,16319,16317,16315,16313,16311,16309,16307,16305,16303,16301,16299,16297,16295,16293,16291,0,16289,16287,16285,16283,16281,16279,16277,16275,16273,16271,16269,16267,16265,16263,16261,0,16259,16257,16255,16253,16251,16249,16247,16245,16243,16241,16239,16237,16235,16233,16231,0,16229,16227,16225,16223,16221,16219,16217,16215,16213,16211,16209,16207,16205,16203,16201,0,16199,16197,16195,16192,16193,17228,17229,18296,18297,19396,19397, - 19697,19696,18581,18580,17496,17494,17492,17490,17488,17486,17484,1,17482,17480,17478,17476,17474,17472,17470,17468,17466,17464,17462,17460,17458,17456,17454,1,17452,17450,17448,17446,17444,17442,17440,17438,17436,17434,17432,17430,17428,17426,17424,1,17422,17420,17418,17416,17414,17412,17410,17408,17406,17404,17402,17400,17398,17396,17394,1,17392,17390,17388,17386,17384,17382,17380,17378,17376,17374,17372,17370,17368,17366,17364,1,17362,17360,17358,17356,17354,17352,17350,17348,17346,17344,17342,17340,17338,17336,17334,1,17332,17330,17328,17326,17324,17322,17320,17318,17316,17314,17312,17310,17308,17306,17304,1,17302,17300,17298,17296,17294,17292,17290,17288,17286,17284,17282,17280,17278,17276,17274,1,17272,17270,17268,17266,17264,17262,17260,17258,17256,17254,17252,17250,17248,17246,17244,1,17242,17240,17238,17236,17234,17230,17231,18298,18299,19398,19399, - 19695,19694,18579,18578,17497,17495,17493,17491,17489,17487,17485,0,17483,17481,17479,17477,17475,17473,17471,17469,17467,17465,17463,17461,17459,17457,17455,0,17453,17451,17449,17447,17445,17443,17441,17439,17437,17435,17433,17431,17429,17427,17425,0,17423,17421,17419,17417,17415,17413,17411,17409,17407,17405,17403,17401,17399,17397,17395,0,17393,17391,17389,17387,17385,17383,17381,17379,17377,17375,17373,17371,17369,17367,17365,0,17363,17361,17359,17357,17355,17353,17351,17349,17347,17345,17343,17341,17339,17337,17335,0,17333,17331,17329,17327,17325,17323,17321,17319,17317,17315,17313,17311,17309,17307,17305,0,17303,17301,17299,17297,17295,17293,17291,17289,17287,17285,17283,17281,17279,17277,17275,0,17273,17271,17269,17267,17265,17263,17261,17259,17257,17255,17253,17251,17249,17247,17245,0,17243,17241,17239,17237,17235,17232,17233,18300,18301,19400,19401, - 19693,19692,18576,18574,18572,18570,18568,18566,18564,18562,18560,1,18558,18556,18554,18552,18550,18548,18546,18544,18542,18540,18538,18536,18534,18532,18530,1,18528,18526,18524,18522,18520,18518,18516,18514,18512,18510,18508,18506,18504,18502,18500,1,18498,18496,18494,18492,18490,18488,18486,18484,18482,18480,18478,18476,18474,18472,18470,1,18468,18466,18464,18462,18460,18458,18456,18454,18452,18450,18448,18446,18444,18442,18440,1,18438,18436,18434,18432,18430,18428,18426,18424,18422,18420,18418,18416,18414,18412,18410,1,18408,18406,18404,18402,18400,18398,18396,18394,18392,18390,18388,18386,18384,18382,18380,1,18378,18376,18374,18372,18370,18368,18366,18364,18362,18360,18358,18356,18354,18352,18350,1,18348,18346,18344,18342,18340,18338,18336,18334,18332,18330,18328,18326,18324,18322,18320,1,18318,18316,18314,18312,18310,18308,18306,18302,18303,19402,19403, - 19691,19690,18577,18575,18573,18571,18569,18567,18565,18563,18561,0,18559,18557,18555,18553,18551,18549,18547,18545,18543,18541,18539,18537,18535,18533,18531,0,18529,18527,18525,18523,18521,18519,18517,18515,18513,18511,18509,18507,18505,18503,18501,0,18499,18497,18495,18493,18491,18489,18487,18485,18483,18481,18479,18477,18475,18473,18471,0,18469,18467,18465,18463,18461,18459,18457,18455,18453,18451,18449,18447,18445,18443,18441,0,18439,18437,18435,18433,18431,18429,18427,18425,18423,18421,18419,18417,18415,18413,18411,0,18409,18407,18405,18403,18401,18399,18397,18395,18393,18391,18389,18387,18385,18383,18381,0,18379,18377,18375,18373,18371,18369,18367,18365,18363,18361,18359,18357,18355,18353,18351,0,18349,18347,18345,18343,18341,18339,18337,18335,18333,18331,18329,18327,18325,18323,18321,0,18319,18317,18315,18313,18311,18309,18307,18304,18305,19404,19405, - 19688,19686,19684,19682,19680,19678,19676,19674,19672,19670,19668,1,19666,19664,19662,19660,19658,19656,19654,19652,19650,19648,19646,19644,19642,19640,19638,1,19636,19634,19632,19630,19628,19626,19624,19622,19620,19618,19616,19614,19612,19610,19608,1,19606,19604,19602,19600,19598,19596,19594,19592,19590,19588,19586,19584,19582,19580,19578,1,19576,19574,19572,19570,19568,19566,19564,19562,19560,19558,19556,19554,19552,19550,19548,1,19546,19544,19542,19540,19538,19536,19534,19532,19530,19528,19526,19524,19522,19520,19518,1,19516,19514,19512,19510,19508,19506,19504,19502,19500,19498,19496,19494,19492,19490,19488,1,19486,19484,19482,19480,19478,19476,19474,19472,19470,19468,19466,19464,19462,19460,19458,1,19456,19454,19452,19450,19448,19446,19444,19442,19440,19438,19436,19434,19432,19430,19428,1,19426,19424,19422,19420,19418,19416,19414,19412,19410,19406,19407, - 19689,19687,19685,19683,19681,19679,19677,19675,19673,19671,19669,0,19667,19665,19663,19661,19659,19657,19655,19653,19651,19649,19647,19645,19643,19641,19639,0,19637,19635,19633,19631,19629,19627,19625,19623,19621,19619,19617,19615,19613,19611,19609,0,19607,19605,19603,19601,19599,19597,19595,19593,19591,19589,19587,19585,19583,19581,19579,0,19577,19575,19573,19571,19569,19567,19565,19563,19561,19559,19557,19555,19553,19551,19549,0,19547,19545,19543,19541,19539,19537,19535,19533,19531,19529,19527,19525,19523,19521,19519,0,19517,19515,19513,19511,19509,19507,19505,19503,19501,19499,19497,19495,19493,19491,19489,0,19487,19485,19483,19481,19479,19477,19475,19473,19471,19469,19467,19465,19463,19461,19459,0,19457,19455,19453,19451,19449,19447,19445,19443,19441,19439,19437,19435,19433,19431,19429,0,19427,19425,19423,19421,19419,19417,19415,19413,19411,19408,19409 - }; - - // 729 - private static int[] c_compactSymbolMap = - { - 609,608,411,413,415,417,419,421,423,425,427,429,431,433,435,437,439,441,443,445,447,449,451,453,455,457,459, - 607,606,410,412,414,416,418,420,422,424,426,428,430,432,434,436,438,440,442,444,446,448,450,452,454,456,458, - 605,604,409,408,243,245,247,249,251,253,255,257,259,261,263,265,267,269,271,273,275,277,279,281,283,460,461, - 603,602,407,406,242,244,246,248,250,252,254,256,258,260,262,264,266,268,270,272,274,276,278,280,282,462,463, - 601,600,405,404,241,240,107,109,111,113,115,117,119,121,123,125,127,129,131,133,135,137,139,284,285,464,465, - 599,598,403,402,239,238,106,108,110,112,114,116,118,120,122,124,126,128,130,132,134,136,138,286,287,466,467, - 597,596,401,400,237,236,105,104,3,5,7,9,11,13,15,17,19,21,23,25,27,140,141,288,289,468,469, - 595,594,399,398,235,234,103,102,2,4,6,8,10,12,14,16,18,20,22,24,26,142,143,290,291,470,471, - 593,592,397,396,233,232,101,100,1,1,2000,2001,2002,2003,2004,2005,2006,0,1,28,29,144,145,292,293,472,473, - 591,590,395,394,231,230,99,98,1,1,1,1,1,1,1,1,1,1,1,30,31,146,147,294,295,474,475, - 589,588,393,392,229,228,97,96,2027,1,0,0,0,0,0,0,0,1,2007,32,33,148,149,296,297,476,477, - 587,586,391,390,227,226,95,94,2026,1,0,1,1,1,1,1,0,1,2008,34,35,150,151,298,299,478,479, - 585,584,389,388,225,224,93,92,2025,1,0,1,0,0,0,1,0,1,2009,36,37,152,153,300,301,480,481, - 583,582,387,386,223,222,91,90,2024,1,0,1,0,1,0,1,0,1,2010,38,39,154,155,302,303,482,483, - 581,580,385,384,221,220,89,88,2023,1,0,1,0,0,0,1,0,1,2011,40,41,156,157,304,305,484,485, - 579,578,383,382,219,218,87,86,2022,1,0,1,1,1,1,1,0,1,2012,42,43,158,159,306,307,486,487, - 577,576,381,380,217,216,85,84,2021,1,0,0,0,0,0,0,0,1,2013,44,45,160,161,308,309,488,489, - 575,574,379,378,215,214,83,82,0,1,1,1,1,1,1,1,1,1,1,46,47,162,163,310,311,490,491, - 573,572,377,376,213,212,81,80,0,0,2020,2019,2018,2017,2016,2015,2014,0,0,48,49,164,165,312,313,492,493, - 571,570,375,374,211,210,78,76,74,72,70,68,66,64,62,60,58,56,54,50,51,166,167,314,315,494,495, - 569,568,373,372,209,208,79,77,75,73,71,69,67,65,63,61,59,57,55,52,53,168,169,316,317,496,497, - 567,566,371,370,206,204,202,200,198,196,194,192,190,188,186,184,182,180,178,176,174,170,171,318,319,498,499, - 565,564,369,368,207,205,203,201,199,197,195,193,191,189,187,185,183,181,179,177,175,172,173,320,321,500,501, - 563,562,366,364,362,360,358,356,354,352,350,348,346,344,342,340,338,336,334,332,330,328,326,322,323,502,503, - 561,560,367,365,363,361,359,357,355,353,351,349,347,345,343,341,339,337,335,333,331,329,327,324,325,504,505, - 558,556,554,552,550,548,546,544,542,540,538,536,534,532,530,528,526,524,522,520,518,516,514,512,510,506,507, - 559,557,555,553,551,549,547,545,543,541,539,537,535,533,531,529,527,525,523,521,519,517,515,513,511,508,509 - }; - - // 128 - // From Table 2 - private int[] c_codeSet = - { - 32, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 12, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 4, 4, 4, 4, 4, 23, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 24, 8, 24, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, - 8, 8, 8, 8, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, 4, 8, 4, 4, 4, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 8, 4, 8, 4, 4 - }; - - // 128 - // From Table 2 - private int[] c_symbolChar = - { - 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 300, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 15, 16, 17, 18, 19, 1, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 16, 301, 18, 302, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 21, 22, - 23, 24, 25, 26, 20, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 27, 21, 28, 22, 23, 24, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 29, 25, 30, 26, 27 - }; - - // 32 - private static string[] c_hexBit = - { - "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", "01000", "01001", - "01010", "01011", "01100", "01101", "01110", "01111", "10000", "10001", "10010", "10011", "10100", "10101", - "10110", "10111", "11000", "11001", "11010", "11011", "11100", "11101", "11110", "11111" - }; - - // 32 - private static string[] c_pentBit = - { - "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", - "1010", "1011", "1100", "1101", "1110", "1111" - }; - - // 32 - private static int[] c_codewordsPerSymbol = - { - 21, 48, 60, 88, 120, 156, 196, 240, 230, 272, 316, 364, 416, 470, 528, 588, 652, 720, 790, - 864, 940, 1020, 920, 992, 1066, 1144, 1224, 1306, 1392, 1480, 1570, 1664 - }; - - // 4 - private static int[] c_codewordsPerCompactSymbol = { 17, 40, 51, 76 }; - - // 32 - // Data bits per symbol maximum with 10% error correction - private static int[] c_ecl10DataSizes = - { - 96, 246, 408, 616, 840, 1104, 1392, 1704, 2040, 2420, 2820, 3250, 3720, 4200, 4730, - 5270, 5840, 6450, 7080, 7750, 8430, 9150, 9900, 10680, 11484, 12324, 13188, 14076, - 15000, 15948, 16920, 17940 - }; - - // 32 - // Data bits per symbol maximum with 23% error correction - private static int[] c_ecl23DataSizes = - { - 84, 204, 352, 520, 720, 944, 1184, 1456, 1750, 2070, 2410, 2780, 3180, 3590, 4040, - 4500, 5000, 5520, 6060, 6630, 7210, 7830, 8472, 9132, 9816, 10536, 11280, 12036, - 12828, 13644, 14472, 15348 - }; - - // 32 - // Data bits per symbol maximum with 36% error correction - private static int[] c_ecl36DataSizes = - { - 66, 168, 288, 432, 592, 776, 984, 1208, 1450, 1720, 2000, 2300, 2640, 2980, 3350, - 3740, 4150, 4580, 5030, 5500, 5990, 6500, 7032, 7584, 8160, 8760, 9372, 9996, 10656, - 11340, 12024, 12744 - }; - - // 32 - // Data bits per symbol maximum with 50% error correction - private static int[] c_ecl50DataSizes = - { - 48, 126, 216, 328, 456, 600, 760, 936, 1120, 1330, 1550, 1790, 2050, 2320, 2610, - 2910, 3230, 3570, 3920, 4290, 4670, 5070, 5484, 5916, 6360, 6828, 7308, 7800, 8316, - 8844, 9384, 9948 - }; - - // 4 - // Data bits per compact symbol maximum with 10% error correction - private static int[] c_eclCompact10DataSizes = { 78, 198, 336, 520 }; - - // Data bits per compact symbol maximum with 23% error correction - private static int[] c_eclCompact23DataSizes = { 66, 168, 288, 440 }; - - // Data bits per compact symbol maximum with 36% error correction - private static int[] c_eclCompact36DataSizes = { 48, 138, 232, 360 }; - - // Data bits per compact symbol maximum with 50% error correction - private static int[] c_eclCompact50DataSizes = { 36, 102, 176, 280 }; - - // 32 - private static int[] c_offsets = - { - 66, 64, 62, 60, 57, 55, 53, 51, 49, 47, 45, 42, 40, 38, 36, 34, 32, 30, 28, 25, 23, 21, - 19, 17, 15, 13, 10, 8, 6, 4, 2, 0 - }; - - // 4 - private static int[] c_compactOffsets = { 6, 4, 2, 0 }; - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/DataMatrixSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/DataMatrixSymbology.cs deleted file mode 100644 index aa9912f1..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/DataMatrixSymbology.cs +++ /dev/null @@ -1,1782 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -/* -Sample online barcode generator: -http://invx.com/code/?code=A%C3%81BC%C4%8CD%C4%8EE%C3%89%C4%9AFGHChI%C3%8DJKLMN%C5%87O%C3%93PQR%C5%98S%C5%A0T%C5%A4U%C3%9A%C5%AEVWXY%C3%9DZ%C5%BD&fg=%23000000&bg=%23ffffff&height=&width= -*/ - -using System; -using System.Text; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using DataMatrix symbology. A Data Matrix code is a - /// two-dimensional matrix barcode consisting of black and white square - /// modules arranged in either a square or rectangular pattern. The - /// information to be encoded can be text or raw data. Usual data size - /// is from a few bytes up to 2 kilobytes. The length of the encoded - /// data depends on the symbol dimension used. - /// - class DataMatrixSymbology : SymbologyDrawing2D - { - class Matrix - { - public Matrix(int totalHeight, int totalWidth, int regionHeight, - int regionWidth, int totalDataCWCount, int regionDataCWCount, - int regionReedSolomonCWCount) - { - m_totalHeight = totalHeight; - m_totalWidth = totalWidth; - m_regionHeight = regionHeight; - m_regionWidth = regionWidth; - m_totalDataCWCount = totalDataCWCount; - m_regionDataCWCount = regionDataCWCount; - m_regionReedSolomonCWCount = regionReedSolomonCWCount; - } - - public int m_totalHeight; - public int m_totalWidth; - public int m_regionHeight; - public int m_regionWidth; - public int m_totalDataCWCount; - public int m_regionDataCWCount; - public int m_regionReedSolomonCWCount; - }; - - private static Matrix[] m_matrices = new Matrix[] - { - new Matrix(10, 10, 10, 10, 3, 3, 5), - new Matrix(12, 12, 12, 12, 5, 5, 7), - new Matrix(8, 18, 8, 18, 5, 5, 7), - new Matrix(14, 14, 14, 14, 8, 8, 10), - new Matrix(8, 32, 8, 16, 10, 10, 11), - new Matrix(16, 16, 16, 16, 12, 12, 12), - new Matrix(12, 26, 12, 26, 16, 16, 14), - new Matrix(18, 18, 18, 18, 18, 18, 14), - new Matrix(20, 20, 20, 20, 22, 22, 18), - new Matrix(12, 36, 12, 18, 22, 22, 18), - new Matrix(22, 22, 22, 22, 30, 30, 20), - new Matrix(16, 36, 16, 18, 32, 32, 24), - new Matrix(24, 24, 24, 24, 36, 36, 24), - new Matrix(26, 26, 26, 26, 44, 44, 28), - new Matrix(16, 48, 16, 24, 49, 49, 28), - new Matrix(32, 32, 16, 16, 62, 62, 36), - new Matrix(36, 36, 18, 18, 86, 86, 42), - new Matrix(40, 40, 20, 20, 114, 114, 48), - new Matrix(44, 44, 22, 22, 144, 144, 56), - new Matrix(48, 48, 24, 24, 174, 174, 68), - new Matrix(52, 52, 26, 26, 204, 102, 42), - new Matrix(64, 64, 16, 16, 280, 140, 56), - new Matrix(72, 72, 18, 18, 368, 92, 36), - new Matrix(80, 80, 20, 20, 456, 114, 48), - new Matrix(88, 88, 22, 22, 576, 144, 56), - new Matrix(96, 96, 24, 24, 696, 174, 68), - new Matrix(104, 104, 26, 26, 816, 136, 56), - new Matrix(120, 120, 20, 20, 1050, 175, 68), - new Matrix(132, 132, 22, 22, 1304, 163, 62), - new Matrix(144, 144, 24, 24, 1558, 156, 62) - }; - - private struct CompactionModesBlock - { - // number of bytes of source that can be encoded in a row - // at this point using this compaction mode - public int source; - - // number of bytes of target generated encoding from - // this point to end if already in this encoding mode - public int target; - }; - - private const int c_maxByteCount = 3116; - - protected byte[] m_data; - private byte[,] m_encodedData; - - private int m_width; - private int m_height; - - private DataMatrixCompactionMode m_compactionMode = DataMatrixCompactionMode.Auto; - - private bool m_useNonSquareMatrices = true; - - public override string Value - { - get { return base.Value; } - set - { - if (ValueIsValid(value, false)) - { - base.Value = value; - } - else - { - throw new BarcodeException("Provided value can't be encoded into DataMatrix of specified size and compaction mode."); - } - } - } - - /// - /// Initializes a new instance of the class. - /// - public DataMatrixSymbology() - : base(TrueSymbologyType.DataMatrix) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public DataMatrixSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.DataMatrix) - { - } - - /// - /// Validates the value using Data Matrix symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - parseSize(Options.DataMatrixSize, out m_width, out m_height); - if (Options.DataMatrixSize == DataMatrixSize.AutoSquareSize) - m_useNonSquareMatrices = false; - - m_compactionMode = Options.DataMatrixCompactionMode; - AdjustCompactionMode(); - - Encoding tmpEncoding = Options.Encoding; - - m_data = tmpEncoding.GetBytes(value); - - try - { - if (m_data.Length != 0) - encodeData(); - } - catch - { - return false; - } - - return true; - } - - private void AdjustCompactionMode() - { - //auto switch to binary mode for multibytes encodings (Unicode, etc) - - byte[] bytes = Options.Encoding.GetBytes(Value); - if (bytes.Length > Value.Length) - m_compactionMode = DataMatrixCompactionMode.Binary; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Data Matrix symbology allows a maximum data size of 2,335 alphanumeric characters.\n"; - } - - /// - /// Gets the barcode value encoded using Data Matrix symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using Data Matrix symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - if (forCaption) - return Value; - - return ""; - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// The encoding pattern for given character. - protected override string getCharPattern(char c) - { - return ""; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - parseSize(Options.DataMatrixSize, out m_width, out m_height); - if (Options.DataMatrixSize == DataMatrixSize.AutoSquareSize) - m_useNonSquareMatrices = false; - - m_compactionMode = Options.DataMatrixCompactionMode; - AdjustCompactionMode(); - - m_data = getDataForEncoding(); - if (m_data.Length != 0) - encodeData(); - - SKSize drawingSize = new SKSize(); - if (m_encodedData != null) - { - int width = m_encodedData.GetLength(0); - int height = m_encodedData.GetLength(1); - - int cellWidth = NarrowBarWidth; - drawingSize.Width = width * cellWidth; - drawingSize.Height = height * cellWidth; - - for (int x = 0; x < width; x++) - { - for (int y = 0; y < height; y++) - { - if (m_encodedData[x, height - y - 1] != 0) - m_rects.Add(new SKRect(x * cellWidth, y * cellWidth, x * cellWidth + cellWidth, y * cellWidth + cellWidth)); - } - } - } - - return drawingSize; - } - - protected virtual DataMatrixCompactionMode CompactionMode - { - get { return m_compactionMode; } - } - - protected virtual byte[] getDataForEncoding() - { - // get encoding used in options - Encoding tmpEncoding = Options.Encoding; - - /* - // detect Unicode and auto switch to UTF-8 ? - - bool UnicodeSymbolFound = !Utils.IsLatinISOEncodingOnly(Value); - - // if we are not with encoding UTF8 - // then we are forcing to use UTF8 - if (UnicodeSymbolFound && tmpEncoding != Encoding.UTF8) - { - // force encoding to UTF8 - tmpEncoding = Encoding.UTF8; - } - */ - - return tmpEncoding.GetBytes(Value); - - //byte[] bbytes = new byte[Value.Length]; - - //int index = 0; - //foreach (char c in Value.ToCharArray()) - //{ - // bbytes[index++] = (byte)c; - //} - //return bbytes; - } - - private static void parseSize(DataMatrixSize size, out int width, out int height) - { - width = 0; - height = 0; - - if (size != DataMatrixSize.AutoSize && size != DataMatrixSize.AutoSquareSize) - { - string[] sizes = size.ToString().Substring(4).Split(new char[] { 'x' }); - width = Convert.ToInt32(sizes[1]); - height = Convert.ToInt32(sizes[0]); - } - } - - private Matrix getWorkingMatrix() - { - for (int i = 0; i < m_matrices.Length; i++) - { - if (m_matrices[i].m_totalWidth == m_width && m_matrices[i].m_totalHeight == m_height) - return m_matrices[i]; - } - - throw new BarcodeException("Too much data for " + m_width + "x" + m_height + " barcode"); - } - - private void encodeData() - { - Matrix matrix = null; - DataMatrixCompactionMode[] compactionModes = getDefaultCompactionModes(); - if (m_width != 0) - matrix = getMatrixAndCompactionForGivenSize(ref compactionModes); - else - matrix = findSuitableMatrixAndCompaction(ref compactionModes); - - if (matrix == null) - throw new BarcodeException("Cannot encode this data. Possibly size is incorrect"); - - byte[] codewords = produceCodewords(compactionModes, matrix.m_totalDataCWCount); - if (codewords == null) - throw new BarcodeException("Too much data for " + m_width + "x" + m_height + " barcode"); - - // Last paramter controls if we should invert first 2 and last 2 bytes in RS codewords - // for 144x144 see #159 (in Decoders) and #686 (in BarCode SDK) - addReedSolomonCodewords(ref codewords, matrix, Options.DataMatrixAlternativeReedSolomonCorrectionFor144x144Size); - - produceEncodedData(matrix, codewords); - } - - private void produceEncodedData(Matrix matrix, byte[] codewords) - { - int columnCount = m_width - 2 * (m_width / matrix.m_regionWidth); - int rowCount = m_height - 2 * (m_height / matrix.m_regionHeight); - int[] places = new int[columnCount * rowCount]; - calculatePlacement(places, rowCount, columnCount); - - m_encodedData = new byte[m_width, m_height]; - - for (int row = 0; row < m_height; row += matrix.m_regionHeight) - { - for (int column = 0; column < m_width; column++) - m_encodedData[column, row] = 1; - - for (int column = 0; column < m_width; column += 2) - m_encodedData[column, row + matrix.m_regionHeight - 1] = 1; - } - - for (int column = 0; column < m_width; column += matrix.m_regionWidth) - { - for (int row = 0; row < m_height; row++) - m_encodedData[column, row] = 1; - - for (int row = 0; row < m_height; row += 2) - m_encodedData[column + matrix.m_regionWidth - 1, row] = 1; - } - - for (int row = 0; row < rowCount; row++) - { - for (int column = 0; column < columnCount; column++) - { - int v = places[(rowCount - row - 1) * columnCount + column]; - if (v == 1 || v > 7 && (codewords[(v >> 3) - 1] & (1 << (v & 7))) != 0) - m_encodedData[1 + column + 2 * (column / (matrix.m_regionWidth - 2)), 1 + row + 2 * (row / (matrix.m_regionHeight - 2))] = 1; - } - } - } - - private Matrix findSuitableMatrixAndCompaction(ref DataMatrixCompactionMode[] compactionModes) - { - int notUsedLength = 0; - if (compactionModes == null) - compactionModes = buildCompactionModes(ref notUsedLength, true); - - Matrix matrix = null; - if (compactionModes != null) - { - matrix = findSuitableMatrixForGivenCompaction(compactionModes); - } - else - { - int len = 0; - compactionModes = buildCompactionModes(ref len, true); - for (int i = 0; i < m_matrices.Length; i++) - { - if (m_matrices[i].m_totalDataCWCount == len) - { - matrix = m_matrices[i]; - break; - } - } - - if (compactionModes != null && matrix == null) - { - // try once more and do not require an exact fit - compactionModes = buildCompactionModes(ref len, false); - for (int i = 0; i < m_matrices.Length; i++) - { - if (m_matrices[i].m_totalDataCWCount < len) - { - matrix = m_matrices[i]; - break; - } - } - } - } - - if (matrix == null) - throw new BarcodeException("Cannot find suitable size, too much data to encode"); - - m_width = matrix.m_totalWidth; - m_height = matrix.m_totalHeight; - return matrix; - } - - private Matrix findSuitableMatrixForGivenCompaction(DataMatrixCompactionMode[] compactionModes) - { - for (int i = 0; i < m_matrices.Length; i++) - { - if (!m_useNonSquareMatrices) - { - if (m_matrices[i].m_totalHeight != m_matrices[i].m_totalWidth) - continue; - } - - byte[] codewords = produceCodewords(compactionModes, m_matrices[i].m_totalDataCWCount); - if (codewords != null) - return m_matrices[i]; - } - - return null; - } - - private Matrix getMatrixAndCompactionForGivenSize(ref DataMatrixCompactionMode[] compactionModes) - { - Matrix matrix = getWorkingMatrix(); - if (compactionModes == null) - { - int len = 0; - compactionModes = buildCompactionModes(ref len, true); - if (compactionModes != null && len != matrix.m_totalDataCWCount) - { - // try once more and do not require an exact fit - compactionModes = buildCompactionModes(ref len, false); - if (len > matrix.m_totalDataCWCount) - throw new BarcodeException("Too much data for " + m_width + "x" + m_height + " barcode"); - } - } - - return matrix; - } - - protected virtual DataMatrixCompactionMode[] getDefaultCompactionModes() - { - DataMatrixCompactionMode[] compactionModes = null; - if (CompactionMode != DataMatrixCompactionMode.Auto) - { - compactionModes = new DataMatrixCompactionMode[m_data.Length]; - for (int i = 0; i < compactionModes.Length; i++) - compactionModes[i] = CompactionMode; - } - - return compactionModes; - } - - private DataMatrixCompactionMode[] buildCompactionModes(ref int lenp, bool exact) - { - if (m_data.Length == 0 || m_data.Length > c_maxByteCount) - return null; - - CompactionModesBlock[,] blocks = new CompactionModesBlock[m_data.Length + 1, CompactionModeLength]; - int pos = m_data.Length; - while ((pos--) > 0) - { - tryAsciiMode(pos, blocks); - tryC40Mode(pos, blocks, exact); - tryTextMode(pos, blocks, exact); - tryX12Mode(pos, blocks, exact); - tryEdifactMode(pos, blocks, exact); - tryBinaryMode(pos, blocks); - } - - return postprocessCompactionBlocks(blocks, ref lenp); - } - - private DataMatrixCompactionMode[] postprocessCompactionBlocks(CompactionModesBlock[,] blocks, ref int lenp) - { - DataMatrixCompactionMode[] compactionModes = new DataMatrixCompactionMode[m_data.Length + 1]; - DataMatrixCompactionMode currentMode = DataMatrixCompactionMode.Ascii; - int pos = 0; - while (pos < m_data.Length) - { - int m = 0; - DataMatrixCompactionMode mode = DataMatrixCompactionMode.Ascii; - for (DataMatrixCompactionMode i = DataMatrixCompactionMode.Ascii; i < LastCompactionMode; i++) - { - int t = blocks[pos, (int)i].target + switchCost(currentMode, i); - if (blocks[pos, (int)i].target != 0 && (t < m || t == m && i == currentMode || m == 0)) - { - mode = i; - m = t; - } - } - - currentMode = mode; - m = blocks[pos, (int)mode].source; - - if (pos == 0 && lenp != 0) - lenp = blocks[pos, (int)mode].target; - - while (pos < m_data.Length && (m--) != 0) - compactionModes[pos++] = (DataMatrixCompactionMode)mode; - } - - compactionModes[pos] = DataMatrixCompactionMode.Ascii; - return compactionModes; - } - - private static void tryBinaryMode(int pos, CompactionModesBlock[,] blocks) - { - int bl = 0; - DataMatrixCompactionMode b = DataMatrixCompactionMode.Ascii; - for (DataMatrixCompactionMode e = DataMatrixCompactionMode.Ascii; e < LastCompactionMode; e++) - { - int extra = 0; - if (e == DataMatrixCompactionMode.Binary && blocks[pos + 1, (int)e].target == 249) - extra = 1; - - int t = blocks[pos + 1, (int)e].target + switchCost(DataMatrixCompactionMode.Binary, e) + extra; - if (blocks[pos + 1, (int)e].target != 0 && (t < bl || bl == 0)) - { - bl = t; - b = e; - } - } - - blocks[pos, (int)DataMatrixCompactionMode.Binary].target = bl + 1; - blocks[pos, (int)DataMatrixCompactionMode.Binary].source = 1; - if (bl != 0 && b == DataMatrixCompactionMode.Binary) - blocks[pos, (int)b].source += blocks[pos + 1, (int)b].source; - } - - private void tryEdifactMode(int pos, CompactionModesBlock[,] enc, bool exact) - { - int bl = 0; - if (m_data[pos] >= 32 && m_data[pos] <= 94) - { - // can encode 1 - int bs = 0; - DataMatrixCompactionMode b = DataMatrixCompactionMode.Ascii; - if (pos + 1 == m_data.Length && (bl == 0 || bl < 2)) - { - bl = 2; - bs = 1; - } - else - { - for (DataMatrixCompactionMode e = DataMatrixCompactionMode.Ascii; e < LastCompactionMode; e++) - { - int t = 2 + enc[pos + 1, (int)e].target + switchCost(DataMatrixCompactionMode.Ascii, e); - if (e != DataMatrixCompactionMode.Edifact && enc[pos + 1, (int)e].target != 0 && (t < bl || bl == 0)) - { - bs = 1; - bl = t; - b = e; - } - } - } - - if (pos + 1 < m_data.Length && m_data[pos + 1] >= 32 && m_data[pos + 1] <= 94) - { - // can encode 2 - if (pos + 2 == m_data.Length && (bl == 0 || bl < 2)) - { - bl = 3; - bs = 2; - } - else - { - for (DataMatrixCompactionMode e = DataMatrixCompactionMode.Ascii; e < LastCompactionMode; e++) - { - int t = 3 + enc[pos + 2, (int)e].target + switchCost(DataMatrixCompactionMode.Ascii, e); - if (e != DataMatrixCompactionMode.Edifact && enc[pos + 2, (int)e].target != 0 && (t < bl || bl == 0)) - { - bs = 2; - bl = t; - b = e; - } - } - } - - if (pos + 2 < m_data.Length && m_data[pos + 2] >= 32 && m_data[pos + 2] <= 94) - { - // can encode 3 - if (pos + 3 == m_data.Length && (bl == 0 || bl < 3)) - { - bl = 3; - bs = 3; - } - else - { - for (DataMatrixCompactionMode e = DataMatrixCompactionMode.Ascii; e < LastCompactionMode; e++) - { - int t = 3 + enc[pos + 3, (int)e].target + switchCost(DataMatrixCompactionMode.Ascii, e); - if (e != DataMatrixCompactionMode.Edifact && enc[pos + 3, (int)e].target != 0 && (t < bl || bl == 0)) - { - bs = 3; - bl = t; - b = e; - } - } - } - - if (pos + 4 < m_data.Length && m_data[pos + 3] >= 32 && m_data[pos + 3] <= 94) - { - // can encode 4 - if (pos + 4 == m_data.Length && (bl == 0 || bl < 3)) - { - bl = 3; - bs = (char)4; - } - else - { - for (DataMatrixCompactionMode e = DataMatrixCompactionMode.Ascii; e < LastCompactionMode; e++) - { - int t = 3 + enc[pos + 4, (int)e].target + switchCost(DataMatrixCompactionMode.Edifact, e); - if (enc[pos + 4, (int)e].target != 0 && (t < bl || bl == 0)) - { - bs = (char)4; - bl = t; - b = e; - } - } - - int temp = 3 + enc[pos + 4, (int)DataMatrixCompactionMode.Ascii].target; - if (exact && enc[pos + 4, (int)DataMatrixCompactionMode.Ascii].target != 0 && enc[pos + 4, (int)DataMatrixCompactionMode.Ascii].target <= 2 && temp < bl) - { - // special case, switch to ASCII for last 1 of two bytes - bs = 4; - bl = temp; - b = DataMatrixCompactionMode.Ascii; - } - } - } - } - } - - enc[pos, (int)DataMatrixCompactionMode.Edifact].target = bl; - enc[pos, (int)DataMatrixCompactionMode.Edifact].source = bs; - if (bl != 0 && b == DataMatrixCompactionMode.Edifact) - enc[pos, (int)b].source += enc[pos + bs, (int)b].source; - } - } - - private void tryX12Mode(int pos, CompactionModesBlock[,] enc, bool exact) - { - int sub = 0; - int tl = 0; - int sl = 0; - do - { - byte c = m_data[pos + sl++]; - if (c != 13 && c != '*' && c != '>' && c != ' ' && !isDigit(c) && !isUpper(c)) - { - sl = 0; - break; - } - - sub++; - while (sub >= 3) - { - sub -= 3; - tl += 2; - } - - } while (sub != 0 && pos + sl < m_data.Length); - - if (sub == 0 && sl != 0) - { - // can encode X12 - int bl = 0; - DataMatrixCompactionMode b = DataMatrixCompactionMode.Ascii; - if (pos + sl < m_data.Length) - { - for (DataMatrixCompactionMode e = DataMatrixCompactionMode.Ascii; e < LastCompactionMode; e++) - { - int t = enc[pos + sl, (int)e].target + switchCost(DataMatrixCompactionMode.X12, e); - if (enc[pos + sl, (int)e].target != 0 && (t < bl || bl == 0)) - { - bl = t; - b = e; - } - } - } - - if (exact && enc[pos + sl, (int)DataMatrixCompactionMode.Ascii].target == 1 && 1 < bl) - { - // special case, switch to ASCII for last bytes - bl = 1; - b = DataMatrixCompactionMode.Ascii; - } - - enc[pos, (int)DataMatrixCompactionMode.X12].target = tl + bl; - enc[pos, (int)DataMatrixCompactionMode.X12].source = sl; - if (bl != 0 && b == DataMatrixCompactionMode.X12) - enc[pos, (int)b].source += enc[pos + sl, (int)b].source; - } - } - - private void tryTextMode(int pos, CompactionModesBlock[,] enc, bool exact) - { - int sub = 0; - int tl = 0; - int sl = 0; - do - { - byte c = m_data[pos + sl++]; - if ((c & 0x80) != 0) - { - // shift + upper - sub += 2; - c &= 0x7F; - } - - if (c != ' ' && !isDigit(c) && !isLower(c)) - sub++; // shift - - sub++; - while (sub >= 3) - { - sub -= 3; - tl += 2; - } - - } while (sub != 0 && (pos + sl < m_data.Length)); - - if (exact && sub == 2 && pos + sl == m_data.Length) - { - // special case, can encode last block with shift 0 at end - sub = 0; - tl += 2; - } - - if (sub == 0 && sl != 0) - { - // can encode Text - int bl = 0; - DataMatrixCompactionMode b = DataMatrixCompactionMode.Ascii; - if (pos + sl < m_data.Length) - { - for (DataMatrixCompactionMode e = DataMatrixCompactionMode.Ascii; e < LastCompactionMode; e++) - { - int t = enc[pos + sl, (int)e].target + switchCost(DataMatrixCompactionMode.Text, e); - if (enc[pos + sl, (int)e].target != 0 && (t < bl || bl == 0)) - { - bl = t; - b = e; - } - } - } - - if (exact && enc[pos + sl, (int)DataMatrixCompactionMode.Ascii].target == 1 && 1 < bl) - { - // special case, switch to ASCII for last bytes - bl = 1; - b = DataMatrixCompactionMode.Ascii; - } - - enc[pos, (int)DataMatrixCompactionMode.Text].target = (short)(tl + bl); - enc[pos, (int)DataMatrixCompactionMode.Text].source = (short)sl; - if (bl != 0 && b == DataMatrixCompactionMode.Text) - enc[pos, (int)b].source += enc[pos + sl, (int)b].source; - } - } - - private void tryC40Mode(int pos, CompactionModesBlock[,] enc, bool exact) - { - int sub = 0; - int tl = 0; - int sl = 0; - - do - { - byte c = m_data[pos + sl]; - sl++; - - if ((c & 0x80) != 0) - { - // shift + upper - sub += 2; - c &= 0x7F; - } - - if (c != ' ' && !isDigit(c) && !isUpper(c)) - { - // shift - sub++; - } - - sub++; - while (sub >= 3) - { - sub -= 3; - tl += 2; - } - - } while (sub != 0 && (pos + sl < m_data.Length)); - - if (exact && sub == 2 && pos + sl == m_data.Length) - { - // special case, can encode last block with shift 0 at end - sub = 0; - tl += 2; - } - - if (sub == 0) - { - // can encode C40 - int bl = 0; - DataMatrixCompactionMode b = DataMatrixCompactionMode.Ascii; - if (pos + sl < m_data.Length) - { - for (DataMatrixCompactionMode e = DataMatrixCompactionMode.Ascii; e < LastCompactionMode; e++) - { - int t = enc[pos + sl, (int)e].target + switchCost(DataMatrixCompactionMode.C40, e); - if (enc[pos + sl, (int)e].target != 0 && (t < bl || bl == 0)) - { - bl = t; - b = e; - } - } - } - - if (exact && enc[pos + sl, (int)DataMatrixCompactionMode.Ascii].target == 1 && 1 < bl) - { - // special case, switch to ASCII for last bytes - bl = 1; - b = DataMatrixCompactionMode.Ascii; - } - enc[pos, (int)DataMatrixCompactionMode.C40].target = (short)(tl + bl); - enc[pos, (int)DataMatrixCompactionMode.C40].source = (short)sl; - if (bl != 0 && b == DataMatrixCompactionMode.C40) - enc[pos, (int)b].source += enc[pos + sl, (int)b].source; - } - } - - private void tryAsciiMode(int pos, CompactionModesBlock[,] enc) - { - int sl = 1; - int tl = 1; - if (isDigit(m_data[pos]) && pos + 1 < m_data.Length && isDigit(m_data[pos + 1])) - { - // double digit - sl = 2; - } - else if ((m_data[pos] & 0x80) != 0) - { - // upper shift - tl = 2; - } - - int bl = 0; - DataMatrixCompactionMode b = DataMatrixCompactionMode.Ascii; - if (pos + sl < m_data.Length) - { - for (DataMatrixCompactionMode e = DataMatrixCompactionMode.Ascii; e < LastCompactionMode; e++) - { - int t = enc[pos + sl, (int)e].target + switchCost(DataMatrixCompactionMode.Ascii, e); - if (enc[pos + sl, (int)e].target != 0 && (t < bl || bl == 0)) - { - bl = t; - b = e; - } - } - } - - enc[pos, (int)DataMatrixCompactionMode.Ascii].target = tl + bl; - enc[pos, (int)DataMatrixCompactionMode.Ascii].source = sl; - - if (bl != 0 && b == DataMatrixCompactionMode.Ascii) - enc[pos, (int)b].source += enc[pos + sl, (int)b].source; - } - - protected virtual void initCodeWords(byte[] codewords, ref int codewordIndex) - { - } - - private byte[] produceCodewords(DataMatrixCompactionMode[] compactionModes, int codewordCountLimit) - { - byte[] codewords = new byte[codewordCountLimit]; - int codewordIndex = 0; - initCodeWords(codewords, ref codewordIndex); - - int dataIndex = 0; - DataMatrixCompactionMode mode = DataMatrixCompactionMode.Ascii; - while (dataIndex < m_data.Length && codewordIndex < codewordCountLimit) - { - DataMatrixCompactionMode newMode = mode; - - // check if we have C30, Text or X12 mode - // and then if we need to force switch to ASCII: - // if we are in C40 and Text modes and we have 1 single character to encode then we should use ASCII - // if we are in x12 mode and we have 2 characters to encode then we should use ASCII mode to encode them - if (codewordCountLimit - codewordIndex <= 1 && - (mode == DataMatrixCompactionMode.C40 || mode == DataMatrixCompactionMode.Text) || - codewordCountLimit - codewordIndex <= 2 && mode == DataMatrixCompactionMode.X12) - { - // revert to ASCII - mode = DataMatrixCompactionMode.Ascii; - } - - newMode = compactionModes[dataIndex]; - - switch (newMode) - { - case DataMatrixCompactionMode.C40: - case DataMatrixCompactionMode.Text: - case DataMatrixCompactionMode.X12: - produceTextBasedCodewords(ref mode, newMode, ref dataIndex, ref codewordIndex, codewordCountLimit, codewords); - break; - - case DataMatrixCompactionMode.Edifact: - produceEdifactCodewords(ref mode, newMode, ref dataIndex, ref codewordIndex, compactionModes, codewordCountLimit, codewords); - break; - - case DataMatrixCompactionMode.Ascii: - produceAsciiCodewords(ref mode, newMode, ref dataIndex, ref codewordIndex, codewordCountLimit, codewords); - break; - - case DataMatrixCompactionMode.Binary: - produceBinaryCodewords(ref mode, newMode, ref dataIndex, ref codewordIndex, compactionModes, codewordCountLimit, codewords); - break; - - default: - throw new BarcodeException("Unknown encoding: " + newMode); - } - } - - // close current chunk of codeword and add padding codewords at the end - addEscapeAndPaddingCodewords(mode, ref codewordIndex, codewordCountLimit, codewords); - - if (codewordIndex > codewordCountLimit || dataIndex < m_data.Length) - { - // did not fit - return null; - } - - return codewords; - } - - private static void addEscapeAndPaddingCodewords(DataMatrixCompactionMode mode, ref int codewordIndex, int codewordCountLimit, byte[] codewords) - { - if (codewordIndex < codewordCountLimit) - { - if (mode == DataMatrixCompactionMode.Ascii) - // ASCII mode padding - codewords[codewordIndex++] = 129; // add padding character in ASCII mode - else - { - // non ASCII mode so we are adding the switch to ASCII mode - if (mode == DataMatrixCompactionMode.C40 || mode == DataMatrixCompactionMode.X12 || mode == DataMatrixCompactionMode.Text) - codewords[codewordIndex++] = 254; // escape X12/C40/Text - else - codewords[codewordIndex++] = 254; // escape EDIFACT - we are adding 254 switch to ASCII as 0x7c switch causes some unpredictable response from decoders - - // as we have switched to ANSI then we should add padding character 129 - if (codewordIndex < codewordCountLimit) - codewords[codewordIndex++] = 129; // padding character in ASCII mode - } - } - - // if we still need to add more padding then we are adding according - // to the specification 129 + some random (see the spec) - while (codewordIndex < codewordCountLimit) - { - // more padding - int v = 129 + (((codewordIndex + 1) * 149) % 253) + 1; - if (v > 254) - v -= 254; - codewords[codewordIndex++] = (byte)v; - } - } - - private void produceBinaryCodewords(ref DataMatrixCompactionMode mode, DataMatrixCompactionMode newMode, ref int dataIndex, ref int codewordIndex, DataMatrixCompactionMode[] compactionModes, int codewordCountLimit, byte[] codewords) - { - - // now perform check if we have enough codewords for this encoding method - // we are in Binary mode: we are encoding 1 byte into 1 codeword - // and we need to add the starting marker + 2 bytes for the length - bool enoughCW = (m_data.Length + 3) <= codewordCountLimit; // if we have enough codewords - - // return enlarged codewordindex so this matrix will wail and - // the calling code will try the next sizes matrix - if (!enoughCW) - { - codewordIndex = codewordCountLimit + 1; - return; - } - - try - { - - // close previous chunk of data if needed - if (mode != newMode) - { - // check if we are not at the beginning and - // need to switch the mode - if (codewordIndex > 0) - { - if ( - mode == DataMatrixCompactionMode.C40 || - mode == DataMatrixCompactionMode.Text || - mode == DataMatrixCompactionMode.X12 - ) - codewords[codewordIndex++] = 254; // escape C40/text/X12 - else if (mode == DataMatrixCompactionMode.Edifact) - codewords[codewordIndex++] = 254; // escape EDIFACT - } - - // set the codeword to open BINARY mode - codewords[codewordIndex++] = 231; - - } - - // set mode to binary - mode = DataMatrixCompactionMode.Binary; - - // length of the data to encode - int lengthToEncode = 0; - - // calculate length of data to encode - if (compactionModes != null) - { - for (int p = dataIndex; p < m_data.Length && compactionModes[p] == DataMatrixCompactionMode.Binary; p++) - lengthToEncode++; - } - - // write the length of the data to encode - // see the specifcation - if (lengthToEncode < 250) - { - // if length < 250 then encoding using the length according the 255 algorithm - codewords[codewordIndex] = (byte)(lengthToEncode + (((codewordIndex + 1) * 149) % 255) + 1); - codewordIndex++; - } - else - { - // if length > 250 then encoding according to the rule described - // in the spec - codewords[codewordIndex++] = (byte)(249 + (lengthToEncode / 250)); - codewords[codewordIndex++] = (byte)(lengthToEncode % 250); - } - - while ((lengthToEncode--) != 0 && codewordIndex < codewordCountLimit) - { - // encoding according to the specification of Base 256 encoding using The 255-state algorithm (see the spec) - codewords[codewordIndex] = (byte)(m_data[dataIndex++] + (((codewordIndex + 1) * 149) % 255) + 1); - codewordIndex++; - } - - } - catch { - // hide exception as it means the mode do not fit - } - } - - protected virtual void produceAsciiCodewords(ref DataMatrixCompactionMode mode, DataMatrixCompactionMode newMode, ref int dataIndex, ref int codewordIndex, int codewordCountLimit, byte[] codewords) - { - try - { - - // switch to the new mode if needed - if (mode != newMode) - { - // close the previous chunk of data if needed - if (codewordIndex > 0) - { - if (mode == DataMatrixCompactionMode.C40 || mode == DataMatrixCompactionMode.Text || mode == DataMatrixCompactionMode.X12) - codewords[codewordIndex++] = 254; // escape C40/text/X12 - else - codewords[codewordIndex++] = 0x7C; // escape EDIFACT - } - } - // set the mode to ASCII - mode = DataMatrixCompactionMode.Ascii; - if (m_data.Length - dataIndex >= 2 && isDigit(m_data[dataIndex]) && isDigit(m_data[dataIndex + 1])) - { - codewords[codewordIndex++] = (byte)((m_data[dataIndex] - '0') * 10 + m_data[dataIndex + 1] - '0' + 130); - dataIndex += 2; - } - else if (m_data[dataIndex] > 127) - { - codewords[codewordIndex++] = 235; - codewords[codewordIndex++] = (byte)(m_data[dataIndex++] - 127); - } - else - codewords[codewordIndex++] = (byte)(m_data[dataIndex++] + 1); - } - catch { - // hide exception as this will cause failing for this size and - // the code will try next size available - } - } - - /// - /// checks if the string has lowercase characters - /// we are using this for Edifact encoding as - /// - /// input string to check - /// - private bool hasLowerCaseCharacters(ref byte[] str) - { - // lower case ASCII symbols are 97 to 122 (a-z) - foreach (byte b in str) - { - if (b > 96 && b < 123) - return true; - } - - return false; - } - - /// - /// Generates Edifact encoded - /// - /// - /// - /// - /// - /// - /// - /// - private void produceEdifactCodewords(ref DataMatrixCompactionMode mode, DataMatrixCompactionMode newMode, - ref int dataIndex, ref int codewordIndex, DataMatrixCompactionMode[] compactionModes, int codewordCountLimit, byte[] codewords) - { - - // check if there are some lower case characters - // and throw exception if any as they are not allowed in Edifact mode - if (hasLowerCaseCharacters(ref m_data)) - throw new BarcodeException("Cannot encode lowercase ASCII characters (a-z) with Edifact, use Auto, ANSI, Text or C40 method"); - - // now perform check if we have enough codewords for this encoding method - // first we are checking if we have enough codewords - // In Edifact mode 4 data characters are compacted in 3 CWs (1.33 compression rate) - bool enoughCW = ((float)m_data.Length / codewordCountLimit) <= 1.4f; // or we have enough codewords for 1.5 compression more than codewords - - // return enlarged codewordindex so this matrix will wail and - // the calling code will try the next sizes matrix - if (!enoughCW) - { - codewordIndex = codewordCountLimit + 1; - return; - } - - - - try - { - // add the mode switch if need to switch the mode - if (mode != newMode) - { - // close the previou chunk of data if needed - if (codewordIndex > 0) - { - // close the previous mode - if (mode == DataMatrixCompactionMode.C40 || mode == DataMatrixCompactionMode.Text || mode == DataMatrixCompactionMode.X12) - codewords[codewordIndex++] = 254; // escape C40/text/X12 - else - codewords[codewordIndex++] = 254; //0x7C; // escape EDIFACT - } - - // add adding the switch codeword to indicate beginning of Edifact mode - codewords[codewordIndex++] = 240; - } - - // set the current mode to Edifact - mode = DataMatrixCompactionMode.Edifact; - - // current position in the buffer - int pos = 0; - // buffer with 4 bytes which we are encode into codeword - byte[] buffer = new byte[4]; - - // run in loop to iterate through all input characters - do - { - // reset the buffer position - - pos = 0; - // fill the bufer with input data (copying by 4 bytes or less if any) - while (dataIndex < m_data.Length && compactionModes[dataIndex] == DataMatrixCompactionMode.Edifact && pos < 4) - buffer[pos++] = m_data[dataIndex++]; - - // if we have empty slots in the buffer[] - // it means we finished with the source data - // and we should fill the reminding data - if (pos < 4) - { - // add "end of data" marker for Edifact, 31 (0x1F) - if (pos < 4) - buffer[pos++] = 31; // the same as 0x1F - - // fill the reminding slots with zero - while (pos < 4) - buffer[pos++] = 0; - - } - - // doing the encoding, each character is encoded by 6 bits - codewords[codewordIndex++] = (byte)((buffer[0] & 0x3F) << 2); - // here and below we are using ++ and -- to provide proper codewordIndex value when the code fails into the exception - codewordIndex--; - // encoding next characters in the buffer - codewords[codewordIndex++] |= (byte)((buffer[1] & 0x30) >> 4); - codewords[codewordIndex++] = (byte)((buffer[1] & 0x0F) << 4); - codewordIndex--; - - if (pos == 2) - codewordIndex++; - else - { - codewords[codewordIndex++] |= (byte)((buffer[2] & 0x3C) >> 2); - codewords[codewordIndex++] = (byte)((buffer[2] & 0x03) << 6); - codewordIndex--; - codewords[codewordIndex++] |= (byte)(buffer[3] & 0x3F); - } - } - while (dataIndex < m_data.Length); // repeat until we have input data to encode - - } - catch { - // suppress as current size do not fit - } - } - - /// - /// Generates codewords from input data in C40, Text or x12 encoding modes - /// - /// old mode - /// new mode (c40, text or x12) - /// index of the data to read from - /// current index of codewords - /// count of allowed codewords (for the current matrix) - /// codewords array to write into - private void produceTextBasedCodewords(ref DataMatrixCompactionMode mode, DataMatrixCompactionMode newMode, - ref int dataIndex, ref int codewordIndex, int codewordCountLimit, byte[] codewords) - { - - // x12 method is not able to encode lower case characters - if (newMode == DataMatrixCompactionMode.X12 && hasLowerCaseCharacters(ref m_data)) - throw new BarcodeException("Cannot encode lowercase ASCII characters (a-z) with X12 method, use Auto, ANSI, Text or C40 method"); - - // now perform check if we have enough codewords for this encoding method - // first we are checking if we have enough codewords - // In Text, C40, x12 modes 3 data characters are compacted in 2 CWs (1.5 compression rate) - bool enoughCW = (m_data.Length < 4 && codewordCountLimit <6) || // if we have 2-3 characters to encode and similar codewords then we may fit - ((float)m_data.Length / codewordCountLimit) <= 1.5f; // or we have enough codewords for 1.5 compression more than codewords - - // return enlarged codewordindex so this matrix will wail and - // the calling code will try the next sizes matrix - if (!enoughCW) - { - codewordIndex = codewordCountLimit + 1; - return; - } - - try - { - - byte[] buffer = new byte[6]; - int pos = 0; - - string s2 = "!\"#$%&'()*+,-./:;<=>?@[\\]_"; - string s3 = ""; - string e = ""; - - if (newMode == DataMatrixCompactionMode.C40) - { - e = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - s3 = "`abcdefghijklmnopqrstuvwxyz{|}~\0177"; - } - else if (newMode == DataMatrixCompactionMode.Text) - { - e = " 0123456789abcdefghijklmnopqrstuvwxyz"; - s3 = "`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~\0177"; - } - else if (newMode == DataMatrixCompactionMode.X12) - e = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\r*>"; - - do - { - char c = (char)m_data[dataIndex++]; - - //if (dataIndex < m_data.Length) - //{ - if ((c & 0x80) != 0) - { - //if (newMode == DataMatrixCompactionMode.X12) - // throw new BarcodeException("Cannot encode char 0x" + c + "X in X12"); - - c &= (char)0x7f; - buffer[pos++] = 1; - buffer[pos++] = 30; - } - - int w_idx = e.IndexOf(c); - if (w_idx >= 0) - buffer[pos++] = (byte)((w_idx + 3) % 40); // +3 is because the specification uses first 3 indexes for control shift commands - else - { - //if (newMode == DataMatrixCompactionMode.X12) - // throw new BarcodeException("Cannot encode char 0x" + c + " in X12"); - - if (c < 32) - { - // shift 1 - buffer[pos++] = 0; - buffer[pos++] = (byte)c; - } - else - { - w_idx = s2.IndexOf(c); - if (w_idx >= 0) - { - // shift 2 - buffer[pos++] = 1; - buffer[pos++] = (byte)w_idx; - } - else - { - w_idx = s3.IndexOf(c); - - if (w_idx >= 0) - { - buffer[pos++] = 2; - buffer[pos++] = (byte)w_idx; - } - else - throw new BarcodeException("Could not encode 0x" + c + "X, unsupported symbols passed"); - - } - } - } - //} - - - // as we are encoding every 3 characters into 1 (one) codeword - // and we may have 4 characters so need to properly encode 4th character - // so we run the loop to make sure all characters were encoded into codewords - // this loop runs if we have 1 symbol left to encode! - do - { - // we are converting every 3 characters from the buffer[6] into codewords - // so we are passing 2 times - // here we check if we are at the last character of the source string - // but but we are at the 4th or 5th position of the buffer: we have 2 bytes left - // so we have to pad these bytes with zero - if ( - (pos == 2 || pos == 5) && // if we encoded some characters already but left 1 the very last slot in buffer[] - dataIndex == m_data.Length - ) - buffer[pos++] = 0; // shift 1 pad at end - - // now encode into codewords - - // 1. check if we have 1 byte left to encode (as we are running in the loop processing every 3 bytes from buffer[]) - // so we check if we have - // so we should encode it as ASCII (see the specification on the remaingin bytes while using C40 mode) - if ( - // if we are in C40 or Text mode and we have 1 character left - // then we should encode the very last character with ASCII - ( - (newMode == DataMatrixCompactionMode.C40 || newMode == DataMatrixCompactionMode.Text) && - dataIndex == m_data.Length && // we are working with the vert last character of the source string - pos < 2 // we have 2 bytes left in the buffer[] (which we convert into codewords) - ) - || // OR - // we are in x12 mode and we have 2 characters left (different from Text and C40 that C40/Text mode encodes the very last character with ANSI, x12 encodes 2 last characters) - // then we should encode the very last character with ASCII - ( - (newMode == DataMatrixCompactionMode.X12) && - dataIndex > m_data.Length-2 && // we are working with the vert last character of the source string - pos < 2 // we have 2 bytes left in the buffer[] (which we convert into codewords) - ) - - ) - { - // checking if we should add ANSI switch in C40 or Text mode - // we add ASCII switch if we have 2 codewords still left then we should add the ASCII mode switch codeword - // and we have 1 character to encode - if ( - (newMode == DataMatrixCompactionMode.C40 || newMode == DataMatrixCompactionMode.Text) && - (codewordIndex + 1 < codewordCountLimit) - ) - { - codewords[codewordIndex++] = 254; // enter ASCII mode - } - - // ELSE we if we are in x12 mode - // then we add ASCII switch if we have 3 codewords still left then we should add the ASCII mode switch codeword - // and we have 2 characters to encode - else if ( - (newMode == DataMatrixCompactionMode.X12) && - (codewordIndex + 2 < codewordCountLimit) - ) - { - codewords[codewordIndex++] = 254; // enter ASCII mode - } - - // otherwise we assume (according to the specification) that decoder treats - // the vert last codeword as ASCII encoded (even without ASCII mode switch codeword) - - // set the mode to ASCII - mode = DataMatrixCompactionMode.Ascii; - - // "c" variable already stores the current character (i.e. c == m_data[dataIndex]) - // encode this sigle character using the ASCII mode (see the spec for ASCII mode) - if ((byte)c > 127) - { - codewords[codewordIndex++] = 235; - codewords[codewordIndex++] = (byte)(c - 127); - } - else - codewords[codewordIndex++] = (byte)(c + 1); - - // decrease the pos index as we just converted the single character into the codeword - pos--; - - // check if we have one more character to encode in ASCII mode - // this may happen if we are in x12 mode - - // "c" variable already stores the current character (i.e. c == m_data[dataIndex]) - // encode this sigle character using the ASCII mode (see the spec for ASCII mode) - if (dataIndex < m_data.Length) - { - c = (char)m_data[dataIndex++]; - if ((byte)c > 127) - { - codewords[codewordIndex++] = 235; - codewords[codewordIndex++] = (byte)(c - 127); - } - else - codewords[codewordIndex++] = (byte)(c + 1); - - // decrease the pos index as we just converted the single character into the codeword - pos--; - } - } - else // we have 3 or more bytes in the buffer[] so we are encoding the set of 3 symbols or more into 2 codewords pair using - { - // process every 3 bytes from buffer[] - while (pos >= 3) - { - // encoding 3 bytes into 1 codewords (16 bit per each codeword) (see the spec) - int v = buffer[0] * 1600 + buffer[1] * 40 + buffer[2] + 1; - - // adding the codeword to indicate the encoding mode switch if neccessary - if (mode != newMode) - { - // check if we are not at the beginning and - // need to switch the mode - if (codewordIndex > 0) - { - if (mode == DataMatrixCompactionMode.C40 || mode == DataMatrixCompactionMode.Text || mode == DataMatrixCompactionMode.X12) - codewords[codewordIndex++] = 254; // escape C40/text/X12 - else if (mode == DataMatrixCompactionMode.Edifact) - codewords[codewordIndex++] = 254; //0x7C; // escape EDIFACT - } - - // and then indicate the beginning of the new mode - if (newMode == DataMatrixCompactionMode.C40) - codewords[codewordIndex++] = 230; - else if (newMode == DataMatrixCompactionMode.Text) - codewords[codewordIndex++] = 239; - else if (newMode == DataMatrixCompactionMode.X12) - codewords[codewordIndex++] = 238; - - mode = newMode; - } - - // encoding codewords according to the specification (C40 mode) - codewords[codewordIndex++] = (byte)(v >> 8); - codewords[codewordIndex++] = (byte)(v & 0xFF); - - // shift the pos index in the buffer[] as we just encoded 3 bytes from the buffer - pos -= 3; - - // copy next 3 bytes into the beginning of the buffer[] - buffer[0] = buffer[3]; - buffer[1] = buffer[4]; - buffer[2] = buffer[5]; - - // clear old 3 bytes (just in case although we have shifted the pos index so this should be ok) - buffer[3] = 0; - buffer[4] = 0; - buffer[5] = 0; - - - } // while still have 3 or more bytes to process in the buffer[] - } // else block (for the case when we have 3 or more bytes in the buffer[] to process) - - } - while (pos == 1 && dataIndex > m_data.Length - 1); // repeat if we still have 1 byte left unprocessed in buffer[] - - } - while (pos >-1 && dataIndex < m_data.Length); // repeat while we have source characters to process - - } - catch { - // return nothing as exception means the barcode not fitting into current size - } - } - - private static void addReedSolomonCodewords( - ref byte[] codewords, - Matrix matrix, - bool exchangeFirstTwoAndLastBytesInRSCodes // used for 144x144 only in the special mode - ) - { - // according to Datamatrix specification - // codewords are located in the following way inside blocks: - // exmple with 4 blocks: - // DATA CODEWORDS: 1 2 3 4 5 6 ... 365 366 367 368 ERROR CORRECTION: 1 2 3 4 5 6 .. 141 142 143 144 - // BLOCK 1: data: 1 5 .. 361 365 error corr: 1 5 .. 137 141 - // BLOCK 2: data: 2 6 .. 362 366 error corr: 2 6 .. 138 142 - // BLOCK 3: data: 3 7 .. 363 367 error corr: 3 7 .. 139 143 - // BLOCK 4: data: 4 8 .. 364 368 error corr: 4 8 .. 140 144 - - int blockCount = (matrix.m_totalDataCWCount + 2) / matrix.m_regionDataCWCount; - - // increase codewords array to accommodate Reed-Solomon CWs - byte[] newCodewords = new byte[codewords.Length + blockCount * matrix.m_regionReedSolomonCWCount]; - Array.Copy(codewords, newCodewords, codewords.Length); - codewords = newCodewords; - - ReedSolomon rsObj = new ReedSolomon(); - rsObj.Init(0x12d); - rsObj.InitCode(matrix.m_regionReedSolomonCWCount, 1); - for (int b = 0; b < blockCount; b++) - { - byte[] buf = new byte[256]; - int position = 0; - for (int n = b; n < matrix.m_totalDataCWCount; n += blockCount) - buf[position++] = codewords[n]; - - byte[] ecc = new byte[256]; - rsObj.Encode(position, buf, out ecc); - - position = matrix.m_regionReedSolomonCWCount - 1; - //position = 0; - - for (int n = b; n < matrix.m_regionReedSolomonCWCount * blockCount; n += blockCount) - codewords[matrix.m_totalDataCWCount + n] = ecc[position--]; - } - - //if (blockCount >= 10) - if (exchangeFirstTwoAndLastBytesInRSCodes) - { - // rearrange RS data in the way that last 2 bytes of each block - // will be moved to the beginning of the block (insider RS data only!) - - int numOfIterations = (codewords.Length - matrix.m_totalDataCWCount)/ blockCount; - - for (int b = 0; b < numOfIterations; b++) - { - // rearrange - byte[] buf = new byte[blockCount]; - Array.Copy(codewords, matrix.m_totalDataCWCount + b * blockCount, buf, 0, blockCount); - - byte[] buf2 = new byte[blockCount]; - // copy buf into buf2 except 2 last bytes + now start from pos2 - Array.Copy(buf, 0, buf2, 2, blockCount - 2); - // copying 2 last bytes into the begining of the buf2 - Array.Copy(buf, blockCount - 2, buf2, 0, 2); - // finally replace the RS block with the modified block - Array.Copy(buf2, 0, codewords, matrix.m_totalDataCWCount + b * blockCount, blockCount); - } - } - - } - - private static int CompactionModeLength - { - get - { - return Enum.GetValues(typeof(DataMatrixCompactionMode)).Length; - } - } - - private static DataMatrixCompactionMode LastCompactionMode - { - get - { -#if !PocketPC && !WindowsCE - return (DataMatrixCompactionMode)Enum.GetValues(typeof(DataMatrixCompactionMode)).GetValue(CompactionModeLength - 1); -#else - // less error-prone way - return DataMatrixCompactionMode.Binary; -#endif - } - } - - protected static bool isDigit(byte b) - { - return (b >= '0' && b <= '9'); - } - - private static bool isUpper(byte b) - { - return (b >= 'A' && b <= 'Z'); - } - - private static bool isLower(byte b) - { - return (b >= 'a' && b <= 'z'); - } - - private static int switchCost(DataMatrixCompactionMode from, DataMatrixCompactionMode to) - { - int[,] switchCostTable = new int[6, 6] - { - {0, 1, 1, 1, 1, 2}, // Ascii - {1, 0, 2, 2, 2, 3}, // C40 - {1, 2, 0, 2, 2, 3}, // Text - {1, 2, 2, 0, 2, 3}, // X12 - {1, 2, 2, 2, 0, 3}, // Edifact - {0, 1, 1, 1, 1, 0} // Binary - }; - - return switchCostTable[(int)from, (int)to]; - } - - private static void calculatePlacement(int[] places, int rowCount, int columnCount) - { - Array.Clear(places, 0, places.Length); - - int position = 1; - int row = 4; - int column = 0; - - do - { - // check corner - if (row == rowCount && column == 0) - calculateCornerAPlacement(places, rowCount, columnCount, position++); - - if ((row == rowCount - 2) && column == 0 && (columnCount % 4) != 0) - calculateCornerBPlacement(places, rowCount, columnCount, position++); - - if (row == rowCount - 2 && column == 0 && (columnCount % 8) == 4) - calculateCornerCPlacement(places, rowCount, columnCount, position++); - - if (row == rowCount + 4 && column == 2 && (columnCount % 8) == 0) - calculateCornerDPlacement(places, rowCount, columnCount, position++); - - // up/right - do - { - if (row < rowCount && column >= 0 && places[row * columnCount + column] == 0) - calculateBlockPlacement(places, rowCount, columnCount, row, column, position++); - - row -= 2; - column += 2; - } - while (row >= 0 && column < columnCount); - - row++; - column += 3; - - // down/left - do - { - if (row >= 0 && column < columnCount && places[row * columnCount + column] == 0) - calculateBlockPlacement(places, rowCount, columnCount, row, column, position++); - - row += 2; - column -= 2; - } - while (row < rowCount && column >= 0); - - row += 3; - column++; - } - while (row < rowCount || column < columnCount); - - // unfilled corner - if (places[rowCount * columnCount - 1] == 0) - { - places[rowCount * columnCount - 1] = 1; - places[rowCount * columnCount - columnCount - 2] = 1; - } - } - - private static void calculateCornerAPlacement(int[] places, int rowCount, int columnCount, int position) - { - calculatePlacementBit(places, rowCount, columnCount, rowCount - 1, 0, position, 7); - calculatePlacementBit(places, rowCount, columnCount, rowCount - 1, 1, position, 6); - calculatePlacementBit(places, rowCount, columnCount, rowCount - 1, 2, position, 5); - calculatePlacementBit(places, rowCount, columnCount, 0, columnCount - 2, position, 4); - calculatePlacementBit(places, rowCount, columnCount, 0, columnCount - 1, position, 3); - calculatePlacementBit(places, rowCount, columnCount, 1, columnCount - 1, position, 2); - calculatePlacementBit(places, rowCount, columnCount, 2, columnCount - 1, position, 1); - calculatePlacementBit(places, rowCount, columnCount, 3, columnCount - 1, position, 0); - } - - private static void calculateCornerBPlacement(int[] places, int rowCount, int columnCount, int position) - { - calculatePlacementBit(places, rowCount, columnCount, rowCount - 3, 0, position, 7); - calculatePlacementBit(places, rowCount, columnCount, rowCount - 2, 0, position, 6); - calculatePlacementBit(places, rowCount, columnCount, rowCount - 1, 0, position, 5); - calculatePlacementBit(places, rowCount, columnCount, 0, columnCount - 4, position, 4); - calculatePlacementBit(places, rowCount, columnCount, 0, columnCount - 3, position, 3); - calculatePlacementBit(places, rowCount, columnCount, 0, columnCount - 2, position, 2); - calculatePlacementBit(places, rowCount, columnCount, 0, columnCount - 1, position, 1); - calculatePlacementBit(places, rowCount, columnCount, 1, columnCount - 1, position, 0); - } - - private static void calculateCornerCPlacement(int[] places, int rowCount, int columnCount, int position) - { - calculatePlacementBit(places, rowCount, columnCount, rowCount - 3, 0, position, 7); - calculatePlacementBit(places, rowCount, columnCount, rowCount - 2, 0, position, 6); - calculatePlacementBit(places, rowCount, columnCount, rowCount - 1, 0, position, 5); - calculatePlacementBit(places, rowCount, columnCount, 0, columnCount - 2, position, 4); - calculatePlacementBit(places, rowCount, columnCount, 0, columnCount - 1, position, 3); - calculatePlacementBit(places, rowCount, columnCount, 1, columnCount - 1, position, 2); - calculatePlacementBit(places, rowCount, columnCount, 2, columnCount - 1, position, 1); - calculatePlacementBit(places, rowCount, columnCount, 3, columnCount - 1, position, 0); - } - - private static void calculateCornerDPlacement(int[] places, int rowCount, int columnCount, int position) - { - calculatePlacementBit(places, rowCount, columnCount, rowCount - 1, 0, position, 7); - calculatePlacementBit(places, rowCount, columnCount, rowCount - 1, columnCount - 1, position, 6); - calculatePlacementBit(places, rowCount, columnCount, 0, columnCount - 3, position, 5); - calculatePlacementBit(places, rowCount, columnCount, 0, columnCount - 2, position, 4); - calculatePlacementBit(places, rowCount, columnCount, 0, columnCount - 1, position, 3); - calculatePlacementBit(places, rowCount, columnCount, 1, columnCount - 3, position, 2); - calculatePlacementBit(places, rowCount, columnCount, 1, columnCount - 2, position, 1); - calculatePlacementBit(places, rowCount, columnCount, 1, columnCount - 1, position, 0); - } - - private static void calculateBlockPlacement(int[] places, int rowCount, int columnCount, int row, int column, int position) - { - calculatePlacementBit(places, rowCount, columnCount, row - 2, column - 2, position, 7); - calculatePlacementBit(places, rowCount, columnCount, row - 2, column - 1, position, 6); - calculatePlacementBit(places, rowCount, columnCount, row - 1, column - 2, position, 5); - calculatePlacementBit(places, rowCount, columnCount, row - 1, column - 1, position, 4); - calculatePlacementBit(places, rowCount, columnCount, row - 1, column - 0, position, 3); - calculatePlacementBit(places, rowCount, columnCount, row - 0, column - 2, position, 2); - calculatePlacementBit(places, rowCount, columnCount, row - 0, column - 1, position, 1); - calculatePlacementBit(places, rowCount, columnCount, row - 0, column - 0, position, 0); - } - - private static void calculatePlacementBit(int[] places, int rowCount, int columnCount, int row, int column, int position, byte b) - { - if (row < 0) - { - row += rowCount; - column += 4 - ((rowCount + 4) % 8); - } - - if (column < 0) - { - column += columnCount; - row += 4 - ((columnCount + 4) % 8); - } - - places[row * columnCount + column] = (position << 3) + b; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/GS1DataMatrixSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/GS1DataMatrixSymbology.cs deleted file mode 100644 index 1f6dd4e5..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/GS1DataMatrixSymbology.cs +++ /dev/null @@ -1,206 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using GS1 DataMatrix symbology. GS1 DataMatrix is a 2D - /// (two-dimensional) barcode that holds - /// large amounts of data in a relatively small space. These barcodes - /// are used primarily in aerospace, pharmaceuticals, medical device - /// manufacturing, and by the U.S. Department of Defense to add - /// visibility to the value chain. GS1 DataMatrix can be used for parts - /// that need to be tracked in the manufacturing process because the - /// barcode allows users to encode a variety of information related - /// to the product, such as date or lot number. They are not intended - /// to be used on items that pass through retail point-of-sale (POS). - /// - /// See also: - /// http://www.gs1us.org/standards/barcodes/gs1_datamatrix - /// http://www.idautomation.com/datamatrixfaq.html#GS1_DataMatrix - /// http://www.pmpnews.com/article/gs1-datamatrix-fnc1-versus-gs-variable-length-field-separator-character - /// - class GS1DataMatrixSymbology : DataMatrixSymbology - { - /// - /// Initializes a new instance of the class. - /// - public GS1DataMatrixSymbology() - : base() - { - m_type = TrueSymbologyType.GS1_DataMatrix; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public GS1DataMatrixSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.GS1_DataMatrix; - } - - /// - /// Validates the value using GS1 Data Matrix symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { -// if (!GS1ValueChecker.Check(value)) -// return false; - - if (value.Length == 0) - return false; - - return true; - - } - - /// - /// Gets or sets the barcode value to encode. - /// The value can be set in 2 forms: with parenthesis for AI and without. - /// If a value comes in form like 'xxxxxxxxxxx' or like 'xxxxxx|xxxxx' then the SDK automatically sets brackets according to GS1 AI (Application Identifiers). - /// If a value comes in form like (xx)yyyyyy(zz)nnn then the SDK do NOT verifies the value against AI. - /// However you may verify if the value is valid according to GS1 rules or not by using Barcode.ValueIsValidGS1() bool function - /// - /// The barcode value to encode. - public override string Value - { - get - { - return base.Value; - } - set - { - bool isBracket = false; - for (int i = 0; i < value.Length; i++) - if (value[i] == '(') - { - isBracket = true; - break; - } - - base.Value = value; - - if (isBracket) - base.Value = value; // just set the value - else - // else find AI identifiers and convert value from the form like "xxxxxx" into "(yy)xxxx(zz)nn" - base.Value = ApplicationIdentifiers.SelectAIs(value); - - } - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - string restriction = "GS1 Data Matrix symbology allows to encode a maximum data size of 2,335 alphanumeric characters.\n"; - restriction += "\r\nThe value can be set in 2 forms: with parenthesis for AI and without."; - restriction += "\r\nIf a value comes in form like 'xxxxxxxxxxx' or like 'xxxxxx|xxxxx' then the SDK automatically sets brackets according to GS1 AI (Application Identifiers)."; - restriction += "\r\nIf a value comes in form like (xx)yyyyyy(zz)nnn then the SDK do NOT verifies the value against AI."; - restriction += "\r\n\r\nHowever you may verify if the value is valid according to GS1 rules or not by using Barcode.ValueIsValidGS1() bool function"; - return restriction; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "1112345617ABCDEF"; - } - - protected override DataMatrixCompactionMode CompactionMode - { - get { return DataMatrixCompactionMode.Ascii; } - } - - protected override byte[] getDataForEncoding() - { - //return Encoding.ASCII.GetBytes(GS1ValueChecker.GetStripped(Value)); - - string ss = GS1ValueChecker.GetStripped(Value); - - byte[] bbytes = new byte[ss.Length]; - - int index = 0; - foreach (char c in ss.ToCharArray()) - { - bbytes[index++] = (byte)c; - } - return bbytes; - } - - protected override DataMatrixCompactionMode[] getDefaultCompactionModes() - { - DataMatrixCompactionMode[] compactionModes = null; - if (CompactionMode != DataMatrixCompactionMode.Auto) - { - compactionModes = new DataMatrixCompactionMode[m_data.Length + 1]; - for (int i = 0; i < compactionModes.Length; i++) - compactionModes[i] = CompactionMode; - } - - return compactionModes; - } - - protected override void initCodeWords(byte[] codewords, ref int codewordIndex) - { - codewords[codewordIndex++] = 232; // FNC1 - } - - protected override void produceAsciiCodewords(ref DataMatrixCompactionMode mode, DataMatrixCompactionMode newMode, ref int dataIndex, ref int codewordIndex, int codewordCountLimit, byte[] codewords) - { - try - { - - if (mode != newMode) - { - if (mode == DataMatrixCompactionMode.C40 || mode == DataMatrixCompactionMode.Text || mode == DataMatrixCompactionMode.X12) - codewords[codewordIndex++] = 254; // escape C40/text/X12 - else - codewords[codewordIndex++] = 0x7C; // escape EDIFACT - } - - mode = DataMatrixCompactionMode.Ascii; - if (m_data.Length - dataIndex >= 2 && isDigit(m_data[dataIndex]) && isDigit(m_data[dataIndex + 1])) - { - codewords[codewordIndex++] = (byte)((m_data[dataIndex] - '0') * 10 + m_data[dataIndex + 1] - '0' + 130); - dataIndex += 2; - } - else if (m_data[dataIndex] > 127) - { - codewords[codewordIndex++] = 235; - codewords[codewordIndex++] = (byte)(m_data[dataIndex++] - 127); - } - else - { - if ((char)m_data[dataIndex] == '(') - { - codewords[codewordIndex++] = 232; // FNC1 - dataIndex++; - } - else - { - codewords[codewordIndex++] = (byte)(m_data[dataIndex++] + 1); - } - } - } - catch { - // suppress exception - } - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/GS1QRSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/GS1QRSymbology.cs deleted file mode 100644 index d2d9860b..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/GS1QRSymbology.cs +++ /dev/null @@ -1,170 +0,0 @@ -using SkiaSharp; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using GS1 QR symbology. GS1 QR is a 2D - /// (two-dimensional) barcode that holds - /// large amounts of data in a relatively small space. These barcodes - /// are used primarily in aerospace, pharmaceuticals, medical device - /// manufacturing, and by the U.S. Department of Defense to add - /// visibility to the value chain. GS1 QR can be used for parts - /// that need to be tracked in the manufacturing process because the - /// barcode allows users to encode a variety of information related - /// to the product, such as date or lot number. They are not intended - /// to be used on items that pass through retail point-of-sale (POS). - /// - class GS1QRSymbology : QRSymbology - { - /// - /// Initializes a new instance of the class. - /// - public GS1QRSymbology() : base() - { - m_type = TrueSymbologyType.GS1_QRCode; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public GS1QRSymbology(SymbologyDrawing prototype) : base(prototype) - { - m_type = TrueSymbologyType.GS1_QRCode; - } - - /// - /// Validates the value using GS1 Data Matrix symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (!GS1ValueChecker.Check(value)) - return false; - - //if (value.Length == 0) - // return false; - - return true; - - } - - /// - /// Gets or sets the barcode value to encode. - /// The value can be set in 2 forms: with parenthesis for AI and without. - /// If a value comes in form like 'xxxxxxxxxxx' or like 'xxxxxx|xxxxx' then the SDK automatically sets brackets according to GS1 AI (Application Identifiers). - /// If a value comes in form like (xx)yyyyyy(zz)nnn then the SDK do NOT verifies the value against AI. - /// However you may verify if the value is valid according to GS1 rules or not by using Barcode.ValueIsValidGS1() bool function - /// - /// The barcode value to encode. - public override string Value - { - get - { - return base.Value; - } - set - { - bool isBracket = false; - for (int i = 0; i < value.Length; i++) - if (value[i] == '(') - { - isBracket = true; - break; - } - - base.Value = value; - - if (isBracket) - base.Value = value; // just set the value - else - // else find AI identifiers and convert value from the form like "xxxxxx" into "(yy)xxxx(zz)nn" - base.Value = ApplicationIdentifiers.SelectAIs(value); - - } - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - string restriction = "GS1 QR symbology allows to encode a maximum data size of 4,296 alphanumeric characters.\n"; - restriction += "\r\nThe value can be set in 2 forms: with parenthesis for AI and without."; - restriction += "\r\nIf a value comes in form like 'xxxxxxxxxxx' or like 'xxxxxx|xxxxx' then the SDK automatically sets brackets according to GS1 AI (Application Identifiers)."; - restriction += "\r\nIf a value comes in form like (xx)yyyyyy(zz)nnn then the SDK do NOT verifies the value against AI."; - restriction += "\r\n\r\nHowever you may verify if the value is valid according to GS1 rules or not by using Barcode.ValueIsValidGS1() bool function"; - return restriction; - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected override string GetIncorrectValueSubstitution() - { - return "1112345617ABCDEF"; - } - - private byte[] getDataForEncoding() - { - string ss = GS1ValueChecker.GetStripped(Value); - - ss = ss.Replace("%", "%%");//% must be encoded as %% - ss = ss.Replace("(", "%");//FNC1 inside string must be encoded as % - - byte[] bbytes = new byte[ss.Length]; - - int index = 0; - foreach (char c in ss.ToCharArray()) - { - bbytes[index++] = (byte)c; - } - return bbytes; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - // a bit weird cycle goes here, but we need it, - // or we can end up with 2-byte chars which is unsupported - StringBuilder sb = new StringBuilder(); - - byte[] bytes = getDataForEncoding(); - - for (int i = 0; i < bytes.Length; i++) - sb.Append((char)bytes[i]); - - SKSize drawingSize = new SKSize(); - - QRSymbol symbol = QRSymbol.EncodeString(sb.ToString(), Options.QRVersion, - Options.QRErrorCorrectionLevel, Options.QREncodeHint, true, true); - - int cellWidth = NarrowBarWidth; - drawingSize.Width = symbol.Width * cellWidth; - drawingSize.Height = drawingSize.Width; - - for (int i = 0; i < symbol.Width; i++) - { - for (int j = 0; j < symbol.Width; j++) - { - if ((symbol[(i * symbol.Width) + j] & 0x01) != 0) - m_rects.Add(new SKRect(j * cellWidth, i * cellWidth, j * cellWidth+cellWidth, i * cellWidth+cellWidth)); - } - } - - return drawingSize; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/GS1ValueChecker.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/GS1ValueChecker.cs deleted file mode 100644 index 1c17eb0b..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/GS1ValueChecker.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Checks whether a value can be encoded using GS1 compatible symbology. - /// - class GS1ValueChecker - { - private static string[] m_fixedLengthAIs = - { - "00", "01", "02", "03", "04", "11", "12", "13", "14", "15", "16", - "17", "18", "19", "20", "31", "32", "33", "34", "35", "36", "41" - }; - - private static int[] m_fixedLengthAILengths = - { - 20, 16, 16, 16, 18, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, - 10, 10, 10, 10, 10, 10, 16 - }; - - private GS1ValueChecker() - { - } - - public static string[] FixedLengthAIs - { - get - { - return m_fixedLengthAIs; - } - } - - public static string GetStripped(string value) - { - StringBuilder stripped = new StringBuilder(); - int lastAI = 0; - bool fixedLengthAI = true; - - for (int i = 0; i < value.Length; i++) - { - if (value[i] != '(' && value[i] != ')') - stripped.Append(value[i]); - - if (value[i] == '(') - { - if (!fixedLengthAI) - stripped.Append('('); - - string ai = value.Substring(i + 1, 2); - lastAI = int.Parse(ai); - fixedLengthAI = false; - - if ((lastAI >= 0) && (lastAI <= 4)) - fixedLengthAI = true; - - if ((lastAI >= 11) && (lastAI <= 20)) - fixedLengthAI = true; - - if ((lastAI >= 31) && (lastAI <= 36)) - fixedLengthAI = true; - - if (lastAI == 41) - fixedLengthAI = true; - } - } - - return stripped.ToString(); - } - - public static bool Check(string value) - { - foreach (char c in value) - { - if ((int)c >= 128) - return false; - - if ((int)c < 32) - return false; - } - - if (value == null || value.Length == 0) - return false; - - // we allow values without () at the start so we will put brackets ourselves - //if (value[0] != '(') - // return false; - - intList leftBracketPos = new intList(); - intList rightBracketPos = new intList(); - if (!FindBracketPositions(value, leftBracketPos, rightBracketPos)) - return false; - - // value should contain at least one AI (application identifier) and - // each AI should be enclosed in parenthesis - if (leftBracketPos.Count != rightBracketPos.Count || leftBracketPos.Count == 0) - return false; - - if (!eachAIisValid(value, leftBracketPos, rightBracketPos)) - return false; - - return true; - } - - public static bool FindBracketPositions(string value, intList leftBracketPos, intList rightBracketPos) - { - for (int i = 0; i < value.Length; i++) - { - char c = value[i]; - if (c == '(') - { - if (leftBracketPos.Count != rightBracketPos.Count) - { - // should not find next '(' before ')' found - return false; - } - - leftBracketPos.Add(i); - } - else if (c == ')') - { - if (leftBracketPos.Count != (rightBracketPos.Count + 1)) - { - // should not find next ')' before '(' found - return false; - } - - rightBracketPos.Add(i); - } - } - - return true; - } - - private static bool eachAIisValid(string value, intList leftBracketPos, intList rightBracketPos) - { - for (int i = 0; i < leftBracketPos.Count; i++) - { - int start = leftBracketPos[i]; - int stop = rightBracketPos[i]; - - // AI should not be longer that 4 characters - if ((stop - start - 1) > 4) - return false; - - // AI should be at least 1 character long - if ((stop - start - 1) <= 1) - return false; - - for (int pos = start + 1; pos < stop; pos++) - { - if (!char.IsDigit(value[pos])) - return false; - } - - if ((start + 3) >= value.Length) - return false; - - string aiStart = value.Substring(start + 1, 2); - for (int j = 0; j < m_fixedLengthAIs.Length; j++) - { - string ai = m_fixedLengthAIs[j]; - if (aiStart == ai) - { - // found fixed length AI - // let's check data field length - - // find next AI start. for last AI use string end as next AI start - int nextStart = value.Length; - if (i != (leftBracketPos.Count - 1)) - nextStart = leftBracketPos[i + 1]; - - int aiLength = nextStart - start - 2; // subtract 2 for ( and ) - if (aiLength != m_fixedLengthAILengths[j]) - return false; - } - } - } - - return true; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/MaxiCodeSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/MaxiCodeSymbology.cs deleted file mode 100644 index b2ec85b7..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/MaxiCodeSymbology.cs +++ /dev/null @@ -1,1150 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using System.Drawing; -using System.Collections; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using MaxiCode symbology. - /// - class MaxiCodeSymbology : SymbologyDrawing2D - { - private const int first_message_length = 10; // +10 symbols of error correction - private const int second_message_length = 124; // total length along with error correction symbols - private const int sec_length = 40; - private const int eec_length = 56; - - private static SKPoint[] c_orientationTemplate = new SKPoint[] - { - new SKPoint(00,28), new SKPoint(00,29), new SKPoint(09,10), new SKPoint(09,11), new SKPoint(10,11), new SKPoint(15,07), new SKPoint(16,08), - new SKPoint(16,20), new SKPoint(17,20), new SKPoint(22,10), new SKPoint(23,10), new SKPoint(22,17), new SKPoint(23,17) - }; - - private static SKPoint[][] c_symbolMap = new SKPoint[][] - { - new SKPoint[] {new SKPoint(15,19), new SKPoint(17,19), new SKPoint(09,16), new SKPoint(10,16), new SKPoint(11,17), new SKPoint(11,16)}, // 01 - new SKPoint[] {new SKPoint(22,13), new SKPoint(22,12), new SKPoint(23,13), new SKPoint(23,12), new SKPoint(21,17), new SKPoint(22,16)}, // 02 - new SKPoint[] {new SKPoint(09,13), new SKPoint(09,12), new SKPoint(10,13), new SKPoint(10,12), new SKPoint(12,10), new SKPoint(20,10)}, // 03 - new SKPoint[] {new SKPoint(20,18), new SKPoint(12,19), new SKPoint(12,18), new SKPoint(13,19), new SKPoint(13,18), new SKPoint(14,19)}, // 04 - new SKPoint[] {new SKPoint(23,15), new SKPoint(23,14), new SKPoint(18,19), new SKPoint(19,19), new SKPoint(19,18), new SKPoint(20,19)}, // 05 - new SKPoint[] {new SKPoint(15,08), new SKPoint(17,08), new SKPoint(21,10), new SKPoint(23,11), new SKPoint(22,15), new SKPoint(22,14)}, // 06 - new SKPoint[] {new SKPoint(09,15), new SKPoint(09,14), new SKPoint(10,15), new SKPoint(10,14), new SKPoint(10,10), new SKPoint(11,10)}, // 07 - new SKPoint[] {new SKPoint(17,21), new SKPoint(09,19), new SKPoint(09,18), new SKPoint(10,19), new SKPoint(11,19), new SKPoint(11,18)}, // 08 - new SKPoint[] {new SKPoint(15,06), new SKPoint(16,06), new SKPoint(17,07), new SKPoint(17,06), new SKPoint(15,21), new SKPoint(15,20)}, // 09 - new SKPoint[] {new SKPoint(12,09), new SKPoint(12,08), new SKPoint(13,09), new SKPoint(13,08), new SKPoint(14,09), new SKPoint(14,08)}, // 10 - new SKPoint[] {new SKPoint(18,09), new SKPoint(18,08), new SKPoint(19,09), new SKPoint(19,08), new SKPoint(20,09), new SKPoint(20,08)}, // 11 - new SKPoint[] {new SKPoint(21,19), new SKPoint(21,18), new SKPoint(22,19), new SKPoint(22,18), new SKPoint(23,19), new SKPoint(23,18)}, // 12 - new SKPoint[] {new SKPoint(21,09), new SKPoint(21,08), new SKPoint(22,09), new SKPoint(22,08), new SKPoint(23,09), new SKPoint(23,08)}, // 13 - new SKPoint[] {new SKPoint(09,09), new SKPoint(09,08), new SKPoint(10,09), new SKPoint(10,08), new SKPoint(11,09), new SKPoint(11,08)}, // 14 - new SKPoint[] {new SKPoint(12,21), new SKPoint(12,20), new SKPoint(13,21), new SKPoint(13,20), new SKPoint(14,21), new SKPoint(14,20)}, // 15 - new SKPoint[] {new SKPoint(18,21), new SKPoint(18,20), new SKPoint(19,21), new SKPoint(19,20), new SKPoint(20,21), new SKPoint(20,20)}, // 16 - new SKPoint[] {new SKPoint(18,07), new SKPoint(18,06), new SKPoint(19,07), new SKPoint(19,06), new SKPoint(20,07), new SKPoint(20,06)}, // 17 - new SKPoint[] {new SKPoint(12,07), new SKPoint(12,06), new SKPoint(13,07), new SKPoint(13,06), new SKPoint(14,07), new SKPoint(14,06)}, // 18 - new SKPoint[] {new SKPoint(09,21), new SKPoint(09,20), new SKPoint(10,21), new SKPoint(10,20), new SKPoint(11,21), new SKPoint(11,20)}, // 19 - new SKPoint[] {new SKPoint(21,21), new SKPoint(21,20), new SKPoint(22,21), new SKPoint(22,20), new SKPoint(23,21), new SKPoint(23,20)}, // 20 - new SKPoint[] {new SKPoint(00,01), new SKPoint(00,00), new SKPoint(01,01), new SKPoint(01,00), new SKPoint(02,01), new SKPoint(02,00)}, // 21 - new SKPoint[] {new SKPoint(00,03), new SKPoint(00,02), new SKPoint(01,03), new SKPoint(01,02), new SKPoint(02,03), new SKPoint(02,02)}, // 22 - new SKPoint[] {new SKPoint(00,05), new SKPoint(00,04), new SKPoint(01,05), new SKPoint(01,04), new SKPoint(02,05), new SKPoint(02,04)}, // 23 - new SKPoint[] {new SKPoint(00,07), new SKPoint(00,06), new SKPoint(01,07), new SKPoint(01,06), new SKPoint(02,07), new SKPoint(02,06)}, // 24 - new SKPoint[] {new SKPoint(00,09), new SKPoint(00,08), new SKPoint(01,09), new SKPoint(01,08), new SKPoint(02,09), new SKPoint(02,08)}, // 25 - new SKPoint[] {new SKPoint(00,11), new SKPoint(00,10), new SKPoint(01,11), new SKPoint(01,10), new SKPoint(02,11), new SKPoint(02,10)}, // 26 - new SKPoint[] {new SKPoint(00,13), new SKPoint(00,12), new SKPoint(01,13), new SKPoint(01,12), new SKPoint(02,13), new SKPoint(02,12)}, // 27 - new SKPoint[] {new SKPoint(00,15), new SKPoint(00,14), new SKPoint(01,15), new SKPoint(01,14), new SKPoint(02,15), new SKPoint(02,14)}, // 28 - new SKPoint[] {new SKPoint(00,17), new SKPoint(00,16), new SKPoint(01,17), new SKPoint(01,16), new SKPoint(02,17), new SKPoint(02,16)}, // 29 - new SKPoint[] {new SKPoint(00,19), new SKPoint(00,18), new SKPoint(01,19), new SKPoint(01,18), new SKPoint(02,19), new SKPoint(02,18)}, // 30 - new SKPoint[] {new SKPoint(00,21), new SKPoint(00,20), new SKPoint(01,21), new SKPoint(01,20), new SKPoint(02,21), new SKPoint(02,20)}, // 31 - new SKPoint[] {new SKPoint(00,23), new SKPoint(00,22), new SKPoint(01,23), new SKPoint(01,22), new SKPoint(02,23), new SKPoint(02,22)}, // 32 - new SKPoint[] {new SKPoint(00,25), new SKPoint(00,24), new SKPoint(01,25), new SKPoint(01,24), new SKPoint(02,25), new SKPoint(02,24)}, // 33 - new SKPoint[] {new SKPoint(00,27), new SKPoint(00,26), new SKPoint(01,27), new SKPoint(01,26), new SKPoint(02,27), new SKPoint(02,26)}, // 34 - new SKPoint[] {new SKPoint(03,27), new SKPoint(03,26), new SKPoint(04,27), new SKPoint(04,26), new SKPoint(05,27), new SKPoint(05,26)}, // 35 - new SKPoint[] {new SKPoint(03,25), new SKPoint(03,24), new SKPoint(04,25), new SKPoint(04,24), new SKPoint(05,25), new SKPoint(05,24)}, // 36 - new SKPoint[] {new SKPoint(03,23), new SKPoint(03,22), new SKPoint(04,23), new SKPoint(04,22), new SKPoint(05,23), new SKPoint(05,22)}, // 37 - new SKPoint[] {new SKPoint(03,21), new SKPoint(03,20), new SKPoint(04,21), new SKPoint(04,20), new SKPoint(05,21), new SKPoint(05,20)}, // 38 - new SKPoint[] {new SKPoint(03,19), new SKPoint(03,18), new SKPoint(04,19), new SKPoint(04,18), new SKPoint(05,19), new SKPoint(05,18)}, // 39 - new SKPoint[] {new SKPoint(03,17), new SKPoint(03,16), new SKPoint(04,17), new SKPoint(04,16), new SKPoint(05,17), new SKPoint(05,16)}, // 40 - new SKPoint[] {new SKPoint(03,15), new SKPoint(03,14), new SKPoint(04,15), new SKPoint(04,14), new SKPoint(05,15), new SKPoint(05,14)}, // 41 - new SKPoint[] {new SKPoint(03,13), new SKPoint(03,12), new SKPoint(04,13), new SKPoint(04,12), new SKPoint(05,13), new SKPoint(05,12)}, // 42 - new SKPoint[] {new SKPoint(03,11), new SKPoint(03,10), new SKPoint(04,11), new SKPoint(04,10), new SKPoint(05,11), new SKPoint(05,10)}, // 43 - new SKPoint[] {new SKPoint(03,09), new SKPoint(03,08), new SKPoint(04,09), new SKPoint(04,08), new SKPoint(05,09), new SKPoint(05,08)}, // 44 - new SKPoint[] {new SKPoint(03,07), new SKPoint(03,06), new SKPoint(04,07), new SKPoint(04,06), new SKPoint(05,07), new SKPoint(05,06)}, // 45 - new SKPoint[] {new SKPoint(03,05), new SKPoint(03,04), new SKPoint(04,05), new SKPoint(04,04), new SKPoint(05,05), new SKPoint(05,04)}, // 46 - new SKPoint[] {new SKPoint(03,03), new SKPoint(03,02), new SKPoint(04,03), new SKPoint(04,02), new SKPoint(05,03), new SKPoint(05,02)}, // 47 - new SKPoint[] {new SKPoint(03,01), new SKPoint(03,00), new SKPoint(04,01), new SKPoint(04,00), new SKPoint(05,01), new SKPoint(05,00)}, // 48 - new SKPoint[] {new SKPoint(06,01), new SKPoint(06,00), new SKPoint(07,01), new SKPoint(07,00), new SKPoint(08,01), new SKPoint(08,00)}, // 49 - new SKPoint[] {new SKPoint(06,03), new SKPoint(06,02), new SKPoint(07,03), new SKPoint(07,02), new SKPoint(08,03), new SKPoint(08,02)}, // 50 - new SKPoint[] {new SKPoint(06,05), new SKPoint(06,04), new SKPoint(07,05), new SKPoint(07,04), new SKPoint(08,05), new SKPoint(08,04)}, // 51 - new SKPoint[] {new SKPoint(06,07), new SKPoint(06,06), new SKPoint(07,07), new SKPoint(07,06), new SKPoint(08,07), new SKPoint(08,06)}, // 52 - new SKPoint[] {new SKPoint(06,09), new SKPoint(06,08), new SKPoint(07,09), new SKPoint(07,08), new SKPoint(08,09), new SKPoint(08,08)}, // 53 - new SKPoint[] {new SKPoint(06,11), new SKPoint(06,10), new SKPoint(07,11), new SKPoint(07,10), new SKPoint(08,11), new SKPoint(08,10)}, // 54 - new SKPoint[] {new SKPoint(06,13), new SKPoint(06,12), new SKPoint(07,13), new SKPoint(07,12), new SKPoint(08,13), new SKPoint(08,12)}, // 55 - new SKPoint[] {new SKPoint(06,15), new SKPoint(06,14), new SKPoint(07,15), new SKPoint(07,14), new SKPoint(08,15), new SKPoint(08,14)}, // 56 - new SKPoint[] {new SKPoint(06,17), new SKPoint(06,16), new SKPoint(07,17), new SKPoint(07,16), new SKPoint(08,17), new SKPoint(08,16)}, // 57 - new SKPoint[] {new SKPoint(06,19), new SKPoint(06,18), new SKPoint(07,19), new SKPoint(07,18), new SKPoint(08,19), new SKPoint(08,18)}, // 58 - new SKPoint[] {new SKPoint(06,21), new SKPoint(06,20), new SKPoint(07,21), new SKPoint(07,20), new SKPoint(08,21), new SKPoint(08,20)}, // 59 - new SKPoint[] {new SKPoint(06,23), new SKPoint(06,22), new SKPoint(07,23), new SKPoint(07,22), new SKPoint(08,23), new SKPoint(08,22)}, // 60 - new SKPoint[] {new SKPoint(06,25), new SKPoint(06,24), new SKPoint(07,25), new SKPoint(07,24), new SKPoint(08,25), new SKPoint(08,24)}, // 61 - new SKPoint[] {new SKPoint(06,27), new SKPoint(06,26), new SKPoint(07,27), new SKPoint(07,26), new SKPoint(08,27), new SKPoint(08,26)}, // 62 - new SKPoint[] {new SKPoint(09,27), new SKPoint(09,26), new SKPoint(10,27), new SKPoint(10,26), new SKPoint(11,27), new SKPoint(11,26)}, // 63 - new SKPoint[] {new SKPoint(09,25), new SKPoint(09,24), new SKPoint(10,25), new SKPoint(10,24), new SKPoint(11,25), new SKPoint(11,24)}, // 64 - new SKPoint[] {new SKPoint(09,23), new SKPoint(09,22), new SKPoint(10,23), new SKPoint(10,22), new SKPoint(11,23), new SKPoint(11,22)}, // 65 - new SKPoint[] {new SKPoint(09,07), new SKPoint(09,06), new SKPoint(10,07), new SKPoint(10,06), new SKPoint(11,07), new SKPoint(11,06)}, // 66 - new SKPoint[] {new SKPoint(09,05), new SKPoint(09,04), new SKPoint(10,05), new SKPoint(10,04), new SKPoint(11,05), new SKPoint(11,04)}, // 67 - new SKPoint[] {new SKPoint(09,03), new SKPoint(09,02), new SKPoint(10,03), new SKPoint(10,02), new SKPoint(11,03), new SKPoint(11,02)}, // 68 - new SKPoint[] {new SKPoint(09,01), new SKPoint(09,00), new SKPoint(10,01), new SKPoint(10,00), new SKPoint(11,01), new SKPoint(11,00)}, // 69 - new SKPoint[] {new SKPoint(12,01), new SKPoint(12,00), new SKPoint(13,01), new SKPoint(13,00), new SKPoint(14,01), new SKPoint(14,00)}, // 70 - new SKPoint[] {new SKPoint(12,03), new SKPoint(12,02), new SKPoint(13,03), new SKPoint(13,02), new SKPoint(14,03), new SKPoint(14,02)}, // 71 - new SKPoint[] {new SKPoint(12,05), new SKPoint(12,04), new SKPoint(13,05), new SKPoint(13,04), new SKPoint(14,05), new SKPoint(14,04)}, // 72 - new SKPoint[] {new SKPoint(12,23), new SKPoint(12,22), new SKPoint(13,23), new SKPoint(13,22), new SKPoint(14,23), new SKPoint(14,22)}, // 73 - new SKPoint[] {new SKPoint(12,25), new SKPoint(12,24), new SKPoint(13,25), new SKPoint(13,24), new SKPoint(14,25), new SKPoint(14,24)}, // 74 - new SKPoint[] {new SKPoint(12,27), new SKPoint(12,26), new SKPoint(13,27), new SKPoint(13,26), new SKPoint(14,27), new SKPoint(14,26)}, // 75 - new SKPoint[] {new SKPoint(15,27), new SKPoint(15,26), new SKPoint(16,27), new SKPoint(16,26), new SKPoint(17,27), new SKPoint(17,26)}, // 76 - new SKPoint[] {new SKPoint(15,25), new SKPoint(15,24), new SKPoint(16,25), new SKPoint(16,24), new SKPoint(17,25), new SKPoint(17,24)}, // 77 - new SKPoint[] {new SKPoint(15,23), new SKPoint(15,22), new SKPoint(16,23), new SKPoint(16,22), new SKPoint(17,23), new SKPoint(17,22)}, // 78 - new SKPoint[] {new SKPoint(15,05), new SKPoint(15,04), new SKPoint(16,05), new SKPoint(16,04), new SKPoint(17,05), new SKPoint(17,04)}, // 79 - new SKPoint[] {new SKPoint(15,03), new SKPoint(15,02), new SKPoint(16,03), new SKPoint(16,02), new SKPoint(17,03), new SKPoint(17,02)}, // 80 - new SKPoint[] {new SKPoint(15,01), new SKPoint(15,00), new SKPoint(16,01), new SKPoint(16,00), new SKPoint(17,01), new SKPoint(17,00)}, // 81 - new SKPoint[] {new SKPoint(18,01), new SKPoint(18,00), new SKPoint(19,01), new SKPoint(19,00), new SKPoint(20,01), new SKPoint(20,00)}, // 82 - new SKPoint[] {new SKPoint(18,03), new SKPoint(18,02), new SKPoint(19,03), new SKPoint(19,02), new SKPoint(20,03), new SKPoint(20,02)}, // 83 - new SKPoint[] {new SKPoint(18,05), new SKPoint(18,04), new SKPoint(19,05), new SKPoint(19,04), new SKPoint(20,05), new SKPoint(20,04)}, // 84 - new SKPoint[] {new SKPoint(18,23), new SKPoint(18,22), new SKPoint(19,23), new SKPoint(19,22), new SKPoint(20,23), new SKPoint(20,22)}, // 85 - new SKPoint[] {new SKPoint(18,25), new SKPoint(18,24), new SKPoint(19,25), new SKPoint(19,24), new SKPoint(20,25), new SKPoint(20,24)}, // 86 - new SKPoint[] {new SKPoint(18,27), new SKPoint(18,26), new SKPoint(19,27), new SKPoint(19,26), new SKPoint(20,27), new SKPoint(20,26)}, // 87 - new SKPoint[] {new SKPoint(21,27), new SKPoint(21,26), new SKPoint(22,27), new SKPoint(22,26), new SKPoint(23,27), new SKPoint(23,26)}, // 88 - new SKPoint[] {new SKPoint(21,25), new SKPoint(21,24), new SKPoint(22,25), new SKPoint(22,24), new SKPoint(23,25), new SKPoint(23,24)}, // 89 - new SKPoint[] {new SKPoint(21,23), new SKPoint(21,22), new SKPoint(22,23), new SKPoint(22,22), new SKPoint(23,23), new SKPoint(23,22)}, // 90 - new SKPoint[] {new SKPoint(21,07), new SKPoint(21,06), new SKPoint(22,07), new SKPoint(22,06), new SKPoint(23,07), new SKPoint(23,06)}, // 91 - new SKPoint[] {new SKPoint(21,05), new SKPoint(21,04), new SKPoint(22,05), new SKPoint(22,04), new SKPoint(23,05), new SKPoint(23,04)}, // 92 - new SKPoint[] {new SKPoint(21,03), new SKPoint(21,02), new SKPoint(22,03), new SKPoint(22,02), new SKPoint(23,03), new SKPoint(23,02)}, // 93 - new SKPoint[] {new SKPoint(21,01), new SKPoint(21,00), new SKPoint(22,01), new SKPoint(22,00), new SKPoint(23,01), new SKPoint(23,00)}, // 94 - new SKPoint[] {new SKPoint(24,01), new SKPoint(24,00), new SKPoint(25,01), new SKPoint(25,00), new SKPoint(26,01), new SKPoint(26,00)}, // 95 - new SKPoint[] {new SKPoint(24,03), new SKPoint(24,02), new SKPoint(25,03), new SKPoint(25,02), new SKPoint(26,03), new SKPoint(26,02)}, // 96 - new SKPoint[] {new SKPoint(24,05), new SKPoint(24,04), new SKPoint(25,05), new SKPoint(25,04), new SKPoint(26,05), new SKPoint(26,04)}, // 97 - new SKPoint[] {new SKPoint(24,07), new SKPoint(24,06), new SKPoint(25,07), new SKPoint(25,06), new SKPoint(26,07), new SKPoint(26,06)}, // 98 - new SKPoint[] {new SKPoint(24,09), new SKPoint(24,08), new SKPoint(25,09), new SKPoint(25,08), new SKPoint(26,09), new SKPoint(26,08)}, // 99 - new SKPoint[] {new SKPoint(24,11), new SKPoint(24,10), new SKPoint(25,11), new SKPoint(25,10), new SKPoint(26,11), new SKPoint(26,10)}, // 100 - new SKPoint[] {new SKPoint(24,13), new SKPoint(24,12), new SKPoint(25,13), new SKPoint(25,12), new SKPoint(26,13), new SKPoint(26,12)}, // 101 - new SKPoint[] {new SKPoint(24,15), new SKPoint(24,14), new SKPoint(25,15), new SKPoint(25,14), new SKPoint(26,15), new SKPoint(26,14)}, // 102 - new SKPoint[] {new SKPoint(24,17), new SKPoint(24,16), new SKPoint(25,17), new SKPoint(25,16), new SKPoint(26,17), new SKPoint(26,16)}, // 103 - new SKPoint[] {new SKPoint(24,19), new SKPoint(24,18), new SKPoint(25,19), new SKPoint(25,18), new SKPoint(26,19), new SKPoint(26,18)}, // 104 - new SKPoint[] {new SKPoint(24,21), new SKPoint(24,20), new SKPoint(25,21), new SKPoint(25,20), new SKPoint(26,21), new SKPoint(26,20)}, // 105 - new SKPoint[] {new SKPoint(24,23), new SKPoint(24,22), new SKPoint(25,23), new SKPoint(25,22), new SKPoint(26,23), new SKPoint(26,22)}, // 106 - new SKPoint[] {new SKPoint(24,25), new SKPoint(24,24), new SKPoint(25,25), new SKPoint(25,24), new SKPoint(26,25), new SKPoint(26,24)}, // 107 - new SKPoint[] {new SKPoint(24,27), new SKPoint(24,26), new SKPoint(25,27), new SKPoint(25,26), new SKPoint(26,27), new SKPoint(26,26)}, // 108 - new SKPoint[] {new SKPoint(27,27), new SKPoint(27,26), new SKPoint(28,27), new SKPoint(28,26), new SKPoint(29,27), new SKPoint(29,26)}, // 109 - new SKPoint[] {new SKPoint(27,25), new SKPoint(27,24), new SKPoint(28,25), new SKPoint(28,24), new SKPoint(29,25), new SKPoint(29,24)}, // 110 - new SKPoint[] {new SKPoint(27,23), new SKPoint(27,22), new SKPoint(28,23), new SKPoint(28,22), new SKPoint(29,23), new SKPoint(29,22)}, // 111 - new SKPoint[] {new SKPoint(27,21), new SKPoint(27,20), new SKPoint(28,21), new SKPoint(28,20), new SKPoint(29,21), new SKPoint(29,20)}, // 112 - new SKPoint[] {new SKPoint(27,19), new SKPoint(27,18), new SKPoint(28,19), new SKPoint(28,18), new SKPoint(29,19), new SKPoint(29,18)}, // 113 - new SKPoint[] {new SKPoint(27,17), new SKPoint(27,16), new SKPoint(28,17), new SKPoint(28,16), new SKPoint(29,17), new SKPoint(29,16)}, // 114 - new SKPoint[] {new SKPoint(27,15), new SKPoint(27,14), new SKPoint(28,15), new SKPoint(28,14), new SKPoint(29,15), new SKPoint(29,14)}, // 115 - new SKPoint[] {new SKPoint(27,13), new SKPoint(27,12), new SKPoint(28,13), new SKPoint(28,12), new SKPoint(29,13), new SKPoint(29,12)}, // 116 - new SKPoint[] {new SKPoint(27,11), new SKPoint(27,10), new SKPoint(28,11), new SKPoint(28,10), new SKPoint(29,11), new SKPoint(29,10)}, // 117 - new SKPoint[] {new SKPoint(27,09), new SKPoint(27,08), new SKPoint(28,09), new SKPoint(28,08), new SKPoint(29,09), new SKPoint(29,08)}, // 118 - new SKPoint[] {new SKPoint(27,07), new SKPoint(27,06), new SKPoint(28,07), new SKPoint(28,06), new SKPoint(29,07), new SKPoint(29,06)}, // 119 - new SKPoint[] {new SKPoint(27,05), new SKPoint(27,04), new SKPoint(28,05), new SKPoint(28,04), new SKPoint(29,05), new SKPoint(29,04)}, // 120 - new SKPoint[] {new SKPoint(27,03), new SKPoint(27,02), new SKPoint(28,03), new SKPoint(28,02), new SKPoint(29,03), new SKPoint(29,02)}, // 121 - new SKPoint[] {new SKPoint(27,01), new SKPoint(27,00), new SKPoint(28,01), new SKPoint(28,00), new SKPoint(29,01), new SKPoint(29,00)}, // 122 - new SKPoint[] {new SKPoint(30,01), new SKPoint(30,00), new SKPoint(31,01), new SKPoint(31,00), new SKPoint(32,01), new SKPoint(32,00)}, // 123 - new SKPoint[] {new SKPoint(30,03), new SKPoint(30,02), new SKPoint(31,03), new SKPoint(31,02), new SKPoint(32,03), new SKPoint(32,02)}, // 124 - new SKPoint[] {new SKPoint(30,05), new SKPoint(30,04), new SKPoint(31,05), new SKPoint(31,04), new SKPoint(32,05), new SKPoint(32,04)}, // 125 - new SKPoint[] {new SKPoint(30,07), new SKPoint(30,06), new SKPoint(31,07), new SKPoint(31,06), new SKPoint(32,07), new SKPoint(32,06)}, // 126 - new SKPoint[] {new SKPoint(30,09), new SKPoint(30,08), new SKPoint(31,09), new SKPoint(31,08), new SKPoint(32,09), new SKPoint(32,08)}, // 127 - new SKPoint[] {new SKPoint(30,11), new SKPoint(30,10), new SKPoint(31,11), new SKPoint(31,10), new SKPoint(32,11), new SKPoint(32,10)}, // 128 - new SKPoint[] {new SKPoint(30,13), new SKPoint(30,12), new SKPoint(31,13), new SKPoint(31,12), new SKPoint(32,13), new SKPoint(32,12)}, // 129 - new SKPoint[] {new SKPoint(30,15), new SKPoint(30,14), new SKPoint(31,15), new SKPoint(31,14), new SKPoint(32,15), new SKPoint(32,14)}, // 130 - new SKPoint[] {new SKPoint(30,17), new SKPoint(30,16), new SKPoint(31,17), new SKPoint(31,16), new SKPoint(32,17), new SKPoint(32,16)}, // 131 - new SKPoint[] {new SKPoint(30,19), new SKPoint(30,18), new SKPoint(31,19), new SKPoint(31,18), new SKPoint(32,19), new SKPoint(32,18)}, // 132 - new SKPoint[] {new SKPoint(30,21), new SKPoint(30,20), new SKPoint(31,21), new SKPoint(31,20), new SKPoint(32,21), new SKPoint(32,20)}, // 133 - new SKPoint[] {new SKPoint(30,23), new SKPoint(30,22), new SKPoint(31,23), new SKPoint(31,22), new SKPoint(32,23), new SKPoint(32,22)}, // 134 - new SKPoint[] {new SKPoint(30,25), new SKPoint(30,24), new SKPoint(31,25), new SKPoint(31,24), new SKPoint(32,25), new SKPoint(32,24)}, // 135 - new SKPoint[] {new SKPoint(30,27), new SKPoint(30,26), new SKPoint(31,27), new SKPoint(31,26), new SKPoint(32,27), new SKPoint(32,26)}, // 136 - new SKPoint[] {new SKPoint(01,28), new SKPoint(02,29), new SKPoint(02,28), new SKPoint(03,28), new SKPoint(04,29), new SKPoint(04,28)}, // 137 - new SKPoint[] {new SKPoint(05,28), new SKPoint(06,29), new SKPoint(06,28), new SKPoint(07,28), new SKPoint(08,29), new SKPoint(08,28)}, // 138 - new SKPoint[] {new SKPoint(09,28), new SKPoint(10,29), new SKPoint(10,28), new SKPoint(11,28), new SKPoint(12,29), new SKPoint(12,28)}, // 139 - new SKPoint[] {new SKPoint(13,28), new SKPoint(14,29), new SKPoint(14,28), new SKPoint(15,28), new SKPoint(16,29), new SKPoint(16,28)}, // 140 - new SKPoint[] {new SKPoint(17,28), new SKPoint(18,29), new SKPoint(18,28), new SKPoint(19,28), new SKPoint(20,29), new SKPoint(20,28)}, // 141 - new SKPoint[] {new SKPoint(21,28), new SKPoint(22,29), new SKPoint(22,28), new SKPoint(23,28), new SKPoint(24,29), new SKPoint(24,28)}, // 142 - new SKPoint[] {new SKPoint(25,28), new SKPoint(26,29), new SKPoint(26,28), new SKPoint(27,28), new SKPoint(28,29), new SKPoint(28,28)}, // 143 - new SKPoint[] {new SKPoint(29,28), new SKPoint(30,29), new SKPoint(30,28), new SKPoint(31,28), new SKPoint(32,29), new SKPoint(32,28)}, // 144 - }; - - private enum codeSets - { - setA, - setB, - setC, - setD, - setE, - } - - private static char[] c_setA = - { - '\r','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '\0', '\x1C', '\x1D', - '\x1E', '\0', ' ', '\0', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', - ',', '—', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', - }; - - private static char[] c_setB = - { - '`','a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '\0', '\x1C', '\x1D', - '\x1E', '\0', '{', '\0', '}', '~', '\x7F', ';', '<', '=', '>', '?', '[', '\\', - ']', '^', '_', ' ', ',', '.', '/', ':', '@', '!', '|', - }; - - private static char[] c_setC = - { - 'À','Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', - 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', '×', 'Ø', 'Ù', 'Ú', '\0', '\x1C', '\x1D', - '\x1E', '\0', 'Û', 'Ü', 'Ý', 'Þ', 'ß', 'ª', '¬', '±', '²', '³', 'µ', '¹', - 'º', '¼', '½', '¾', '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', - '\x87', '\x88', '\x89', '\0', ' ', - };// - - private static char[] c_setD = - { - 'à','á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', - 'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', '÷', 'ø', 'ù', 'ú', '\0', '\x1C', '\x1D', - '\x1E', '\0', 'û', 'ü', 'ý', 'þ', 'ÿ', '¡', '¨', '«', '¯', '°', '´', '·', - '¸', '»', '¿', '\x8A', '\x8B', '\x8C', '\x8D', '\x8E', '\x8F', '\x90', - '\x91', '\x92', '\x93', '\x94', '\0', ' ', - };// - - - private static char[] c_setE = - { - '\0','\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\a', '\b', '\t', '\n', - '\v', '\f', '\r', '\x0E', '\x0F', '\x10', '\x11', '\x12', '\x13', '\x14', - '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A','\0','\0','\0', '\x1B', '\0', - '\x1C', '\x1D', '\x1E', '\x1F', '\x9F', '\xA0', '¢', '£', '¤', '¥', '¦', '§', - '©', '\xAD', '®', '¶', '\x95', '\x96', '\x97', '\x98', '\x99', '\x9A', - '\x9B', '\x9C', '\x9D', '\x9E', '\0', ' ', - }; - - // Control Characters - private static byte NS = 31; // Numeric Shift - private static byte Shift2A = 56; // 2 Shift A - private static byte Shift3A = 57; // 3 Shift A - private static byte LatchA = 58; // Latch A - private static byte ShiftAB = 59; // Shift A & Shift B - private static byte LatchAB = 63; // Latch A & Latch B - private static byte ShiftC = 60; // Shift C & Lock-In-C - private static byte ShiftD = 61; // Shift D & Lock-In-D - private static byte ShiftE = 62; // Shift E & Lock-In-E - private static byte Pad = 33; // Padding - - private ArrayList hexagonsList; - private int m_symbolWidth = 300; // in pixels - private int m_realSymbolWidth = 300; // in pixels - private int m_symbolHeight = 300; // in pixels - private int m_realSymbolHeight = 300; // in pixels - - /// - /// Initializes a new instance of the class. - /// - public MaxiCodeSymbology() - : base(TrueSymbologyType.MaxiCode) - { - } - - public MaxiCodeSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.MaxiCode) - { - } - - /// - /// Validates the value using MaxiCode symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - if (string.IsNullOrEmpty(value)) - return true; - - try - { - byte[] bytes = null; - switch (Options.MaxiCodeMode) - { - case 2: - case 3: - { - bytes = getCodewordsSequence23(value); - break; - } - case 4: - case 5: - case 6: - { - bytes = getCodewordsSequence(value); - break; - } - } - - getSymbolSigns(bytes); - } - catch (Exception) - { - return false; - } - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "MaxiCode symbology allows a maximum data size of 138 numeric or 93 alphabetic characters.\n"; - } - - - /// - /// Gets the barcode value encoded using MaxiCode symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using MaxiCode symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - if (forCaption) - return Value; - - return ""; - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// The encoding pattern for given character. - protected override string getCharPattern(char c) - { - return ""; - } - - private struct SignCode - { - byte signvalue; - codeSets codeset; - - public SignCode(int v, codeSets code) - { - signvalue = (byte)v; - codeset = code; - } - - public byte SignValue - { - get { return signvalue; } - set { signvalue = value; } - } - - public codeSets CodeSet - { - get { return codeset; } - set { codeset = value; } - } - } - - private sbyte getSignValue(char sign, char[] set) - { - for (sbyte i = 0; i < set.Length; i++) - { - if (sign == set[i]) - return i; - } - return -1; - } - - /// - /// Gets the sign value and code set. - /// - /// The sign. - /// The current code set. - /// - private SignCode getSignValueCodeSets(char sign, codeSets current) - { - if (sign == '\0') - { - return new SignCode(0, codeSets.setE); - } - - sbyte index = -1; - // because few (five?) punctuation signs setA and setB are the same, - // then we try to optimize - if (current == codeSets.setB) - { - index = getSignValue(sign, c_setB); - if (index > -1) - { - return new SignCode(index, codeSets.setB); - } - - index = getSignValue(sign, c_setA); - if (index > -1) - { - return new SignCode(index, codeSets.setA); - } - } - else - { - index = getSignValue(sign, c_setA); - if (index > -1) - { - return new SignCode(index, codeSets.setA); - } - - index = getSignValue(sign, c_setB); - if (index > -1) - { - return new SignCode(index, codeSets.setB); - } - } - - index = getSignValue(sign, c_setC); - if (index > -1) - { - return new SignCode(index, codeSets.setC); - } - - index = getSignValue(sign, c_setD); - if (index > -1) - { - return new SignCode(index, codeSets.setD); - } - - index = getSignValue(sign, c_setE); - if (index > -1) - { - return new SignCode(index, codeSets.setE); - } - - return new SignCode(-1, codeSets.setA); - } - - /// - /// Gets the codewords sequence for 4, 5, and 6 modes. - /// - /// The message for encoding. - /// - private byte[] getCodewordsSequence(string value) - { - byte[] charBytes = Options.Encoding.GetBytes(value); - - string message = Encoding.ASCII.GetString(charBytes, 0, charBytes.Length); - - byteList bytes = new byteList(); - if (Options.MaxiCodeMode != 2 && Options.MaxiCodeMode != 3) - bytes.Add(Options.MaxiCodeMode); - - codeSets currentSet = codeSets.setA; - SignCode sign1 = new SignCode(0, currentSet); - SignCode sign2 = new SignCode(0, currentSet); - SignCode sign3 = new SignCode(0, currentSet); - SignCode sign4 = new SignCode(0, currentSet); - int i = 0; - while (i < message.Length) - { - // Numeric Shift - if ((char.IsDigit(message[i])) && (i + 9 < message.Length)) - { - bool ns = true; - for (int j = 1; j < 9; j++) - { - if (!char.IsDigit(message[i + j])) - { - ns = false; - break; - } - } - if (ns) - { - // use numeric shift - int number = int.Parse(message.Substring(i, 9)); - String bin_number = Convert.ToString(number, 2); - while (bin_number.Length < 30) - bin_number = bin_number.Insert(0, "0"); - bytes.Add(NS); - for (int j = 0; j < 30; j += 6) - bytes.Add(Convert.ToByte(bin_number.Substring(j, 6), 2)); - i += 9; - break; - } - } - - sign1 = getSignValueCodeSets(message[i], currentSet); - if (i + 1 < message.Length) - { - sign2 = getSignValueCodeSets(message[i + 1], currentSet); - if (i + 2 < message.Length) - { - sign3 = getSignValueCodeSets(message[i + 2], currentSet); - if (i + 3 < message.Length) - sign4 = getSignValueCodeSets(message[i + 3], currentSet); - } - } - - if (currentSet == sign1.CodeSet) - bytes.Add(sign1.SignValue); - - else - switch (sign1.CodeSet) - { - case codeSets.setA: - if (currentSet == codeSets.setB) - { - if (sign2.CodeSet == codeSets.setA && - sign3.CodeSet == codeSets.setA && - sign4.CodeSet == codeSets.setA && - (i + 3 < message.Length)) - { - // Latch A and switching into codeSets.setA - bytes.Add(LatchAB); - bytes.Add(sign1.SignValue); - currentSet = codeSets.setA; - } - else if (sign2.CodeSet == codeSets.setA && - sign3.CodeSet == codeSets.setA && - (i + 2 < message.Length)) - { - // 3 Shift A and writing 3 characters into codeSets.setA - // but what if the next characters will not be codeSets.setB? - bytes.Add(Shift3A); - bytes.Add(sign1.SignValue); - bytes.Add(sign2.SignValue); - bytes.Add(sign3.SignValue); - i += 2; // also adding i++ at the end of the block - } - else if (sign2.CodeSet == codeSets.setA && (i + 1 < message.Length)) - { - // 2 Shift A and writing 2 characters into codeSets.setA - bytes.Add(Shift2A); - bytes.Add(sign1.SignValue); - bytes.Add(sign2.SignValue); - i += 1; // and i++ in the end of the block - } - else - { - // Shift A and writing one character into codeSets.setA - bytes.Add(ShiftAB); - bytes.Add(sign1.SignValue); - } - } - else - { - // Latch A and switching into codeSets.setA - bytes.Add(LatchA); - bytes.Add(sign1.SignValue); - currentSet = codeSets.setA; - } - break; - case codeSets.setB: - if (currentSet == codeSets.setA) - { - if ((sign2.CodeSet == codeSets.setB) && (i + 1 < message.Length)) - { - // Latch B and switching into codeSets.setB - bytes.Add(LatchAB); - bytes.Add(sign1.SignValue); - currentSet = codeSets.setB; - } - else - { - // Shift B and writing one character into codeSets.setB - bytes.Add(ShiftAB); - bytes.Add(sign1.SignValue); - } - } - else - { - // Latch B and switching into codeSets.setB - bytes.Add(LatchAB); - bytes.Add(sign1.SignValue); - currentSet = codeSets.setB; - } - break; - case codeSets.setC: - if ((sign2.CodeSet == codeSets.setC) && (i + 1 < message.Length)) - { - // Lock-In C and switching into codeSets.setC - bytes.Add(ShiftC); - bytes.Add(ShiftC); - bytes.Add(sign1.SignValue); - currentSet = codeSets.setC; - } - else - { - // Shift C and writing one character into codeSets.setC - bytes.Add(ShiftC); - bytes.Add(sign1.SignValue); - } - break; - case codeSets.setD: - if ((sign2.CodeSet == codeSets.setD) && (i + 1 < message.Length)) - { - // Lock-In D and switching into codeSets.setD - bytes.Add(ShiftD); - bytes.Add(ShiftD); - bytes.Add(sign1.SignValue); - currentSet = codeSets.setD; - } - else - { - // Shift D and writing one character into codeSets.setD - bytes.Add(ShiftD); - bytes.Add(sign1.SignValue); - } - break; - case codeSets.setE: - if ((sign2.CodeSet == codeSets.setE) && (i + 1 < message.Length)) - { - // Lock-In E and switching into codeSets.setE - bytes.Add(ShiftE); - bytes.Add(ShiftE); - bytes.Add(sign1.SignValue); - currentSet = codeSets.setE; - } - else - { - // Shift E and writing one character into codeSets.setE - bytes.Add(ShiftE); - bytes.Add(sign1.SignValue); - } - break; - } - i++; - } - - // depending on Options.MaxiCodeMode we fill with paddings - if (Options.MaxiCodeMode == 5) - { - if (bytes.Count > first_message_length + second_message_length - eec_length) - throw new BarcodeException("String is too long"); - else - while (bytes.Count < first_message_length + second_message_length - eec_length) - bytes.Add(Pad); - } - else if (Options.MaxiCodeMode == 2 || Options.MaxiCodeMode == 3) - { - if (bytes.Count > second_message_length - sec_length) - throw new BarcodeException("String is too long"); - else - while (bytes.Count < second_message_length - sec_length) - bytes.Add(Pad); - } - else - { - if (bytes.Count > first_message_length + second_message_length - sec_length) - throw new BarcodeException("String is too long"); - else - while (bytes.Count < first_message_length + second_message_length - sec_length) - bytes.Add(Pad); - } - return bytes.ToArray(); - } - - /// - /// Gets the codewords sequence for 2, and 3 modes. - /// - /// The message for encoding. - /// - private byte[] getCodewordsSequence23(string message) - { - try - { - StringBuilder binary_str = new StringBuilder(); - string ups_sig = "[)>" + '\x1E' + "01" + '\x1D'; - bool ups = false; - int index1 = 0; - if (ups_sig == message.Substring(0, 7)) - { - ups = true; - index1 = 9; - } - int index2 = message.IndexOf('\x1D', index1); - string postalcode = message.Substring(index1, index2 - index1); - - index1 = index2 + 1; - index2 = message.IndexOf('\x1D', index1); - string countrycode = message.Substring(index1, index2 - index1); - string countrycode_bin = Convert.ToString(int.Parse(countrycode), 2); - if (countrycode_bin.Length > 10) - throw new BarcodeException("Ivalid MaxiCode mode"); - while (countrycode_bin.Length < 10) - countrycode_bin = countrycode_bin.Insert(0, "0"); - - index1 = index2 + 1; - index2 = message.IndexOf('\x1D', index1); - string CoS = message.Substring(index1, index2 - index1); - string CoS_bin = Convert.ToString(int.Parse(CoS), 2); - if (CoS_bin.Length > 10) - throw new BarcodeException("Ivalid MaxiCode mode"); - while (CoS_bin.Length < 10) - CoS_bin = CoS_bin.Insert(0, "0"); - - if (ups) - { - // must be set mode 2, if postal code is numeric, - // and mode 3, if postal code is alphanumeric - Options._maxiCodeMode = 2; - for (int i = 0; i < postalcode.Length; i++) - if (!char.IsDigit(postalcode[i])) - { - Options._maxiCodeMode = 3; - break; - } - } - else if (Options.MaxiCodeMode == 2) - { - for (int i = 0; i < postalcode.Length; i++) - if (!char.IsDigit(postalcode[i])) - throw new BarcodeException("Ivalid MaxiCode mode"); - } - - if (Options.MaxiCodeMode == 2) - { - binary_str.Append("0010"); // mode 2 - string postalcode_length = Convert.ToString(postalcode.Length, 2); - while (postalcode_length.Length < 6) - postalcode_length = postalcode_length.Insert(0, "0"); - string postalcode_bin2 = Convert.ToString(int.Parse(postalcode), 2); - while (postalcode_bin2.Length < 30) - postalcode_bin2 = postalcode_bin2.Insert(0, "0"); - binary_str.Insert(0, postalcode_bin2.Substring(28, 2));// 1..2 - binary_str.Append(postalcode_bin2.Substring(22, 6)); // 7..12 - binary_str.Append(postalcode_bin2.Substring(16, 6)); // 13..18 - binary_str.Append(postalcode_bin2.Substring(10, 6)); // 19..24 - binary_str.Append(postalcode_bin2.Substring(4, 6)); // 25..30 - binary_str.Append(postalcode_length.Substring(4, 2)); // 31..32 - binary_str.Append(postalcode_bin2.Substring(0, 4)); // 33..36 - binary_str.Append(countrycode_bin.Substring(8, 2)); // 37..38 - binary_str.Append(postalcode_length.Substring(0, 4)); // 39..42 - } - else - { - binary_str.Append("0011"); // mode 3, 3..6 - if (postalcode.Length > 6) - postalcode = postalcode.Substring(0, 6); - else - while (postalcode.Length < 6) - postalcode = postalcode.Insert(0, "0"); - string postalcode_bin3 = string.Empty; - for (int i = 0; i < postalcode.Length; i++) - { - SignCode sign = new SignCode(postalcode[i], codeSets.setA); - if (sign.CodeSet == codeSets.setA) - { - string bin = Convert.ToString(sign.SignValue, 2); - while (bin.Length < 6) - bin = bin.Insert(0, "0"); - postalcode_bin3 += bin; - } - } - - binary_str.Insert(0, postalcode_bin3.Substring(34, 2));// 1..2 - binary_str.Append(postalcode_bin3.Substring(28, 6)); // 7..12 - binary_str.Append(postalcode_bin3.Substring(22, 6)); // 13..18 - binary_str.Append(postalcode_bin3.Substring(16, 6)); // 19..24 - binary_str.Append(postalcode_bin3.Substring(10, 6)); // 25..30 - binary_str.Append(postalcode_bin3.Substring(4, 6)); // 31..36 - binary_str.Append(countrycode_bin.Substring(8, 2)); // 37..38 - binary_str.Append(postalcode_bin3.Substring(0, 4)); // 39..42 - } - binary_str.Append(countrycode_bin.Substring(2, 6)); // 43..48 - binary_str.Append(CoS_bin.Substring(6, 4)); // 49..52 - binary_str.Append(countrycode_bin.Substring(0, 2)); // 53..54 - binary_str.Append(CoS_bin.Substring(0, 6)); // 55..60 - - - - // first message - string message1 = binary_str.ToString(); - byte[] first_message = new byte[10]; - for (int i = 0; i < first_message.Length; i++) - { - first_message[i] = Convert.ToByte(message1.Substring(i * 6, 6), 2); - } - - // secondary message - string message2; - if (ups) - message2 = message.Substring(0, 9) + message.Substring(index2 + 1); - else - message2 = message.Substring(index2 + 1); - byte[] second_message = getCodewordsSequence(message2); - - byteList bytes = new byteList(); - bytes.AddRange(first_message); - bytes.AddRange(second_message); - return bytes.ToArray(); - - } - catch (Exception) - { - throw new BarcodeException("Ivalid Structured Carrier Message"); - } - } - - private byte[] getSymbolSigns(byte[] codewords) - { - // checking codewords size, it must be 94 or 78 - if (Options.MaxiCodeMode == 5 && codewords.Length != first_message_length + second_message_length - eec_length) - throw new BarcodeException("Ivalid MaxiCode mode"); - else if (Options.MaxiCodeMode != 5 && codewords.Length != first_message_length + second_message_length - sec_length) - throw new BarcodeException("Ivalid MaxiCode mode"); - - byteList bytes = new byteList(); - - // initial message - byte[] first_message = new byte[first_message_length]; - //first_message[0] = bytes[0]; - for (int i = 0; i < first_message_length; i++) - { - bytes.Add(codewords[i]); - first_message[i] = codewords[i]; - } - // calculating error correction codewords - RSCodec rs = new RSCodec(67); - int[] ecc = rs.Encode(first_message, first_message_length); - // and adding them to the end of the initial message - for (int i = ecc.Length - 1; i >= 0; i--) - bytes.Add((byte)ecc[i]); - - // second message - byteList odd_subset = new byteList(); - byteList even_subset = new byteList(); - for (int i = first_message_length; i < codewords.Length; i++) - { - bytes.Add(codewords[i]); - if ((i % 2) == 0) - even_subset.Add(codewords[i]); - else - odd_subset.Add(codewords[i]); - } - int ecclen; - if (Options.MaxiCodeMode == 5) - ecclen = eec_length / 2; - else - ecclen = sec_length / 2; - int[] ecc_odd = rs.Encode(odd_subset.ToArray(), ecclen); - int[] ecc_even = rs.Encode(even_subset.ToArray(), ecclen); - for (int i = ecc_odd.Length - 1; i >= 0; i--) - { - bytes.Add((byte)ecc_even[i]); - bytes.Add((byte)ecc_odd[i]); - } - //#if DEBUG - // for (int i = 0; i < bytes.Count; i++) - // { - // System.Diagnostics.Debug.Write(bytes[i].ToString() + ","); - // } - //#endif - - return bytes.ToArray(); - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - // - // / \ - // / \ - // / \ - // | | - // | | - // | | - // |______w______| t - // | | - // | | - // \ / | - // \ / | h - // \ /_____| - // - // R=t; h=t/2 - - if (Options.MaxiCodeMode > 6) - throw new BarcodeException("Ivalid MaxiCode mode"); - - SKSize drawingSize = new SKSize(); - byte[] bytes1; - if (Options.MaxiCodeMode == 2 || Options.MaxiCodeMode == 3) - bytes1 = getCodewordsSequence23(Value); - else - bytes1 = getCodewordsSequence(Value); - byte[] codewords = getSymbolSigns(bytes1); - if (Options.MaxiCodeEnableCustomWidth) - { - m_symbolWidth = NarrowBarWidth * 29; - m_symbolHeight = m_symbolWidth; - } - else - { - // Width of symbol from the center of the leftmost module to the center of the rightmost module = 25.5mm (24-27mm) - float dpiX = 96; - m_symbolWidth = (int)Math.Round(dpiX); - // height of symbol from the center of the top row to the center of the bottom row - //m_symbolHeight = (int)Math.Round(graphics.DpiY * 0.9556); - - // because used instead of m_symbolWidth for finding height of hexagon - float dpiY = 96f; - m_symbolHeight = (int)Math.Round(dpiY); - NarrowBarWidth = (int)Math.Round(m_symbolWidth / 29.0); - } - // width of hexagon - int w = NarrowBarWidth; - // width from center of the leftmost module to the center of the rightmost module - m_realSymbolWidth = w * 29; - - // Height of hexagon (radius of the inscribed circle, if DpiX==DpiY) - int R = (int)Math.Round(m_symbolHeight * 0.0398172599441121217); - int Y = (int)Math.Round(m_symbolHeight * 0.0298629449580840913); // Y = t + h = m_realSymbolWidth /sqrt(3)/29*3/2 - double dY; - if (m_symbolHeight < 150) - dY = m_symbolHeight * 0.0298629449580840913; - else - dY = Y; - - // "full" height from the top of the top row to the bottom of the bottom row - m_realSymbolHeight = (int)Math.Round(dY * 33.3333333); - dY = m_realSymbolHeight / 33.3333333; - - hexagonsList = new ArrayList(); - for (int i = 0; i < codewords.Length; i++) - { - StringBuilder sign = new StringBuilder(Convert.ToString(codewords[i], 2)); - while (sign.Length < 6) - { - sign.Insert(0, "0"); - } - // array with coordinates of hexagons for i sign of symbol - SKPoint[] mpoins = c_symbolMap[i]; - for (int j = 0; j < sign.Length; j++) - { - if (sign[j] == '1') - { - float x = w * mpoins[j].Y; - //int y = Y * mpoins[j].X; - float y = (int)Math.Round(dY * mpoins[j].X); - if (mpoins[j].X % 2 != 0) - x += (int)Math.Round(w * 0.5); - hexagonsList.Add(createHexagon(x, y, w, R)); // adding SKPoint[] - } - } - } - - for (int i = 0; i < c_orientationTemplate.Length; i++) - { - float x = w * c_orientationTemplate[i].Y; - //int y = Y * c_orientationTemplate[i].X; - float y = (float)Math.Round(dY * c_orientationTemplate[i].X); - if (c_orientationTemplate[i].X % 2 != 0) - x += (int)Math.Round(w * 0.5); - hexagonsList.Add(createHexagon(x, y, w, R)); // adding SKPoint[] - } - - drawingSize.Width = w * 30; - drawingSize.Height = m_realSymbolHeight; - - return drawingSize; - } - - protected override void drawBars(SKCanvas canvas, SKPaint paint, SKPoint position) - { - float strokeWidth = 1f; - SKColor strokeColor = paint.Color; // equivalent of pen using brush color - - // Draw all hexagons - for (int i = 0; i < hexagonsList.Count; i++) - { - var hexPoints = (SKPoint[])hexagonsList[i]; - DrawHexagon(canvas, hexPoints, paint.Color, strokeColor, strokeWidth, position); - } - - // Draw circles (you need a SkiaSharp version of drawCircles) - drawCircles(canvas, paint.Color, position); - } - - protected void DrawHexagon(SKCanvas canvas, SKPoint[] points, SKColor fillColor, SKColor strokeColor, float strokeWidth, SKPoint position) - { - // Offset all points by position - SKPoint[] offsetPoints = new SKPoint[points.Length]; - for (int i = 0; i < points.Length; i++) - { - offsetPoints[i] = new SKPoint(points[i].X + position.X, points[i].Y + position.Y); - } - - // Create path for polygon - using (var path = new SKPath()) - { - path.MoveTo(offsetPoints[0]); - for (int i = 1; i < offsetPoints.Length; i++) - path.LineTo(offsetPoints[i]); - path.Close(); - - // Fill polygon - using (var fillPaint = new SKPaint - { - Style = SKPaintStyle.Fill, - Color = fillColor, - IsAntialias = true - }) - { - canvas.DrawPath(path, fillPaint); - } - - // Draw polygon outline - using (var strokePaint = new SKPaint - { - Style = SKPaintStyle.Stroke, - Color = strokeColor, - StrokeWidth = strokeWidth, - IsAntialias = true - }) - { - canvas.DrawPath(path, strokePaint); - } - } - } - - protected void drawCircles(SKCanvas canvas, SKColor color, SKPoint position) - { - drawCircle(canvas, color, position, 3.29, 3.98, 3.61); - drawCircle(canvas, color, position, 5.04, 6.85, 5.81); - drawCircle(canvas, color, position, 10.81, 25, 15.09); - //drawCircle2(grfx, color, position, 3.29); - //drawCircle2(grfx, Color.White, position, 3.98); - //drawCircle2(grfx, color, position, 5.04); - //drawCircle2(grfx, Color.White, position, 6.85); - //drawCircle2(grfx, color, position, 10.81); - //drawCircle2(grfx, Color.White, position, 25); - } - - protected void drawCircle(SKCanvas canvas, SKColor color, SKPoint position, double r1, double r2, double r12) - { - // Calculate pen width - float penWidth = (float)Math.Round(m_realSymbolWidth * (1 / r1 - 1 / r2) * 0.5); - - bool oddWidth = m_realSymbolWidth % 2 != 0; - bool oddHeight = m_realSymbolHeight % 2 != 0; - - double diameter = m_realSymbolWidth / r12; - int Dx = (int)Math.Round(diameter); - int Dy = Dx; - - // Adjust Dx for odd/even width - if ((oddWidth && (Dx % 2 == 0)) || (!oddWidth && (Dx % 2 != 0))) - Dx--; - - int x = (int)((m_realSymbolWidth - Dx) * 0.5); - - // Adjust Dy for odd/even height - if ((oddHeight && (Dy % 2 == 0)) || (!oddHeight && (Dy % 2 != 0))) - Dy--; - - int y = (int)((m_realSymbolHeight - Dy) * 0.5); - - // Create paint for stroke - using (var paint = new SKPaint - { - Color = color, - Style = SKPaintStyle.Stroke, - StrokeWidth = penWidth, - IsAntialias = true - }) - { - // Draw oval - var rect = new SKRect( - x + position.X, - y + position.Y, - x + position.X + Dx, - y + position.Y + Dy - ); - - canvas.DrawOval(rect, paint); - } - } - - //protected void drawCircle2(Graphics grfx, Color color, SKPoint position, double r) - //{ - // Brush brush = new SolidBrush(color); - - // bool oddWidth = m_realSymbolWidth % 2 != 0; - // bool oddHeight = m_realSymbolHeight % 2 != 0; - // double diameter = m_realSymbolWidth / r; - // int Dx = (int)Math.Round(diameter); - - // if (m_realSymbolWidth < 205) - // { - // Dx--; - // } - - // int Dy = Dx; - // if ((oddWidth && (Dx % 2 == 0)) || (!oddWidth && (Dx % 2 != 0))) - // { - // Dx--; - // } - // int x = (int)((m_realSymbolWidth - Dx) * 0.5); - - // if ((oddHeight && (Dy % 2 == 0)) || (!oddHeight && (Dy % 2 != 0))) - // { - // Dy--; - // } - // int y = (int)((m_realSymbolHeight - Dy) * 0.5); - // grfx.FillEllipse(brush, x + position.X, y + position.Y, Dx, Dy); - //} - protected SKPoint[] createHexagon(float x, float y, float w, float v) - { - float r = w * 0.5f; // Radius of inscribed circle - float y1 = v * 0.25f; - float y2 = v * 0.75f; - - SKPoint[] hex; - if (((int)w) % 2 == 0) - hex = new SKPoint[8]; - else - hex = new SKPoint[6]; - - hex[0] = new SKPoint(x, y + y1); - - if (hex.Length == 6) - { - hex[1] = new SKPoint(x + r, y); - hex[2] = new SKPoint(x + w - 1, y + y1); - hex[3] = new SKPoint(x + w - 1, y + y2); - hex[4] = new SKPoint(x + r, y + v); - hex[5] = new SKPoint(x, y + y2); - } - else - { - hex[1] = new SKPoint(x + (float)Math.Floor(r - 1), y); - hex[2] = new SKPoint(x + (float)Math.Ceiling(r), y); - hex[3] = new SKPoint(x + w - 1, y + y1); - hex[4] = new SKPoint(x + w - 1, y + y2 - 1); - hex[5] = new SKPoint(x + (float)Math.Ceiling(r), y + v - 1); - hex[6] = new SKPoint(x + (float)Math.Floor(r - 1), y + v - 1); - hex[7] = new SKPoint(x, y + y2 - 1); - } - - return hex; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/PDF417MicroSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/PDF417MicroSymbology.cs deleted file mode 100644 index 46818943..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/PDF417MicroSymbology.cs +++ /dev/null @@ -1,636 +0,0 @@ -using System; -using System.Text; -using System.Collections; - -namespace BarcodeWriter.Core.Internal -{ - class PDF417MicroSymbology : PDF417Symbology - { - private int m_symbolVariant; - - /// - /// Initializes a new instance of the class. - /// - public PDF417MicroSymbology() - : base() - { - m_type = TrueSymbologyType.MicroPDF417; - Options.PDF417CreateMacro = false; - } - - public PDF417MicroSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.MicroPDF417; - Options.PDF417CreateMacro = false; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Micro PDF417 symbology allows a maximum data size of 250 characters (if all characters are uppercase), or 150 ASCII characters, or 366 digits.\n"; - } - - protected override void encodeData() - { - if (Options.PDF417CompactionMode == PDF417CompactionMode.Auto) - { - splitTextIntoChunks(); - optimizeChunks(); - } - - produceCodewords(); - - if (m_codewords.Count > 126) - throw new BarcodeException("Input string is too long for Micro PDF 417 barcode."); - - if (!Options.PDF417UseManualSize) - findSymbolVariant(); - else - findSymbolVariantForManualSize(Options.PDF417RowCount); - - m_dataColumnCount = m_microVariants[m_symbolVariant]; - int rows = m_microVariants[m_symbolVariant + 34]; - int checkCodewordsCount = m_microVariants[m_symbolVariant + 68]; - int paddingLength = (m_dataColumnCount * rows) - checkCodewordsCount - m_codewords.Count; - if (paddingLength < 0) - throw new BarcodeException("Input string is too long for specified number of data columns and rows."); - - int coeffOffset = m_microVariants[m_symbolVariant + 102]; - - addPadding(paddingLength); - addCheckCodewords(checkCodewordsCount, coeffOffset); - - encodeRows(rows); - } - - protected override void splitTextIntoChunks() - { - // see MicroPDF spec, Annex N, Algorithm to minimise the number of codewords - // for some hints about algorithm below - int numThreshold = 13; - int textTreshold = 5; - - m_chunks.Clear(); - - for (int textPos = 0; textPos < m_text.Length; ) - { - int numLength = 0; - for (int i = textPos; i < m_text.Length; i++, numLength++) - { - char c = m_text[i]; - if (c < '0' || c > '9') - break; - } - - if (numLength >= numThreshold) - { - m_chunks.Add(new EncoderChunk(EncoderMode.Numeric, numLength)); - textPos += numLength; - continue; - } - else - { - int textLength = numLength; - numLength = 0; - - for (int i = textPos + textLength; i < m_text.Length; i++, textLength++) - { - char c = m_text[i]; - if (c >= '0' && c <= '9') - numLength++; - else - numLength = 0; - - if (numLength >= numThreshold) - { - textLength -= numLength - 1; - break; - } - - if ((c < ' ' || c > '~') && c != '\t' && c != '\n' && c != '\r') - break; - } - - if (textLength >= textTreshold) - { - m_chunks.Add(new EncoderChunk(EncoderMode.Text, textLength)); - textPos += textLength; - continue; - } - else - { - int byteLength = textLength; - textLength = 0; - - for (int i = textPos + byteLength; i < m_text.Length; i++, byteLength++) - { - char c = m_text[i]; - if (c >= ' ' && c <= '~') - textLength++; - else - textLength = 0; - - if (textLength >= textTreshold) - { - byteLength -= textLength - 1; - break; - } - } - - m_chunks.Add(new EncoderChunk(EncoderMode.Byte, byteLength)); - textPos += byteLength; - } - } - } - } - - private void findSymbolVariant() - { - if (m_dataColumnCount > 4) - m_dataColumnCount = 4; - - m_symbolVariant = 0; - - // check if data fits specified column count. - // if it doesn't, then reset data column count. - // it will be auto-detected later. - - if ((m_dataColumnCount == 1) && (m_codewords.Count > 20)) - m_dataColumnCount = 0; - else if ((m_dataColumnCount == 2) && (m_codewords.Count > 37)) - m_dataColumnCount = 0; - else if ((m_dataColumnCount == 3) && (m_codewords.Count > 82)) - m_dataColumnCount = 0; - - if (m_dataColumnCount == 1) - { - m_symbolVariant = 6; - - if (m_codewords.Count <= 16) - m_symbolVariant = 5; - if (m_codewords.Count <= 12) - m_symbolVariant = 4; - if (m_codewords.Count <= 10) - m_symbolVariant = 3; - if (m_codewords.Count <= 7) - m_symbolVariant = 2; - if (m_codewords.Count <= 4) - m_symbolVariant = 1; - } - else if (m_dataColumnCount == 2) - { - m_symbolVariant = 13; - - if (m_codewords.Count <= 33) - m_symbolVariant = 12; - if (m_codewords.Count <= 29) - m_symbolVariant = 11; - if (m_codewords.Count <= 24) - m_symbolVariant = 10; - if (m_codewords.Count <= 19) - m_symbolVariant = 9; - if (m_codewords.Count <= 13) - m_symbolVariant = 8; - if (m_codewords.Count <= 8) - m_symbolVariant = 7; - } - else if (m_dataColumnCount == 3) - { - m_symbolVariant = 23; - - if (m_codewords.Count <= 70) - m_symbolVariant = 22; - if (m_codewords.Count <= 58) - m_symbolVariant = 21; - if (m_codewords.Count <= 46) - m_symbolVariant = 20; - if (m_codewords.Count <= 34) - m_symbolVariant = 19; - if (m_codewords.Count <= 24) - m_symbolVariant = 18; - if (m_codewords.Count <= 18) - m_symbolVariant = 17; - if (m_codewords.Count <= 14) - m_symbolVariant = 16; - if (m_codewords.Count <= 10) - m_symbolVariant = 15; - if (m_codewords.Count <= 6) - m_symbolVariant = 14; - } - else if (m_dataColumnCount == 4) - { - m_symbolVariant = 34; - - if (m_codewords.Count <= 108) - m_symbolVariant = 33; - if (m_codewords.Count <= 90) - m_symbolVariant = 32; - if (m_codewords.Count <= 72) - m_symbolVariant = 31; - if (m_codewords.Count <= 54) - m_symbolVariant = 30; - if (m_codewords.Count <= 39) - m_symbolVariant = 29; - if (m_codewords.Count <= 30) - m_symbolVariant = 28; - if (m_codewords.Count <= 24) - m_symbolVariant = 27; - if (m_codewords.Count <= 18) - m_symbolVariant = 26; - if (m_codewords.Count <= 12) - m_symbolVariant = 25; - if (m_codewords.Count <= 8) - m_symbolVariant = 24; - } - - if (m_symbolVariant == 0) - { - // no selection was made until this point. - // so, we should auto-detect symbol variant - for (int i = 27; i >= 0; i--) - { - if (m_microAutosize[i] >= m_codewords.Count) - m_symbolVariant = m_microAutosize[i + 28]; - } - } - - m_symbolVariant--; - } - - private void findSymbolVariantForManualSize(int rowCount) - { - if (m_dataColumnCount > 4) - throw new BarcodeException("Micro PDF417 barcode allows no more than 4 data columns."); - - if (m_dataColumnCount == 0 || rowCount == 0) - throw new BarcodeException("Incorrect size for Micro PDF417 barcode."); - - m_symbolVariant = 0; - - if (m_dataColumnCount == 1) - { - if (rowCount == 11) - m_symbolVariant = 1; - else if (rowCount == 14) - m_symbolVariant = 2; - else if (rowCount == 17) - m_symbolVariant = 3; - else if (rowCount == 20) - m_symbolVariant = 4; - else if (rowCount == 24) - m_symbolVariant = 5; - else if (rowCount == 28) - m_symbolVariant = 6; - else - throw new BarcodeException("Invalid row count. Should be 11, 14, 17, 20, 24 or 28 when column count is 1."); - } - else if (m_dataColumnCount == 2) - { - if (rowCount == 8) - m_symbolVariant = 7; - else if (rowCount == 11) - m_symbolVariant = 8; - else if (rowCount == 14) - m_symbolVariant = 9; - else if (rowCount == 17) - m_symbolVariant = 10; - else if (rowCount == 20) - m_symbolVariant = 11; - else if (rowCount == 23) - m_symbolVariant = 12; - else if (rowCount == 26) - m_symbolVariant = 13; - else - throw new BarcodeException("Invalid row count. Should be 8, 11, 14, 17, 20, 23 or 26 when column count is 2."); - } - else if (m_dataColumnCount == 3) - { - if (rowCount == 6) - m_symbolVariant = 14; - else if (rowCount == 8) - m_symbolVariant = 15; - else if (rowCount == 10) - m_symbolVariant = 16; - else if (rowCount == 12) - m_symbolVariant = 17; - else if (rowCount == 15) - m_symbolVariant = 18; - else if (rowCount == 20) - m_symbolVariant = 19; - else if (rowCount == 26) - m_symbolVariant = 20; - else if (rowCount == 32) - m_symbolVariant = 21; - else if (rowCount == 38) - m_symbolVariant = 22; - else if (rowCount == 44) - m_symbolVariant = 23; - else - throw new BarcodeException("Invalid row count. Should be 6, 8, 10, 12, 15, 20, 26, 32, 38 or 44 when column count is 3."); - } - else if (m_dataColumnCount == 4) - { - if (rowCount == 4) - m_symbolVariant = 24; - else if (rowCount == 6) - m_symbolVariant = 25; - else if (rowCount == 8) - m_symbolVariant = 26; - else if (rowCount == 10) - m_symbolVariant = 27; - else if (rowCount == 12) - m_symbolVariant = 28; - else if (rowCount == 15) - m_symbolVariant = 29; - else if (rowCount == 20) - m_symbolVariant = 30; - else if (rowCount == 26) - m_symbolVariant = 31; - else if (rowCount == 32) - m_symbolVariant = 32; - else if (rowCount == 38) - m_symbolVariant = 33; - else if (rowCount == 44) - m_symbolVariant = 34; - else - throw new BarcodeException("Invalid row count. Should be 4, 6, 8, 10, 12, 15, 20, 26, 32, 38 or 44 when column count is 4."); - } - - m_symbolVariant--; - } - - private void addPadding(int paddingLength) - { - while (paddingLength > 0) - { - m_codewords.Add(900); - paddingLength--; - } - } - - protected void addCheckCodewords(int checkCodewordsCount, int coeffOffset) - { - int[] rsSymbols = new int[50]; - for (int i = 0; i < 50; i++) - rsSymbols[i] = 0; - - for (int i = 0; i < m_codewords.Count; i++) - { - int total = (m_codewords[i] + rsSymbols[checkCodewordsCount - 1]) % 929; - for (int j = checkCodewordsCount - 1; j >= 0; j--) - { - if (j == 0) - rsSymbols[j] = (929 - (total * m_microCoeffs[coeffOffset + j]) % 929) % 929; - else - rsSymbols[j] = (rsSymbols[j - 1] + 929 - (total * m_microCoeffs[coeffOffset + j]) % 929) % 929; - } - } - - for (int j = 0; j < checkCodewordsCount; j++) - { - if (rsSymbols[j] != 0) - rsSymbols[j] = 929 - rsSymbols[j]; - } - - for (int i = checkCodewordsCount - 1; i >= 0; i--) - m_codewords.Add(rsSymbols[i]); - } - - private void encodeRows(int rows) - { - m_encodedData = new string[rows]; - - int leftRAP = m_rapTable[m_symbolVariant]; - int centerRAP = m_rapTable[m_symbolVariant + 34]; - int rightRAP = m_rapTable[m_symbolVariant + 68]; - - // cluster can be 0, 1 or 2 for cluster(0), cluster(3) and cluster(6) - int cluster = m_rapTable[m_symbolVariant + 102] / 3; - - for (int row = 0; row < rows; row++) - { - string alphaPattern = buildAlphaPattern(row, leftRAP, centerRAP, rightRAP, cluster); - m_encodedData[row] = buildPattern(alphaPattern); - - leftRAP++; - if (leftRAP == 53) - leftRAP = 1; - - centerRAP++; - if (centerRAP == 53) - centerRAP = 1; - - rightRAP++; - if (rightRAP == 53) - rightRAP = 1; - - cluster++; - if (cluster == 3) - cluster = 0; - } - } - - private string buildAlphaPattern(int row, int leftRAP, int centerRAP, int rightRAP, int cluster) - { - int offset = 929 * cluster; - - int[] dummy = new int[5]; - for (int i = 0; i < m_dataColumnCount; i++) - dummy[i + 1] = m_codewords[row * m_dataColumnCount + i]; - - StringBuilder pattern = new StringBuilder(); - pattern.Append(m_leftRightRAP[leftRAP]); - pattern.Append("1"); - pattern.Append(m_alphaPatternTable[offset + dummy[1]]); - pattern.Append("1"); - - if (m_dataColumnCount == 3) - pattern.Append(m_centerRAP[centerRAP]); - - if (m_dataColumnCount >= 2) - { - pattern.Append("1"); - pattern.Append(m_alphaPatternTable[offset + dummy[2]]); - pattern.Append("1"); - } - - if (m_dataColumnCount == 4) - pattern.Append(m_centerRAP[centerRAP]); - - if (m_dataColumnCount >= 3) - { - pattern.Append("1"); - pattern.Append(m_alphaPatternTable[offset + dummy[3]]); - pattern.Append("1"); - } - - if (m_dataColumnCount == 4) - { - pattern.Append("1"); - pattern.Append(m_alphaPatternTable[offset + dummy[4]]); - pattern.Append("1"); - } - - pattern.Append(m_leftRightRAP[rightRAP]); - pattern.Append("1"); - return pattern.ToString(); - } - - private static string buildPattern(string alphaPattern) - { - bool setOne = true; - StringBuilder pattern = new StringBuilder(); - for (int loop = 0; loop < alphaPattern.Length; loop++) - { - if ((alphaPattern[loop] >= '0') && (alphaPattern[loop] <= '9')) - { - for (int k = 0; k < ctoi(alphaPattern[loop]); k++) - { - if (setOne) - pattern.Append('1'); - else - pattern.Append('0'); - } - - setOne = !setOne; - } - else - { - for (int i = 0; i < m_patternAlphabet.Length; i++) - { - if (alphaPattern[loop] == m_patternAlphabet[i]) - pattern.Append(m_barsForAlpha[i]); - } - } - } - - return pattern.ToString(); - } - - ////////////////////////////////////////////////////////////////////////// - // - // Data part - // - ////////////////////////////////////////////////////////////////////////// - - // Automatic sizing table - static int[] m_microAutosize = - { - 4, 6, 7, 8, 10, 12, 13, 14, 16, 18, 19, 20, 24, 29, 30, 33, 34, 37, - 39, 46, 54, 58, 70, 72, 82, 90, 108, 126, 1, 14, 2, 7, 3, 25, 8, 16, - 5, 17, 9, 6, 10, 11, 28, 12, 19, 13, 29, 20, 30, 21, 22, 31, 23, 32, - 33, 34 - }; - - // rows, columns, check codewords, coefficiet offset of valid MicroPDF417 - // sizes from ISO/IEC 24728:2006 - static int[] m_microVariants = - { - 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 11, 14, 17, 20, 24, 28, 8, 11, 14, - 17, 20, 23, 26, 6, 8, 10, 12, 15, 20, 26, 32, 38, 44, 4, 6, 8, 10, - 12, 15, 20, 26, 32, 38, 44, 7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 11, 13, - 15, 12, 14, 16, 18, 21, 26, 32, 38, 44, 50, 8, 12, 14, 16, 18, 21, - 26, 32, 38, 44, 50, 0, 0, 0, 7, 7, 7, 7, 15, 15, 24, 34, 57, 84, 45, - 70, 99, 115, 133, 154, 180, 212, 250, 294, 7, 45, 70, 99, 115, 133, - 154, 180, 212, 250, 294 - }; - - // MicroPDF417 coefficients from ISO/IEC 24728:2006 Annex F - static int[] m_microCoeffs = - { - /* k = 7 */ - 76, 925, 537, 597, 784, 691, 437, - /* k = 8 */ - 237, 308, 436, 284, 646, 653, 428, 379, - /* k = 9 */ - 567, 527, 622, 257, 289, 362, 501, 441, 205, - /* k = 10 */ - 377, 457, 64, 244, 826, 841, 818, 691, 266, 612, - /* k = 11 */ - 462, 45, 565, 708, 825, 213, 15, 68, 327, 602, 904, - /* k = 12 */ - 597, 864, 757, 201, 646, 684, 347, 127, 388, 7, 69, 851, - /* k = 13 */ - 764, 713, 342, 384, 606, 583, 322, 592, 678, 204, 184, 394, 692, - /* k = 14 */ - 669, 677, 154, 187, 241, 286, 274, 354, 478, 915, 691, 833, 105, 215, - /* k = 15 */ - 460, 829, 476, 109, 904, 664, 230, 5, 80, 74, 550, 575, 147, 868, 642, - /* k = 16 */ - 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, - 176, 65, - /* k = 18 */ - 279, 577, 315, 624, 37, 855, 275, 739, 120, 297, 312, 202, 560, 321, - 233, 756, 760, 573, - /* k = 21 */ - 108, 519, 781, 534, 129, 425, 681, 553, 422, 716, 763, 693, 624, 610, - 310, 691, 347, 165, 193, 259, 568, - /* k = 26 */ - 443, 284, 887, 544, 788, 93, 477, 760, 331, 608, 269, 121, 159, 830, - 446, 893, 699, 245, 441, 454, 325, 858, 131, 847, 764, 169, - /* k = 32 */ - 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, - 193, 517, 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, - 685, 330, 63, 410, - /* k = 38 */ - 234, 228, 438, 848, 133, 703, 529, 721, 788, 322, 280, 159, 738, 586, - 388, 684, 445, 680, 245, 595, 614, 233, 812, 32, 284, 658, 745, 229, - 95, 689, 920, 771, 554, 289, 231, 125, 117, 518, - /* k = 44 */ - 476, 36, 659, 848, 678, 64, 764, 840, 157, 915, 470, 876, 109, 25, - 632, 405, 417, 436, 714, 60, 376, 97, 413, 706, 446, 21, 3, 773, 569, - 267, 272, 213, 31, 560, 231, 758, 103, 271, 572, 436, 339, 730, 82, 285, - /* k = 50 */ - 923, 797, 576, 875, 156, 706, 63, 81, 257, 874, 411, 416, 778, 50, - 205, 303, 188, 535, 909, 155, 637, 230, 534, 96, 575, 102, 264, 233, - 919, 593, 865, 26, 579, 623, 766, 146, 10, 739, 246, 127, 71, 244, - 211, 477, 920, 876, 427, 820, 718, 435 - }; - - // Left RAP (Row Address Pattern), Center RAP, Right RAP and Start Cluster - // from ISO/IEC 24728:2006 tables 10, 11 and 12 - static int[] m_rapTable = - { - 1, 8, 36, 19, 9, 25, 1, 1, 8, 36, 19, 9, 27, 1, 7, 15, 25, 37, 1, 1, - 21, 15, 1, 47, 1, 7, 15, 25, 37, 1, 1, 21, 15, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1, 7, 15, 25, 37, 17, 9, 29, 31, 25, 19, 1, 7, - 15, 25, 37, 17, 9, 29, 31, 25, 9, 8, 36, 19, 17, 33, 1, 9, 8, 36, 19, - 17, 35, 1, 7, 15, 25, 37, 33, 17, 37, 47, 49, 43, 1, 7, 15, 25, 37, - 33, 17, 37, 47, 49, 0, 3, 6, 0, 6, 0, 0, 0, 3, 6, 0, 6, 6, 0, 0, 6, - 0, 0, 0, 0, 6, 6, 0, 3, 0, 0, 6, 0, 0, 0, 0, 6, 6, 0 - }; - - // Left and Right Row Address Pattern from Table 2 - static string[] m_leftRightRAP = - { - "", "221311", "311311", "312211", "222211", "213211", "214111", - "223111", "313111", "322111", "412111", "421111", "331111", "241111", - "232111", "231211", "321211", "411211", "411121", "411112", "321112", - "312112", "311212", "311221", "311131", "311122", "311113", "221113", - "221122", "221131", "221221", "222121", "312121", "321121", "231121", - "231112", "222112", "213112", "212212", "212221", "212131", "212122", - "212113", "211213", "211123", "211132", "211141", "211231", "211222", - "211312", "211321", "211411", "212311" - }; - - // Center Row Address Pattern from Table 2 - static string[] m_centerRAP = - { - "", "112231", "121231", "122131", "131131", "131221", "132121", - "141121", "141211", "142111", "133111", "132211", "131311", "122311", - "123211", "124111", "115111", "114211", "114121", "123121", "123112", - "122212", "122221", "121321", "121411", "112411", "113311", "113221", - "113212", "113122", "122122", "131122", "131113", "122113", "113113", - "112213", "112222", "112312", "112321", "111421", "111331", "111322", - "111232", "111223", "111133", "111124", "111214", "112114", "121114", - "121123", "121132", "112132", "112141" - }; - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/PDF417Symbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/PDF417Symbology.cs deleted file mode 100644 index 2186b267..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/PDF417Symbology.cs +++ /dev/null @@ -1,1751 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using System.Drawing; -using System.Collections; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - class PDF417Symbology : SymbologyDrawing2D - { - /// - /// Encoder compaction mode (some modes are more suitable for some data) - /// - protected enum EncoderMode - { - /// - /// Mode undefined - /// - Undefined = 0, - - /// - /// Text compaction mode (allows optimized text encoding) - /// - Text = 900, - - /// - /// Byte compaction mode (allows optimized bytes encoding) - /// - Byte = 901, - - /// - /// Numeric compaction mode (allows optimized encoding of numbers) - /// - Numeric = 902 - } - - /// - /// Contains information about one chunk of a string being encoded - /// - protected class EncoderChunk - { - /// - /// Encoder mode to use for chunk - /// - public EncoderMode mode; - - /// - /// Number of symbols to encode - /// - public int length; - - /// - /// Initializes a new instance of the class. - /// - /// The encoder mode to use for chunk - /// The number of symbols to encode. - public EncoderChunk(EncoderMode mode, int length) - { - this.mode = mode; - this.length = length; - } - } - - /// - /// Encoder chunks. - /// First chunk start from first symbol of a string being encoded. - /// Next chunk start immediately after first and so on. - /// - protected ArrayList m_chunks = new ArrayList(); - - protected intList m_codewords; - - protected bool m_compact; - public int m_errorCorrectionLevel = -1; - public int m_dataColumnCount; - - private const int c_maxCodewordsCount = 928; - public string[] m_encodedData; - protected string m_text; - - /// - /// Initializes a new instance of the class. - /// - public PDF417Symbology() - : base(TrueSymbologyType.PDF417) - { - } - - public PDF417Symbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.PDF417) - { - } - - /// - /// Validates the value using PDF417 symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "PDF417 symbology allows a maximum data size of 1850 text characters, or 2710 digits.\n"; - } - - /// - /// Gets the barcode value encoded using PDF417 symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using PDF417 symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - if (forCaption) - return Value; - - return ""; - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// The encoding pattern for given character. - protected override string getCharPattern(char c) - { - return ""; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - - // a bit weird cycle goes here, but we need it, - // or we can end up with 2-byte chars which is unsupported - StringBuilder sb = new StringBuilder(); - - // get encoding used in options - Encoding tmpEncoding = Options.Encoding; - - /* - // detect Unicode and auto switch to UTF-8 ? - - bool UnicodeSymbolFound = !Utils.IsLatinISOEncodingOnly(Value); - - // if we are not with encoding UTF8 - // then we are forcing to use UTF8 - if (UnicodeSymbolFound && tmpEncoding != Encoding.UTF8) - { - // force encoding to UTF8 - tmpEncoding = Encoding.UTF8; - } - */ - - - byte[] bytes = tmpEncoding.GetBytes(Value); - - // after inserting this code RussianText UnitTest does not pass (#320 Commit: 334 and #352 Commit: 368) - /* - byte[] bytes = new byte[Value.Length]; - - int index = 0; - foreach (char c in Value.ToCharArray()) - { - bytes[index++] = (byte)c; - } - */ - // after inserting this code RussianText UnitTest does not pass (#320 Commit: 334 and #352 Commit: 368) - - for (int i = 0; i < bytes.Length; i++) - sb.Append((char)bytes[i]); - - m_text = sb.ToString(); - - if (Options.PDF417UseManualSize) - m_dataColumnCount = Options.PDF417ColumnCount; - else - m_dataColumnCount = Options.PDF417MinimumColumnCount; - - m_errorCorrectionLevel = (int)Options.PDF417ErrorCorrectionLevel; - - if (Options.PDF417CompactionMode != PDF417CompactionMode.Auto) - validateCompactionMode(Options.PDF417CompactionMode); - - encodeData(); - - int symbolwidth = 0; - if (m_encodedData.Length != 0) - symbolwidth = m_encodedData[0].Length; - - drawingSize.Width = symbolwidth * NarrowBarWidth; - drawingSize.Height = m_encodedData.Length * BarHeight; - - for (int x = 0; x < symbolwidth; x++) - { - for (int y = 0; y < m_encodedData.Length; y++) - { - if (m_encodedData[y][x] == '1') - m_rects.Add(new SKRect(x * NarrowBarWidth, y * BarHeight, x * NarrowBarWidth+NarrowBarWidth, y * BarHeight+BarHeight)); - } - } - - return drawingSize; - } - - protected virtual void encodeData() - { - if (Options.PDF417CompactionMode == PDF417CompactionMode.Auto) - { - splitTextIntoChunks(); - optimizeChunks(); - } - - produceCodewords(); - adjustErrorCorrectionLevel(); - - int checkCodewordsCount = calculateCheckCodewordsCount(); - - if (!Options.PDF417UseManualSize) - { - adjustDataColumnCount(checkCodewordsCount); - checkBounds(checkCodewordsCount, -1); - addPaddingIfNeeded(checkCodewordsCount); - } - else - { - checkBounds(checkCodewordsCount, Options.PDF417RowCount); - addPaddingIfNeededForManualSize(checkCodewordsCount, Options.PDF417RowCount); - } - - // we should add the length descriptor - m_codewords.Insert(0, m_codewords.Count + 1); - - addCheckCodewords(checkCodewordsCount); - - // following integers (c1, c2, c3) are precalculated - // values for left|right codewords. - // which value to use depends on current row encoding table - - int c1 = (m_codewords.Count / m_dataColumnCount - 1) / 3; - int c2 = m_errorCorrectionLevel * 3 + (m_codewords.Count / m_dataColumnCount - 1) % 3; - int c3 = m_dataColumnCount - 1; - - int rows = (m_codewords.Count / m_dataColumnCount); - m_encodedData = new string[rows]; - encodeRows(c1, c2, c3); - } - - private void encodeRows(int c1, int c2, int c3) - { - for (int row = 0; row < (m_codewords.Count / m_dataColumnCount); row++) - { - int[] rowCodewords = fillRowCodewords(row, c1, c2, c3); - string alphaPattern = buildAlphaPattern(row, rowCodewords); - - StringBuilder pattern = new StringBuilder(); - for (int i = 0; i < alphaPattern.Length; i++) - { - for (int j = 0; j < m_patternAlphabet.Length; j++) - { - if (alphaPattern[i] == m_patternAlphabet[j]) - { - pattern.Append(m_barsForAlpha[j]); - break; - } - } - } - - m_encodedData[row] = pattern.ToString(); - } - } - - private string buildAlphaPattern(int row, int[] rowCodewords) - { - // Start with a start char and a separator - StringBuilder alphaPattern = new StringBuilder(); - alphaPattern.Append("+*"); - - // truncated symbol differs from ordinary PDF417 symbol by last 5 chars - int stopIndex = m_dataColumnCount + 1; - if (m_compact) - stopIndex--; - - for (int i = 0; i <= stopIndex; i++) - { - int offset = 0; // cluster 0 - switch (row % 3) - { - case 1: - // cluster 3 - offset = 929; - break; - - case 2: - // cluster 6 - offset = 1858; - break; - } - - alphaPattern.Append(m_alphaPatternTable[offset + rowCodewords[i]]); - alphaPattern.Append("*"); - } - - if (!m_compact) - alphaPattern.Append("-"); - - return alphaPattern.ToString(); - } - - private int[] fillRowCodewords(int row, int c1, int c2, int c3) - { - int[] rowCodewords = new int[m_dataColumnCount + 2]; - for (int i = 0; i < m_dataColumnCount; i++) - rowCodewords[i + 1] = m_codewords[row * m_dataColumnCount + i]; - - int rowCheckCodewordsCount = (row / 3) * 30; - switch (row % 3) - { - /* follows this pattern from US Patent 5,243,655: - Row 0: L0 (row #, # of rows) R0 (row #, # of columns) - Row 1: L1 (row #, security level) R1 (row #, # of rows) - Row 2: L2 (row #, # of columns) R2 (row #, security level) - Row 3: L3 (row #, # of rows) R3 (row #, # of columns) - etc. - */ - - case 0: - rowCodewords[0] = rowCheckCodewordsCount + c1; - rowCodewords[m_dataColumnCount + 1] = rowCheckCodewordsCount + c3; - break; - - case 1: - rowCodewords[0] = rowCheckCodewordsCount + c2; - rowCodewords[m_dataColumnCount + 1] = rowCheckCodewordsCount + c1; - break; - - case 2: - rowCodewords[0] = rowCheckCodewordsCount + c3; - rowCodewords[m_dataColumnCount + 1] = rowCheckCodewordsCount + c2; - break; - } - - return rowCodewords; - } - - protected void addCheckCodewords(int checkCodewordsCount) - { - int[] correctionCodewords = calculateReedSolomonCodes(checkCodewordsCount); - for (int i = checkCodewordsCount - 1; i >= 0; i--) - m_codewords.Add(correctionCodewords[i]); - } - - private int[] calculateReedSolomonCodes(int checkCodewordsCount) - { - int offset = 0; - switch (m_errorCorrectionLevel) - { - case 1: - offset = 2; - break; - - case 2: - offset = 6; - break; - - case 3: - offset = 14; - break; - - case 4: - offset = 30; - break; - - case 5: - offset = 62; - break; - - case 6: - offset = 126; - break; - - case 7: - offset = 254; - break; - - case 8: - offset = 510; - break; - } - - int[] correctionCodewords = new int[checkCodewordsCount]; - Array.Clear(correctionCodewords, 0, correctionCodewords.Length); - - for (int i = 0; i < m_codewords.Count; i++) - { - int total = (m_codewords[i] + correctionCodewords[checkCodewordsCount - 1]) % 929; - for (int j = checkCodewordsCount - 1; j >= 0; j--) - { - if (j == 0) - correctionCodewords[j] = (929 - (total * m_errorCorrectionCoefficients[offset + j]) % 929) % 929; - else - correctionCodewords[j] = (correctionCodewords[j - 1] + 929 - (total * m_errorCorrectionCoefficients[offset + j]) % 929) % 929; - } - } - - for (int i = 0; i < checkCodewordsCount; i++) - { - if (correctionCodewords[i] != 0) - correctionCodewords[i] = 929 - correctionCodewords[i]; - } - - return correctionCodewords; - } - - private void addPaddingIfNeeded(int checkCodewordsCount) - { - int totalCodewordsCount = m_codewords.Count + 1 + checkCodewordsCount; - - int paddingLength = 0; - if ((totalCodewordsCount / m_dataColumnCount) < 3) - { - // a barcode must have at least three rows - paddingLength = (m_dataColumnCount * 3) - totalCodewordsCount; - } - else - { - if ((totalCodewordsCount % m_dataColumnCount) > 0) - paddingLength = m_dataColumnCount - (totalCodewordsCount % m_dataColumnCount); - } - - while (paddingLength > 0) - { - m_codewords.Add(900); - paddingLength--; - } - } - - private void addPaddingIfNeededForManualSize(int checkCodewordsCount, int rowCount) - { - // if row count is zero or less - // auto calculate row count - if (rowCount <= 0 && m_dataColumnCount > 0) - { - rowCount = (m_codewords.Count + checkCodewordsCount) / m_dataColumnCount + 1; - } - - - int totalCodewordsCount = m_codewords.Count + 1 + checkCodewordsCount; - int paddingLength = m_dataColumnCount * rowCount - totalCodewordsCount; - if (paddingLength < 0) - throw new BarcodeException(String.Format("Input string is too long for specified number of data columns and rows. Row count provided = {0}, data columns count = {1}", rowCount, m_dataColumnCount)); - - for (int i = 0; i < paddingLength; i++) - m_codewords.Add(900); - } - - private void checkBounds(int checkCodewordsCount, int rowCount) - { - // if row count is zero or less - // auto calculate row count - if (rowCount <= 0 && m_dataColumnCount > 0) - { - rowCount = (m_codewords.Count + checkCodewordsCount) / m_dataColumnCount + 1; - } - - if (m_codewords.Count + checkCodewordsCount > c_maxCodewordsCount) - throw new BarcodeException("Input string is too long for PDF 417 barcode."); - - // check if row count or column count exceeds allowed - if (m_dataColumnCount == 0 || rowCount == 0 || m_dataColumnCount > 30 || rowCount > 90) - throw new BarcodeException(String.Format("Incorrect size. PDF417 barcode should contain 1..90 data rows and 1..20 data columns. Row count provided = {0}, data columns count = {1}", rowCount, m_dataColumnCount)); - - // check if row count exceeds required - //if (((m_codewords.Count + checkCodewordsCount) / m_dataColumnCount) > 90) - // throw new BarcodeException("Input string is too long for specified number of data columns."); - } - - private void adjustDataColumnCount(int checkCodewordsCount) - { - if (m_dataColumnCount > 30) - m_dataColumnCount = 30; - - if (m_dataColumnCount < 1) - m_dataColumnCount = (int)(0.5f + Math.Sqrt((m_codewords.Count + checkCodewordsCount) / 3.0f)); - - if (((m_codewords.Count + checkCodewordsCount) / m_dataColumnCount) > 90) - { - // prevent too tall columns - m_dataColumnCount++; - } - } - - private int calculateCheckCodewordsCount() - { - int checkCodewordsCount = 1; - for (int i = 1; i <= (m_errorCorrectionLevel + 1); i++) - checkCodewordsCount *= 2; - - return checkCodewordsCount; - } - - private void adjustErrorCorrectionLevel() - { - if (m_errorCorrectionLevel < 0) - { - // error correction level 8 is never used automatically - m_errorCorrectionLevel = 7; - - if (m_codewords.Count <= 1280) - m_errorCorrectionLevel = 6; - - if (m_codewords.Count <= 640) - m_errorCorrectionLevel = 5; - - if (m_codewords.Count <= 320) - m_errorCorrectionLevel = 4; - - if (m_codewords.Count <= 160) - m_errorCorrectionLevel = 3; - - if (m_codewords.Count <= 40) - m_errorCorrectionLevel = 2; - } - } - - protected void produceCodewords() - { - int textPos = 0; - m_codewords = new intList(); - for (int i = 0; i < m_chunks.Count; i++) - { - switch ((m_chunks[i] as EncoderChunk).mode) - { - case EncoderMode.Text: - processTextChunk(textPos, i); - break; - - case EncoderMode.Byte: - processByteChunk(textPos, i); - break; - - case EncoderMode.Numeric: - processNumberChunk(textPos, i); - break; - } - - textPos += (m_chunks[i] as EncoderChunk).length; - } - - if (Options.PDF417CreateMacro) - { - m_codewords.Add(928); - //segment index - var t = 100000 + Options.PDF417SegmentIndex; - var d1 = t % 900; - t = t / 900; - var d2 = t % 900; - m_codewords.Add(d2); - m_codewords.Add(d1); - //file ID - m_codewords.Add((Options.PDF417FileID / 1000) % 900); - m_codewords.Add((Options.PDF417FileID % 1000) % 900); - - if (Options.PDF417LastSegment) - m_codewords.Add(922); - } - } - - private static EncoderMode detectMode(char codeascii) - { - EncoderMode mode = EncoderMode.Byte; - - if ((codeascii == '\t') || (codeascii == '\n') || (codeascii >= ' ' && codeascii <= '~') || (codeascii == '\r')) - mode = EncoderMode.Text; - - if ((codeascii >= '0') && (codeascii <= '9')) - mode = EncoderMode.Numeric; - - return mode; - } - - /// - /// Optimizes the chunks. Tries to remove unnecessary mode switches and - /// re-groups chunks after that. - /// - protected virtual void optimizeChunks() - { - for (int i = 0; i < m_chunks.Count; i++) - { - EncoderMode prevMode = EncoderMode.Undefined; - EncoderMode nextMode = EncoderMode.Undefined; - getPrevAndNextMode(out prevMode, out nextMode, i); - - EncoderChunk chunk = (m_chunks[i] as EncoderChunk); - if (chunk.mode == EncoderMode.Numeric) - { - int currentChunkLength = chunk.length; - - if (i == 0 && m_chunks.Count > 1) - { - // first block and there are other blocks - if ((nextMode == EncoderMode.Text) && (currentChunkLength < 8)) - chunk.mode = EncoderMode.Text; - - if ((nextMode == EncoderMode.Byte) && (currentChunkLength == 1)) - chunk.mode = EncoderMode.Byte; - } - else - { - if (i == m_chunks.Count - 1) - { - // last block - if ((prevMode == EncoderMode.Text) && (currentChunkLength < 7)) - chunk.mode = EncoderMode.Text; - - if ((prevMode == EncoderMode.Byte) && (currentChunkLength == 1)) - chunk.mode = EncoderMode.Byte; - } - else - { - // not first or last block - if (((prevMode == EncoderMode.Byte) && (nextMode == EncoderMode.Byte)) && (currentChunkLength < 4)) - chunk.mode = EncoderMode.Byte; - - if (((prevMode == EncoderMode.Byte) && (nextMode == EncoderMode.Text)) && (currentChunkLength < 4)) - chunk.mode = EncoderMode.Text; - - if (((prevMode == EncoderMode.Text) && (nextMode == EncoderMode.Byte)) && (currentChunkLength < 5)) - chunk.mode = EncoderMode.Text; - - if (((prevMode == EncoderMode.Text) && (nextMode == EncoderMode.Text)) && (currentChunkLength < 8)) - chunk.mode = EncoderMode.Text; - } - } - } - } - - condenseChunks(); - - for (int i = 0; i < m_chunks.Count; i++) - { - EncoderChunk chunk = (m_chunks[i] as EncoderChunk); - int currentChunkLength = chunk.length; - - EncoderMode prevMode = EncoderMode.Undefined; - EncoderMode nextMode = EncoderMode.Undefined; - getPrevAndNextMode(out prevMode, out nextMode, i); - - if ((chunk.mode == EncoderMode.Text) && (i > 0)) - { - if (i == m_chunks.Count - 1) - { - if ((prevMode == EncoderMode.Byte) && (currentChunkLength == 1)) - chunk.mode = EncoderMode.Byte; - } - else - { - if (((prevMode == EncoderMode.Byte) && (nextMode == EncoderMode.Byte)) && (currentChunkLength < 5)) - chunk.mode = EncoderMode.Byte; - - if ((((prevMode == EncoderMode.Byte) && (nextMode != EncoderMode.Byte)) || ((prevMode != EncoderMode.Byte) && (nextMode == EncoderMode.Byte))) && (currentChunkLength < 3)) - chunk.mode = EncoderMode.Byte; - } - } - } - - condenseChunks(); - } - - void getPrevAndNextMode(out EncoderMode prevMode, out EncoderMode nextMode, int currentChunk) - { - prevMode = EncoderMode.Undefined; - if (currentChunk != 0) - prevMode = (m_chunks[currentChunk - 1] as EncoderChunk).mode; - - nextMode = EncoderMode.Undefined; - if (currentChunk != m_chunks.Count - 1) - nextMode = (m_chunks[currentChunk + 1] as EncoderChunk).mode; - } - - void processTextChunk(int startTextPos, int currentChunk) - { - EncoderChunk chunk = m_chunks[currentChunk] as EncoderChunk; - int[] tableNumber = new int[chunk.length]; - int[] charCodePos = new int[chunk.length]; - buildTextModeArrays(currentChunk, startTextPos, tableNumber, charCodePos); - - // default table is Uppercase - int currentTable = 1; - - // charCodePos is array of character positions - // charCodePosFinal is array of character positions with all required table switches - intList charCodePosFinal = new intList(); - - for (int i = 0; i < chunk.length; i++) - { - if ((tableNumber[i] & currentTable) != 0) - { - // the character is in the current table - charCodePosFinal.Add(charCodePos[i]); - } - else - { - //character does not contained in current table. we need to change table - bool changedTableForOneCharacter = false; - if (shouldChangeTableForOneCharacter(currentChunk, i, tableNumber)) - changedTableForOneCharacter = tryChangeTableForOneCharacter(tableNumber, charCodePos, charCodePosFinal, i, currentTable); - - if (!changedTableForOneCharacter) - { - currentTable = changeTable(tableNumber, charCodePosFinal, currentTable, currentChunk, i); - charCodePosFinal.Add(charCodePos[i]); - } - } - } - - if ((charCodePosFinal.Count % 2) != 0) - charCodePosFinal.Add(29); - - if (currentChunk > 0) - m_codewords.Add(900); - - for (int i = 0; i < charCodePosFinal.Count; i += 2) - { - int cwNumber = (30 * charCodePosFinal[i]) + charCodePosFinal[i + 1]; - m_codewords.Add(cwNumber); - } - } - - private int changeTable(int[] tableNumber, intList charCodePosFinal, int currentTable, int currentChunk, int i) - { - int newTable; - if (i == ((m_chunks[currentChunk] as EncoderChunk).length - 1)) - { - newTable = tableNumber[i]; - } - else - { - if ((tableNumber[i] & tableNumber[i + 1]) == 0) - newTable = tableNumber[i]; - else - newTable = tableNumber[i] & tableNumber[i + 1]; - } - - newTable = findSingleNewTable(newTable); - addTableSwitches(currentTable, newTable, charCodePosFinal); - return newTable; - } - - private static void addTableSwitches(int currentTable, int newTable, intList charCodePosFinal) - { - switch (currentTable) - { - case 1: - switch (newTable) - { - case 2: - charCodePosFinal.Add(27); - break; - - case 4: - charCodePosFinal.Add(28); - break; - - case 8: - charCodePosFinal.Add(28); - charCodePosFinal.Add(25); - break; - } - break; - - case 2: - switch (newTable) - { - case 1: - charCodePosFinal.Add(28); - charCodePosFinal.Add(28); - break; - - case 4: - charCodePosFinal.Add(28); - break; - - case 8: - charCodePosFinal.Add(28); - charCodePosFinal.Add(25); - break; - } - break; - - case 4: - switch (newTable) - { - case 1: - charCodePosFinal.Add(28); - break; - - case 2: - charCodePosFinal.Add(27); - break; - - case 8: - charCodePosFinal.Add(25); - break; - } - break; - - case 8: - switch (newTable) - { - case 1: - charCodePosFinal.Add(29); - break; - - case 2: - charCodePosFinal.Add(29); - charCodePosFinal.Add(27); - break; - - case 4: - charCodePosFinal.Add(29); - charCodePosFinal.Add(28); - break; - } - break; - } - } - - private static int findSingleNewTable(int newTableCandidate) - { - // Use the first eligible table - int newTable = newTableCandidate; - switch (newTableCandidate) - { - case 3: - case 5: - case 7: - case 9: - case 11: - case 13: - case 15: - newTable = 1; - break; - - case 6: - case 10: - case 14: - newTable = 2; - break; - - case 12: - newTable = 4; - break; - } - - return newTable; - } - - private static bool tryChangeTableForOneCharacter(int[] tableNumber, int[] charCodePos, intList charCodePosFinal, int currentChunkPos, int currentTable) - { - bool changedTableForOneCharacter = true; - - if (((tableNumber[currentChunkPos] & 1) != 0) && (currentTable == 2)) - { - charCodePosFinal.Add(27); // T_UPP - charCodePosFinal.Add(charCodePos[currentChunkPos]); - } - - if ((tableNumber[currentChunkPos] & 8) != 0) - { - charCodePosFinal.Add(29); // T_PUN - charCodePosFinal.Add(charCodePos[currentChunkPos]); - } - - if (!((((tableNumber[currentChunkPos] & 1) != 0) && (currentTable == 2)) || ((tableNumber[currentChunkPos] & 8) != 0))) - { - // can't change for one character - changedTableForOneCharacter = false; - } - - return changedTableForOneCharacter; - } - - private bool shouldChangeTableForOneCharacter(int currentChunk, int currentChunkPos, int[] tableNumber) - { - bool shouldChange = false; - - if (currentChunkPos == ((m_chunks[currentChunk] as EncoderChunk).length - 1)) - shouldChange = true; - else - { - if ((tableNumber[currentChunkPos] & tableNumber[currentChunkPos + 1]) == 0) - shouldChange = true; - } - - return shouldChange; - } - - /// - /// Builds the text mode arrays. - /// - /// The current chunk. - /// The start text pos. - /// The table numbers array. - /// The charcode positions array. - private void buildTextModeArrays(int currentChunk, int startTextPos, int[] tableNumber, int[] charCodePos) - { - // Charcode positions array and table numbers array are synchronized. - // It means that charCodePos[i] and tableNumber[i] are for symbol m_text[startTextPos + i]. - - // Table numbers array contains encoded values. - // Each encoded value indicates tables where a symbol can be found. - - // Please take a look at "Papers/The PDF417 code.rtf" - - // For example: - // m_text[startTextPos + i] == '\n' (LF) - // tableNumber[i] is 8. - // 8 is 1000 in binary representation. - // tables are [Punctuation, Mixed, Lowercase, Uppercase] - // so, LF symbol can be found in Punctuation table. - // charCodePos[i] is 15 - // so, LF symbol can be found in Punctuation table at line with number 15 - - int chunkLength = (m_chunks[currentChunk] as EncoderChunk).length; - for (int i = 0; i < chunkLength; i++) - { - char asciiCode = m_text[startTextPos + i]; - switch (asciiCode) - { - case '\t': - tableNumber[i] = 12; - charCodePos[i] = 12; - break; - - case '\n': - tableNumber[i] = 8; - charCodePos[i] = 15; - break; - - case (char)13: - tableNumber[i] = 12; - charCodePos[i] = 11; - break; - - default: - tableNumber[i] = m_textModeTableNumbers[asciiCode - 32]; - charCodePos[i] = m_textModeCharPos[asciiCode - 32]; - break; - } - } - } - - /// - /// readonly array with precalculated values of 256 in different pows from 0 to 5 - /// - private readonly long[] arrPow256 = { - 1, // Math.Pow(256, 0); - 256, // Math.Pow(256, 1) - 65536, // Math.Pow(256, 2) - 16777216, // Math.Pow(256, 3) - 4294967296, // Math.Pow(256, 4) - 1099511627776, // Math.Pow(256, 5) - }; - - void processByteChunk(int startTextPos, int currentChunk) - { - EncoderChunk chunk = m_chunks[currentChunk] as EncoderChunk; - if (chunk.length == 1) - { - // single byte mode - // see http://www.geocities.ws/ooosawaddee3pdf417/high.htm#Byte Compaction Mode - // and http://grandzebu.net/informatique/codbar-en/pdf417.htm - m_codewords.Add(913); - m_codewords.Add(m_text[startTextPos]); - } - else - { - // mode latch 924 when multiple 6 - if (chunk.length % 6 == 0) - m_codewords.Add(924); - else // mode latch 901 - m_codewords.Add(901); - - for (int i = 0; i < chunk.length; ) - { - /* from old implementation (see #452, 26 July 2013) - short[] accum = new short[112]; - short[] xReg = new short[112]; - short[] yReg = new short[112]; - */ - long sumAll = 0; - int chunkLength = chunk.length - i; - if (chunkLength >= 6) - { - #region old 256 to 900 base conversion, old implementation see #452, 26 July 2013 - /* - for (int j = 0; j < 112; j++) - { - accum[j] = 0; - xReg[j] = 0; - yReg[j] = 0; - } - - chunkLength = 6; - for (int j = 0; j < chunkLength; j++) - { - for (int k = 0; k < 8; k++) - shiftUp(yReg); - - if ((m_text[startTextPos + i + j] & 0x80) != 0) - yReg[7] = 1; - - if ((m_text[startTextPos + i + j] & 0x40) != 0) - yReg[6] = 1; - - if ((m_text[startTextPos + i + j] & 0x20) != 0) - yReg[5] = 1; - - if ((m_text[startTextPos + i + j] & 0x10) != 0) - yReg[4] = 1; - - if ((m_text[startTextPos + i + j] & 0x08) != 0) - yReg[3] = 1; - - if ((m_text[startTextPos + i + j] & 0x04) != 0) - yReg[2] = 1; - - if ((m_text[startTextPos + i + j] & 0x02) != 0) - yReg[1] = 1; - - if ((m_text[startTextPos + i + j] & 0x01) != 0) - yReg[0] = 1; - - } - - int[] chunkCodewords = new int[5]; - for (int j = 0; j < 4; j++) - { - for (int k = 0; k < 112; k++) - { - accum[k] = yReg[k]; - yReg[k] = 0; - xReg[k] = 0; - } - - xReg[101] = 1; - xReg[100] = 1; - xReg[99] = 1; - xReg[94] = 1; - - for (int k = 92; k >= 0; k--) - { - yReg[k] = isLarger(accum, xReg); - if (yReg[k] == 1) - binarySubtract(accum, xReg); - - shiftdown(xReg); - } - - chunkCodewords[j] = (accum[9] * 512) + (accum[8] * 256) + (accum[7] * 128) + (accum[6] * 64) + (accum[5] * 32) + (accum[4] * 16) + (accum[3] * 8) + (accum[2] * 4) + (accum[1] * 2) + accum[0]; - } - - chunkCodewords[4] = (yReg[9] * 512) + (yReg[8] * 256) + (yReg[7] * 128) + (yReg[6] * 64) + (yReg[5] * 32) + (yReg[4] * 16) + (yReg[3] * 8) + (yReg[2] * 4) + (yReg[1] * 2) + yReg[0]; - - for (int j = 0; j < 5; j++) - m_codewords.Add(chunkCodewords[4 - j]); - */ - - #endregion - - int alreadyProcessed = 0; - int loopCount = chunkLength / 6; - // iterate through each 6 - for (int ilooper = 0; ilooper < loopCount; ilooper++) - { - for (int j = 0; j < 6; j++) - sumAll += m_text[startTextPos + i + alreadyProcessed + j] * arrPow256[5 - j]; - long[] newValues = new long[5]; - - - for(int k=0; k<5; k++) - { - // save reminder - newValues[k] = sumAll % 900; - // save divided value - sumAll = sumAll / 900; - } - - // adding inversed newValues array - for (int k = 4; k >= 0; k-- ) - m_codewords.Add((int)newValues[k]); - - // shift by 6 bytes (source), not 5 as we are converting 6 bytes into 5 (in base of 900, see papers/pdf417..rtf) - alreadyProcessed += 6; - } - // shift by number of processed symbols (with 6 symbols per each loop) - i += loopCount * 6; - } - else - { - // less than 6 bytes remains - for (int j = 0; j < chunkLength; j++) - { - m_codewords.Add(m_text[startTextPos + i + j]); - } - // exit the loop as we processed the remaining less than 6 bytes - break; - } - - } - } - } - - void processNumberChunk(int startTextPos, int currentChunk) - { - m_codewords.Add(902); - - EncoderChunk chunk = m_chunks[currentChunk] as EncoderChunk; - for (int i = 0; i < chunk.length; ) - { - int blockLength = chunk.length - i; - if (blockLength > 44) - blockLength = 44; - - string blockToEncode = "1" + m_text.Substring(startTextPos + i, blockLength); - string encodedBlock = ""; - intList blockCodewords = new intList(); - do - { - int divider = 900; - encodedBlock = ""; - - int number = 0; - while (blockToEncode.Length != 0) - { - number *= 10; - number += ctoi(blockToEncode[0]); - - blockToEncode = blockToEncode.Remove(0, 1); - - if (number < divider) - { - if (encodedBlock.Length != 0) - encodedBlock += "0"; - } - else - { - char temp = (char)((number / divider) + '0'); - encodedBlock += temp; - } - - number = number % divider; - } - - divider = number; - blockCodewords.Insert(0, divider); - blockToEncode = encodedBlock; - - } while (encodedBlock.Length != 0); - - for (int j = 0; j < blockCodewords.Count; j++) - m_codewords.Add(blockCodewords[j]); - - i += blockLength; - } - } - - /// - /// Condenses the chunks. Brings together blocks with same mode. - /// - void condenseChunks() - { - if (m_chunks.Count > 1) - { - int i = 1; - while (i < m_chunks.Count) - { - if ((m_chunks[i - 1] as EncoderChunk).mode == (m_chunks[i] as EncoderChunk).mode) - { - // sum lengths of adjustment chunks with same mode - (m_chunks[i - 1] as EncoderChunk).length += (m_chunks[i] as EncoderChunk).length; - m_chunks.RemoveAt(i); - } - - i++; - } - } - } - - private static void shiftUp(short[] buffer) - { - for (int i = 102; i > 0; i--) - buffer[i] = buffer[i - 1]; - - buffer[0] = 0; - } - - private static short isLarger(short[] accum, short[] reg) - { - bool found = false; - short larger = 0; - - int i = 103; - do - { - if ((accum[i] == 1) && (reg[i] == 0)) - { - found = true; - larger = 1; - } - - if ((accum[i] == 0) && (reg[i] == 1)) - found = true; - - i--; - } while (!found && (i > -1)); - - return larger; - } - - private static void binarySubtract(short[] accumulator, short[] inputBuffer) - { - // 2's compliment subtraction - // take inputBuffer from accumulator and put answer in accumulator - - short[] subBuffer = new short[112]; - for (int i = 0; i < 112; i++) - { - if (inputBuffer[i] == 0) - subBuffer[i] = 1; - else - subBuffer[i] = 0; - } - - binaryAdd(accumulator, subBuffer); - - subBuffer[0] = 1; - for (int i = 1; i < 112; i++) - subBuffer[i] = 0; - - binaryAdd(accumulator, subBuffer); - } - - private static void binaryAdd(short[] accumulator, short[] inputBuffer) - { - int carry = 0; - for (int i = 0; i < 112; i++) - { - bool done = false; - if (((inputBuffer[i] == 0) && (accumulator[i] == 0)) && ((carry == 0) && !done)) - { - accumulator[i] = 0; - carry = 0; - done = true; - } - if (((inputBuffer[i] == 0) && (accumulator[i] == 0)) && ((carry == 1) && !done)) - { - accumulator[i] = 1; - carry = 0; - done = true; - } - if (((inputBuffer[i] == 0) && (accumulator[i] == 1)) && ((carry == 0) && !done)) - { - accumulator[i] = 1; - carry = 0; - done = true; - } - if (((inputBuffer[i] == 0) && (accumulator[i] == 1)) && ((carry == 1) && !done)) - { - accumulator[i] = 0; - carry = 1; - done = true; - } - if (((inputBuffer[i] == 1) && (accumulator[i] == 0)) && ((carry == 0) && !done)) - { - accumulator[i] = 1; - carry = 0; - done = true; - } - if (((inputBuffer[i] == 1) && (accumulator[i] == 0)) && ((carry == 1) && !done)) - { - accumulator[i] = 0; - carry = 1; - done = true; - } - if (((inputBuffer[i] == 1) && (accumulator[i] == 1)) && ((carry == 0) && !done)) - { - accumulator[i] = 0; - carry = 1; - done = true; - } - if (((inputBuffer[i] == 1) && (accumulator[i] == 1)) && ((carry == 1) && !done)) - { - accumulator[i] = 1; - carry = 1; - done = true; - } - } - } - - // Converts a character 0-9 to its equivalent integer value - protected static int ctoi(char source) - { - if ((source >= '0') && (source <= '9')) - return (source - '0'); - - return (source - 'A' + 10); - } - - private static void shiftdown(short[] buffer) - { - buffer[102] = 0; - buffer[103] = 0; - - for (int i = 0; i < 102; i++) - buffer[i] = buffer[i + 1]; - } - - protected void validateCompactionMode(PDF417CompactionMode mode) - { - m_chunks.Clear(); - bool cantEncode = false; - - if (mode != PDF417CompactionMode.Binary) - { - if (mode == PDF417CompactionMode.Numeric) - { - foreach (char c in m_text) - { - if (c < '0' || c > '9') - { - cantEncode = true; - break; - } - } - } - else if (mode == PDF417CompactionMode.Text) - { - foreach (char c in m_text) - { - if ((c < ' ' || c > '~') && (c != '\t') && (c != '\n') && (c != 13)) - { - cantEncode = true; - break; - } - } - } - } - - if (cantEncode) - throw new BarcodeException(string.Format("Input string can not be encoded with {0} compaction mode", mode)); - - EncoderMode em = EncoderMode.Byte; - if (mode == PDF417CompactionMode.Numeric) - em = EncoderMode.Numeric; - else if (mode == PDF417CompactionMode.Text) - em = EncoderMode.Text; - - m_chunks.Add(new EncoderChunk(em, m_text.Length)); - } - - protected virtual void splitTextIntoChunks() - { - m_chunks.Clear(); - - for (int textPos = 0; textPos < m_text.Length; ) - { - EncoderMode mode = detectMode(m_text[textPos]); - int chunkLength = 0; - for (; textPos < m_text.Length; textPos++) - { - EncoderMode newMode = detectMode(m_text[textPos]); - if (newMode != mode) - break; - - chunkLength++; - } - - m_chunks.Add(new EncoderChunk(mode, chunkLength)); - } - } - - - ////////////////////////////////////////////////////////////////////////// - // - // Data part - // - ////////////////////////////////////////////////////////////////////////// - - /// - /// Alphabet of symbols that may be found in a alpha pattern. - /// - protected static string m_patternAlphabet = "ABCDEFabcdefghijklmnopqrstuvwxyz*+-"; - - /// - /// Bar patterns for symbols of alpha pattern alphabet - /// - protected static string[] m_barsForAlpha = - { - "00000", "00001", "00010", "00011", "00100", "00101", "00110", "00111", - "01000", "01001", "01010", "01011", "01100", "01101", "01110", "01111", "10000", "10001", - "10010", "10011", "10100", "10101", "10110", "10111", "11000", "11001", "11010", - "11011", "11100", "11101", "11110", "11111", "01", "1111111101010100", "11111101000101001" - }; - - static int[] m_textModeTableNumbers = - { - 7, 8, 8, 4, 12, 4, 4, 8, 8, 8, 12, 4, 12, 12, 12, 12, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 12, 8, 8, 4, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 8, 8, 8, 4, 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 8, 8, 8, 8 - }; - - static int[] m_textModeCharPos = - { - 26, 10, 20, 15, 18, 21, 10, 28, 23, 24, 22, 20, 13, 16, 17, 19, 0, 1, 2, 3, - 4, 5, 6, 7, 8, 9, 14, 0, 1, 23, 2, 25, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 4, 5, 6, 24, 7, 8, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 21, 27, 9 - }; - - /* PDF417 error correction coefficients*/ - static int[] m_errorCorrectionCoefficients = - { - /* k = 2 */ - 27, 917, - - /* k = 4 */ - 522, 568, 723, 809, - - /* k = 8 */ - 237, 308, 436, 284, 646, 653, 428, 379, - - /* k = 16 */ - 274, 562, 232, 755, 599, 524, 801, 132, 295, 116, 442, 428, 295, 42, 176, 65, - - /* k = 32 */ - 361, 575, 922, 525, 176, 586, 640, 321, 536, 742, 677, 742, 687, 284, 193, 517, - 273, 494, 263, 147, 593, 800, 571, 320, 803, 133, 231, 390, 685, 330, 63, 410, - - /* k = 64 */ - 539, 422, 6, 93, 862, 771, 453, 106, 610, 287, 107, 505, 733, 877, 381, 612, - 723, 476, 462, 172, 430, 609, 858, 822, 543, 376, 511, 400, 672, 762, 283, 184, - 440, 35, 519, 31, 460, 594, 225, 535, 517, 352, 605, 158, 651, 201, 488, 502, - 648, 733, 717, 83, 404, 97, 280, 771, 840, 629, 4, 381, 843, 623, 264, 543, - - /* k = 128 */ - 521, 310, 864, 547, 858, 580, 296, 379, 53, 779, 897, 444, 400, 925, 749, 415, - 822, 93, 217, 208, 928, 244, 583, 620, 246, 148, 447, 631, 292, 908, 490, 704, - 516, 258, 457, 907, 594, 723, 674, 292, 272, 96, 684, 432, 686, 606, 860, 569, - 193, 219, 129, 186, 236, 287, 192, 775, 278, 173, 40, 379, 712, 463, 646, 776, - 171, 491, 297, 763, 156, 732, 95, 270, 447, 90, 507, 48, 228, 821, 808, 898, - 784, 663, 627, 378, 382, 262, 380, 602, 754, 336, 89, 614, 87, 432, 670, 616, - 157, 374, 242, 726, 600, 269, 375, 898, 845, 454, 354, 130, 814, 587, 804, 34, - 211, 330, 539, 297, 827, 865, 37, 517, 834, 315, 550, 86, 801, 4, 108, 539, - - /* k = 256 */ - 524, 894, 75, 766, 882, 857, 74, 204, 82, 586, 708, 250, 905, 786, 138, 720, - 858, 194, 311, 913, 275, 190, 375, 850, 438, 733, 194, 280, 201, 280, 828, 757, - 710, 814, 919, 89, 68, 569, 11, 204, 796, 605, 540, 913, 801, 700, 799, 137, - 439, 418, 592, 668, 353, 859, 370, 694, 325, 240, 216, 257, 284, 549, 209, 884, - 315, 70, 329, 793, 490, 274, 877, 162, 749, 812, 684, 461, 334, 376, 849, 521, - 307, 291, 803, 712, 19, 358, 399, 908, 103, 511, 51, 8, 517, 225, 289, 470, - 637, 731, 66, 255, 917, 269, 463, 830, 730, 433, 848, 585, 136, 538, 906, 90, - 2, 290, 743, 199, 655, 903, 329, 49, 802, 580, 355, 588, 188, 462, 10, 134, - 628, 320, 479, 130, 739, 71, 263, 318, 374, 601, 192, 605, 142, 673, 687, 234, - 722, 384, 177, 752, 607, 640, 455, 193, 689, 707, 805, 641, 48, 60, 732, 621, - 895, 544, 261, 852, 655, 309, 697, 755, 756, 60, 231, 773, 434, 421, 726, 528, - 503, 118, 49, 795, 32, 144, 500, 238, 836, 394, 280, 566, 319, 9, 647, 550, - 73, 914, 342, 126, 32, 681, 331, 792, 620, 60, 609, 441, 180, 791, 893, 754, - 605, 383, 228, 749, 760, 213, 54, 297, 134, 54, 834, 299, 922, 191, 910, 532, - 609, 829, 189, 20, 167, 29, 872, 449, 83, 402, 41, 656, 505, 579, 481, 173, - 404, 251, 688, 95, 497, 555, 642, 543, 307, 159, 924, 558, 648, 55, 497, 10, - - /* k = 512 */ - 352, 77, 373, 504, 35, 599, 428, 207, 409, 574, 118, 498, 285, 380, 350, 492, - 197, 265, 920, 155, 914, 299, 229, 643, 294, 871, 306, 88, 87, 193, 352, 781, - 846, 75, 327, 520, 435, 543, 203, 666, 249, 346, 781, 621, 640, 268, 794, 534, - 539, 781, 408, 390, 644, 102, 476, 499, 290, 632, 545, 37, 858, 916, 552, 41, - 542, 289, 122, 272, 383, 800, 485, 98, 752, 472, 761, 107, 784, 860, 658, 741, - 290, 204, 681, 407, 855, 85, 99, 62, 482, 180, 20, 297, 451, 593, 913, 142, - 808, 684, 287, 536, 561, 76, 653, 899, 729, 567, 744, 390, 513, 192, 516, 258, - 240, 518, 794, 395, 768, 848, 51, 610, 384, 168, 190, 826, 328, 596, 786, 303, - 570, 381, 415, 641, 156, 237, 151, 429, 531, 207, 676, 710, 89, 168, 304, 402, - 40, 708, 575, 162, 864, 229, 65, 861, 841, 512, 164, 477, 221, 92, 358, 785, - 288, 357, 850, 836, 827, 736, 707, 94, 8, 494, 114, 521, 2, 499, 851, 543, - 152, 729, 771, 95, 248, 361, 578, 323, 856, 797, 289, 51, 684, 466, 533, 820, - 669, 45, 902, 452, 167, 342, 244, 173, 35, 463, 651, 51, 699, 591, 452, 578, - 37, 124, 298, 332, 552, 43, 427, 119, 662, 777, 475, 850, 764, 364, 578, 911, - 283, 711, 472, 420, 245, 288, 594, 394, 511, 327, 589, 777, 699, 688, 43, 408, - 842, 383, 721, 521, 560, 644, 714, 559, 62, 145, 873, 663, 713, 159, 672, 729, - 624, 59, 193, 417, 158, 209, 563, 564, 343, 693, 109, 608, 563, 365, 181, 772, - 677, 310, 248, 353, 708, 410, 579, 870, 617, 841, 632, 860, 289, 536, 35, 777, - 618, 586, 424, 833, 77, 597, 346, 269, 757, 632, 695, 751, 331, 247, 184, 45, - 787, 680, 18, 66, 407, 369, 54, 492, 228, 613, 830, 922, 437, 519, 644, 905, - 789, 420, 305, 441, 207, 300, 892, 827, 141, 537, 381, 662, 513, 56, 252, 341, - 242, 797, 838, 837, 720, 224, 307, 631, 61, 87, 560, 310, 756, 665, 397, 808, - 851, 309, 473, 795, 378, 31, 647, 915, 459, 806, 590, 731, 425, 216, 548, 249, - 321, 881, 699, 535, 673, 782, 210, 815, 905, 303, 843, 922, 281, 73, 469, 791, - 660, 162, 498, 308, 155, 422, 907, 817, 187, 62, 16, 425, 535, 336, 286, 437, - 375, 273, 610, 296, 183, 923, 116, 667, 751, 353, 62, 366, 691, 379, 687, 842, - 37, 357, 720, 742, 330, 5, 39, 923, 311, 424, 242, 749, 321, 54, 669, 316, - 342, 299, 534, 105, 667, 488, 640, 672, 576, 540, 316, 486, 721, 610, 46, 656, - 447, 171, 616, 464, 190, 531, 297, 321, 762, 752, 533, 175, 134, 14, 381, 433, - 717, 45, 111, 20, 596, 284, 736, 138, 646, 411, 877, 669, 141, 919, 45, 780, - 407, 164, 332, 899, 165, 726, 600, 325, 498, 655, 357, 752, 768, 223, 849, 647, - 63, 310, 863, 251, 366, 304, 282, 738, 675, 410, 389, 244, 31, 121, 303, 263 - }; - - protected static string[] m_alphaPatternTable = - { - "urA", "xfs", "ypy", "unk", "xdw", "yoz", "pDA", "uls", "pBk", "eBA", - "pAs", "eAk", "prA", "uvs", "xhy", "pnk", "utw", "xgz", "fDA", "pls", "fBk", "frA", "pvs", - "uxy", "fnk", "ptw", "uwz", "fls", "psy", "fvs", "pxy", "ftw", "pwz", "fxy", "yrx", "ufk", - "xFw", "ymz", "onA", "uds", "xEy", "olk", "ucw", "dBA", "oks", "uci", "dAk", "okg", "dAc", - "ovk", "uhw", "xaz", "dnA", "ots", "ugy", "dlk", "osw", "ugj", "dks", "osi", "dvk", "oxw", - "uiz", "dts", "owy", "dsw", "owj", "dxw", "oyz", "dwy", "dwj", "ofA", "uFs", "xCy", "odk", - "uEw", "xCj", "clA", "ocs", "uEi", "ckk", "ocg", "ckc", "ckE", "cvA", "ohs", "uay", "ctk", - "ogw", "uaj", "css", "ogi", "csg", "csa", "cxs", "oiy", "cww", "oij", "cwi", "cyy", "oFk", - "uCw", "xBj", "cdA", "oEs", "uCi", "cck", "oEg", "uCb", "ccc", "oEa", "ccE", "oED", "chk", - "oaw", "uDj", "cgs", "oai", "cgg", "oab", "cga", "cgD", "obj", "cib", "cFA", "oCs", "uBi", - "cEk", "oCg", "uBb", "cEc", "oCa", "cEE", "oCD", "cEC", "cas", "cag", "caa", "cCk", "uAr", - "oBa", "oBD", "cCB", "tfk", "wpw", "yez", "mnA", "tds", "woy", "mlk", "tcw", "woj", "FBA", - "mks", "FAk", "mvk", "thw", "wqz", "FnA", "mts", "tgy", "Flk", "msw", "Fks", "Fkg", "Fvk", - "mxw", "tiz", "Fts", "mwy", "Fsw", "Fsi", "Fxw", "myz", "Fwy", "Fyz", "vfA", "xps", "yuy", - "vdk", "xow", "yuj", "qlA", "vcs", "xoi", "qkk", "vcg", "xob", "qkc", "vca", "mfA", "tFs", - "wmy", "qvA", "mdk", "tEw", "wmj", "qtk", "vgw", "xqj", "hlA", "Ekk", "mcg", "tEb", "hkk", - "qsg", "hkc", "EvA", "mhs", "tay", "hvA", "Etk", "mgw", "taj", "htk", "qww", "vij", "hss", - "Esg", "hsg", "Exs", "miy", "hxs", "Eww", "mij", "hww", "qyj", "hwi", "Eyy", "hyy", "Eyj", - "hyj", "vFk", "xmw", "ytj", "qdA", "vEs", "xmi", "qck", "vEg", "xmb", "qcc", "vEa", "qcE", - "qcC", "mFk", "tCw", "wlj", "qhk", "mEs", "tCi", "gtA", "Eck", "vai", "tCb", "gsk", "Ecc", - "mEa", "gsc", "qga", "mED", "EcC", "Ehk", "maw", "tDj", "gxk", "Egs", "mai", "gws", "qii", - "mab", "gwg", "Ega", "EgD", "Eiw", "mbj", "gyw", "Eii", "gyi", "Eib", "gyb", "gzj", "qFA", - "vCs", "xli", "qEk", "vCg", "xlb", "qEc", "vCa", "qEE", "vCD", "qEC", "qEB", "EFA", "mCs", - "tBi", "ghA", "EEk", "mCg", "tBb", "ggk", "qag", "vDb", "ggc", "EEE", "mCD", "ggE", "qaD", - "ggC", "Eas", "mDi", "gis", "Eag", "mDb", "gig", "qbb", "gia", "EaD", "giD", "gji", "gjb", - "qCk", "vBg", "xkr", "qCc", "vBa", "qCE", "vBD", "qCC", "qCB", "ECk", "mBg", "tAr", "gak", - "ECc", "mBa", "gac", "qDa", "mBD", "gaE", "ECC", "gaC", "ECB", "EDg", "gbg", "gba", "gbD", - "vAq", "vAn", "qBB", "mAq", "EBE", "gDE", "gDC", "gDB", "lfA", "sps", "wey", "ldk", "sow", - "ClA", "lcs", "soi", "Ckk", "lcg", "Ckc", "CkE", "CvA", "lhs", "sqy", "Ctk", "lgw", "sqj", - "Css", "lgi", "Csg", "Csa", "Cxs", "liy", "Cww", "lij", "Cwi", "Cyy", "Cyj", "tpk", "wuw", - "yhj", "ndA", "tos", "wui", "nck", "tog", "wub", "ncc", "toa", "ncE", "toD", "lFk", "smw", - "wdj", "nhk", "lEs", "smi", "atA", "Cck", "tqi", "smb", "ask", "ngg", "lEa", "asc", "CcE", - "asE", "Chk", "law", "snj", "axk", "Cgs", "trj", "aws", "nii", "lab", "awg", "Cga", "awa", - "Ciw", "lbj", "ayw", "Cii", "ayi", "Cib", "Cjj", "azj", "vpA", "xus", "yxi", "vok", "xug", - "yxb", "voc", "xua", "voE", "xuD", "voC", "nFA", "tms", "wti", "rhA", "nEk", "xvi", "wtb", - "rgk", "vqg", "xvb", "rgc", "nEE", "tmD", "rgE", "vqD", "nEB", "CFA", "lCs", "sli", "ahA", - "CEk", "lCg", "slb", "ixA", "agk", "nag", "tnb", "iwk", "rig", "vrb", "lCD", "iwc", "agE", - "naD", "iwE", "CEB", "Cas", "lDi", "ais", "Cag", "lDb", "iys", "aig", "nbb", "iyg", "rjb", - "CaD", "aiD", "Cbi", "aji", "Cbb", "izi", "ajb", "vmk", "xtg", "ywr", "vmc", "xta", "vmE", - "xtD", "vmC", "vmB", "nCk", "tlg", "wsr", "rak", "nCc", "xtr", "rac", "vna", "tlD", "raE", - "nCC", "raC", "nCB", "raB", "CCk", "lBg", "skr", "aak", "CCc", "lBa", "iik", "aac", "nDa", - "lBD", "iic", "rba", "CCC", "iiE", "aaC", "CCB", "aaB", "CDg", "lBr", "abg", "CDa", "ijg", - "aba", "CDD", "ija", "abD", "CDr", "ijr", "vlc", "xsq", "vlE", "xsn", "vlC", "vlB", "nBc", - "tkq", "rDc", "nBE", "tkn", "rDE", "vln", "rDC", "nBB", "rDB", "CBc", "lAq", "aDc", "CBE", - "lAn", "ibc", "aDE", "nBn", "ibE", "rDn", "CBB", "ibC", "aDB", "ibB", "aDq", "ibq", "ibn", - "xsf", "vkl", "tkf", "nAm", "nAl", "CAo", "aBo", "iDo", "CAl", "aBl", "kpk", "BdA", "kos", - "Bck", "kog", "seb", "Bcc", "koa", "BcE", "koD", "Bhk", "kqw", "sfj", "Bgs", "kqi", "Bgg", - "kqb", "Bga", "BgD", "Biw", "krj", "Bii", "Bib", "Bjj", "lpA", "sus", "whi", "lok", "sug", - "loc", "sua", "loE", "suD", "loC", "BFA", "kms", "sdi", "DhA", "BEk", "svi", "sdb", "Dgk", - "lqg", "svb", "Dgc", "BEE", "kmD", "DgE", "lqD", "BEB", "Bas", "kni", "Dis", "Bag", "knb", - "Dig", "lrb", "Dia", "BaD", "Bbi", "Dji", "Bbb", "Djb", "tuk", "wxg", "yir", "tuc", "wxa", - "tuE", "wxD", "tuC", "tuB", "lmk", "stg", "nqk", "lmc", "sta", "nqc", "tva", "stD", "nqE", - "lmC", "nqC", "lmB", "nqB", "BCk", "klg", "Dak", "BCc", "str", "bik", "Dac", "lna", "klD", - "bic", "nra", "BCC", "biE", "DaC", "BCB", "DaB", "BDg", "klr", "Dbg", "BDa", "bjg", "Dba", - "BDD", "bja", "DbD", "BDr", "Dbr", "bjr", "xxc", "yyq", "xxE", "yyn", "xxC", "xxB", "ttc", - "wwq", "vvc", "xxq", "wwn", "vvE", "xxn", "vvC", "ttB", "vvB", "llc", "ssq", "nnc", "llE", - "ssn", "rrc", "nnE", "ttn", "rrE", "vvn", "llB", "rrC", "nnB", "rrB", "BBc", "kkq", "DDc", - "BBE", "kkn", "bbc", "DDE", "lln", "jjc", "bbE", "nnn", "BBB", "jjE", "rrn", "DDB", "jjC", - "BBq", "DDq", "BBn", "bbq", "DDn", "jjq", "bbn", "jjn", "xwo", "yyf", "xwm", "xwl", "tso", - "wwf", "vto", "xwv", "vtm", "tsl", "vtl", "lko", "ssf", "nlo", "lkm", "rno", "nlm", "lkl", - "rnm", "nll", "rnl", "BAo", "kkf", "DBo", "lkv", "bDo", "DBm", "BAl", "jbo", "bDm", "DBl", - "jbm", "bDl", "jbl", "DBv", "jbv", "xwd", "vsu", "vst", "nku", "rlu", "rlt", "DAu", "bBu", - "jDu", "jDt", "ApA", "Aok", "keg", "Aoc", "AoE", "AoC", "Aqs", "Aqg", "Aqa", "AqD", "Ari", - "Arb", "kuk", "kuc", "sha", "kuE", "shD", "kuC", "kuB", "Amk", "kdg", "Bqk", "kvg", "kda", - "Bqc", "kva", "BqE", "kvD", "BqC", "AmB", "BqB", "Ang", "kdr", "Brg", "kvr", "Bra", "AnD", - "BrD", "Anr", "Brr", "sxc", "sxE", "sxC", "sxB", "ktc", "lvc", "sxq", "sgn", "lvE", "sxn", - "lvC", "ktB", "lvB", "Alc", "Bnc", "AlE", "kcn", "Drc", "BnE", "AlC", "DrE", "BnC", "AlB", - "DrC", "BnB", "Alq", "Bnq", "Aln", "Drq", "Bnn", "Drn", "wyo", "wym", "wyl", "swo", "txo", - "wyv", "txm", "swl", "txl", "kso", "sgf", "lto", "swv", "nvo", "ltm", "ksl", "nvm", "ltl", - "nvl", "Ako", "kcf", "Blo", "ksv", "Dno", "Blm", "Akl", "bro", "Dnm", "Bll", "brm", "Dnl", - "Akv", "Blv", "Dnv", "brv", "yze", "yzd", "wye", "xyu", "wyd", "xyt", "swe", "twu", "swd", - "vxu", "twt", "vxt", "kse", "lsu", "ksd", "ntu", "lst", "rvu", "ypk", "zew", "xdA", "yos", - "zei", "xck", "yog", "zeb", "xcc", "yoa", "xcE", "yoD", "xcC", "xhk", "yqw", "zfj", "utA", - "xgs", "yqi", "usk", "xgg", "yqb", "usc", "xga", "usE", "xgD", "usC", "uxk", "xiw", "yrj", - "ptA", "uws", "xii", "psk", "uwg", "xib", "psc", "uwa", "psE", "uwD", "psC", "pxk", "uyw", - "xjj", "ftA", "pws", "uyi", "fsk", "pwg", "uyb", "fsc", "pwa", "fsE", "pwD", "fxk", "pyw", - "uzj", "fws", "pyi", "fwg", "pyb", "fwa", "fyw", "pzj", "fyi", "fyb", "xFA", "yms", "zdi", - "xEk", "ymg", "zdb", "xEc", "yma", "xEE", "ymD", "xEC", "xEB", "uhA", "xas", "yni", "ugk", - "xag", "ynb", "ugc", "xaa", "ugE", "xaD", "ugC", "ugB", "oxA", "uis", "xbi", "owk", "uig", - "xbb", "owc", "uia", "owE", "uiD", "owC", "owB", "dxA", "oys", "uji", "dwk", "oyg", "ujb", - "dwc", "oya", "dwE", "oyD", "dwC", "dys", "ozi", "dyg", "ozb", "dya", "dyD", "dzi", "dzb", - "xCk", "ylg", "zcr", "xCc", "yla", "xCE", "ylD", "xCC", "xCB", "uak", "xDg", "ylr", "uac", - "xDa", "uaE", "xDD", "uaC", "uaB", "oik", "ubg", "xDr", "oic", "uba", "oiE", "ubD", "oiC", - "oiB", "cyk", "ojg", "ubr", "cyc", "oja", "cyE", "ojD", "cyC", "cyB", "czg", "ojr", "cza", - "czD", "czr", "xBc", "ykq", "xBE", "ykn", "xBC", "xBB", "uDc", "xBq", "uDE", "xBn", "uDC", - "uDB", "obc", "uDq", "obE", "uDn", "obC", "obB", "cjc", "obq", "cjE", "obn", "cjC", "cjB", - "cjq", "cjn", "xAo", "ykf", "xAm", "xAl", "uBo", "xAv", "uBm", "uBl", "oDo", "uBv", "oDm", - "oDl", "cbo", "oDv", "cbm", "cbl", "xAe", "xAd", "uAu", "uAt", "oBu", "oBt", "wpA", "yes", - "zFi", "wok", "yeg", "zFb", "woc", "yea", "woE", "yeD", "woC", "woB", "thA", "wqs", "yfi", - "tgk", "wqg", "yfb", "tgc", "wqa", "tgE", "wqD", "tgC", "tgB", "mxA", "tis", "wri", "mwk", - "tig", "wrb", "mwc", "tia", "mwE", "tiD", "mwC", "mwB", "FxA", "mys", "tji", "Fwk", "myg", - "tjb", "Fwc", "mya", "FwE", "myD", "FwC", "Fys", "mzi", "Fyg", "mzb", "Fya", "FyD", "Fzi", - "Fzb", "yuk", "zhg", "hjs", "yuc", "zha", "hbw", "yuE", "zhD", "hDy", "yuC", "yuB", "wmk", - "ydg", "zEr", "xqk", "wmc", "zhr", "xqc", "yva", "ydD", "xqE", "wmC", "xqC", "wmB", "xqB", - "tak", "wng", "ydr", "vik", "tac", "wna", "vic", "xra", "wnD", "viE", "taC", "viC", "taB", - "viB", "mik", "tbg", "wnr", "qyk", "mic", "tba", "qyc", "vja", "tbD", "qyE", "miC", "qyC", - "miB", "qyB", "Eyk", "mjg", "tbr", "hyk", "Eyc", "mja", "hyc", "qza", "mjD", "hyE", "EyC", - "hyC", "EyB", "Ezg", "mjr", "hzg", "Eza", "hza", "EzD", "hzD", "Ezr", "ytc", "zgq", "grw", - "ytE", "zgn", "gny", "ytC", "glz", "ytB", "wlc", "ycq", "xnc", "wlE", "ycn", "xnE", "ytn", - "xnC", "wlB", "xnB", "tDc", "wlq", "vbc", "tDE", "wln", "vbE", "xnn", "vbC", "tDB", "vbB", - "mbc", "tDq", "qjc", "mbE", "tDn", "qjE", "vbn", "qjC", "mbB", "qjB", "Ejc", "mbq", "gzc", - "EjE", "mbn", "gzE", "qjn", "gzC", "EjB", "gzB", "Ejq", "gzq", "Ejn", "gzn", "yso", "zgf", - "gfy", "ysm", "gdz", "ysl", "wko", "ycf", "xlo", "ysv", "xlm", "wkl", "xll", "tBo", "wkv", - "vDo", "tBm", "vDm", "tBl", "vDl", "mDo", "tBv", "qbo", "vDv", "qbm", "mDl", "qbl", "Ebo", - "mDv", "gjo", "Ebm", "gjm", "Ebl", "gjl", "Ebv", "gjv", "yse", "gFz", "ysd", "wke", "xku", - "wkd", "xkt", "tAu", "vBu", "tAt", "vBt", "mBu", "qDu", "mBt", "qDt", "EDu", "gbu", "EDt", - "gbt", "ysF", "wkF", "xkh", "tAh", "vAx", "mAx", "qBx", "wek", "yFg", "zCr", "wec", "yFa", - "weE", "yFD", "weC", "weB", "sqk", "wfg", "yFr", "sqc", "wfa", "sqE", "wfD", "sqC", "sqB", - "lik", "srg", "wfr", "lic", "sra", "liE", "srD", "liC", "liB", "Cyk", "ljg", "srr", "Cyc", - "lja", "CyE", "ljD", "CyC", "CyB", "Czg", "ljr", "Cza", "CzD", "Czr", "yhc", "zaq", "arw", - "yhE", "zan", "any", "yhC", "alz", "yhB", "wdc", "yEq", "wvc", "wdE", "yEn", "wvE", "yhn", - "wvC", "wdB", "wvB", "snc", "wdq", "trc", "snE", "wdn", "trE", "wvn", "trC", "snB", "trB", - "lbc", "snq", "njc", "lbE", "snn", "njE", "trn", "njC", "lbB", "njB", "Cjc", "lbq", "azc", - "CjE", "lbn", "azE", "njn", "azC", "CjB", "azB", "Cjq", "azq", "Cjn", "azn", "zio", "irs", - "rfy", "zim", "inw", "rdz", "zil", "ily", "ikz", "ygo", "zaf", "afy", "yxo", "ziv", "ivy", - "adz", "yxm", "ygl", "itz", "yxl", "wco", "yEf", "wto", "wcm", "xvo", "yxv", "wcl", "xvm", - "wtl", "xvl", "slo", "wcv", "tno", "slm", "vro", "tnm", "sll", "vrm", "tnl", "vrl", "lDo", - "slv", "nbo", "lDm", "rjo", "nbm", "lDl", "rjm", "nbl", "rjl", "Cbo", "lDv", "ajo", "Cbm", - "izo", "ajm", "Cbl", "izm", "ajl", "izl", "Cbv", "ajv", "zie", "ifw", "rFz", "zid", "idy", - "icz", "yge", "aFz", "ywu", "ygd", "ihz", "ywt", "wce", "wsu", "wcd", "xtu", "wst", "xtt", - "sku", "tlu", "skt", "vnu", "tlt", "vnt", "lBu", "nDu", "lBt", "rbu", "nDt", "rbt", "CDu", - "abu", "CDt", "iju", "abt", "ijt", "ziF", "iFy", "iEz", "ygF", "ywh", "wcF", "wsh", "xsx", - "skh", "tkx", "vlx", "lAx", "nBx", "rDx", "CBx", "aDx", "ibx", "iCz", "wFc", "yCq", "wFE", - "yCn", "wFC", "wFB", "sfc", "wFq", "sfE", "wFn", "sfC", "sfB", "krc", "sfq", "krE", "sfn", - "krC", "krB", "Bjc", "krq", "BjE", "krn", "BjC", "BjB", "Bjq", "Bjn", "yao", "zDf", "Dfy", - "yam", "Ddz", "yal", "wEo", "yCf", "who", "wEm", "whm", "wEl", "whl", "sdo", "wEv", "svo", - "sdm", "svm", "sdl", "svl", "kno", "sdv", "lro", "knm", "lrm", "knl", "lrl", "Bbo", "knv", - "Djo", "Bbm", "Djm", "Bbl", "Djl", "Bbv", "Djv", "zbe", "bfw", "npz", "zbd", "bdy", "bcz", - "yae", "DFz", "yiu", "yad", "bhz", "yit", "wEe", "wgu", "wEd", "wxu", "wgt", "wxt", "scu", - "stu", "sct", "tvu", "stt", "tvt", "klu", "lnu", "klt", "nru", "lnt", "nrt", "BDu", "Dbu", - "BDt", "bju", "Dbt", "bjt", "jfs", "rpy", "jdw", "roz", "jcy", "jcj", "zbF", "bFy", "zjh", - "jhy", "bEz", "jgz", "yaF", "yih", "yyx", "wEF", "wgh", "wwx", "xxx", "sch", "ssx", "ttx", - "vvx", "kkx", "llx", "nnx", "rrx", "BBx", "DDx", "bbx", "jFw", "rmz", "jEy", "jEj", "bCz", - "jaz", "jCy", "jCj", "jBj", "wCo", "wCm", "wCl", "sFo", "wCv", "sFm", "sFl", "kfo", "sFv", - "kfm", "kfl", "Aro", "kfv", "Arm", "Arl", "Arv", "yDe", "Bpz", "yDd", "wCe", "wau", "wCd", - "wat", "sEu", "shu", "sEt", "sht", "kdu", "kvu", "kdt", "kvt", "Anu", "Bru", "Ant", "Brt", - "zDp", "Dpy", "Doz", "yDF", "ybh", "wCF", "wah", "wix", "sEh", "sgx", "sxx", "kcx", "ktx", - "lvx", "Alx", "Bnx", "Drx", "bpw", "nuz", "boy", "boj", "Dmz", "bqz", "jps", "ruy", "jow", - "ruj", "joi", "job", "bmy", "jqy", "bmj", "jqj", "jmw", "rtj", "jmi", "jmb", "blj", "jnj", - "jli", "jlb", "jkr", "sCu", "sCt", "kFu", "kFt", "Afu", "Aft", "wDh", "sCh", "sax", "kEx", - "khx", "Adx", "Avx", "Buz", "Duy", "Duj", "buw", "nxj", "bui", "bub", "Dtj", "bvj", "jus", - "rxi", "jug", "rxb", "jua", "juD", "bti", "jvi", "btb", "jvb", "jtg", "rwr", "jta", "jtD", - "bsr", "jtr", "jsq", "jsn", "Bxj", "Dxi", "Dxb", "bxg", "nyr", "bxa", "bxD", "Dwr", "bxr", - "bwq", "bwn", "pjk", "urw", "ejA", "pbs", "uny", "ebk", "pDw", "ulz", "eDs", "pBy", "eBw", - "zfc", "fjk", "prw", "zfE", "fbs", "pny", "zfC", "fDw", "plz", "zfB", "fBy", "yrc", "zfq", - "frw", "yrE", "zfn", "fny", "yrC", "flz", "yrB", "xjc", "yrq", "xjE", "yrn", "xjC", "xjB", - "uzc", "xjq", "uzE", "xjn", "uzC", "uzB", "pzc", "uzq", "pzE", "uzn", "pzC", "djA", "ors", - "ufy", "dbk", "onw", "udz", "dDs", "oly", "dBw", "okz", "dAy", "zdo", "drs", "ovy", "zdm", - "dnw", "otz", "zdl", "dly", "dkz", "yno", "zdv", "dvy", "ynm", "dtz", "ynl", "xbo", "ynv", - "xbm", "xbl", "ujo", "xbv", "ujm", "ujl", "ozo", "ujv", "ozm", "ozl", "crk", "ofw", "uFz", - "cns", "ody", "clw", "ocz", "cky", "ckj", "zcu", "cvw", "ohz", "zct", "cty", "csz", "ylu", - "cxz", "ylt", "xDu", "xDt", "ubu", "ubt", "oju", "ojt", "cfs", "oFy", "cdw", "oEz", "ccy", - "ccj", "zch", "chy", "cgz", "ykx", "xBx", "uDx", "cFw", "oCz", "cEy", "cEj", "caz", "cCy", - "cCj", "FjA", "mrs", "tfy", "Fbk", "mnw", "tdz", "FDs", "mly", "FBw", "mkz", "FAy", "zFo", - "Frs", "mvy", "zFm", "Fnw", "mtz", "zFl", "Fly", "Fkz", "yfo", "zFv", "Fvy", "yfm", "Ftz", - "yfl", "wro", "yfv", "wrm", "wrl", "tjo", "wrv", "tjm", "tjl", "mzo", "tjv", "mzm", "mzl", - "qrk", "vfw", "xpz", "hbA", "qns", "vdy", "hDk", "qlw", "vcz", "hBs", "qky", "hAw", "qkj", - "hAi", "Erk", "mfw", "tFz", "hrk", "Ens", "mdy", "hns", "qty", "mcz", "hlw", "Eky", "hky", - "Ekj", "hkj", "zEu", "Evw", "mhz", "zhu", "zEt", "hvw", "Ety", "zht", "hty", "Esz", "hsz", - "ydu", "Exz", "yvu", "ydt", "hxz", "yvt", "wnu", "xru", "wnt", "xrt", "tbu", "vju", "tbt", - "vjt", "mju", "mjt", "grA", "qfs", "vFy", "gnk", "qdw", "vEz", "gls", "qcy", "gkw", "qcj", - "gki", "gkb", "Efs", "mFy", "gvs", "Edw", "mEz", "gtw", "qgz", "gsy", "Ecj", "gsj", "zEh", - "Ehy", "zgx", "gxy", "Egz", "gwz", "ycx", "ytx", "wlx", "xnx", "tDx", "vbx", "mbx", "gfk", - "qFw", "vCz", "gds", "qEy", "gcw", "qEj", "gci", "gcb", "EFw", "mCz", "ghw", "EEy", "ggy", - "EEj", "ggj", "Eaz", "giz", "gFs", "qCy", "gEw", "qCj", "gEi", "gEb", "ECy", "gay", "ECj", - "gaj", "gCw", "qBj", "gCi", "gCb", "EBj", "gDj", "gBi", "gBb", "Crk", "lfw", "spz", "Cns", - "ldy", "Clw", "lcz", "Cky", "Ckj", "zCu", "Cvw", "lhz", "zCt", "Cty", "Csz", "yFu", "Cxz", - "yFt", "wfu", "wft", "sru", "srt", "lju", "ljt", "arA", "nfs", "tpy", "ank", "ndw", "toz", - "als", "ncy", "akw", "ncj", "aki", "akb", "Cfs", "lFy", "avs", "Cdw", "lEz", "atw", "ngz", - "asy", "Ccj", "asj", "zCh", "Chy", "zax", "axy", "Cgz", "awz", "yEx", "yhx", "wdx", "wvx", - "snx", "trx", "lbx", "rfk", "vpw", "xuz", "inA", "rds", "voy", "ilk", "rcw", "voj", "iks", - "rci", "ikg", "rcb", "ika", "afk", "nFw", "tmz", "ivk", "ads", "nEy", "its", "rgy", "nEj", - "isw", "aci", "isi", "acb", "isb", "CFw", "lCz", "ahw", "CEy", "ixw", "agy", "CEj", "iwy", - "agj", "iwj", "Caz", "aiz", "iyz", "ifA", "rFs", "vmy", "idk", "rEw", "vmj", "ics", "rEi", - "icg", "rEb", "ica", "icD", "aFs", "nCy", "ihs", "aEw", "nCj", "igw", "raj", "igi", "aEb", - "igb", "CCy", "aay", "CCj", "iiy", "aaj", "iij", "iFk", "rCw", "vlj", "iEs", "rCi", "iEg", - "rCb", "iEa", "iED", "aCw", "nBj", "iaw", "aCi", "iai", "aCb", "iab", "CBj", "aDj", "ibj", - "iCs", "rBi", "iCg", "rBb", "iCa", "iCD", "aBi", "iDi", "aBb", "iDb", "iBg", "rAr", "iBa", - "iBD", "aAr", "iBr", "iAq", "iAn", "Bfs", "kpy", "Bdw", "koz", "Bcy", "Bcj", "Bhy", "Bgz", - "yCx", "wFx", "sfx", "krx", "Dfk", "lpw", "suz", "Dds", "loy", "Dcw", "loj", "Dci", "Dcb", - "BFw", "kmz", "Dhw", "BEy", "Dgy", "BEj", "Dgj", "Baz", "Diz", "bfA", "nps", "tuy", "bdk", - "now", "tuj", "bcs", "noi", "bcg", "nob", "bca", "bcD", "DFs", "lmy", "bhs", "DEw", "lmj", - "bgw", "DEi", "bgi", "DEb", "bgb", "BCy", "Day", "BCj", "biy", "Daj", "bij", "rpk", "vuw", - "xxj", "jdA", "ros", "vui", "jck", "rog", "vub", "jcc", "roa", "jcE", "roD", "jcC", "bFk", - "nmw", "ttj", "jhk", "bEs", "nmi", "jgs", "rqi", "nmb", "jgg", "bEa", "jga", "bED", "jgD", - "DCw", "llj", "baw", "DCi", "jiw", "bai", "DCb", "jii", "bab", "jib", "BBj", "DDj", "bbj", - "jjj", "jFA", "rms", "vti", "jEk", "rmg", "vtb", "jEc", "rma", "jEE", "rmD", "jEC", "jEB", - "bCs", "nli", "jas", "bCg", "nlb", "jag", "rnb", "jaa", "bCD", "jaD", "DBi", "bDi", "DBb", - "jbi", "bDb", "jbb", "jCk", "rlg", "vsr", "jCc", "rla", "jCE", "rlD", "jCC", "jCB", "bBg", - "nkr", "jDg", "bBa", "jDa", "bBD", "jDD", "DAr", "bBr", "jDr", "jBc", "rkq", "jBE", "rkn", - "jBC", "jBB", "bAq", "jBq", "bAn", "jBn", "jAo", "rkf", "jAm", "jAl", "bAf", "jAv", "Apw", - "kez", "Aoy", "Aoj", "Aqz", "Bps", "kuy", "Bow", "kuj", "Boi", "Bob", "Amy", "Bqy", "Amj", - "Bqj", "Dpk", "luw", "sxj", "Dos", "lui", "Dog", "lub", "Doa", "DoD", "Bmw", "ktj", "Dqw", - "Bmi", "Dqi", "Bmb", "Dqb", "Alj", "Bnj", "Drj", "bpA", "nus", "txi", "bok", "nug", "txb", - "boc", "nua", "boE", "nuD", "boC", "boB", "Dms", "lti", "bqs", "Dmg", "ltb", "bqg", "nvb", - "bqa", "DmD", "bqD", "Bli", "Dni", "Blb", "bri", "Dnb", "brb", "ruk", "vxg", "xyr", "ruc", - "vxa", "ruE", "vxD", "ruC", "ruB", "bmk", "ntg", "twr", "jqk", "bmc", "nta", "jqc", "rva", - "ntD", "jqE", "bmC", "jqC", "bmB", "jqB", "Dlg", "lsr", "bng", "Dla", "jrg", "bna", "DlD", - "jra", "bnD", "jrD", "Bkr", "Dlr", "bnr", "jrr", "rtc", "vwq", "rtE", "vwn", "rtC", "rtB", - "blc", "nsq", "jnc", "blE", "nsn", "jnE", "rtn", "jnC", "blB", "jnB", "Dkq", "blq", "Dkn", - "jnq", "bln", "jnn", "rso", "vwf", "rsm", "rsl", "bko", "nsf", "jlo", "bkm", "jlm", "bkl", - "jll", "Dkf", "bkv", "jlv", "rse", "rsd", "bke", "jku", "bkd", "jkt", "Aey", "Aej", "Auw", - "khj", "Aui", "Aub", "Adj", "Avj", "Bus", "kxi", "Bug", "kxb", "Bua", "BuD", "Ati", "Bvi", - "Atb", "Bvb", "Duk", "lxg", "syr", "Duc", "lxa", "DuE", "lxD", "DuC", "DuB", "Btg", "kwr", - "Dvg", "lxr", "Dva", "BtD", "DvD", "Asr", "Btr", "Dvr", "nxc", "tyq", "nxE", "tyn", "nxC", - "nxB", "Dtc", "lwq", "bvc", "nxq", "lwn", "bvE", "DtC", "bvC", "DtB", "bvB", "Bsq", "Dtq", - "Bsn", "bvq", "Dtn", "bvn", "vyo", "xzf", "vym", "vyl", "nwo", "tyf", "rxo", "nwm", "rxm", - "nwl", "rxl", "Dso", "lwf", "bto", "Dsm", "jvo", "btm", "Dsl", "jvm", "btl", "jvl", "Bsf", - "Dsv", "btv", "jvv", "vye", "vyd", "nwe", "rwu", "nwd", "rwt", "Dse", "bsu", "Dsd", "jtu", - "bst", "jtt", "vyF", "nwF", "rwh", "DsF", "bsh", "jsx", "Ahi", "Ahb", "Axg", "kir", "Axa", - "AxD", "Agr", "Axr", "Bxc", "kyq", "BxE", "kyn", "BxC", "BxB", "Awq", "Bxq", "Awn", "Bxn", - "lyo", "szf", "lym", "lyl", "Bwo", "kyf", "Dxo", "lyv", "Dxm", "Bwl", "Dxl", "Awf", "Bwv", - "Dxv", "tze", "tzd", "lye", "nyu", "lyd", "nyt", "Bwe", "Dwu", "Bwd", "bxu", "Dwt", "bxt", - "tzF", "lyF", "nyh", "BwF", "Dwh", "bwx", "Aiq", "Ain", "Ayo", "kjf", "Aym", "Ayl", "Aif", - "Ayv", "kze", "kzd", "Aye", "Byu", "Ayd", "Byt", "szp" - }; - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/PDF417TruncatedSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/PDF417TruncatedSymbology.cs deleted file mode 100644 index 95b869f7..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/PDF417TruncatedSymbology.cs +++ /dev/null @@ -1,47 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - class PDF417TruncatedSymbology : PDF417Symbology - { - /// - /// Initializes a new instance of the class. - /// - public PDF417TruncatedSymbology() - : base() - { - m_type = TrueSymbologyType.PDF417Truncated; - m_compact = true; - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public PDF417TruncatedSymbology(SymbologyDrawing prototype) - : base(prototype) - { - m_type = TrueSymbologyType.PDF417Truncated; - m_compact = true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "Truncated PDF417 symbology allows a maximum data size of 1850 text characters, or 2710 digits.\n"; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/QRBitStream.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/QRBitStream.cs deleted file mode 100644 index 2f0d405b..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/QRBitStream.cs +++ /dev/null @@ -1,117 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Stores bits (not bytes) and allows accumulation of bits in groups - /// with length not equal to byte length (not equal to 8). - /// - class QRBitStream - { - private char[] m_bits; - - public QRBitStream() - { - } - - public QRBitStream(int bitCount, int value) - { - m_bits = new char[bitCount]; - int mask = 1 << (bitCount - 1); - for (int i = 0; i < bitCount; i++) - { - if ((value & mask) != 0) - m_bits[i] = '1'; - else - m_bits[i] = '0'; - - mask = mask >> 1; - } - } - - public void Append(QRBitStream stream) - { - if (stream == null || stream.m_bits == null) - return; - - if (m_bits == null) - { - m_bits = new char[stream.m_bits.Length]; - Array.Copy(stream.m_bits, m_bits, m_bits.Length); - return; - } - - char[] newBits = new char[m_bits.Length + stream.m_bits.Length]; - Array.Copy(m_bits, newBits, m_bits.Length); - Array.Copy(stream.m_bits, 0, newBits, m_bits.Length, stream.m_bits.Length); - - m_bits = newBits; - } - - public void AppendNumber(int bitCount, int value) - { - Append(new QRBitStream(bitCount, value)); - } - - public int Length - { - get - { - if (m_bits == null) - return 0; - - return m_bits.Length; - } - } - - public byte[] GetBytes() - { - byte[] bytes = new byte[(Length + 7) / 8]; - int byteCount = Length / 8; - - int dataIndex = 0; - for (int i = 0; i < byteCount; i++) - { - byte v = 0; - - for (int j = 0; j < 8; j++) - { - v = (byte)(v << 1); - - if (m_bits[dataIndex] == '1') - v++; - - dataIndex++; - } - - bytes[i] = v; - } - - if ((Length & 7) != 0) - { - byte v = 0; - - for (int j = 0; j < (Length & 7); j++) - { - v = (byte)(v << 1); - if (m_bits[dataIndex] == '1') - v++; - - dataIndex++; - } - - bytes[byteCount] = v; - } - - return bytes; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/QREncodeMode.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/QREncodeMode.cs deleted file mode 100644 index f77e2d96..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/QREncodeMode.cs +++ /dev/null @@ -1,43 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// QR Input data chunk encoding mode. - /// - enum QREncodeMode - { - /// - /// Incorrect/Undefined mode - /// - Incorrect = -1, - - /// - /// Numeric mode - /// - Numeric = 0, - - /// - /// Alphabet-numeric mode - /// - AlphaNumeric, - - /// - /// 8-bit data mode - /// - Mode8, - - /// - /// Kanji/Kana (shift-jis) mode - /// - Kanji, - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/QRInputData.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/QRInputData.cs deleted file mode 100644 index 7a39a189..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/QRInputData.cs +++ /dev/null @@ -1,804 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using System.Collections; - -namespace BarcodeWriter.Core.Internal -{ - class QRInputData - { - private class InputDataChunk - { - public QREncodeMode m_encodeMode; - public byte[] m_data; - public QRBitStream m_stream; - - public InputDataChunk(QREncodeMode mode, byte[] data, int dataLength) - { - m_encodeMode = mode; - m_data = new byte[dataLength]; - Array.Copy(data, m_data, m_data.Length); - } - }; - - private static int[] alphaNumericTable = - { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, - -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 - }; - - public int m_symbolVersion; - public QRErrorCorrectionLevel m_ecLevel; - private ArrayList m_chunks; - - /// - /// Need to insert FNC1 prefix? - /// - public bool FNC1Mode = false; - - /// - /// Initializes a new instance of the class. - /// - public QRInputData() - { - init(0, QRErrorCorrectionLevel.Low); - } - - /// - /// Initializes a new instance of the class. - /// - /// The minimum version (may be increased if neccessary). - /// The error correction level. - public QRInputData(int version, QRErrorCorrectionLevel level) - { - init(version, level); - } - - /// - /// Appends the data. - /// - /// The encoded mode. - /// The data to append. - /// - public bool AppendData(QREncodeMode mode, byte[] data) - { - if (!validateData(mode, data.Length, data)) - return false; - - InputDataChunk chunk = new InputDataChunk(mode, data, data.Length); - if (chunk == null) - return false; - - m_chunks.Add(chunk); - return true; - } - - private static bool validateData(QREncodeMode mode, int dataLength, byte[] data) - { - if (dataLength <= 0) - return false; - - switch (mode) - { - case QREncodeMode.Numeric: - return validateNumericData(dataLength, data); - - case QREncodeMode.AlphaNumeric: - return validateAlphaNumericData(dataLength, data); - - case QREncodeMode.Kanji: - return validateKanjiData(dataLength, data); - - case QREncodeMode.Mode8: - return true; - } - - return false; - } - - /// - /// Gets the bytes of all bit streams. Bits get padded if needed. - /// - /// - public byte[] GetBytes() - { - QRBitStream bstream = getCompoundBitStream(); - if (bstream == null) - return null; - - return bstream.GetBytes(); - } - - private int estimateBitStreamSize(int version) - { - int bits = 0; - - if (FNC1Mode) - bits += 4; //FNC1 prefix = 4 bits - - foreach (InputDataChunk chunk in m_chunks) - bits += estimateBitStreamSize(chunk, version); - - return bits; - } - - /// - /// Merges all bit streams. - /// - /// - private QRBitStream mergeBitStreams() - { - if (!convertData()) - return null; - - QRBitStream bstream = new QRBitStream(); - if (bstream == null) - return null; - - if (FNC1Mode) - bstream.AppendNumber(4, 5);//FNC1 = 0101 - - foreach (InputDataChunk chunk in m_chunks) - bstream.Append(chunk.m_stream); - - return bstream; - } - - /// - /// Gets the compound bit stream (merged from all bit streams and padded). - /// - /// - private QRBitStream getCompoundBitStream() - { - QRBitStream bstream = mergeBitStreams(); - if (bstream == null) - return null; - - QRBitStream padding = createPaddingBitStream(); - if (padding != null) - bstream.Append(padding); - - return bstream; - } - - /// - /// Estimates the numeric chunk bit count. - /// - private static int estimateNumericBitCount(int numericByteCount) - { - int w = numericByteCount / 3; - int bitCount = w * 10; - switch (numericByteCount - w * 3) - { - case 1: - bitCount += 4; - break; - - case 2: - bitCount += 7; - break; - } - - return bitCount; - } - - /// - /// Estimates the alpha numeric chunk bit count. - /// - /// - private static int estimateAlphaNumericBitCount(int alphaNumericByteCount) - { - int w = alphaNumericByteCount / 2; - int bitCount = w * 11; - if ((alphaNumericByteCount & 1) != 0) - bitCount += 6; - - - return bitCount; - } - - /// - /// Estimates the mode8 chunk bit count. - /// - /// - private static int estimateMode8BitCount(int byteCount) - { - return byteCount * 8; - } - - /// - /// Estimates the kanji chunk bit count. - /// - /// - private static int estimateKanjiBitCount(int size) - { - return (size / 2) * 13; - } - - private static int getValueForAlphaNumeric(int c) - { - if ((c & 0x80) != 0) - return -1; - - return alphaNumericTable[c]; - } - - private void init(int version, QRErrorCorrectionLevel level) - { - if (version < 0 || version > QRSpec.MaximumSymbolVersion || level < QRErrorCorrectionLevel.Low || level > QRErrorCorrectionLevel.High) - throw new BarcodeException("Incorrect version or error correction level"); - - m_chunks = new ArrayList(); - m_symbolVersion = version; - m_ecLevel = level; - } - - /// - /// Creates the bit stream from input data chunks. - /// - /// - private int createBitStream() - { - int bits = 0; - - foreach (InputDataChunk chunk in m_chunks) - bits += encodeBitStream(chunk, m_symbolVersion); - - return bits; - } - - /// - /// Converts the data to a bit stream. - /// - /// - private bool convertData() - { - int ver = estimateVersion(); - if (ver > m_symbolVersion) - m_symbolVersion = ver; - - for ( ; ; ) - { - int bits = createBitStream(); - ver = QRSpec.GetMinimumVersion((bits + 7) / 8, m_ecLevel); - if (ver < 0) - return false; - else if (ver > m_symbolVersion) - m_symbolVersion = ver; - else - break; - } - - return true; - } - - /// - /// Encodes input data chunk into a bit stream. - /// - /// - private static int encodeBitStream(InputDataChunk chunk, int version) - { - chunk.m_stream = null; - - int words = QRSpec.GetMaximumWords(chunk.m_encodeMode, version); - if (chunk.m_data.Length > words) - { - byte[] temp = new byte[chunk.m_data.Length - words]; - Array.Copy(chunk.m_data, words, temp, 0, chunk.m_data.Length - words); - - InputDataChunk st1 = new InputDataChunk(chunk.m_encodeMode, chunk.m_data, words); - InputDataChunk st2 = new InputDataChunk(chunk.m_encodeMode, temp, chunk.m_data.Length - words); - - encodeBitStream(st1, version); - encodeBitStream(st2, version); - - chunk.m_stream = new QRBitStream(); - chunk.m_stream.Append(st1.m_stream); - chunk.m_stream.Append(st2.m_stream); - } - else - { - switch (chunk.m_encodeMode) - { - case QREncodeMode.Numeric: - encodeNumeric(chunk, version); - break; - - case QREncodeMode.AlphaNumeric: - encodeAlphaNumeric(chunk, version); - break; - - case QREncodeMode.Mode8: - encodeMode8(chunk, version); - break; - - case QREncodeMode.Kanji: - encodeModeKanji(chunk, version); - break; - } - } - - return chunk.m_stream.Length; - } - - private static bool validateAlphaNumericData(int size, byte[] data) - { - for (int i = 0; i < size; i++) - { - if (getValueForAlphaNumeric(data[i]) < 0) - return false; - } - - return true; - } - - private static void encodeAlphaNumeric(InputDataChunk chunk, int version) - { - int words = chunk.m_data.Length / 2; - chunk.m_stream = new QRBitStream(); - - int val = 0x2; - chunk.m_stream.AppendNumber(4, val); - - val = chunk.m_data.Length; - chunk.m_stream.AppendNumber(QRSpec.GetLengthIndicator(QREncodeMode.AlphaNumeric, version), val); - - for (int i = 0; i < words; i++) - { - val = getValueForAlphaNumeric(chunk.m_data[i * 2]) * 45; - val += getValueForAlphaNumeric(chunk.m_data[i * 2 + 1]); - chunk.m_stream.AppendNumber(11, val); - } - - if ((chunk.m_data.Length & 1) != 0) - { - val = getValueForAlphaNumeric(chunk.m_data[words * 2]); - chunk.m_stream.AppendNumber(6, val); - } - } - - private static bool validateNumericData(int size, byte[] data) - { - for (int i = 0; i < size; i++) - { - if (data[i] < '0' || data[i] > '9') - return false; - } - - return true; - } - - private static bool validateKanjiData(int size, byte[] data) - { - if ((size & 1) != 0) - return false; - - for (int i = 0; i < size; i += 2) - { - uint val = ((uint)data[i] << 8) | data[i + 1]; - if (val < 0x8140 || (val > 0x9ffc && val < 0xe040) || val > 0xebbf) - return false; - } - - return true; - } - - private static int estimateBitStreamSize(InputDataChunk chunk, int version) - { - if (version == 0) - version = 1; - - int bits = 0; - switch (chunk.m_encodeMode) - { - case QREncodeMode.Numeric: - bits = estimateNumericBitCount(chunk.m_data.Length); - break; - - case QREncodeMode.AlphaNumeric: - bits = estimateAlphaNumericBitCount(chunk.m_data.Length); - break; - - case QREncodeMode.Mode8: - bits = estimateMode8BitCount(chunk.m_data.Length); - break; - - case QREncodeMode.Kanji: - bits = estimateKanjiBitCount(chunk.m_data.Length); - break; - - default: - return 0; - } - - int l = QRSpec.GetLengthIndicator(chunk.m_encodeMode, version); - int m = 1 << l; - int num = (chunk.m_data.Length + m - 1) / m; - - // mode indicator (4bits) + length indicator - bits += num * (4 + l); - return bits; - } - - private QRBitStream createPaddingBitStream() - { - int bits = 0; - foreach (InputDataChunk chunk in m_chunks) - bits += chunk.m_stream.Length; - - int maxwords = QRSpec.GetDataLength(m_symbolVersion, m_ecLevel); - int maxbits = maxwords * 8; - QRBitStream bstream = null; - - if (maxbits - bits < 5) - { - if (maxbits == bits) - return null; - else - { - bstream = new QRBitStream(); - bstream.AppendNumber(maxbits - bits, 0); - return bstream; - } - } - - bits += 4; - int words = (bits + 7) / 8; - - bstream = new QRBitStream(); - bstream.AppendNumber(words * 8 - bits + 4, 0); - - for (int i = 0; i < maxwords - words; i++) - { - if ((i & 1) != 0) - bstream.AppendNumber(8, 0x11); - else - bstream.AppendNumber(8, 0xec); - } - - return bstream; - } - - /// - /// Estimates the minimum version required in order to encode data. - /// - /// - private int estimateVersion() - { - int version = 0; - int prevVersion; - - do - { - prevVersion = version; - - int bits = estimateBitStreamSize(prevVersion); - version = QRSpec.GetMinimumVersion((bits + 7) / 8, m_ecLevel); - if (version < 0) - return -1; - - } while (version > prevVersion); - - return version; - } - - private static void encodeNumeric(InputDataChunk chunk, int version) - { - int words = chunk.m_data.Length / 3; - chunk.m_stream = new QRBitStream(); - - int val = 0x1; - chunk.m_stream.AppendNumber(4, val); - - val = chunk.m_data.Length; - chunk.m_stream.AppendNumber(QRSpec.GetLengthIndicator(QREncodeMode.Numeric, version), val); - - for (int i = 0; i < words; i++) - { - val = (chunk.m_data[i * 3] - '0') * 100; - val += (chunk.m_data[i * 3 + 1] - '0') * 10; - val += (chunk.m_data[i * 3 + 2] - '0'); - - chunk.m_stream.AppendNumber(10, val); - } - - if (chunk.m_data.Length - words * 3 == 1) - { - val = chunk.m_data[words * 3] - '0'; - chunk.m_stream.AppendNumber(4, val); - } - else if (chunk.m_data.Length - words * 3 == 2) - { - val = (chunk.m_data[words * 3] - '0') * 10; - val += (chunk.m_data[words * 3 + 1] - '0'); - chunk.m_stream.AppendNumber(7, val); - } - } - - private static void encodeMode8(InputDataChunk chunk, int version) - { - chunk.m_stream = new QRBitStream(); - - int val = 0x4; - chunk.m_stream.AppendNumber(4, val); - - val = chunk.m_data.Length; - chunk.m_stream.AppendNumber(QRSpec.GetLengthIndicator(QREncodeMode.Mode8, version), val); - - for (int i = 0; i < chunk.m_data.Length; i++) - chunk.m_stream.AppendNumber(8, chunk.m_data[i]); - } - - private static void encodeModeKanji(InputDataChunk chunk, int version) - { - chunk.m_stream = new QRBitStream(); - - int val = 0x8; - chunk.m_stream.AppendNumber(4, val); - - val = chunk.m_data.Length / 2; - chunk.m_stream.AppendNumber(QRSpec.GetLengthIndicator(QREncodeMode.Kanji, version), val); - - for (int i = 0; i < chunk.m_data.Length; i += 2) - { - val = (int)(((uint)chunk.m_data[i] << 8) | chunk.m_data[i + 1]); - if (val <= 0x9ffc) - val -= 0x8140; - else - val -= 0xc140; - - int h = (val >> 8) * 0xc0; - val = (val & 0xff) + h; - - chunk.m_stream.AppendNumber(13, val); - } - } - - public bool BuildFromString(string s, QREncodeMode hint, bool caseSensitive) - { - if (!caseSensitive) - { - string newstr = toUpper(s, hint); - if (newstr == null) - return false; - - return splitString(newstr, hint); - } - - return splitString(s, hint); - } - - private static QREncodeMode identifyMode(string s, QREncodeMode hint) - { - if (s.Length == 0) - return QREncodeMode.Incorrect; - - if (isDigit((byte)s[0])) - return QREncodeMode.Numeric; - else if (isAlphaNumeric((byte)s[0])) - return QREncodeMode.AlphaNumeric; - else if (hint == QREncodeMode.Kanji && s.Length > 1) - { - uint word = ((uint)s[0] << 8) | s[1]; - if ((word >= 0x8140 && word <= 0x9ffc) || (word >= 0xe040 && word <= 0xebbf)) - return QREncodeMode.Kanji; - } - - return QREncodeMode.Mode8; - } - - private int eatNumericChunk(string s, QREncodeMode hint) - { - int chunkLength = 0; - for (int i = 0; i < s.Length && isDigit((byte)s[chunkLength]); i++) - chunkLength++; - - int li = QRSpec.GetLengthIndicator(QREncodeMode.Numeric, m_symbolVersion); - - QREncodeMode mode = identifyMode(s.Substring(chunkLength), hint); - if (mode == QREncodeMode.Mode8) - { - int dif = estimateNumericBitCount(chunkLength) + 4 + li + estimateMode8BitCount(1) - estimateMode8BitCount(chunkLength + 1); - if (dif > 0) - return eatMode8Chunk(s, hint); - } - else if (mode == QREncodeMode.AlphaNumeric) - { - int dif = estimateNumericBitCount(chunkLength) + 4 + li + estimateAlphaNumericBitCount(1) - estimateAlphaNumericBitCount(chunkLength + 1); - if (dif > 0) - return eatAlphaNumericChunk(s, hint); - } - - bool ret = AppendData(QREncodeMode.Numeric, Encoding.UTF8.GetBytes(s.Substring(0, chunkLength))); - if (!ret) - return -1; - - return chunkLength; - } - - private int eatAlphaNumericChunk(string s, QREncodeMode hint) - { - int liNumeric = QRSpec.GetLengthIndicator(QREncodeMode.Numeric, m_symbolVersion); - - int pos = 0; - while (pos < s.Length && isAlphaNumeric((byte)s[pos])) - { - if (isDigit((byte)s[pos])) - { - int numericPartPos = pos; - while (numericPartPos < s.Length && isDigit((byte)s[numericPartPos])) - numericPartPos++; - - int dif = estimateAlphaNumericBitCount(pos) + estimateNumericBitCount(numericPartPos - pos) + 4 + liNumeric - estimateAlphaNumericBitCount(numericPartPos); - if (dif < 0) - break; - else - pos = numericPartPos; - } - else - pos++; - } - - int liAlphaNumeric = QRSpec.GetLengthIndicator(QREncodeMode.AlphaNumeric, m_symbolVersion); - int chunkLength = pos; - if (pos != s.Length && !isAlphaNumeric((byte)s[pos])) - { - int dif = estimateAlphaNumericBitCount(chunkLength) + 4 + liAlphaNumeric + estimateMode8BitCount(1) - estimateMode8BitCount(chunkLength + 1); - if (dif > 0) - return eatMode8Chunk(s, hint); - } - - bool ret = AppendData(QREncodeMode.AlphaNumeric, Encoding.UTF8.GetBytes(s.Substring(0, chunkLength))); - if (!ret) - return -1; - - return chunkLength; - } - - private int eatMode8Chunk(string s, QREncodeMode hint) - { - int pos = 1; - while (pos < s.Length && s[pos] != '\0') - { - QREncodeMode mode = identifyMode(s.Substring(pos), hint); - if (mode == QREncodeMode.Kanji) - { - break; - } - if (mode == QREncodeMode.Numeric) - { - int numericChunkPos = pos; - while (numericChunkPos < s.Length && isDigit((byte)s[numericChunkPos])) - numericChunkPos++; - - int liNumeric = QRSpec.GetLengthIndicator(QREncodeMode.Numeric, m_symbolVersion); - int dif = estimateMode8BitCount(pos) + estimateNumericBitCount(numericChunkPos - pos) + 4 + liNumeric - estimateMode8BitCount(numericChunkPos); - if (dif < 0) - break; - else - pos = numericChunkPos; - } - else if (mode == QREncodeMode.AlphaNumeric) - { - int alphaNumericChunkPos = pos; - while (alphaNumericChunkPos < s.Length && isAlphaNumeric((byte)s[alphaNumericChunkPos])) - alphaNumericChunkPos++; - - int liAlphaNumeric = QRSpec.GetLengthIndicator(QREncodeMode.AlphaNumeric, m_symbolVersion); - int dif = estimateMode8BitCount(pos) + estimateAlphaNumericBitCount(alphaNumericChunkPos - pos) + 4 + liAlphaNumeric - estimateMode8BitCount(alphaNumericChunkPos); - if (dif < 0) - break; - else - pos = alphaNumericChunkPos; - } - else - pos++; - } - - //byte[] bytes = Encoding.ASCII.GetBytes(s.Substring(0, pos)); - - string ss = s.Substring(0, pos); - - byte[] bbytes = new byte[ss.Length]; - - int index = 0; - foreach (char c in ss.ToCharArray()){ - bbytes[index++] = (byte)c; - } - - bool ret = AppendData(QREncodeMode.Mode8, bbytes); - if (!ret) - return -1; - - return pos; - } - - private int eatKanjiChunk(string s, QREncodeMode hint) - { - int chunkLength = 0; - for (int i = 0; i < s.Length && identifyMode(s.Substring(i), hint) == QREncodeMode.Kanji; i++) - chunkLength += 2; - - bool ret = AppendData(QREncodeMode.Kanji, Encoding.UTF8.GetBytes(s.Substring(0, chunkLength))); - if (!ret) - return -1; - - return chunkLength; - } - - private bool splitString(string s, QREncodeMode hint) - { - if (s.Length == 0) - return true; - - QREncodeMode mode = identifyMode(s, hint); - int length = 0; - - if (mode == QREncodeMode.Numeric) - length = eatNumericChunk(s, hint); - else if (mode == QREncodeMode.AlphaNumeric) - length = eatAlphaNumericChunk(s, hint); - else if (mode == QREncodeMode.Kanji && hint == QREncodeMode.Kanji) - length = eatKanjiChunk(s, hint); - else - length = eatMode8Chunk(s, hint); - - if (length == 0) - return true; - - if (length < 0) - return false; - - return splitString(s.Substring(length), hint); - } - - private static string toUpper(string str, QREncodeMode hint) - { - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < str.Length; ) - { - QREncodeMode mode = identifyMode(str.Substring(i), hint); - if (mode == QREncodeMode.Kanji) - { - sb.Append(str[i]); - sb.Append(str[i + 1]); - i += 2; - } - else - { - if (str[i] >= 'a' && str[i] <= 'z') - sb.Append((char)((int)str[i] - 32)); - else - sb.Append(str[i]); - - i++; - } - } - - return sb.ToString(); - } - - private static bool isDigit(byte c) - { - return ((byte)(c - '0') < 10); - } - - private static bool isAlphaNumeric(byte c) - { - return (getValueForAlphaNumeric(c) >= 0); - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/QRReedSolomon.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/QRReedSolomon.cs deleted file mode 100644 index a785df3d..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/QRReedSolomon.cs +++ /dev/null @@ -1,169 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using System.Runtime.InteropServices; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Reed Solomon algorithm adapted for QR Code barcodes. - /// - class QRReedSolomon - { - private int m_bitsPerSymbol; - private int m_symbolsPerBlock; - - /// - /// Log lookup table. - /// - private byte[] m_alphaTo; - - /// - /// Antilog lookup table. - /// - private byte[] m_indexOf; - - /// - /// Generator polynomial. - /// - private byte[] m_genPoly; - - /// - /// Number of generator roots (equal to number of produced ecc). - /// - private int m_generatorRootCount; - - private int m_padByteCount; - - /// - /// Initializes a new instance of the class. - /// - /// Size of the symbol in bits. - /// Field generator polynomial coefficients. - /// The first root. - /// The primitive element to generate polynomial roots. - /// The root count. - /// The pad count. - public QRReedSolomon(int symbolSize, int fgpoly, int firstRoot, int primitive, int rootCount, int padCount) - { - if (symbolSize < 0 || symbolSize > 8) - return; - - if (firstRoot < 0 || firstRoot >= (1 << symbolSize)) - return; - - if (primitive <= 0 || primitive >= (1 << symbolSize)) - return; - - if (rootCount < 0 || rootCount >= (1 << symbolSize)) - return; - - if (padCount < 0 || padCount >= ((1 << symbolSize) - 1 - rootCount)) - return; - - m_bitsPerSymbol = symbolSize; - m_symbolsPerBlock = (1 << symbolSize) - 1; - m_padByteCount = padCount; - - m_alphaTo = new byte[m_symbolsPerBlock + 1]; - if (m_alphaTo == null) - return; - - m_indexOf = new byte[m_symbolsPerBlock + 1]; - if (m_indexOf == null) - return; - - // Generate Galois field lookup tables - m_indexOf[0] = (byte)m_symbolsPerBlock; - m_alphaTo[m_symbolsPerBlock] = 0; - - int sr = 1; - for (int i = 0; i < m_symbolsPerBlock; i++) - { - m_indexOf[sr] = (byte)i; - m_alphaTo[i] = (byte)sr; - sr <<= 1; - if ((sr & (1 << symbolSize)) != 0) - sr ^= fgpoly; - sr &= m_symbolsPerBlock; - } - - if (sr != 1) - { - // field generator polynomial is not primitive. - return; - } - - // form RS code generator polynomial from its roots. - m_genPoly = new byte[rootCount + 1]; - if (m_genPoly == null) - return; - - m_generatorRootCount = rootCount; - - m_genPoly[0] = 1; - for (int i = 0, root = firstRoot * primitive; i < rootCount; i++, root += primitive) - { - m_genPoly[i + 1] = 1; - - for (int j = i; j > 0; j--) - { - if (m_genPoly[j] != 0) - m_genPoly[j] = (byte)(m_genPoly[j - 1] ^ m_alphaTo[modnn(m_indexOf[m_genPoly[j]] + root)]); - else - m_genPoly[j] = m_genPoly[j - 1]; - } - - m_genPoly[0] = m_alphaTo[modnn(m_indexOf[m_genPoly[0]] + root)]; - } - - for (int i = 0; i <= rootCount; i++) - m_genPoly[i] = m_indexOf[m_genPoly[i]]; - } - - /// - /// Produces the error correction codes for the given data. - /// - /// The data. - /// The array to put error correction codes to. - public void ProduceCodes(byte[] data, byte[] rsData) - { - Array.Clear(rsData, 0, m_generatorRootCount); - - for (int i = 0; i < m_symbolsPerBlock - m_generatorRootCount - m_padByteCount; i++) - { - int feedback = m_indexOf[data[i] ^ rsData[0]]; - if (feedback != m_symbolsPerBlock) - { - for (int j = 1; j < m_generatorRootCount; j++) - rsData[j] ^= m_alphaTo[modnn(feedback + m_genPoly[m_generatorRootCount - j])]; - } - - for (int pos = 0; pos < m_generatorRootCount - 1; pos++) - rsData[pos] = rsData[pos + 1]; - - if (feedback != m_symbolsPerBlock) - rsData[m_generatorRootCount - 1] = m_alphaTo[modnn(feedback + m_genPoly[0])]; - else - rsData[m_generatorRootCount - 1] = 0; - } - } - - private int modnn(int x) - { - while (x >= m_symbolsPerBlock) - { - x -= m_symbolsPerBlock; - x = (x >> m_bitsPerSymbol) + (x & m_symbolsPerBlock); - } - - return x; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/QRSpec.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/QRSpec.cs deleted file mode 100644 index 15de8cc6..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/QRSpec.cs +++ /dev/null @@ -1,644 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// QR Code specification in convenient format. The following data / specifications - /// are taken from - /// "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004) - /// or - /// "Automatic identification and data capture techniques -- QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006) - /// - class QRSpec - { - /// - /// Maximum version (size) of QR-code symbol. - /// - public const int MaximumSymbolVersion = 40; - - /// - /// Maximum width of a symbol - /// - public const int MaximumSymbolWidth = 177; - - /// - /// Cache of initial frames. - /// - private static byte[][] frames = new byte[][] - { - null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null - }; - - /// - /// Table of the capacity of symbols. - /// See Table 1 (pp.13) and Table 12-16 (pp.30-36), JIS X0510:2004. - /// - private struct SymbolCapacity - { - /// - /// Edge length of the symbol - /// - public int width; - - /// - /// Data capacity (bytes) - /// - public int words; - - /// - /// Remainder bits - /// - public int remainder; - - /// - /// Number of ECC code (bytes) - /// - public int[] ec; - - public SymbolCapacity(int width, int words, int remainder, int[] ec) - { - this.width = width; - this.words = words; - this.remainder = remainder; - this.ec = ec; - } - }; - - private static SymbolCapacity[] symbolCapacity = { - new SymbolCapacity( 0, 0, 0, new int[] { 0, 0, 0, 0}), - new SymbolCapacity(21, 26, 0, new int[] { 7, 10, 13, 17}), // 1 - new SymbolCapacity(25, 44, 7, new int[] { 10, 16, 22, 28}), - new SymbolCapacity(29, 70, 7, new int[] { 15, 26, 36, 44}), - new SymbolCapacity(33, 100, 7, new int[] { 20, 36, 52, 64}), - new SymbolCapacity(37, 134, 7, new int[] { 26, 48, 72, 88}), // 5 - new SymbolCapacity(41, 172, 7, new int[] { 36, 64, 96, 112}), - new SymbolCapacity(45, 196, 0, new int[] { 40, 72, 108, 130}), - new SymbolCapacity(49, 242, 0, new int[] { 48, 88, 132, 156}), - new SymbolCapacity(53, 292, 0, new int[] { 60, 110, 160, 192}), - new SymbolCapacity(57, 346, 0, new int[] { 72, 130, 192, 224}), //10 - new SymbolCapacity(61, 404, 0, new int[] { 80, 150, 224, 264}), - new SymbolCapacity(65, 466, 0, new int[] { 96, 176, 260, 308}), - new SymbolCapacity(69, 532, 0, new int[] {104, 198, 288, 352}), - new SymbolCapacity(73, 581, 3, new int[] {120, 216, 320, 384}), - new SymbolCapacity(77, 655, 3, new int[] {132, 240, 360, 432}), //15 - new SymbolCapacity(81, 733, 3, new int[] {144, 280, 408, 480}), - new SymbolCapacity(85, 815, 3, new int[] {168, 308, 448, 532}), - new SymbolCapacity(89, 901, 3, new int[] {180, 338, 504, 588}), - new SymbolCapacity(93, 991, 3, new int[] {196, 364, 546, 650}), - new SymbolCapacity(97, 1085, 3, new int[] {224, 416, 600, 700}), //20 - new SymbolCapacity(101, 1156, 4, new int[] {224, 442, 644, 750}), - new SymbolCapacity(105, 1258, 4, new int[] {252, 476, 690, 816}), - new SymbolCapacity(109, 1364, 4, new int[] {270, 504, 750, 900}), - new SymbolCapacity(113, 1474, 4, new int[] {300, 560, 810, 960}), - new SymbolCapacity(117, 1588, 4, new int[] {312, 588, 870, 1050}), //25 - new SymbolCapacity(121, 1706, 4, new int[] {336, 644, 952, 1110}), - new SymbolCapacity(125, 1828, 4, new int[] {360, 700, 1020, 1200}), - new SymbolCapacity(129, 1921, 3, new int[] {390, 728, 1050, 1260}), - new SymbolCapacity(133, 2051, 3, new int[] {420, 784, 1140, 1350}), - new SymbolCapacity(137, 2185, 3, new int[] {450, 812, 1200, 1440}), //30 - new SymbolCapacity(141, 2323, 3, new int[] {480, 868, 1290, 1530}), - new SymbolCapacity(145, 2465, 3, new int[] {510, 924, 1350, 1620}), - new SymbolCapacity(149, 2611, 3, new int[] {540, 980, 1440, 1710}), - new SymbolCapacity(153, 2761, 3, new int[] {570, 1036, 1530, 1800}), - new SymbolCapacity(157, 2876, 0, new int[] {570, 1064, 1590, 1890}), //35 - new SymbolCapacity(161, 3034, 0, new int[] {600, 1120, 1680, 1980}), - new SymbolCapacity(165, 3196, 0, new int[] {630, 1204, 1770, 2100}), - new SymbolCapacity(169, 3362, 0, new int[] {660, 1260, 1860, 2220}), - new SymbolCapacity(173, 3532, 0, new int[] {720, 1316, 1950, 2310}), - new SymbolCapacity(177, 3706, 0, new int[] {750, 1372, 2040, 2430}) //40 - }; - - private static int[][] lengthIndicatorTable = - { - new int[] {10, 12, 14}, - new int[] { 9, 11, 13}, - new int[] { 8, 16, 16}, - new int[] { 8, 10, 12} - }; - - /// - /// Table of the error correction code (Reed-Solomon block) - /// See Table 12-16 (pp.30-36), JIS X0510:2004. - /// - private static int[][][] eccTable = { - new int[][] { new int[] { 0, 0}, new int[] { 0, 0}, new int[] { 0, 0}, new int[] { 0, 0}}, - new int[][] { new int[] { 1, 0}, new int[] { 1, 0}, new int[] { 1, 0}, new int[] { 1, 0}}, // 1 - new int[][] { new int[] { 1, 0}, new int[] { 1, 0}, new int[] { 1, 0}, new int[] { 1, 0}}, - new int[][] { new int[] { 1, 0}, new int[] { 1, 0}, new int[] { 2, 0}, new int[] { 2, 0}}, - new int[][] { new int[] { 1, 0}, new int[] { 2, 0}, new int[] { 2, 0}, new int[] { 4, 0}}, - new int[][] { new int[] { 1, 0}, new int[] { 2, 0}, new int[] { 2, 2}, new int[] { 2, 2}}, // 5 - new int[][] { new int[] { 2, 0}, new int[] { 4, 0}, new int[] { 4, 0}, new int[] { 4, 0}}, - new int[][] { new int[] { 2, 0}, new int[] { 4, 0}, new int[] { 2, 4}, new int[] { 4, 1}}, - new int[][] { new int[] { 2, 0}, new int[] { 2, 2}, new int[] { 4, 2}, new int[] { 4, 2}}, - new int[][] { new int[] { 2, 0}, new int[] { 3, 2}, new int[] { 4, 4}, new int[] { 4, 4}}, - new int[][] { new int[] { 2, 2}, new int[] { 4, 1}, new int[] { 6, 2}, new int[] { 6, 2}}, //10 - new int[][] { new int[] { 4, 0}, new int[] { 1, 4}, new int[] { 4, 4}, new int[] { 3, 8}}, - new int[][] { new int[] { 2, 2}, new int[] { 6, 2}, new int[] { 4, 6}, new int[] { 7, 4}}, - new int[][] { new int[] { 4, 0}, new int[] { 8, 1}, new int[] { 8, 4}, new int[] {12, 4}}, - new int[][] { new int[] { 3, 1}, new int[] { 4, 5}, new int[] {11, 5}, new int[] {11, 5}}, - new int[][] { new int[] { 5, 1}, new int[] { 5, 5}, new int[] { 5, 7}, new int[] {11, 7}}, //15 - new int[][] { new int[] { 5, 1}, new int[] { 7, 3}, new int[] {15, 2}, new int[] { 3, 13}}, - new int[][] { new int[] { 1, 5}, new int[] {10, 1}, new int[] { 1, 15}, new int[] { 2, 17}}, - new int[][] { new int[] { 5, 1}, new int[] { 9, 4}, new int[] {17, 1}, new int[] { 2, 19}}, - new int[][] { new int[] { 3, 4}, new int[] { 3, 11}, new int[] {17, 4}, new int[] { 9, 16}}, - new int[][] { new int[] { 3, 5}, new int[] { 3, 13}, new int[] {15, 5}, new int[] {15, 10}}, //20 - new int[][] { new int[] { 4, 4}, new int[] {17, 0}, new int[] {17, 6}, new int[] {19, 6}}, - new int[][] { new int[] { 2, 7}, new int[] {17, 0}, new int[] { 7, 16}, new int[] {34, 0}}, - new int[][] { new int[] { 4, 5}, new int[] { 4, 14}, new int[] {11, 14}, new int[] {16, 14}}, - new int[][] { new int[] { 6, 4}, new int[] { 6, 14}, new int[] {11, 16}, new int[] {30, 2}}, - new int[][] { new int[] { 8, 4}, new int[] { 8, 13}, new int[] { 7, 22}, new int[] {22, 13}}, //25 - new int[][] { new int[] {10, 2}, new int[] {19, 4}, new int[] {28, 6}, new int[] {33, 4}}, - new int[][] { new int[] { 8, 4}, new int[] {22, 3}, new int[] { 8, 26}, new int[] {12, 28}}, - new int[][] { new int[] { 3, 10}, new int[] { 3, 23}, new int[] { 4, 31}, new int[] {11, 31}}, - new int[][] { new int[] { 7, 7}, new int[] {21, 7}, new int[] { 1, 37}, new int[] {19, 26}}, - new int[][] { new int[] { 5, 10}, new int[] {19, 10}, new int[] {15, 25}, new int[] {23, 25}}, //30 - new int[][] { new int[] {13, 3}, new int[] { 2, 29}, new int[] {42, 1}, new int[] {23, 28}}, - new int[][] { new int[] {17, 0}, new int[] {10, 23}, new int[] {10, 35}, new int[] {19, 35}}, - new int[][] { new int[] {17, 1}, new int[] {14, 21}, new int[] {29, 19}, new int[] {11, 46}}, - new int[][] { new int[] {13, 6}, new int[] {14, 23}, new int[] {44, 7}, new int[] {59, 1}}, - new int[][] { new int[] {12, 7}, new int[] {12, 26}, new int[] {39, 14}, new int[] {22, 41}}, //35 - new int[][] { new int[] { 6, 14}, new int[] { 6, 34}, new int[] {46, 10}, new int[] { 2, 64}}, - new int[][] { new int[] {17, 4}, new int[] {29, 14}, new int[] {49, 10}, new int[] {24, 46}}, - new int[][] { new int[] { 4, 18}, new int[] {13, 32}, new int[] {48, 14}, new int[] {42, 32}}, - new int[][] { new int[] {20, 4}, new int[] {40, 7}, new int[] {43, 22}, new int[] {10, 67}}, - new int[][] { new int[] {19, 6}, new int[] {18, 31}, new int[] {34, 34}, new int[] {20, 61}},//40 - }; - - /// - /// Positions of alignment patterns. - /// This array includes only the second and the third position of - /// the alignment patterns. Rest of them can be calculated from the - /// distance between them. - /// - /// See Table 1 in Appendix E (pp.71) of JIS X0510:2004. - /// - private static int[][] alignmentPatternPos = - { - new int[] { 0, 0}, - new int[] { 0, 0}, new int[] {18, 0}, new int[] {22, 0}, new int[] {26, 0}, new int[] {30, 0}, // 1- 5 - new int[] {34, 0}, new int[] {22, 38}, new int[] {24, 42}, new int[] {26, 46}, new int[] {28, 50}, // 6-10 - new int[] {30, 54}, new int[] {32, 58}, new int[] {34, 62}, new int[] {26, 46}, new int[] {26, 48}, //11-15 - new int[] {26, 50}, new int[] {30, 54}, new int[] {30, 56}, new int[] {30, 58}, new int[] {34, 62}, //16-20 - new int[] {28, 50}, new int[] {26, 50}, new int[] {30, 54}, new int[] {28, 54}, new int[] {32, 58}, //21-25 - new int[] {30, 58}, new int[] {34, 62}, new int[] {26, 50}, new int[] {30, 54}, new int[] {26, 52}, //26-30 - new int[] {30, 56}, new int[] {34, 60}, new int[] {30, 58}, new int[] {34, 62}, new int[] {30, 54}, //31-35 - new int[] {24, 50}, new int[] {28, 54}, new int[] {32, 58}, new int[] {26, 54}, new int[] {30, 58}, //35-40 - }; - - /// - /// Version information pattern (BCH coded). - /// See Table 1 in Appendix D (pp.68) of JIS X0510:2004. - /// - private static int[] versionPattern = - { - 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, - 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, - 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, - 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, - 0x27541, 0x28c69 - }; - - private static int[][] formatInfo = - { - new int[] {0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976}, - new int[] {0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0}, - new int[] {0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed}, - new int[] {0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b} - }; - - /// - /// Array of positions of alignment patterns. X and Y coordinates - /// are interleaved into 'pos'. - /// - private class AlignmentPatternPosition - { - /// - /// Number of patterns - /// - public int n; - public int[] pos; - }; - - /// - /// Gets maximum data code length (bytes) for the version. - /// - public static int GetDataLength(int version, QRErrorCorrectionLevel level) - { - return symbolCapacity[version].words - symbolCapacity[version].ec[(int)level]; - } - - /// - /// Gets maximum error correction code length (bytes) for the version. - /// - private static int getECCLength(int version, QRErrorCorrectionLevel level) - { - return symbolCapacity[version].ec[(int)level]; - } - - /// - /// Gets a version number that satisfies the input code length. - /// - public static int GetMinimumVersion(int size, QRErrorCorrectionLevel level) - { - for (int i = 1; i <= MaximumSymbolVersion; i++) - { - int words = symbolCapacity[i].words - symbolCapacity[i].ec[(int)level]; - if (words >= size) - return i; - } - - return -1; - } - - /// - /// Gets the width of the symbol for the version. - /// - public static int GetWidth(int version) - { - return symbolCapacity[version].width; - } - - /// - /// Gets the number of remainder bits. - /// - public static int GetRemainderLength(int version) - { - return symbolCapacity[version].remainder; - } - - /// - /// Gets the size of length indicator for the mode and version. - /// - public static int GetLengthIndicator(QREncodeMode mode, int version) - { - int l = 2; - - if (version <= 9) - l = 0; - else if (version <= 26) - l = 1; - - return lengthIndicatorTable[(int)mode][l]; - } - - /// - /// Gets the maximum length for the mode and version. - /// - public static int GetMaximumWords(QREncodeMode mode, int version) - { - int l = 2; - if (version <= 9) - l = 0; - else if (version <= 26) - l = 1; - - int bits = lengthIndicatorTable[(int)mode][l]; - int words = (1 << bits) - 1; - if (mode == QREncodeMode.Kanji) - words *= 2; - - return words; - } - - /// - /// Gets an array of ECC specification. - /// an array of ECC specification contains as following: - /// # of type1 blocks, # of data code, # of ecc code, - /// # of type2 blocks, # of data code, # of ecc code - /// - public static int[] GetEccSpec(int version, QRErrorCorrectionLevel level) - { - int b1 = eccTable[version][(int)level][0]; - int b2 = eccTable[version][(int)level][1]; - int data = QRSpec.GetDataLength(version, level); - int ecc = QRSpec.getECCLength(version, level); - - int[] array = new int[6]; - - if (b2 == 0) - { - array[0] = b1; - array[1] = data / b1; - array[2] = ecc / b1; - array[3] = 0; - array[4] = 0; - array[5] = 0; - } - else - { - array[0] = b1; - array[1] = data / (b1 + b2); - array[2] = ecc / (b1 + b2); - array[3] = b2; - array[4] = array[1] + 1; - array[5] = (ecc - (array[2] * b1)) / b2; - } - - return array; - } - - public static int rsBlockNum(int[] spec) - { - return spec[0] + spec[3]; - } - - public static int rsBlockNum1(int[] spec) - { - return spec[0]; - } - - public static int rsDataCodes1(int[] spec) - { - return spec[1]; - } - - public static int rsEccCodes1(int[] spec) - { - return spec[2]; - } - - public static int rsBlockNum2(int[] spec) - { - return spec[3]; - } - - public static int rsDataCodes2(int[] spec) - { - return spec[4]; - } - - public static int rsEccCodes2(int[] spec) - { - return spec[5]; - } - - /// - /// Gets positions of alignment patterns. - /// - private static AlignmentPatternPosition getAlignmentPattern(int version) - { - if (version < 2) - return null; - - AlignmentPatternPosition al = new AlignmentPatternPosition(); - - int width = symbolCapacity[version].width; - int d = alignmentPatternPos[version][1] - alignmentPatternPos[version][0]; - - int w = 2; - if (d >= 0) - w = (width - alignmentPatternPos[version][0]) / d + 2; - - al.n = w * w - 3; - al.pos = new int[al.n * 2]; - - if (al.n == 1) - { - al.pos[0] = alignmentPatternPos[version][0]; - al.pos[1] = alignmentPatternPos[version][0]; - return al; - } - - int posIndex = 0; - int cx = alignmentPatternPos[version][0]; - for (int x = 1; x < w - 1; x++) - { - al.pos[posIndex] = 6; - al.pos[posIndex + 1] = cx; - al.pos[posIndex + 2] = cx; - al.pos[posIndex + 3] = 6; - cx += d; - posIndex += 4; - } - - int cy = alignmentPatternPos[version][0]; - for (int y = 0; y < w - 1; y++) - { - cx = alignmentPatternPos[version][0]; - for (int x = 0; x < w - 1; x++) - { - al.pos[posIndex] = cx; - al.pos[posIndex + 1] = cy; - cx += d; - posIndex += 2; - } - - cy += d; - } - - return al; - } - - /// - /// Gets BCH encoded version information pattern that is used for - /// the symbol of version 7 or greater. Use lower 18 bits. - /// - private static int getVersionPattern(int version) - { - if (version < 7 || version > MaximumSymbolVersion) - return 0; - - return versionPattern[version - 7]; - } - - /// - /// Gets BCH encoded format information pattern. - /// - public static int GetFormatInfo(int mask, QRErrorCorrectionLevel level) - { - if (mask < 0 || mask > 7) - return 0; - - return formatInfo[(int)level][mask]; - } - - /// - /// Gets a copy of initialized frame. When the same version is - /// requested twice or more, a copy of cached frame is returned. - /// - public static byte[] CreateNewFrame(int version) - { - if (version < 1 || version > MaximumSymbolVersion) - return null; - - if (frames[version] == null) - frames[version] = QRSpec.createFrame(version); - - int width = symbolCapacity[version].width; - byte[] frame = new byte[width * width]; - Array.Copy(frames[version], frame, width * width); - return frame; - } - - private static byte[] createFrame(int version) - { - int width = symbolCapacity[version].width; - byte[] frame = new byte[width * width]; - Array.Clear(frame, 0, width * width); - - /* Finder pattern */ - putFinderPattern(frame, width, 0, 0); - putFinderPattern(frame, width, width - 7, 0); - putFinderPattern(frame, width, 0, width - 7); - - /* Separator */ - int pIndex = 0; - int qIndex = width * (width - 7); - for (int y = 0; y < 7; y++) - { - frame[pIndex + 7] = 0xc0; - frame[pIndex + width - 8] = 0xc0; - frame[qIndex + 7] = 0xc0; - pIndex += width; - qIndex += width; - } - - for (int i = 0; i < 8; i++) - frame[width * 7 + i] = 0xc0; - - for (int i = 0; i < 8; i++) - frame[width * 8 - 8 + i] = 0xc0; - - for (int i = 0; i < 8; i++) - frame[width * (width - 8) + i] = 0xc0; - - /* Mask format information area */ - - for (int i = 0; i < 9; i++) - frame[width * 8 + i] = 0x84; - - for (int i = 0; i < 8; i++) - frame[width * 9 - 8 + i] = 0x84; - - pIndex = 8; - for (int y = 0; y < 8; y++) - { - frame[pIndex] = 0x84; - pIndex += width; - } - - pIndex = width * (width - 7) + 8; - for (int y = 0; y < 7; y++) - { - frame[pIndex] = 0x84; - pIndex += width; - } - - /* Timing pattern */ - pIndex = width * 6 + 8; - qIndex = width * 8 + 6; - for (int x = 1; x < width - 15; x++) - { - frame[pIndex] = (byte)(0x90 | (x & 1)); - frame[qIndex] = (byte)(0x90 | (x & 1)); - pIndex++; - qIndex += width; - } - - /* Alignment pattern */ - AlignmentPatternPosition alignment = QRSpec.getAlignmentPattern(version); - if (alignment != null) - { - for (int x = 0; x < alignment.n; x++) - putAlignmentPattern(frame, width, alignment.pos[x * 2], alignment.pos[x * 2 + 1]); - } - - /* Version information */ - if (version >= 7) - { - int verinfo = QRSpec.getVersionPattern(version); - - pIndex = width * (width - 11); - int v = verinfo; - for (int x = 0; x < 6; x++) - { - for (int y = 0; y < 3; y++) - { - frame[pIndex + width * y + x] = (byte)(0x88 | (v & 1)); - v = v >> 1; - } - } - - pIndex = width - 11; - v = verinfo; - for (int y = 0; y < 6; y++) - { - for (int x = 0; x < 3; x++) - { - frame[pIndex + x] = (byte)(0x88 | (v & 1)); - v = v >> 1; - } - pIndex += width; - } - } - - frame[width * (width - 8) + 8] = 0x81; - return frame; - } - - /// - /// Puts an alignment pattern. ox,oy is center coordinate of the pattern - /// - private static void putAlignmentPattern(byte[] frame, int width, int ox, int oy) - { - byte[] finder = - { - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - 0xa1, 0xa0, 0xa0, 0xa0, 0xa1, - 0xa1, 0xa0, 0xa1, 0xa0, 0xa1, - 0xa1, 0xa0, 0xa0, 0xa0, 0xa1, - 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, - }; - - int frameIndex = (oy - 2) * width + ox - 2; - int finderIndex = 0; - for (int y = 0; y < 5; y++) - { - for (int x = 0; x < 5; x++) - frame[frameIndex + x] = finder[finderIndex + x]; - - frameIndex += width; - finderIndex += 5; - } - } - - /// - /// Puts a finder pattern. ox,oy is upper-left coordinate of the pattern - /// - private static void putFinderPattern(byte[] frame, int width, int ox, int oy) - { - byte[] finder = - { - 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, - 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, - 0xc1, 0xc0, 0xc1, 0xc1, 0xc1, 0xc0, 0xc1, - 0xc1, 0xc0, 0xc1, 0xc1, 0xc1, 0xc0, 0xc1, - 0xc1, 0xc0, 0xc1, 0xc1, 0xc1, 0xc0, 0xc1, - 0xc1, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc1, - 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, - }; - - int frameIndex = oy * width + ox; - int finderIndex = 0; - for (int y = 0; y < 7; y++) - { - for (int x = 0; x < 7; x++) - frame[frameIndex + x] = finder[finderIndex + x]; - - frameIndex += width; - finderIndex += 7; - } - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/QRSymbol.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/QRSymbol.cs deleted file mode 100644 index 63bb81ee..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/QRSymbol.cs +++ /dev/null @@ -1,676 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using System.Drawing; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Produces encoded QR Code symbols. Symbol data is represented as an - /// array that contains width * width bytes. If the less significant bit - /// of the data byte is 1, the corresponding module is black. - /// - class QRSymbol - { - private class ReedSolomonBlock - { - public byte[] m_data; - public byte[] m_eccData; - - public void Init(byte[] data, int startIndex, int dataLength, int eccLength) - { - m_data = new byte[dataLength]; - Array.Copy(data, startIndex, m_data, 0, dataLength); - - QRReedSolomon rs = new QRReedSolomon(8, 0x11d, 0, 1, eccLength, 255 - dataLength - eccLength); - m_eccData = new byte[eccLength]; - rs.ProduceCodes(m_data, m_eccData); - } - }; - - private class EncodedData - { - public int m_symbolVersion; - public ReedSolomonBlock[] m_rsBlocks; - - public int m_dataLength; - public int m_eccDataLength; - - private byte[] m_data; - private int m_count; - private int m_blockNum1; - - public EncodedData(QRInputData input) - { - m_data = input.GetBytes(); - if (m_data == null) - return; - - int[] spec = QRSpec.GetEccSpec(input.m_symbolVersion, input.m_ecLevel); - m_symbolVersion = input.m_symbolVersion; - - m_rsBlocks = new ReedSolomonBlock[QRSpec.rsBlockNum(spec)]; - for (int i = 0; i < m_rsBlocks.Length; i++) - m_rsBlocks[i] = new ReedSolomonBlock(); - - int dataCodePos = 0; - int rsBlockIndex = 0; - for (int i = 0; i < QRSpec.rsBlockNum1(spec); i++) - { - m_rsBlocks[rsBlockIndex].Init(m_data, dataCodePos, QRSpec.rsDataCodes1(spec), QRSpec.rsEccCodes1(spec)); - dataCodePos += QRSpec.rsDataCodes1(spec); - rsBlockIndex++; - } - - for (int i = 0; i < QRSpec.rsBlockNum2(spec); i++) - { - m_rsBlocks[rsBlockIndex].Init(m_data, dataCodePos, QRSpec.rsDataCodes2(spec), QRSpec.rsEccCodes2(spec)); - dataCodePos += QRSpec.rsDataCodes2(spec); - rsBlockIndex++; - } - - m_blockNum1 = QRSpec.rsBlockNum1(spec); - m_dataLength = QRSpec.rsBlockNum1(spec) * QRSpec.rsDataCodes1(spec) + QRSpec.rsBlockNum2(spec) * QRSpec.rsDataCodes2(spec); - m_eccDataLength = QRSpec.rsBlockNum(spec) * QRSpec.rsEccCodes1(spec); - } - - public byte GetCode() - { - if (m_count < m_dataLength) - { - int row = m_count % m_rsBlocks.Length; - int col = m_count / m_rsBlocks.Length; - if (col >= m_rsBlocks[row].m_data.Length) - row += m_blockNum1; - - m_count++; - return m_rsBlocks[row].m_data[col]; - } - else if (m_count < m_dataLength + m_eccDataLength) - { - int row = (m_count - m_dataLength) % m_rsBlocks.Length; - int col = (m_count - m_dataLength) / m_rsBlocks.Length; - - m_count++; - return m_rsBlocks[row].m_eccData[col]; - } - - return 0; - } - }; - - private class SymbolFrameDriver - { - private int m_symbolWidth; - - private byte[] m_frame; - - private int m_currentX; - private int m_currentY; - - private int m_verticalShiftAmount; - - private bool m_moveRight; - private bool m_firstPositionRequested; - - public byte this[int pos] - { - get - { - return m_frame[pos]; - } - set - { - m_frame[pos] = value; - } - } - - public SymbolFrameDriver(int symbolWidth, byte[] frame) - { - m_symbolWidth = symbolWidth; - - m_frame = frame; - - m_currentX = symbolWidth - 1; - m_currentY = symbolWidth - 1; - m_verticalShiftAmount = -1; - } - - public int GetNextFillPosition() - { - if (!m_firstPositionRequested) - { - m_firstPositionRequested = true; - return m_currentY * m_symbolWidth + m_currentX; - } - - if (!m_moveRight) - m_currentX--; - else - { - m_currentX++; - m_currentY += m_verticalShiftAmount; - } - - m_moveRight = !m_moveRight; - - if (m_verticalShiftAmount < 0) - { - if (m_currentY < 0) - { - m_currentY = 0; - m_currentX -= 2; - m_verticalShiftAmount = 1; - if (m_currentX == 6) - { - m_currentX--; - m_currentY = 9; - } - } - } - else - { - if (m_currentY == m_symbolWidth) - { - m_currentY = m_symbolWidth - 1; - m_currentX -= 2; - m_verticalShiftAmount = -1; - if (m_currentX == 6) - { - m_currentX--; - m_currentY -= 8; - } - } - } - - if (m_currentX < 0 || m_currentY < 0) - throw new BarcodeException("Incorrect encoded data."); - - if ((m_frame[m_currentY * m_symbolWidth + m_currentX] & 0x80) != 0) - return GetNextFillPosition(); - - return m_currentY * m_symbolWidth + m_currentX; - } - }; - - /// - /// Demerit coefficients. See Section 8.8.2, pp.45, JIS X0510:2004. - /// - private const int N1 = 3; - private const int N2 = 3; - private const int N3 = 40; - private const int N4 = 10; - - private int m_width; - private byte[] m_data; - - /// - /// Gets the width of the produced symbol. - /// - /// The width of the produced symbol. - public int Width - { - get - { - return m_width; - } - } - - /// - /// Gets the of produced symbol data with the specified index. - /// - /// - public byte this[int index] - { - get - { - return m_data[index]; - } - } - - /// - /// Encodes the input data and produces QR Code symbol. - /// - /// The input data. - /// The produced QR Code symbol. - private static QRSymbol encodeInput(QRInputData input) - { - return encodeUsingMask(input, -1); - } - - /// - /// Encodes the string and produces QR Code symbol. - /// - /// The value to encode. - /// The minimum symbol version. - /// The error correction level. - /// The encode hint. - /// if set to false then value will be converted to upper case before processing. - /// Need to insert FNC1 prefix? - /// The produced QR Code symbol. - public static QRSymbol EncodeString(string value, int symbolVersion, QRErrorCorrectionLevel errorCorrectionLevel, QREncodeHint encodeHint, bool caseSensitive, bool FNC1Mode) - { - QREncodeMode internalHint = QREncodeMode.Mode8; - if (encodeHint == QREncodeHint.Kanji) - internalHint = QREncodeMode.Kanji; - - QRInputData input = new QRInputData(symbolVersion, errorCorrectionLevel); - if (input == null) - return null; - - if (FNC1Mode) - input.FNC1Mode = true; - - bool ret = input.BuildFromString(value, internalHint, caseSensitive); - if (!ret) - return null; - - return encodeInput(input); - } - - private static QRSymbol encodeUsingMask(QRInputData input, int maskVersion) - { - if (input.m_symbolVersion < 0 || input.m_symbolVersion > QRSpec.MaximumSymbolVersion) - throw new BarcodeException("Incorrect symbol version"); - - if (input.m_ecLevel < QRErrorCorrectionLevel.Low || input.m_ecLevel > QRErrorCorrectionLevel.High) - throw new BarcodeException("Incorrect error correction level"); - - EncodedData encoded = new EncodedData(input); - if (encoded.m_rsBlocks == null) - throw new BarcodeException("Failed to produce error correction codes"); - - int version = encoded.m_symbolVersion; - int width = QRSpec.GetWidth(version); - byte[] frame = QRSpec.CreateNewFrame(version); - SymbolFrameDriver frameDriver = new SymbolFrameDriver(width, frame); - - for (int i = 0; i < encoded.m_dataLength + encoded.m_eccDataLength; i++) - { - byte code = encoded.GetCode(); - byte bitMask = 0x80; - for (int j = 0; j < 8; j++) - { - int framePos = frameDriver.GetNextFillPosition(); - - if ((bitMask & code) != 0) - frameDriver[framePos] = 0x03; - else - frameDriver[framePos] = 0x02; - - bitMask = (byte)(bitMask >> 1); - } - } - - int remainderLength = QRSpec.GetRemainderLength(version); - for (int i = 0; i < remainderLength; i++) - frameDriver[frameDriver.GetNextFillPosition()] = 0x02; - - byte[] masked = null; - if (maskVersion < 0) - masked = MakeMask(width, frame, input.m_ecLevel); - else - { - masked = MakeMask(width, frame, maskVersion); - addFormatInfo(width, masked, maskVersion, input.m_ecLevel); - } - - return new QRSymbol(width, masked); - } - - private static int addFormatInfo(int width, byte[] frame, int maskVersion, QRErrorCorrectionLevel level) - { - int format = QRSpec.GetFormatInfo(maskVersion, level); - int blacks = 0; - - for (int i = 0; i < 8; i++) - { - byte v = 0x84; - if ((format & 1) != 0) - { - blacks += 2; - v = 0x85; - } - - frame[width * 8 + width - 1 - i] = v; - - if (i < 6) - frame[width * i + 8] = v; - else - frame[width * (i + 1) + 8] = v; - - format = format >> 1; - } - - for (int i = 0; i < 7; i++) - { - byte v = 0x84; - if ((format & 1) != 0) - { - blacks += 2; - v = 0x85; - } - - frame[width * (width - 7 + i) + 8] = v; - - if (i == 0) - frame[width * 8 + 7] = v; - else - frame[width * 8 + 6 - i] = v; - - format = format >> 1; - } - - return blacks; - } - - private QRSymbol(int width, byte[] data) - { - m_width = width; - m_data = data; - } - - public static byte[] MakeMask(int symbolWidth, byte[] symbolFrame, int maskVersion) - { - byte[] masked = new byte[symbolWidth * symbolWidth]; - - switch (maskVersion) - { - case 0: - makeMask0(symbolWidth, symbolFrame, masked); - break; - case 1: - makeMask1(symbolWidth, symbolFrame, masked); - break; - case 2: - makeMask2(symbolWidth, symbolFrame, masked); - break; - case 3: - makeMask3(symbolWidth, symbolFrame, masked); - break; - case 4: - makeMask4(symbolWidth, symbolFrame, masked); - break; - case 5: - makeMask5(symbolWidth, symbolFrame, masked); - break; - case 6: - makeMask6(symbolWidth, symbolFrame, masked); - break; - case 7: - makeMask7(symbolWidth, symbolFrame, masked); - break; - } - - return masked; - } - - public static byte[] MakeMask(int symbolWidth, byte[] symbolFrame, QRErrorCorrectionLevel level) - { - byte[] bestMask = null; - int minDemerit = int.MaxValue; - - for (int i = 0; i < 8; i++) - { - byte[] currentMask = new byte[symbolWidth * symbolWidth]; - - int demerit = 0; - int blacks = 0; - - switch (i) - { - case 0: - blacks = makeMask0(symbolWidth, symbolFrame, currentMask); - break; - case 1: - blacks = makeMask1(symbolWidth, symbolFrame, currentMask); - break; - case 2: - blacks = makeMask2(symbolWidth, symbolFrame, currentMask); - break; - case 3: - blacks = makeMask3(symbolWidth, symbolFrame, currentMask); - break; - case 4: - blacks = makeMask4(symbolWidth, symbolFrame, currentMask); - break; - case 5: - blacks = makeMask5(symbolWidth, symbolFrame, currentMask); - break; - case 6: - blacks = makeMask6(symbolWidth, symbolFrame, currentMask); - break; - case 7: - blacks = makeMask7(symbolWidth, symbolFrame, currentMask); - break; - } - - blacks += addFormatInfo(symbolWidth, currentMask, i, level); - blacks = 100 * blacks / (symbolWidth * symbolWidth); - demerit = (Math.Abs(blacks - 50) / 5) * N4; - demerit += evaluateSymbol(symbolWidth, currentMask); - - if (demerit < minDemerit) - { - minDemerit = demerit; - bestMask = currentMask; - } - else - { - currentMask = null; - } - } - - return bestMask; - } - - private static int evaluateSymbol(int symbolWidth, byte[] symbolFrame) - { - int demerit = 0; - int frameIndex = 0; - - int[] runLength = new int[QRSpec.MaximumSymbolWidth + 1]; - - for (int y = 0; y < symbolWidth; y++) - { - int head = 0; - runLength[0] = 1; - for (int x = 0; x < symbolWidth; x++) - { - if (x > 0 && y > 0) - { - byte b22 = (byte)(symbolFrame[frameIndex] & symbolFrame[frameIndex - 1] & symbolFrame[frameIndex - symbolWidth] & symbolFrame[frameIndex - symbolWidth - 1]); - byte w22 = (byte)(symbolFrame[frameIndex] | symbolFrame[frameIndex - 1] | symbolFrame[frameIndex - symbolWidth] | symbolFrame[frameIndex - symbolWidth - 1]); - if (((b22 | (w22 ^ 1)) & 1) != 0) - demerit += N2; - } - if (x == 0 && (symbolFrame[frameIndex] & 1) != 0) - { - runLength[0] = -1; - head = 1; - runLength[head] = 1; - } - else if (x > 0) - { - if (((symbolFrame[frameIndex] ^ symbolFrame[frameIndex - 1]) & 1) != 0) - { - head++; - runLength[head] = 1; - } - else - runLength[head]++; - } - - frameIndex++; - } - - demerit += calcN1N3(head + 1, runLength); - } - - for (int x = 0; x < symbolWidth; x++) - { - int head = 0; - runLength[0] = 1; - frameIndex = x; - for (int y = 0; y < symbolWidth; y++) - { - if (y == 0 && (symbolFrame[frameIndex] & 1) != 0) - { - runLength[0] = -1; - head = 1; - runLength[head] = 1; - } - else if (y > 0) - { - if (((symbolFrame[frameIndex] ^ symbolFrame[frameIndex - symbolWidth]) & 1) != 0) - { - head++; - runLength[head] = 1; - } - else - runLength[head]++; - } - - frameIndex += symbolWidth; - } - - demerit += calcN1N3(head + 1, runLength); - } - - return demerit; - } - - private static int calcN1N3(int length, int[] runLength) - { - int demerit = 0; - for (int i = 0; i < length; i++) - { - if (runLength[i] >= 5) - demerit += N1 + (runLength[i] - 5); - - if ((i & 1) != 0) - { - if (i >= 3 && i < length - 2 && (runLength[i] % 3) == 0) - { - int fact = runLength[i] / 3; - if (runLength[i - 2] == fact && runLength[i - 1] == fact && runLength[i + 1] == fact && runLength[i + 2] == fact) - { - if (runLength[i - 3] < 0 || runLength[i - 3] >= 4 * fact) - demerit += N3; - else if (i + 3 >= length || runLength[i + 3] >= 4 * fact) - demerit += N3; - } - } - } - } - - return demerit; - } - - private static int makeMaskImpl(int symbolWidth, byte[] symbolFrame, byte[] symbolMask, int maskVersion) - { - int b = 0; - int frameIndex = 0; - int maskIndex = 0; - - for (int y = 0; y < symbolWidth; y++) - { - for (int x = 0; x < symbolWidth; x++) - { - if ((symbolFrame[frameIndex] & 0x80) != 0) - { - symbolMask[maskIndex] = symbolFrame[frameIndex]; - } - else - { - int expression = 0; - switch (maskVersion) - { - case 0: - expression = (x + y) & 1; - break; - case 1: - expression = y & 1; - break; - case 2: - expression = x % 3; - break; - case 3: - expression = (x + y) % 3; - break; - case 4: - expression = ((y / 2) + (x / 3)) & 1; - break; - case 5: - expression = ((x * y) & 1) + (x * y) % 3; - break; - case 6: - expression = (((x * y) & 1) + (x * y) % 3) & 1; - break; - case 7: - expression = (((x * y) % 3) + ((x + y) & 1)) & 1; - break; - } - - if (expression == 0) - symbolMask[maskIndex] = (byte)(symbolFrame[frameIndex] ^ 1); - else - symbolMask[maskIndex] = (byte)(symbolFrame[frameIndex] ^ 0); - } - - b += (symbolMask[maskIndex] & 1); - frameIndex++; - maskIndex++; - } - } - - return b; - } - - private static int makeMask0(int symbolWidth, byte[] symbolFrame, byte[] symbolMask) - { - return makeMaskImpl(symbolWidth, symbolFrame, symbolMask, 0); - } - - private static int makeMask1(int symbolWidth, byte[] symbolFrame, byte[] symbolMask) - { - return makeMaskImpl(symbolWidth, symbolFrame, symbolMask, 1); - } - - private static int makeMask2(int symbolWidth, byte[] symbolFrame, byte[] symbolMask) - { - return makeMaskImpl(symbolWidth, symbolFrame, symbolMask, 2); - } - - private static int makeMask3(int symbolWidth, byte[] symbolFrame, byte[] symbolMask) - { - return makeMaskImpl(symbolWidth, symbolFrame, symbolMask, 3); - } - - private static int makeMask4(int symbolWidth, byte[] symbolFrame, byte[] symbolMask) - { - return makeMaskImpl(symbolWidth, symbolFrame, symbolMask, 4); - } - - private static int makeMask5(int symbolWidth, byte[] symbolFrame, byte[] symbolMask) - { - return makeMaskImpl(symbolWidth, symbolFrame, symbolMask, 5); - } - - private static int makeMask6(int symbolWidth, byte[] symbolFrame, byte[] symbolMask) - { - return makeMaskImpl(symbolWidth, symbolFrame, symbolMask, 6); - } - - private static int makeMask7(int symbolWidth, byte[] symbolFrame, byte[] symbolMask) - { - return makeMaskImpl(symbolWidth, symbolFrame, symbolMask, 7); - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/QRSymbology.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/QRSymbology.cs deleted file mode 100644 index 259f468d..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/QRSymbology.cs +++ /dev/null @@ -1,140 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using System.Drawing; -using SkiaSharp; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Draws barcodes using QR Code symbology. QR Code initially was used - /// for tracking parts in vehicle manufacturing, but now QR Codes used - /// in a much broader context, including both commercial tracking applications - /// and convenience-oriented applications aimed at mobile phone users - /// (known as mobile tagging). - /// - class QRSymbology : SymbologyDrawing2D - { - /// - /// Initializes a new instance of the class. - /// - public QRSymbology() - : base(TrueSymbologyType.QRCode) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The prototype. - public QRSymbology(SymbologyDrawing prototype) - : base(prototype, TrueSymbologyType.QRCode) - { - } - - /// - /// Validates the value using QR Code symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Parameter is not applicable to this symbology. - /// - /// true if value is valid (can be encoded); otherwise, false. - /// - public override bool ValueIsValid(string value, bool checksumIsMandatory) - { - return true; - } - - /// - /// Gets the value restrictions description string. - /// - /// - /// The value restrictions description string. - /// - public override string getValueRestrictions() - { - return "QR Code symbology allows a maximum data size of 7,089 numeric, or 4,296 alphanumeric, or 2,953 bytes, or 1,817 Kanji/Kana characters.\n"; - } - - /// - /// Gets the barcode value encoded using QR Code symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// - /// The barcode value encoded using QR Code symbology rules. - /// - public override string GetEncodedValue(bool forCaption) - { - if (forCaption) - return Value; - - return ""; - } - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// The encoding pattern for given character. - protected override string getCharPattern(char c) - { - return ""; - } - - protected override SKSize buildBars(SKCanvas canvas, SKFont font) - { - // a bit weird cycle goes here, but we need it, - // or we can end up with 2-byte chars which is unsupported - StringBuilder sb = new StringBuilder(); - - // get encoding used in options - Encoding tmpEncoding = Options.Encoding; - - /* - // detect Unicode and auto switch to UTF-8 ? - - bool UnicodeSymbolFound = !Utils.IsLatinISOEncodingOnly(Value); - - // if we are not with encoding UTF8 - // then we are forcing to use UTF8 - if (UnicodeSymbolFound && tmpEncoding != Encoding.UTF8) - { - // force encoding to UTF8 - tmpEncoding = Encoding.UTF8; - } - */ - - byte[] bytes = tmpEncoding.GetBytes(Value); - - for (int i = 0; i < bytes.Length; i++) - sb.Append((char)bytes[i]); - - SKSize drawingSize = new SKSize(); - - QRSymbol symbol = QRSymbol.EncodeString(sb.ToString(), Options.QRVersion, - Options.QRErrorCorrectionLevel, Options.QREncodeHint, true, false); - - int cellWidth = NarrowBarWidth; - drawingSize.Width = symbol.Width * cellWidth; - drawingSize.Height = drawingSize.Width; - - for (int i = 0; i < symbol.Width; i++) - { - for (int j = 0; j < symbol.Width; j++) - { - if ((symbol[(i * symbol.Width) + j] & 0x01) != 0) - m_rects.Add(new SKRect(j * cellWidth, i * cellWidth, j * cellWidth+cellWidth, i * cellWidth+cellWidth)); - } - } - - return drawingSize; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/2D/ReedSolomon.cs b/MuPDF.NET/Barcode/Encoders/Internal/2D/ReedSolomon.cs deleted file mode 100644 index 7efec77a..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/2D/ReedSolomon.cs +++ /dev/null @@ -1,121 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - class ReedSolomon - { - private int logmod; // 2**symsize - 1 - private int rlen; - - private int[] log; - private int[] alog; - private int[] rspoly; - - public void Init(int poly) - { - int m, b, p, v; - - // Find the top bit, and hence the symbol size - for (b = 1, m = 0; b <= poly; b <<= 1) - m++; - b >>= 1; - m--; - - // Calculate the log/alog tables - logmod = (1 << m) - 1; - log = new int[logmod + 1]; - alog = new int[logmod]; - - for (p = 1, v = 0; v < logmod; v++) - { - alog[v] = p; - log[p] = v; - p <<= 1; - if ((p & b) != 0) - p ^= poly; - } - - } - - public void InitCode(int nsym, int index) - { - int i, k; - - rspoly = new int[nsym + 1]; - - rlen = nsym; - - rspoly[0] = 1; - for (i = 1; i <= nsym; i++) - { - rspoly[i] = 1; - for (k = i - 1; k > 0; k--) - { - if (rspoly[k] != 0) - rspoly[k] = - alog[(log[rspoly[k]] + index) % logmod]; - rspoly[k] ^= rspoly[k - 1]; - } - rspoly[0] = alog[(log[rspoly[0]] + index) % logmod]; - index++; - } - } - - public void Encode(int len, byte[] data,out byte[] res) - { - int i, k, m; - - res = new byte[rlen]; - for (i = 0; i < rlen; i++) - res[i] = 0; - for (i = 0; i < len; i++) - { - m = res[rlen - 1] ^ data[i]; - for (k = rlen - 1; k > 0; k--) - { - if (m != 0 && rspoly[k] != 0) - res[k] = (byte)(res[k - 1] ^ alog[(log[m] + log[rspoly[k]]) % logmod]); - else - res[k] = res[k - 1]; - } - if (m != 0 && rspoly[0] != 0) - res[0] = (byte)alog[(log[m] + log[rspoly[0]]) % logmod]; - else - res[0] = 0; - } - } - - public void EncodeLong(int len, uint[] data, out uint[] res) - { - int i, k; - uint m; - - res = new uint[rlen]; - for (i = 0; i < rlen; i++) - res[i] = 0; - for (i = 0; i < len; i++) - { - m = res[rlen - 1] ^ data[i]; - for (k = rlen - 1; k > 0; k--) - { - if (m != 0 && rspoly[k] != 0) - res[k] = (uint)(res[k - 1] ^ alog[(log[m] + log[rspoly[k]]) % logmod]); - else - res[k] = res[k - 1]; - } - if (m != 0 && rspoly[0] != 0) - res[0] = (uint)alog[(log[m] + log[rspoly[0]]) % logmod]; - else - res[0] = 0; - } - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/ApplicationIdentifiers.cs b/MuPDF.NET/Barcode/Encoders/Internal/ApplicationIdentifiers.cs deleted file mode 100644 index ef94da01..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/ApplicationIdentifiers.cs +++ /dev/null @@ -1,259 +0,0 @@ -using System; -using System.Text; -using System.Collections; - -namespace BarcodeWriter.Core.Internal -{ - internal enum AIDataType - { - alphabetic, - numeric, - alphameric, - } - - internal struct AIDataFormat - { - public AIDataType datatype; - public int minLength; - public int maxLength; - - public AIDataFormat(AIDataType datatype, int minLength, int maxLength) - { - this.datatype = datatype; - this.minLength = minLength; - this.maxLength = maxLength; - } - } - - - /// - /// AI identifiers used in GS1 type barcodes - /// - internal class AI - { - private string m_identifier; - private int m_identifierLength; - private ArrayList m_formatList = new ArrayList(); - - public AI(string identifier, int idLength, AIDataType datatype, int length) - { - m_identifier = identifier; - m_identifierLength = idLength; - m_formatList.Add(new AIDataFormat(datatype, length, length)); - } - - public AI(string identifier, int idLength, AIDataType datatype, int minLength, int maxLength) - { - m_identifier = identifier; - m_identifierLength = idLength; - m_formatList.Add(new AIDataFormat(datatype, minLength, maxLength)); - } - - public AI(string identifier, int idLength, AIDataType datatype1, int length1, AIDataType datatype2, int minLength2, int maxLength2) - { - m_identifier = identifier; - m_identifierLength = idLength; - m_formatList.Add(new AIDataFormat(datatype1, length1, length1)); - m_formatList.Add(new AIDataFormat(datatype2, minLength2, maxLength2)); - } - - public string Identifier - { - get { return m_identifier; } - } - - public int IdentifierLength - { - get { return m_identifierLength; } - } - - public int FormatsCount - { - get { return m_formatList.Count; } - } - - public AIDataFormat Format(int index) - { - return (AIDataFormat)m_formatList[index]; - } - } - - internal class ApplicationIdentifiers - { - // on the basis of GS1 General Specifications, Version 12 - Section 3: GS1 Application Identifier Definitions - private static AI[] m_ais = - { - new AI("00", 2, AIDataType.numeric, 18), - new AI("01", 2, AIDataType.numeric, 14), - new AI("02", 2, AIDataType.numeric, 14), - new AI("10", 2, AIDataType.alphameric, 1, 20), - new AI("11", 2, AIDataType.numeric, 6), - new AI("12", 2, AIDataType.numeric, 6), - new AI("13", 2, AIDataType.numeric, 6), - new AI("15", 2, AIDataType.numeric, 6), - new AI("17", 2, AIDataType.numeric, 6), - new AI("20", 2, AIDataType.numeric, 2), - new AI("21", 2, AIDataType.alphameric, 1, 20), - new AI("22", 2, AIDataType.alphameric, 1, 29), - new AI("240", 3, AIDataType.alphameric, 1, 30), - new AI("241", 3, AIDataType.alphameric, 1, 30), - new AI("242", 3, AIDataType.numeric, 1, 6), - new AI("250", 3, AIDataType.alphameric, 1, 30), - new AI("251", 3, AIDataType.alphameric, 1, 30), - new AI("253", 3, AIDataType.numeric, 13, AIDataType.alphameric, 1, 17), - new AI("254", 3, AIDataType.alphameric, 1, 20), - new AI("30", 2, AIDataType.numeric, 1, 8), - new AI("310", 4, AIDataType.numeric, 6), - new AI("311", 4, AIDataType.numeric, 6), - new AI("312", 4, AIDataType.numeric, 6), - new AI("313", 4, AIDataType.numeric, 6), - new AI("314", 4, AIDataType.numeric, 6), - new AI("315", 4, AIDataType.numeric, 6), - new AI("316", 4, AIDataType.numeric, 6), - new AI("32", 4, AIDataType.numeric, 6), - new AI("330", 4, AIDataType.numeric, 6), - new AI("331", 4, AIDataType.numeric, 6), - new AI("332", 4, AIDataType.numeric, 6), - new AI("333", 4, AIDataType.numeric, 6), - new AI("334", 4, AIDataType.numeric, 6), - new AI("335", 4, AIDataType.numeric, 6), - new AI("336", 4, AIDataType.numeric, 6), - new AI("337", 4, AIDataType.numeric, 6), - new AI("34", 4, AIDataType.numeric, 6), - new AI("350", 4, AIDataType.numeric, 6), - new AI("351", 4, AIDataType.numeric, 6), - new AI("352", 4, AIDataType.numeric, 6), - new AI("353", 4, AIDataType.numeric, 6), - new AI("354", 4, AIDataType.numeric, 6), - new AI("355", 4, AIDataType.numeric, 6), - new AI("356", 4, AIDataType.numeric, 6), - new AI("357", 4, AIDataType.numeric, 6), - new AI("36", 4, AIDataType.numeric, 6), - new AI("37", 2, AIDataType.numeric, 1, 8), - new AI("390", 4, AIDataType.numeric, 1, 15), - new AI("391", 4, AIDataType.numeric, 4, 18), - new AI("392", 4, AIDataType.numeric, 1, 15), - new AI("393", 4, AIDataType.numeric, 4, 18), - new AI("400", 3, AIDataType.alphameric, 1, 30), - new AI("401", 3, AIDataType.alphameric, 1, 30), - new AI("402", 3, AIDataType.numeric, 17), - new AI("403", 3, AIDataType.alphameric, 1, 30), - new AI("410", 3, AIDataType.numeric, 13), - new AI("411", 3, AIDataType.numeric, 13), - new AI("412", 3, AIDataType.numeric, 13), - new AI("413", 3, AIDataType.numeric, 13), - new AI("414", 3, AIDataType.numeric, 13), - new AI("415", 3, AIDataType.numeric, 13), - new AI("420", 3, AIDataType.alphameric, 1, 20), - new AI("421", 3, AIDataType.numeric, 4, 12), - new AI("422", 3, AIDataType.numeric, 3), - new AI("423", 3, AIDataType.numeric, 4, 15), - new AI("424", 3, AIDataType.numeric, 3), - new AI("425", 3, AIDataType.numeric, 3), - new AI("426", 3, AIDataType.numeric, 3), - new AI("7001", 4, AIDataType.numeric, 13), - new AI("7002", 4, AIDataType.alphameric, 1, 30), - new AI("7003", 4, AIDataType.numeric, 10), - new AI("7004", 4, AIDataType.numeric, 1, 4), - new AI("703", 4, AIDataType.numeric, 3, AIDataType.alphameric, 1, 27), - new AI("8001", 4, AIDataType.numeric, 14), - new AI("8002", 4, AIDataType.alphameric, 1, 20), - new AI("8003", 4, AIDataType.numeric, 14, AIDataType.alphameric, 1, 16), - new AI("8004", 4, AIDataType.alphameric, 1, 30), - new AI("8005", 4, AIDataType.numeric, 6), - new AI("8006", 4, AIDataType.numeric, 18), - new AI("8007", 4, AIDataType.alphameric, 1, 30), - new AI("8008", 4, AIDataType.numeric, 9, 12), - new AI("8018", 4, AIDataType.numeric, 18), - new AI("8020", 4, AIDataType.alphameric, 1, 25), - new AI("8100", 4, AIDataType.numeric, 6), - new AI("8101", 4, AIDataType.numeric, 10), - new AI("8102", 4, AIDataType.numeric, 2), - new AI("8110", 4, AIDataType.alphameric, 1, 70), - new AI("8200", 4, AIDataType.alphameric, 1, 70), - new AI("9", 2, AIDataType.alphameric, 1, 30), - }; - - /// - /// Converts the input string like 019931265099999891ZLE000119601000930207|4201890|9261683880|8008150601081850 - /// into string like "(01)99312650999998(91)ZLE000119601000930207(420)1890(92)61683880(8008)150601081850" - /// where values are devided by appropriate AI and force ending separator '|' - /// - /// - /// - internal static string SelectAIs(string value) - { - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < value.Length; i++) - { - for (int j = 0; j < m_ais.Length; j++) - { - if (m_ais[j].Identifier.Length > value.Length - i) - throw new BarcodeException("Incorrect length for the AI identifier"); - - if (m_ais[j].Identifier == value.Substring(i, m_ais[j].Identifier.Length)) - { - sb.Append('('); - sb.Append(value.Substring(i, m_ais[j].IdentifierLength)); - sb.Append(')'); - i += m_ais[j].IdentifierLength; - // search if we have any separator ahead - int nextSeparatorPos = value.IndexOf('|', i); - - for (int k = 0; k < m_ais[j].FormatsCount; k++) - { - if (m_ais[j].Format(k).minLength == m_ais[j].Format(k).maxLength) - { - if (m_ais[j].Format(k).minLength > value.Length - i) - throw new BarcodeException("Incorrect part length for the GS1 barcode value"); - - int valLength = m_ais[j].Format(k).minLength; - if (nextSeparatorPos > -1 && (nextSeparatorPos - i) < valLength) - valLength = nextSeparatorPos - i; - - sb.Append(value.Substring(i, valLength)); - i += valLength; - } - else - { - // if we have left the string greater than max length allowed for AI - // then we cut the string to max length only - // so we will move further to another AI - if (value.Length - i > m_ais[j].Format(k).maxLength) - { - int valLength = m_ais[j].Format(k).maxLength; - if (nextSeparatorPos > -1 && (nextSeparatorPos - i) < valLength) - valLength = nextSeparatorPos - i; - - sb.Append(value.Substring(i, valLength)); - i += valLength; - } - else - { - // otherwise just cut to the end - - int valLength = value.Length - i; - if (nextSeparatorPos > -1 && (nextSeparatorPos - i) < valLength) - valLength = nextSeparatorPos - i; - - - sb.Append(value.Substring(i, valLength)); - i += valLength; - } - //if (i + m_ais[j].Format(k).maxLength >= value.Length - 1) - //{ - // sb.Append(value.Substring(i)); - //} - } - } - i--; - break; - } - } - } - - return sb.ToString(); - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/BigInteger.cs b/MuPDF.NET/Barcode/Encoders/Internal/BigInteger.cs deleted file mode 100644 index 2860cb33..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/BigInteger.cs +++ /dev/null @@ -1,2975 +0,0 @@ -//************************************************************************************ -// BigInteger Class Version 1.03 -// -// Copyright (c) 2002 Chew Keong TAN -// *. -// -// 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, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, provided that the above -// copyright notice(s) and this permission notice appear in all copies of -// the Software and that both the above copyright notice(s) and this -// permission notice appear in supporting documentation. -// -// 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 -// OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -// HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL -// INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING -// FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -// WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -// -// -// Disclaimer -// ---------- -// Although reasonable care has been taken to ensure the correctness of this -// implementation, this code should never be used in any application without -// proper verification and testing. I disclaim all liability and responsibility -// to any person or entity with respect to any loss or damage caused, or alleged -// to be caused, directly or indirectly, by the use of this BigInteger class. -// -// Comments, bugs and suggestions to -// (http://www.codeproject.com/csharp/biginteger.asp) -// -// -// Overloaded Operators +, -, *, /, %, >>, <<, ==, !=, >, <, >=, <=, &, |, ^, ++, --, ~ -// -// Features -// -------- -// 1) Arithmetic operations involving large signed integers (2's complement). -// 2) Primality test using Fermat little theorm, Rabin Miller's method, -// Solovay Strassen's method and Lucas strong pseudoprime. -// 3) Modulo exponential with Barrett's reduction. -// 4) Inverse modulo. -// 5) Pseudo prime generation. -// 6) Co-prime generation. -// -// -// Known Problem -// ------------- -// This pseudoprime passes my implementation of -// primality test but failed in JDK's isProbablePrime test. -// -// byte[] pseudoPrime1 = { (byte)0x00, -// (byte)0x85, (byte)0x84, (byte)0x64, (byte)0xFD, (byte)0x70, (byte)0x6A, -// (byte)0x9F, (byte)0xF0, (byte)0x94, (byte)0x0C, (byte)0x3E, (byte)0x2C, -// (byte)0x74, (byte)0x34, (byte)0x05, (byte)0xC9, (byte)0x55, (byte)0xB3, -// (byte)0x85, (byte)0x32, (byte)0x98, (byte)0x71, (byte)0xF9, (byte)0x41, -// (byte)0x21, (byte)0x5F, (byte)0x02, (byte)0x9E, (byte)0xEA, (byte)0x56, -// (byte)0x8D, (byte)0x8C, (byte)0x44, (byte)0xCC, (byte)0xEE, (byte)0xEE, -// (byte)0x3D, (byte)0x2C, (byte)0x9D, (byte)0x2C, (byte)0x12, (byte)0x41, -// (byte)0x1E, (byte)0xF1, (byte)0xC5, (byte)0x32, (byte)0xC3, (byte)0xAA, -// (byte)0x31, (byte)0x4A, (byte)0x52, (byte)0xD8, (byte)0xE8, (byte)0xAF, -// (byte)0x42, (byte)0xF4, (byte)0x72, (byte)0xA1, (byte)0x2A, (byte)0x0D, -// (byte)0x97, (byte)0xB1, (byte)0x31, (byte)0xB3, -// }; -// -// -// Change Log -// ---------- -// 1) September 23, 2002 (Version 1.03) -// - Fixed operator- to give correct data length. -// - Added Lucas sequence generation. -// - Added Strong Lucas Primality test. -// - Added integer square root method. -// - Added setBit/unsetBit methods. -// - New isProbablePrime() method which do not require the -// confident parameter. -// -// 2) August 29, 2002 (Version 1.02) -// - Fixed bug in the exponentiation of negative numbers. -// - Faster modular exponentiation using Barrett reduction. -// - Added getBytes() method. -// - Fixed bug in ToHexString method. -// - Added overloading of ^ operator. -// - Faster computation of Jacobi symbol. -// -// 3) August 19, 2002 (Version 1.01) -// - Big integer is stored and manipulated as unsigned integers (4 bytes) instead of -// individual bytes this gives significant performance improvement. -// - Updated Fermat's Little Theorem test to use a^(p-1) mod p = 1 -// - Added isProbablePrime method. -// - Updated documentation. -// -// 4) August 9, 2002 (Version 1.0) -// - Initial Release. -// -// -// References -// [1] D. E. Knuth, "Seminumerical Algorithms", The Art of Computer Programming Vol. 2, -// 3rd Edition, Addison-Wesley, 1998. -// -// [2] K. H. Rosen, "Elementary Number Theory and Its Applications", 3rd Ed, -// Addison-Wesley, 1993. -// -// [3] B. Schneier, "Applied Cryptography", 2nd Ed, John Wiley & Sons, 1996. -// -// [4] A. Menezes, P. van Oorschot, and S. Vanstone, "Handbook of Applied Cryptography", -// CRC Press, 1996, www.cacr.math.uwaterloo.ca/hac -// -// [5] A. Bosselaers, R. Govaerts, and J. Vandewalle, "Comparison of Three Modular -// Reduction Functions," Proc. CRYPTO'93, pp.175-186. -// -// [6] R. Baillie and S. S. Wagstaff Jr, "Lucas Pseudoprimes", Mathematics of Computation, -// Vol. 35, No. 152, Oct 1980, pp. 1391-1417. -// -// [7] H. C. Williams, "douard Lucas and Primality Testing", Canadian Mathematical -// Society Series of Monographs and Advance Texts, vol. 22, John Wiley & Sons, New York, -// NY, 1998. -// -// [8] P. Ribenboim, "The new book of prime number records", 3rd edition, Springer-Verlag, -// New York, NY, 1995. -// -// [9] M. Joye and J.-J. Quisquater, "Efficient computation of full Lucas sequences", -// Electronics Letters, 32(6), 1996, pp 537-538. -// -//************************************************************************************ - -using System; - -namespace BarcodeWriter.Core.Internal -{ - class BigInteger - { - // maximum length of the BigInteger in uint (4 bytes) - // change this to suit the required level of precision. - - private const int maxLength = 70; - - // primes smaller than 2000 to test the generated prime number - - public static readonly int[] primesBelow2000 = { - 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, - 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, - 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, - 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, - 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, - 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, - 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, - 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, - 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, - 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, - 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, - 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, - 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, - 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, - 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, - 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, - 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, - 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, - 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, - 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999 }; - - - private uint[] data = null; // stores bytes from the Big Integer - public int dataLength; // number of actual chars used - - - //*********************************************************************** - // Constructor (Default value for BigInteger is 0 - //*********************************************************************** - - public BigInteger() - { - data = new uint[maxLength]; - dataLength = 1; - } - - - //*********************************************************************** - // Constructor (Default value provided by long) - //*********************************************************************** - - public BigInteger(long value) - { - data = new uint[maxLength]; - long tempVal = value; - - // copy bytes from long to BigInteger without any assumption of - // the length of the long datatype - - dataLength = 0; - while (value != 0 && dataLength < maxLength) - { - data[dataLength] = (uint)(value & 0xFFFFFFFF); - value >>= 32; - dataLength++; - } - - if (tempVal > 0) // overflow check for +ve value - { - if (value != 0 || (data[maxLength - 1] & 0x80000000) != 0) - throw (new ArithmeticException("Positive overflow in constructor.")); - } - else if (tempVal < 0) // underflow check for -ve value - { - if (value != -1 || (data[dataLength - 1] & 0x80000000) == 0) - throw (new ArithmeticException("Negative underflow in constructor.")); - } - - if (dataLength == 0) - dataLength = 1; - } - - - //*********************************************************************** - // Constructor (Default value provided by ulong) - //*********************************************************************** - - public BigInteger(ulong value) - { - data = new uint[maxLength]; - - // copy bytes from ulong to BigInteger without any assumption of - // the length of the ulong datatype - - dataLength = 0; - while (value != 0 && dataLength < maxLength) - { - data[dataLength] = (uint)(value & 0xFFFFFFFF); - value >>= 32; - dataLength++; - } - - if (value != 0 || (data[maxLength - 1] & 0x80000000) != 0) - throw (new ArithmeticException("Positive overflow in constructor.")); - - if (dataLength == 0) - dataLength = 1; - } - - - - //*********************************************************************** - // Constructor (Default value provided by BigInteger) - //*********************************************************************** - - public BigInteger(BigInteger bi) - { - data = new uint[maxLength]; - - dataLength = bi.dataLength; - - for (int i = 0; i < dataLength; i++) - data[i] = bi.data[i]; - } - - - //*********************************************************************** - // Constructor (Default value provided by a string of digits of the - // specified base) - // - // Example (base 10) - // ----------------- - // To initialize "a" with the default value of 1234 in base 10 - // BigInteger a = new BigInteger("1234", 10) - // - // To initialize "a" with the default value of -1234 - // BigInteger a = new BigInteger("-1234", 10) - // - // Example (base 16) - // ----------------- - // To initialize "a" with the default value of 0x1D4F in base 16 - // BigInteger a = new BigInteger("1D4F", 16) - // - // To initialize "a" with the default value of -0x1D4F - // BigInteger a = new BigInteger("-1D4F", 16) - // - // Note that string values are specified in the - // format. - // - //*********************************************************************** - - public BigInteger(string value, int radix) - { - BigInteger multiplier = new BigInteger(1); - BigInteger result = new BigInteger(); - value = (value.ToUpper()).Trim(); - int limit = 0; - - if (value[0] == '-') - limit = 1; - - for (int i = value.Length - 1; i >= limit; i--) - { - int posVal = (int)value[i]; - - if (posVal >= '0' && posVal <= '9') - posVal -= '0'; - else if (posVal >= 'A' && posVal <= 'Z') - posVal = (posVal - 'A') + 10; - else - posVal = 9999999; // arbitrary large - - - if (posVal >= radix) - throw (new ArithmeticException("Invalid string in constructor.")); - else - { - if (value[0] == '-') - posVal = -posVal; - - result = result + (multiplier * posVal); - - if ((i - 1) >= limit) - multiplier = multiplier * radix; - } - } - - if (value[0] == '-') // negative values - { - if ((result.data[maxLength - 1] & 0x80000000) == 0) - throw (new ArithmeticException("Negative underflow in constructor.")); - } - else // positive values - { - if ((result.data[maxLength - 1] & 0x80000000) != 0) - throw (new ArithmeticException("Positive overflow in constructor.")); - } - - data = new uint[maxLength]; - for (int i = 0; i < result.dataLength; i++) - data[i] = result.data[i]; - - dataLength = result.dataLength; - } - - - //*********************************************************************** - // Constructor (Default value provided by an array of bytes) - // - // The lowest index of the input byte array (i.e [0]) should contain the - // most significant byte of the number, and the highest index should - // contain the least significant byte. - // - // E.g. - // To initialize "a" with the default value of 0x1D4F in base 16 - // byte[] temp = { 0x1D, 0x4F }; - // BigInteger a = new BigInteger(temp) - // - // Note that this method of initialization does not allow the - // sign to be specified. - // - //*********************************************************************** - - public BigInteger(byte[] inData) - { - dataLength = inData.Length >> 2; - - int leftOver = inData.Length & 0x3; - if (leftOver != 0) // length not multiples of 4 - dataLength++; - - - if (dataLength > maxLength) - throw (new ArithmeticException("Byte overflow in constructor.")); - - data = new uint[maxLength]; - - for (int i = inData.Length - 1, j = 0; i >= 3; i -= 4, j++) - { - data[j] = (uint)((inData[i - 3] << 24) + (inData[i - 2] << 16) + - (inData[i - 1] << 8) + inData[i]); - } - - if (leftOver == 1) - data[dataLength - 1] = (uint)inData[0]; - else if (leftOver == 2) - data[dataLength - 1] = (uint)((inData[0] << 8) + inData[1]); - else if (leftOver == 3) - data[dataLength - 1] = (uint)((inData[0] << 16) + (inData[1] << 8) + inData[2]); - - - while (dataLength > 1 && data[dataLength - 1] == 0) - dataLength--; - - //Console.WriteLine("Len = " + dataLength); - } - - - //*********************************************************************** - // Constructor (Default value provided by an array of bytes of the - // specified length.) - //*********************************************************************** - - public BigInteger(byte[] inData, int inLen) - { - dataLength = inLen >> 2; - - int leftOver = inLen & 0x3; - if (leftOver != 0) // length not multiples of 4 - dataLength++; - - if (dataLength > maxLength || inLen > inData.Length) - throw (new ArithmeticException("Byte overflow in constructor.")); - - - data = new uint[maxLength]; - - for (int i = inLen - 1, j = 0; i >= 3; i -= 4, j++) - { - data[j] = (uint)((inData[i - 3] << 24) + (inData[i - 2] << 16) + - (inData[i - 1] << 8) + inData[i]); - } - - if (leftOver == 1) - data[dataLength - 1] = (uint)inData[0]; - else if (leftOver == 2) - data[dataLength - 1] = (uint)((inData[0] << 8) + inData[1]); - else if (leftOver == 3) - data[dataLength - 1] = (uint)((inData[0] << 16) + (inData[1] << 8) + inData[2]); - - - if (dataLength == 0) - dataLength = 1; - - while (dataLength > 1 && data[dataLength - 1] == 0) - dataLength--; - - //Console.WriteLine("Len = " + dataLength); - } - - - //*********************************************************************** - // Constructor (Default value provided by an array of unsigned integers) - //********************************************************************* - - public BigInteger(uint[] inData) - { - dataLength = inData.Length; - - if (dataLength > maxLength) - throw (new ArithmeticException("Byte overflow in constructor.")); - - data = new uint[maxLength]; - - for (int i = dataLength - 1, j = 0; i >= 0; i--, j++) - data[j] = inData[i]; - - while (dataLength > 1 && data[dataLength - 1] == 0) - dataLength--; - - //Console.WriteLine("Len = " + dataLength); - } - - - //*********************************************************************** - // Overloading of the typecast operator. - // For BigInteger bi = 10; - //*********************************************************************** - - public static implicit operator BigInteger(long value) - { - return (new BigInteger(value)); - } - - public static implicit operator BigInteger(ulong value) - { - return (new BigInteger(value)); - } - - public static implicit operator BigInteger(int value) - { - return (new BigInteger((long)value)); - } - - public static implicit operator BigInteger(uint value) - { - return (new BigInteger((ulong)value)); - } - - - //*********************************************************************** - // Overloading of addition operator - //*********************************************************************** - - public static BigInteger operator +(BigInteger bi1, BigInteger bi2) - { - BigInteger result = new BigInteger(); - - result.dataLength = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; - - long carry = 0; - for (int i = 0; i < result.dataLength; i++) - { - long sum = (long)bi1.data[i] + (long)bi2.data[i] + carry; - carry = sum >> 32; - result.data[i] = (uint)(sum & 0xFFFFFFFF); - } - - if (carry != 0 && result.dataLength < maxLength) - { - result.data[result.dataLength] = (uint)(carry); - result.dataLength++; - } - - while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) - result.dataLength--; - - - // overflow check - int lastPos = maxLength - 1; - if ((bi1.data[lastPos] & 0x80000000) == (bi2.data[lastPos] & 0x80000000) && - (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) - { - throw (new ArithmeticException()); - } - - return result; - } - - - //*********************************************************************** - // Overloading of the unary ++ operator - //*********************************************************************** - - public static BigInteger operator ++(BigInteger bi1) - { - BigInteger result = new BigInteger(bi1); - - long val, carry = 1; - int index = 0; - - while (carry != 0 && index < maxLength) - { - val = (long)(result.data[index]); - val++; - - result.data[index] = (uint)(val & 0xFFFFFFFF); - carry = val >> 32; - - index++; - } - - if (index > result.dataLength) - result.dataLength = index; - else - { - while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) - result.dataLength--; - } - - // overflow check - int lastPos = maxLength - 1; - - // overflow if initial value was +ve but ++ caused a sign - // change to negative. - - if ((bi1.data[lastPos] & 0x80000000) == 0 && - (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) - { - throw (new ArithmeticException("Overflow in ++.")); - } - return result; - } - - - //*********************************************************************** - // Overloading of subtraction operator - //*********************************************************************** - - public static BigInteger operator -(BigInteger bi1, BigInteger bi2) - { - BigInteger result = new BigInteger(); - - result.dataLength = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; - - long carryIn = 0; - for (int i = 0; i < result.dataLength; i++) - { - long diff; - - diff = (long)bi1.data[i] - (long)bi2.data[i] - carryIn; - result.data[i] = (uint)(diff & 0xFFFFFFFF); - - if (diff < 0) - carryIn = 1; - else - carryIn = 0; - } - - // roll over to negative - if (carryIn != 0) - { - for (int i = result.dataLength; i < maxLength; i++) - result.data[i] = 0xFFFFFFFF; - result.dataLength = maxLength; - } - - // fixed in v1.03 to give correct datalength for a - (-b) - while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) - result.dataLength--; - - // overflow check - - int lastPos = maxLength - 1; - if ((bi1.data[lastPos] & 0x80000000) != (bi2.data[lastPos] & 0x80000000) && - (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) - { - throw (new ArithmeticException()); - } - - return result; - } - - - //*********************************************************************** - // Overloading of the unary -- operator - //*********************************************************************** - - public static BigInteger operator --(BigInteger bi1) - { - BigInteger result = new BigInteger(bi1); - - long val; - bool carryIn = true; - int index = 0; - - while (carryIn && index < maxLength) - { - val = (long)(result.data[index]); - val--; - - result.data[index] = (uint)(val & 0xFFFFFFFF); - - if (val >= 0) - carryIn = false; - - index++; - } - - if (index > result.dataLength) - result.dataLength = index; - - while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) - result.dataLength--; - - // overflow check - int lastPos = maxLength - 1; - - // overflow if initial value was -ve but -- caused a sign - // change to positive. - - if ((bi1.data[lastPos] & 0x80000000) != 0 && - (result.data[lastPos] & 0x80000000) != (bi1.data[lastPos] & 0x80000000)) - { - throw (new ArithmeticException("Underflow in --.")); - } - - return result; - } - - - //*********************************************************************** - // Overloading of multiplication operator - //*********************************************************************** - - public static BigInteger operator *(BigInteger bi1, BigInteger bi2) - { - int lastPos = maxLength - 1; - bool bi1Neg = false, bi2Neg = false; - - // take the absolute value of the inputs - try - { - if ((bi1.data[lastPos] & 0x80000000) != 0) // bi1 negative - { - bi1Neg = true; bi1 = -bi1; - } - if ((bi2.data[lastPos] & 0x80000000) != 0) // bi2 negative - { - bi2Neg = true; bi2 = -bi2; - } - } - catch (Exception) { } - - BigInteger result = new BigInteger(); - - // multiply the absolute values - try - { - for (int i = 0; i < bi1.dataLength; i++) - { - if (bi1.data[i] == 0) continue; - - ulong mcarry = 0; - for (int j = 0, k = i; j < bi2.dataLength; j++, k++) - { - // k = i + j - ulong val = ((ulong)bi1.data[i] * (ulong)bi2.data[j]) + - (ulong)result.data[k] + mcarry; - - result.data[k] = (uint)(val & 0xFFFFFFFF); - mcarry = (val >> 32); - } - - if (mcarry != 0) - result.data[i + bi2.dataLength] = (uint)mcarry; - } - } - catch (Exception) - { - throw (new ArithmeticException("Multiplication overflow.")); - } - - - result.dataLength = bi1.dataLength + bi2.dataLength; - if (result.dataLength > maxLength) - result.dataLength = maxLength; - - while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) - result.dataLength--; - - // overflow check (result is -ve) - if ((result.data[lastPos] & 0x80000000) != 0) - { - if (bi1Neg != bi2Neg && result.data[lastPos] == 0x80000000) // different sign - { - // handle the special case where multiplication produces - // a max negative number in 2's complement. - - if (result.dataLength == 1) - return result; - else - { - bool isMaxNeg = true; - for (int i = 0; i < result.dataLength - 1 && isMaxNeg; i++) - { - if (result.data[i] != 0) - isMaxNeg = false; - } - - if (isMaxNeg) - return result; - } - } - - throw (new ArithmeticException("Multiplication overflow.")); - } - - // if input has different signs, then result is -ve - if (bi1Neg != bi2Neg) - return -result; - - return result; - } - - - - //*********************************************************************** - // Overloading of unary << operators - //*********************************************************************** - - public static BigInteger operator <<(BigInteger bi1, int shiftVal) - { - BigInteger result = new BigInteger(bi1); - result.dataLength = shiftLeft(result.data, shiftVal); - - return result; - } - - - // least significant bits at lower part of buffer - - private static int shiftLeft(uint[] buffer, int shiftVal) - { - int shiftAmount = 32; - int bufLen = buffer.Length; - - while (bufLen > 1 && buffer[bufLen - 1] == 0) - bufLen--; - - for (int count = shiftVal; count > 0; ) - { - if (count < shiftAmount) - shiftAmount = count; - - //Console.WriteLine("shiftAmount = {0}", shiftAmount); - - ulong carry = 0; - for (int i = 0; i < bufLen; i++) - { - ulong val = ((ulong)buffer[i]) << shiftAmount; - val |= carry; - - buffer[i] = (uint)(val & 0xFFFFFFFF); - carry = val >> 32; - } - - if (carry != 0) - { - if (bufLen + 1 <= buffer.Length) - { - buffer[bufLen] = (uint)carry; - bufLen++; - } - } - count -= shiftAmount; - } - return bufLen; - } - - - //*********************************************************************** - // Overloading of unary >> operators - //*********************************************************************** - - public static BigInteger operator >>(BigInteger bi1, int shiftVal) - { - BigInteger result = new BigInteger(bi1); - result.dataLength = shiftRight(result.data, shiftVal); - - - if ((bi1.data[maxLength - 1] & 0x80000000) != 0) // negative - { - for (int i = maxLength - 1; i >= result.dataLength; i--) - result.data[i] = 0xFFFFFFFF; - - uint mask = 0x80000000; - for (int i = 0; i < 32; i++) - { - if ((result.data[result.dataLength - 1] & mask) != 0) - break; - - result.data[result.dataLength - 1] |= mask; - mask >>= 1; - } - result.dataLength = maxLength; - } - - return result; - } - - - private static int shiftRight(uint[] buffer, int shiftVal) - { - int shiftAmount = 32; - int invShift = 0; - int bufLen = buffer.Length; - - while (bufLen > 1 && buffer[bufLen - 1] == 0) - bufLen--; - - //Console.WriteLine("bufLen = " + bufLen + " buffer.Length = " + buffer.Length); - - for (int count = shiftVal; count > 0; ) - { - if (count < shiftAmount) - { - shiftAmount = count; - invShift = 32 - shiftAmount; - } - - //Console.WriteLine("shiftAmount = {0}", shiftAmount); - - ulong carry = 0; - for (int i = bufLen - 1; i >= 0; i--) - { - ulong val = ((ulong)buffer[i]) >> shiftAmount; - val |= carry; - - carry = ((ulong)buffer[i]) << invShift; - buffer[i] = (uint)(val); - } - - count -= shiftAmount; - } - - while (bufLen > 1 && buffer[bufLen - 1] == 0) - bufLen--; - - return bufLen; - } - - - //*********************************************************************** - // Overloading of the NOT operator (1's complement) - //*********************************************************************** - - public static BigInteger operator ~(BigInteger bi1) - { - BigInteger result = new BigInteger(bi1); - - for (int i = 0; i < maxLength; i++) - result.data[i] = (uint)(~(bi1.data[i])); - - result.dataLength = maxLength; - - while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) - result.dataLength--; - - return result; - } - - - //*********************************************************************** - // Overloading of the NEGATE operator (2's complement) - //*********************************************************************** - - public static BigInteger operator -(BigInteger bi1) - { - // handle neg of zero separately since it'll cause an overflow - // if we proceed. - - if (bi1.dataLength == 1 && bi1.data[0] == 0) - return (new BigInteger()); - - BigInteger result = new BigInteger(bi1); - - // 1's complement - for (int i = 0; i < maxLength; i++) - result.data[i] = (uint)(~(bi1.data[i])); - - // add one to result of 1's complement - long val, carry = 1; - int index = 0; - - while (carry != 0 && index < maxLength) - { - val = (long)(result.data[index]); - val++; - - result.data[index] = (uint)(val & 0xFFFFFFFF); - carry = val >> 32; - - index++; - } - - if ((bi1.data[maxLength - 1] & 0x80000000) == (result.data[maxLength - 1] & 0x80000000)) - throw (new ArithmeticException("Overflow in negation.\n")); - - result.dataLength = maxLength; - - while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) - result.dataLength--; - return result; - } - - - //*********************************************************************** - // Overloading of equality operator - //*********************************************************************** - - public static bool operator ==(BigInteger bi1, BigInteger bi2) - { - return bi1.Equals(bi2); - } - - - public static bool operator !=(BigInteger bi1, BigInteger bi2) - { - return !(bi1.Equals(bi2)); - } - - - public override bool Equals(object o) - { - BigInteger bi = (BigInteger)o; - - if (this.dataLength != bi.dataLength) - return false; - - for (int i = 0; i < this.dataLength; i++) - { - if (this.data[i] != bi.data[i]) - return false; - } - return true; - } - - - public override int GetHashCode() - { - return this.ToString().GetHashCode(); - } - - - //*********************************************************************** - // Overloading of inequality operator - //*********************************************************************** - - public static bool operator >(BigInteger bi1, BigInteger bi2) - { - int pos = maxLength - 1; - - // bi1 is negative, bi2 is positive - if ((bi1.data[pos] & 0x80000000) != 0 && (bi2.data[pos] & 0x80000000) == 0) - return false; - - // bi1 is positive, bi2 is negative - else if ((bi1.data[pos] & 0x80000000) == 0 && (bi2.data[pos] & 0x80000000) != 0) - return true; - - // same sign - int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; - for (pos = len - 1; pos >= 0 && bi1.data[pos] == bi2.data[pos]; pos--) ; - - if (pos >= 0) - { - if (bi1.data[pos] > bi2.data[pos]) - return true; - return false; - } - return false; - } - - - public static bool operator <(BigInteger bi1, BigInteger bi2) - { - int pos = maxLength - 1; - - // bi1 is negative, bi2 is positive - if ((bi1.data[pos] & 0x80000000) != 0 && (bi2.data[pos] & 0x80000000) == 0) - return true; - - // bi1 is positive, bi2 is negative - else if ((bi1.data[pos] & 0x80000000) == 0 && (bi2.data[pos] & 0x80000000) != 0) - return false; - - // same sign - int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; - for (pos = len - 1; pos >= 0 && bi1.data[pos] == bi2.data[pos]; pos--) ; - - if (pos >= 0) - { - if (bi1.data[pos] < bi2.data[pos]) - return true; - return false; - } - return false; - } - - - public static bool operator >=(BigInteger bi1, BigInteger bi2) - { - return (bi1 == bi2 || bi1 > bi2); - } - - - public static bool operator <=(BigInteger bi1, BigInteger bi2) - { - return (bi1 == bi2 || bi1 < bi2); - } - - - //*********************************************************************** - // Private function that supports the division of two numbers with - // a divisor that has more than 1 digit. - // - // Algorithm taken from [1] - //*********************************************************************** - - private static void multiByteDivide(BigInteger bi1, BigInteger bi2, - BigInteger outQuotient, BigInteger outRemainder) - { - uint[] result = new uint[maxLength]; - - int remainderLen = bi1.dataLength + 1; - uint[] remainder = new uint[remainderLen]; - - uint mask = 0x80000000; - uint val = bi2.data[bi2.dataLength - 1]; - int shift = 0, resultPos = 0; - - while (mask != 0 && (val & mask) == 0) - { - shift++; mask >>= 1; - } - - //Console.WriteLine("shift = {0}", shift); - //Console.WriteLine("Before bi1 Len = {0}, bi2 Len = {1}", bi1.dataLength, bi2.dataLength); - - for (int i = 0; i < bi1.dataLength; i++) - remainder[i] = bi1.data[i]; - shiftLeft(remainder, shift); - bi2 = bi2 << shift; - - /* - Console.WriteLine("bi1 Len = {0}, bi2 Len = {1}", bi1.dataLength, bi2.dataLength); - Console.WriteLine("dividend = " + bi1 + "\ndivisor = " + bi2); - for(int q = remainderLen - 1; q >= 0; q--) - Console.Write("{0:x2}", remainder[q]); - Console.WriteLine(); - */ - - int j = remainderLen - bi2.dataLength; - int pos = remainderLen - 1; - - ulong firstDivisorByte = bi2.data[bi2.dataLength - 1]; - ulong secondDivisorByte = bi2.data[bi2.dataLength - 2]; - - int divisorLen = bi2.dataLength + 1; - uint[] dividendPart = new uint[divisorLen]; - - while (j > 0) - { - ulong dividend = ((ulong)remainder[pos] << 32) + (ulong)remainder[pos - 1]; - //Console.WriteLine("dividend = {0}", dividend); - - ulong q_hat = dividend / firstDivisorByte; - ulong r_hat = dividend % firstDivisorByte; - - //Console.WriteLine("q_hat = {0:X}, r_hat = {1:X}", q_hat, r_hat); - - bool done = false; - while (!done) - { - done = true; - - if (q_hat == 0x100000000 || - (q_hat * secondDivisorByte) > ((r_hat << 32) + remainder[pos - 2])) - { - q_hat--; - r_hat += firstDivisorByte; - - if (r_hat < 0x100000000) - done = false; - } - } - - for (int h = 0; h < divisorLen; h++) - dividendPart[h] = remainder[pos - h]; - - BigInteger kk = new BigInteger(dividendPart); - BigInteger ss = bi2 * (long)q_hat; - - //Console.WriteLine("ss before = " + ss); - while (ss > kk) - { - q_hat--; - ss -= bi2; - //Console.WriteLine(ss); - } - BigInteger yy = kk - ss; - - //Console.WriteLine("ss = " + ss); - //Console.WriteLine("kk = " + kk); - //Console.WriteLine("yy = " + yy); - - for (int h = 0; h < divisorLen; h++) - remainder[pos - h] = yy.data[bi2.dataLength - h]; - - /* - Console.WriteLine("dividend = "); - for(int q = remainderLen - 1; q >= 0; q--) - Console.Write("{0:x2}", remainder[q]); - Console.WriteLine("\n************ q_hat = {0:X}\n", q_hat); - */ - - result[resultPos++] = (uint)q_hat; - - pos--; - j--; - } - - outQuotient.dataLength = resultPos; - int y = 0; - for (int x = outQuotient.dataLength - 1; x >= 0; x--, y++) - outQuotient.data[y] = result[x]; - for (; y < maxLength; y++) - outQuotient.data[y] = 0; - - while (outQuotient.dataLength > 1 && outQuotient.data[outQuotient.dataLength - 1] == 0) - outQuotient.dataLength--; - - if (outQuotient.dataLength == 0) - outQuotient.dataLength = 1; - - outRemainder.dataLength = shiftRight(remainder, shift); - - for (y = 0; y < outRemainder.dataLength; y++) - outRemainder.data[y] = remainder[y]; - for (; y < maxLength; y++) - outRemainder.data[y] = 0; - } - - - //*********************************************************************** - // Private function that supports the division of two numbers with - // a divisor that has only 1 digit. - //*********************************************************************** - - private static void singleByteDivide(BigInteger bi1, BigInteger bi2, - BigInteger outQuotient, BigInteger outRemainder) - { - uint[] result = new uint[maxLength]; - int resultPos = 0; - - // copy dividend to reminder - for (int i = 0; i < maxLength; i++) - outRemainder.data[i] = bi1.data[i]; - outRemainder.dataLength = bi1.dataLength; - - while (outRemainder.dataLength > 1 && outRemainder.data[outRemainder.dataLength - 1] == 0) - outRemainder.dataLength--; - - ulong divisor = (ulong)bi2.data[0]; - int pos = outRemainder.dataLength - 1; - ulong dividend = (ulong)outRemainder.data[pos]; - - //Console.WriteLine("divisor = " + divisor + " dividend = " + dividend); - //Console.WriteLine("divisor = " + bi2 + "\ndividend = " + bi1); - - if (dividend >= divisor) - { - ulong quotient = dividend / divisor; - result[resultPos++] = (uint)quotient; - - outRemainder.data[pos] = (uint)(dividend % divisor); - } - pos--; - - while (pos >= 0) - { - //Console.WriteLine(pos); - - dividend = ((ulong)outRemainder.data[pos + 1] << 32) + (ulong)outRemainder.data[pos]; - ulong quotient = dividend / divisor; - result[resultPos++] = (uint)quotient; - - outRemainder.data[pos + 1] = 0; - outRemainder.data[pos--] = (uint)(dividend % divisor); - //Console.WriteLine(">>>> " + bi1); - } - - outQuotient.dataLength = resultPos; - int j = 0; - for (int i = outQuotient.dataLength - 1; i >= 0; i--, j++) - outQuotient.data[j] = result[i]; - for (; j < maxLength; j++) - outQuotient.data[j] = 0; - - while (outQuotient.dataLength > 1 && outQuotient.data[outQuotient.dataLength - 1] == 0) - outQuotient.dataLength--; - - if (outQuotient.dataLength == 0) - outQuotient.dataLength = 1; - - while (outRemainder.dataLength > 1 && outRemainder.data[outRemainder.dataLength - 1] == 0) - outRemainder.dataLength--; - } - - - //*********************************************************************** - // Overloading of division operator - //*********************************************************************** - - public static BigInteger operator /(BigInteger bi1, BigInteger bi2) - { - BigInteger quotient = new BigInteger(); - BigInteger remainder = new BigInteger(); - - int lastPos = maxLength - 1; - bool divisorNeg = false, dividendNeg = false; - - if ((bi1.data[lastPos] & 0x80000000) != 0) // bi1 negative - { - bi1 = -bi1; - dividendNeg = true; - } - if ((bi2.data[lastPos] & 0x80000000) != 0) // bi2 negative - { - bi2 = -bi2; - divisorNeg = true; - } - - if (bi1 < bi2) - { - return quotient; - } - - else - { - if (bi2.dataLength == 1) - singleByteDivide(bi1, bi2, quotient, remainder); - else - multiByteDivide(bi1, bi2, quotient, remainder); - - if (dividendNeg != divisorNeg) - return -quotient; - - return quotient; - } - } - - - //*********************************************************************** - // Overloading of modulus operator - //*********************************************************************** - - public static BigInteger operator %(BigInteger bi1, BigInteger bi2) - { - BigInteger quotient = new BigInteger(); - BigInteger remainder = new BigInteger(bi1); - - int lastPos = maxLength - 1; - bool dividendNeg = false; - - if ((bi1.data[lastPos] & 0x80000000) != 0) // bi1 negative - { - bi1 = -bi1; - dividendNeg = true; - } - if ((bi2.data[lastPos] & 0x80000000) != 0) // bi2 negative - bi2 = -bi2; - - if (bi1 < bi2) - { - return remainder; - } - - else - { - if (bi2.dataLength == 1) - singleByteDivide(bi1, bi2, quotient, remainder); - else - multiByteDivide(bi1, bi2, quotient, remainder); - - if (dividendNeg) - return -remainder; - - return remainder; - } - } - - - //*********************************************************************** - // Overloading of bitwise AND operator - //*********************************************************************** - - public static BigInteger operator &(BigInteger bi1, BigInteger bi2) - { - BigInteger result = new BigInteger(); - - int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; - - for (int i = 0; i < len; i++) - { - uint sum = (uint)(bi1.data[i] & bi2.data[i]); - result.data[i] = sum; - } - - result.dataLength = maxLength; - - while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) - result.dataLength--; - - return result; - } - - - //*********************************************************************** - // Overloading of bitwise OR operator - //*********************************************************************** - - public static BigInteger operator |(BigInteger bi1, BigInteger bi2) - { - BigInteger result = new BigInteger(); - - int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; - - for (int i = 0; i < len; i++) - { - uint sum = (uint)(bi1.data[i] | bi2.data[i]); - result.data[i] = sum; - } - - result.dataLength = maxLength; - - while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) - result.dataLength--; - - return result; - } - - - //*********************************************************************** - // Overloading of bitwise XOR operator - //*********************************************************************** - - public static BigInteger operator ^(BigInteger bi1, BigInteger bi2) - { - BigInteger result = new BigInteger(); - - int len = (bi1.dataLength > bi2.dataLength) ? bi1.dataLength : bi2.dataLength; - - for (int i = 0; i < len; i++) - { - uint sum = (uint)(bi1.data[i] ^ bi2.data[i]); - result.data[i] = sum; - } - - result.dataLength = maxLength; - - while (result.dataLength > 1 && result.data[result.dataLength - 1] == 0) - result.dataLength--; - - return result; - } - - - //*********************************************************************** - // Returns max(this, bi) - //*********************************************************************** - - public BigInteger max(BigInteger bi) - { - if (this > bi) - return (new BigInteger(this)); - else - return (new BigInteger(bi)); - } - - - //*********************************************************************** - // Returns min(this, bi) - //*********************************************************************** - - public BigInteger min(BigInteger bi) - { - if (this < bi) - return (new BigInteger(this)); - else - return (new BigInteger(bi)); - - } - - - //*********************************************************************** - // Returns the absolute value - //*********************************************************************** - - public BigInteger abs() - { - if ((this.data[maxLength - 1] & 0x80000000) != 0) - return (-this); - else - return (new BigInteger(this)); - } - - - //*********************************************************************** - // Returns a string representing the BigInteger in base 10. - //*********************************************************************** - - public override string ToString() - { - return ToString(10); - } - - - //*********************************************************************** - // Returns a string representing the BigInteger in sign-and-magnitude - // format in the specified radix. - // - // Example - // ------- - // If the value of BigInteger is -255 in base 10, then - // ToString(16) returns "-FF" - // - //*********************************************************************** - - public string ToString(int radix) - { - if (radix < 2 || radix > 36) - throw (new ArgumentException("Radix must be >= 2 and <= 36")); - - string charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - string result = ""; - - BigInteger a = this; - - bool negative = false; - if ((a.data[maxLength - 1] & 0x80000000) != 0) - { - negative = true; - try - { - a = -a; - } - catch (Exception) { } - } - - BigInteger quotient = new BigInteger(); - BigInteger remainder = new BigInteger(); - BigInteger biRadix = new BigInteger(radix); - - if (a.dataLength == 1 && a.data[0] == 0) - result = "0"; - else - { - while (a.dataLength > 1 || (a.dataLength == 1 && a.data[0] != 0)) - { - singleByteDivide(a, biRadix, quotient, remainder); - - if (remainder.data[0] < 10) - result = remainder.data[0] + result; - else - result = charSet[(int)remainder.data[0] - 10] + result; - - a = quotient; - } - if (negative) - result = "-" + result; - } - - return result; - } - - - //*********************************************************************** - // Returns a hex string showing the contains of the BigInteger - // - // Examples - // ------- - // 1) If the value of BigInteger is 255 in base 10, then - // ToHexString() returns "FF" - // - // 2) If the value of BigInteger is -255 in base 10, then - // ToHexString() returns ".....FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01", - // which is the 2's complement representation of -255. - // - //*********************************************************************** - - public string ToHexString() - { - string result = data[dataLength - 1].ToString("X"); - - for (int i = dataLength - 2; i >= 0; i--) - { - result += data[i].ToString("X8"); - } - - return result; - } - - - - //*********************************************************************** - // Modulo Exponentiation - //*********************************************************************** - - public BigInteger modPow(BigInteger exp, BigInteger n) - { - if ((exp.data[maxLength - 1] & 0x80000000) != 0) - throw (new ArithmeticException("Positive exponents only.")); - - BigInteger resultNum = 1; - BigInteger tempNum; - bool thisNegative = false; - - if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative this - { - tempNum = -this % n; - thisNegative = true; - } - else - tempNum = this % n; // ensures (tempNum * tempNum) < b^(2k) - - if ((n.data[maxLength - 1] & 0x80000000) != 0) // negative n - n = -n; - - // calculate constant = b^(2k) / m - BigInteger constant = new BigInteger(); - - int i = n.dataLength << 1; - constant.data[i] = 0x00000001; - constant.dataLength = i + 1; - - constant = constant / n; - int totalBits = exp.bitCount(); - int count = 0; - - // perform squaring and multiply exponentiation - for (int pos = 0; pos < exp.dataLength; pos++) - { - uint mask = 0x01; - //Console.WriteLine("pos = " + pos); - - for (int index = 0; index < 32; index++) - { - if ((exp.data[pos] & mask) != 0) - resultNum = BarrettReduction(resultNum * tempNum, n, constant); - - mask <<= 1; - - tempNum = BarrettReduction(tempNum * tempNum, n, constant); - - - if (tempNum.dataLength == 1 && tempNum.data[0] == 1) - { - if (thisNegative && (exp.data[0] & 0x1) != 0) //odd exp - return -resultNum; - return resultNum; - } - count++; - if (count == totalBits) - break; - } - } - - if (thisNegative && (exp.data[0] & 0x1) != 0) //odd exp - return -resultNum; - - return resultNum; - } - - - - //*********************************************************************** - // Fast calculation of modular reduction using Barrett's reduction. - // Requires x < b^(2k), where b is the base. In this case, base is - // 2^32 (uint). - // - // Reference [4] - //*********************************************************************** - - private BigInteger BarrettReduction(BigInteger x, BigInteger n, BigInteger constant) - { - int k = n.dataLength, - kPlusOne = k + 1, - kMinusOne = k - 1; - - BigInteger q1 = new BigInteger(); - - // q1 = x / b^(k-1) - for (int i = kMinusOne, j = 0; i < x.dataLength; i++, j++) - q1.data[j] = x.data[i]; - q1.dataLength = x.dataLength - kMinusOne; - if (q1.dataLength <= 0) - q1.dataLength = 1; - - - BigInteger q2 = q1 * constant; - BigInteger q3 = new BigInteger(); - - // q3 = q2 / b^(k+1) - for (int i = kPlusOne, j = 0; i < q2.dataLength; i++, j++) - q3.data[j] = q2.data[i]; - q3.dataLength = q2.dataLength - kPlusOne; - if (q3.dataLength <= 0) - q3.dataLength = 1; - - - // r1 = x mod b^(k+1) - // i.e. keep the lowest (k+1) words - BigInteger r1 = new BigInteger(); - int lengthToCopy = (x.dataLength > kPlusOne) ? kPlusOne : x.dataLength; - for (int i = 0; i < lengthToCopy; i++) - r1.data[i] = x.data[i]; - r1.dataLength = lengthToCopy; - - - // r2 = (q3 * n) mod b^(k+1) - // partial multiplication of q3 and n - - BigInteger r2 = new BigInteger(); - for (int i = 0; i < q3.dataLength; i++) - { - if (q3.data[i] == 0) continue; - - ulong mcarry = 0; - int t = i; - for (int j = 0; j < n.dataLength && t < kPlusOne; j++, t++) - { - // t = i + j - ulong val = ((ulong)q3.data[i] * (ulong)n.data[j]) + - (ulong)r2.data[t] + mcarry; - - r2.data[t] = (uint)(val & 0xFFFFFFFF); - mcarry = (val >> 32); - } - - if (t < kPlusOne) - r2.data[t] = (uint)mcarry; - } - r2.dataLength = kPlusOne; - while (r2.dataLength > 1 && r2.data[r2.dataLength - 1] == 0) - r2.dataLength--; - - r1 -= r2; - if ((r1.data[maxLength - 1] & 0x80000000) != 0) // negative - { - BigInteger val = new BigInteger(); - val.data[kPlusOne] = 0x00000001; - val.dataLength = kPlusOne + 1; - r1 += val; - } - - while (r1 >= n) - r1 -= n; - - return r1; - } - - - //*********************************************************************** - // Returns gcd(this, bi) - //*********************************************************************** - - public BigInteger gcd(BigInteger bi) - { - BigInteger x; - BigInteger y; - - if ((data[maxLength - 1] & 0x80000000) != 0) // negative - x = -this; - else - x = this; - - if ((bi.data[maxLength - 1] & 0x80000000) != 0) // negative - y = -bi; - else - y = bi; - - BigInteger g = y; - - while (x.dataLength > 1 || (x.dataLength == 1 && x.data[0] != 0)) - { - g = x; - x = y % x; - y = g; - } - - return g; - } - - - //*********************************************************************** - // Populates "this" with the specified amount of random bits - //*********************************************************************** - - public void genRandomBits(int bits, Random rand) - { - int dwords = bits >> 5; - int remBits = bits & 0x1F; - - if (remBits != 0) - dwords++; - - if (dwords > maxLength) - throw (new ArithmeticException("Number of required bits > maxLength.")); - - for (int i = 0; i < dwords; i++) - data[i] = (uint)(rand.NextDouble() * 0x100000000); - - for (int i = dwords; i < maxLength; i++) - data[i] = 0; - - if (remBits != 0) - { - uint mask = (uint)(0x01 << (remBits - 1)); - data[dwords - 1] |= mask; - - mask = (uint)(0xFFFFFFFF >> (32 - remBits)); - data[dwords - 1] &= mask; - } - else - data[dwords - 1] |= 0x80000000; - - dataLength = dwords; - - if (dataLength == 0) - dataLength = 1; - } - - - //*********************************************************************** - // Returns the position of the most significant bit in the BigInteger. - // - // Eg. The result is 0, if the value of BigInteger is 0...0000 0000 - // The result is 1, if the value of BigInteger is 0...0000 0001 - // The result is 2, if the value of BigInteger is 0...0000 0010 - // The result is 2, if the value of BigInteger is 0...0000 0011 - // - //*********************************************************************** - - public int bitCount() - { - while (dataLength > 1 && data[dataLength - 1] == 0) - dataLength--; - - uint value = data[dataLength - 1]; - uint mask = 0x80000000; - int bits = 32; - - while (bits > 0 && (value & mask) == 0) - { - bits--; - mask >>= 1; - } - bits += ((dataLength - 1) << 5); - - return bits; - } - - - //*********************************************************************** - // Probabilistic prime test based on Fermat's little theorem - // - // for any a < p (p does not divide a) if - // a^(p-1) mod p != 1 then p is not prime. - // - // Otherwise, p is probably prime (pseudoprime to the chosen base). - // - // Returns - // ------- - // True if "this" is a pseudoprime to randomly chosen - // bases. The number of chosen bases is given by the "confidence" - // parameter. - // - // False if "this" is definitely NOT prime. - // - // Note - this method is fast but fails for Carmichael numbers except - // when the randomly chosen base is a factor of the number. - // - //*********************************************************************** - - public bool FermatLittleTest(int confidence) - { - BigInteger thisVal; - if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative - thisVal = -this; - else - thisVal = this; - - if (thisVal.dataLength == 1) - { - // test small numbers - if (thisVal.data[0] == 0 || thisVal.data[0] == 1) - return false; - else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) - return true; - } - - if ((thisVal.data[0] & 0x1) == 0) // even numbers - return false; - - int bits = thisVal.bitCount(); - BigInteger a = new BigInteger(); - BigInteger p_sub1 = thisVal - (new BigInteger(1)); - Random rand = new Random(); - - for (int round = 0; round < confidence; round++) - { - bool done = false; - - while (!done) // generate a < n - { - int testBits = 0; - - // make sure "a" has at least 2 bits - while (testBits < 2) - testBits = (int)(rand.NextDouble() * bits); - - a.genRandomBits(testBits, rand); - - int byteLen = a.dataLength; - - // make sure "a" is not 0 - if (byteLen > 1 || (byteLen == 1 && a.data[0] != 1)) - done = true; - } - - // check whether a factor exists (fix for version 1.03) - BigInteger gcdTest = a.gcd(thisVal); - if (gcdTest.dataLength == 1 && gcdTest.data[0] != 1) - return false; - - // calculate a^(p-1) mod p - BigInteger expResult = a.modPow(p_sub1, thisVal); - - int resultLen = expResult.dataLength; - - // is NOT prime is a^(p-1) mod p != 1 - - if (resultLen > 1 || (resultLen == 1 && expResult.data[0] != 1)) - { - //Console.WriteLine("a = " + a.ToString()); - return false; - } - } - - return true; - } - - - //*********************************************************************** - // Probabilistic prime test based on Rabin-Miller's - // - // for any p > 0 with p - 1 = 2^s * t - // - // p is probably prime (strong pseudoprime) if for any a < p, - // 1) a^t mod p = 1 or - // 2) a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1 - // - // Otherwise, p is composite. - // - // Returns - // ------- - // True if "this" is a strong pseudoprime to randomly chosen - // bases. The number of chosen bases is given by the "confidence" - // parameter. - // - // False if "this" is definitely NOT prime. - // - //*********************************************************************** - - public bool RabinMillerTest(int confidence) - { - BigInteger thisVal; - if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative - thisVal = -this; - else - thisVal = this; - - if (thisVal.dataLength == 1) - { - // test small numbers - if (thisVal.data[0] == 0 || thisVal.data[0] == 1) - return false; - else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) - return true; - } - - if ((thisVal.data[0] & 0x1) == 0) // even numbers - return false; - - - // calculate values of s and t - BigInteger p_sub1 = thisVal - (new BigInteger(1)); - int s = 0; - - for (int index = 0; index < p_sub1.dataLength; index++) - { - uint mask = 0x01; - - for (int i = 0; i < 32; i++) - { - if ((p_sub1.data[index] & mask) != 0) - { - index = p_sub1.dataLength; // to break the outer loop - break; - } - mask <<= 1; - s++; - } - } - - BigInteger t = p_sub1 >> s; - - int bits = thisVal.bitCount(); - BigInteger a = new BigInteger(); - Random rand = new Random(); - - for (int round = 0; round < confidence; round++) - { - bool done = false; - - while (!done) // generate a < n - { - int testBits = 0; - - // make sure "a" has at least 2 bits - while (testBits < 2) - testBits = (int)(rand.NextDouble() * bits); - - a.genRandomBits(testBits, rand); - - int byteLen = a.dataLength; - - // make sure "a" is not 0 - if (byteLen > 1 || (byteLen == 1 && a.data[0] != 1)) - done = true; - } - - // check whether a factor exists (fix for version 1.03) - BigInteger gcdTest = a.gcd(thisVal); - if (gcdTest.dataLength == 1 && gcdTest.data[0] != 1) - return false; - - BigInteger b = a.modPow(t, thisVal); - - /* - Console.WriteLine("a = " + a.ToString(10)); - Console.WriteLine("b = " + b.ToString(10)); - Console.WriteLine("t = " + t.ToString(10)); - Console.WriteLine("s = " + s); - */ - - bool result = false; - - if (b.dataLength == 1 && b.data[0] == 1) // a^t mod p = 1 - result = true; - - for (int j = 0; result == false && j < s; j++) - { - if (b == p_sub1) // a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1 - { - result = true; - break; - } - - b = (b * b) % thisVal; - } - - if (result == false) - return false; - } - return true; - } - - - //*********************************************************************** - // Probabilistic prime test based on Solovay-Strassen (Euler Criterion) - // - // p is probably prime if for any a < p (a is not multiple of p), - // a^((p-1)/2) mod p = J(a, p) - // - // where J is the Jacobi symbol. - // - // Otherwise, p is composite. - // - // Returns - // ------- - // True if "this" is a Euler pseudoprime to randomly chosen - // bases. The number of chosen bases is given by the "confidence" - // parameter. - // - // False if "this" is definitely NOT prime. - // - //*********************************************************************** - - public bool SolovayStrassenTest(int confidence) - { - BigInteger thisVal; - if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative - thisVal = -this; - else - thisVal = this; - - if (thisVal.dataLength == 1) - { - // test small numbers - if (thisVal.data[0] == 0 || thisVal.data[0] == 1) - return false; - else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) - return true; - } - - if ((thisVal.data[0] & 0x1) == 0) // even numbers - return false; - - - int bits = thisVal.bitCount(); - BigInteger a = new BigInteger(); - BigInteger p_sub1 = thisVal - 1; - BigInteger p_sub1_shift = p_sub1 >> 1; - - Random rand = new Random(); - - for (int round = 0; round < confidence; round++) - { - bool done = false; - - while (!done) // generate a < n - { - int testBits = 0; - - // make sure "a" has at least 2 bits - while (testBits < 2) - testBits = (int)(rand.NextDouble() * bits); - - a.genRandomBits(testBits, rand); - - int byteLen = a.dataLength; - - // make sure "a" is not 0 - if (byteLen > 1 || (byteLen == 1 && a.data[0] != 1)) - done = true; - } - - // check whether a factor exists (fix for version 1.03) - BigInteger gcdTest = a.gcd(thisVal); - if (gcdTest.dataLength == 1 && gcdTest.data[0] != 1) - return false; - - // calculate a^((p-1)/2) mod p - - BigInteger expResult = a.modPow(p_sub1_shift, thisVal); - if (expResult == p_sub1) - expResult = -1; - - // calculate Jacobi symbol - BigInteger jacob = Jacobi(a, thisVal); - - //Console.WriteLine("a = " + a.ToString(10) + " b = " + thisVal.ToString(10)); - //Console.WriteLine("expResult = " + expResult.ToString(10) + " Jacob = " + jacob.ToString(10)); - - // if they are different then it is not prime - if (expResult != jacob) - return false; - } - - return true; - } - - - //*********************************************************************** - // Implementation of the Lucas Strong Pseudo Prime test. - // - // Let n be an odd number with gcd(n,D) = 1, and n - J(D, n) = 2^s * d - // with d odd and s >= 0. - // - // If Ud mod n = 0 or V2^r*d mod n = 0 for some 0 <= r < s, then n - // is a strong Lucas pseudoprime with parameters (P, Q). We select - // P and Q based on Selfridge. - // - // Returns True if number is a strong Lucus pseudo prime. - // Otherwise, returns False indicating that number is composite. - //*********************************************************************** - - public bool LucasStrongTest() - { - BigInteger thisVal; - if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative - thisVal = -this; - else - thisVal = this; - - if (thisVal.dataLength == 1) - { - // test small numbers - if (thisVal.data[0] == 0 || thisVal.data[0] == 1) - return false; - else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) - return true; - } - - if ((thisVal.data[0] & 0x1) == 0) // even numbers - return false; - - return LucasStrongTestHelper(thisVal); - } - - - private bool LucasStrongTestHelper(BigInteger thisVal) - { - // Do the test (selects D based on Selfridge) - // Let D be the first element of the sequence - // 5, -7, 9, -11, 13, ... for which J(D,n) = -1 - // Let P = 1, Q = (1-D) / 4 - - long D = 5, sign = -1, dCount = 0; - bool done = false; - - while (!done) - { - int Jresult = BigInteger.Jacobi(D, thisVal); - - if (Jresult == -1) - done = true; // J(D, this) = 1 - else - { - if (Jresult == 0 && Math.Abs(D) < thisVal) // divisor found - return false; - - if (dCount == 20) - { - // check for square - BigInteger root = thisVal.sqrt(); - if (root * root == thisVal) - return false; - } - - //Console.WriteLine(D); - D = (Math.Abs(D) + 2) * sign; - sign = -sign; - } - dCount++; - } - - long Q = (1 - D) >> 2; - - /* - Console.WriteLine("D = " + D); - Console.WriteLine("Q = " + Q); - Console.WriteLine("(n,D) = " + thisVal.gcd(D)); - Console.WriteLine("(n,Q) = " + thisVal.gcd(Q)); - Console.WriteLine("J(D|n) = " + BigInteger.Jacobi(D, thisVal)); - */ - - BigInteger p_add1 = thisVal + 1; - int s = 0; - - for (int index = 0; index < p_add1.dataLength; index++) - { - uint mask = 0x01; - - for (int i = 0; i < 32; i++) - { - if ((p_add1.data[index] & mask) != 0) - { - index = p_add1.dataLength; // to break the outer loop - break; - } - mask <<= 1; - s++; - } - } - - BigInteger t = p_add1 >> s; - - // calculate constant = b^(2k) / m - // for Barrett Reduction - BigInteger constant = new BigInteger(); - - int nLen = thisVal.dataLength << 1; - constant.data[nLen] = 0x00000001; - constant.dataLength = nLen + 1; - - constant = constant / thisVal; - - BigInteger[] lucas = LucasSequenceHelper(1, Q, t, thisVal, constant, 0); - bool isPrime = false; - - if ((lucas[0].dataLength == 1 && lucas[0].data[0] == 0) || - (lucas[1].dataLength == 1 && lucas[1].data[0] == 0)) - { - // u(t) = 0 or V(t) = 0 - isPrime = true; - } - - for (int i = 1; i < s; i++) - { - if (!isPrime) - { - // doubling of index - lucas[1] = thisVal.BarrettReduction(lucas[1] * lucas[1], thisVal, constant); - lucas[1] = (lucas[1] - (lucas[2] << 1)) % thisVal; - - //lucas[1] = ((lucas[1] * lucas[1]) - (lucas[2] << 1)) % thisVal; - - if ((lucas[1].dataLength == 1 && lucas[1].data[0] == 0)) - isPrime = true; - } - - lucas[2] = thisVal.BarrettReduction(lucas[2] * lucas[2], thisVal, constant); //Q^k - } - - - if (isPrime) // additional checks for composite numbers - { - // If n is prime and gcd(n, Q) == 1, then - // Q^((n+1)/2) = Q * Q^((n-1)/2) is congruent to (Q * J(Q, n)) mod n - - BigInteger g = thisVal.gcd(Q); - if (g.dataLength == 1 && g.data[0] == 1) // gcd(this, Q) == 1 - { - if ((lucas[2].data[maxLength - 1] & 0x80000000) != 0) - lucas[2] += thisVal; - - BigInteger temp = (Q * BigInteger.Jacobi(Q, thisVal)) % thisVal; - if ((temp.data[maxLength - 1] & 0x80000000) != 0) - temp += thisVal; - - if (lucas[2] != temp) - isPrime = false; - } - } - - return isPrime; - } - - - //*********************************************************************** - // Determines whether a number is probably prime, using the Rabin-Miller's - // test. Before applying the test, the number is tested for divisibility - // by primes < 2000 - // - // Returns true if number is probably prime. - //*********************************************************************** - - public bool isProbablePrime(int confidence) - { - BigInteger thisVal; - if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative - thisVal = -this; - else - thisVal = this; - - - // test for divisibility by primes < 2000 - for (int p = 0; p < primesBelow2000.Length; p++) - { - BigInteger divisor = primesBelow2000[p]; - - if (divisor >= thisVal) - break; - - BigInteger resultNum = thisVal % divisor; - if (resultNum.IntValue() == 0) - { - /* - Console.WriteLine("Not prime! Divisible by {0}\n", - primesBelow2000[p]); - */ - return false; - } - } - - if (thisVal.RabinMillerTest(confidence)) - return true; - else - { - //Console.WriteLine("Not prime! Failed primality test\n"); - return false; - } - } - - - //*********************************************************************** - // Determines whether this BigInteger is probably prime using a - // combination of base 2 strong pseudoprime test and Lucas strong - // pseudoprime test. - // - // The sequence of the primality test is as follows, - // - // 1) Trial divisions are carried out using prime numbers below 2000. - // if any of the primes divides this BigInteger, then it is not prime. - // - // 2) Perform base 2 strong pseudoprime test. If this BigInteger is a - // base 2 strong pseudoprime, proceed on to the next step. - // - // 3) Perform strong Lucas pseudoprime test. - // - // Returns True if this BigInteger is both a base 2 strong pseudoprime - // and a strong Lucas pseudoprime. - // - // For a detailed discussion of this primality test, see [6]. - // - //*********************************************************************** - - public bool isProbablePrime() - { - BigInteger thisVal; - if ((this.data[maxLength - 1] & 0x80000000) != 0) // negative - thisVal = -this; - else - thisVal = this; - - if (thisVal.dataLength == 1) - { - // test small numbers - if (thisVal.data[0] == 0 || thisVal.data[0] == 1) - return false; - else if (thisVal.data[0] == 2 || thisVal.data[0] == 3) - return true; - } - - if ((thisVal.data[0] & 0x1) == 0) // even numbers - return false; - - - // test for divisibility by primes < 2000 - for (int p = 0; p < primesBelow2000.Length; p++) - { - BigInteger divisor = primesBelow2000[p]; - - if (divisor >= thisVal) - break; - - BigInteger resultNum = thisVal % divisor; - if (resultNum.IntValue() == 0) - { - //Console.WriteLine("Not prime! Divisible by {0}\n", - // primesBelow2000[p]); - - return false; - } - } - - // Perform BASE 2 Rabin-Miller Test - - // calculate values of s and t - BigInteger p_sub1 = thisVal - (new BigInteger(1)); - int s = 0; - - for (int index = 0; index < p_sub1.dataLength; index++) - { - uint mask = 0x01; - - for (int i = 0; i < 32; i++) - { - if ((p_sub1.data[index] & mask) != 0) - { - index = p_sub1.dataLength; // to break the outer loop - break; - } - mask <<= 1; - s++; - } - } - - BigInteger t = p_sub1 >> s; - - int bits = thisVal.bitCount(); - BigInteger a = 2; - - // b = a^t mod p - BigInteger b = a.modPow(t, thisVal); - bool result = false; - - if (b.dataLength == 1 && b.data[0] == 1) // a^t mod p = 1 - result = true; - - for (int j = 0; result == false && j < s; j++) - { - if (b == p_sub1) // a^((2^j)*t) mod p = p-1 for some 0 <= j <= s-1 - { - result = true; - break; - } - - b = (b * b) % thisVal; - } - - // if number is strong pseudoprime to base 2, then do a strong lucas test - if (result) - result = LucasStrongTestHelper(thisVal); - - return result; - } - - - - //*********************************************************************** - // Returns the lowest 4 bytes of the BigInteger as an int. - //*********************************************************************** - - public int IntValue() - { - return (int)data[0]; - } - - - //*********************************************************************** - // Returns the lowest 8 bytes of the BigInteger as a long. - //*********************************************************************** - - public long LongValue() - { - long val = 0; - - val = (long)data[0]; - try - { // exception if maxLength = 1 - val |= (long)data[1] << 32; - } - catch (Exception) - { - if ((data[0] & 0x80000000) != 0) // negative - val = (int)data[0]; - } - - return val; - } - - - //*********************************************************************** - // Computes the Jacobi Symbol for a and b. - // Algorithm adapted from [3] and [4] with some optimizations - //*********************************************************************** - - public static int Jacobi(BigInteger a, BigInteger b) - { - // Jacobi defined only for odd integers - if ((b.data[0] & 0x1) == 0) - throw (new ArgumentException("Jacobi defined only for odd integers.")); - - if (a >= b) a %= b; - if (a.dataLength == 1 && a.data[0] == 0) return 0; // a == 0 - if (a.dataLength == 1 && a.data[0] == 1) return 1; // a == 1 - - if (a < 0) - { - if ((((b - 1).data[0]) & 0x2) == 0) //if( (((b-1) >> 1).data[0] & 0x1) == 0) - return Jacobi(-a, b); - else - return -Jacobi(-a, b); - } - - int e = 0; - for (int index = 0; index < a.dataLength; index++) - { - uint mask = 0x01; - - for (int i = 0; i < 32; i++) - { - if ((a.data[index] & mask) != 0) - { - index = a.dataLength; // to break the outer loop - break; - } - mask <<= 1; - e++; - } - } - - BigInteger a1 = a >> e; - - int s = 1; - if ((e & 0x1) != 0 && ((b.data[0] & 0x7) == 3 || (b.data[0] & 0x7) == 5)) - s = -1; - - if ((b.data[0] & 0x3) == 3 && (a1.data[0] & 0x3) == 3) - s = -s; - - if (a1.dataLength == 1 && a1.data[0] == 1) - return s; - else - return (s * Jacobi(b % a1, a1)); - } - - - - //*********************************************************************** - // Generates a positive BigInteger that is probably prime. - //*********************************************************************** - - public static BigInteger genPseudoPrime(int bits, int confidence, Random rand) - { - BigInteger result = new BigInteger(); - bool done = false; - - while (!done) - { - result.genRandomBits(bits, rand); - result.data[0] |= 0x01; // make it odd - - // prime test - done = result.isProbablePrime(confidence); - } - return result; - } - - - //*********************************************************************** - // Generates a random number with the specified number of bits such - // that gcd(number, this) = 1 - //*********************************************************************** - - public BigInteger genCoPrime(int bits, Random rand) - { - bool done = false; - BigInteger result = new BigInteger(); - - while (!done) - { - result.genRandomBits(bits, rand); - //Console.WriteLine(result.ToString(16)); - - // gcd test - BigInteger g = result.gcd(this); - if (g.dataLength == 1 && g.data[0] == 1) - done = true; - } - - return result; - } - - - //*********************************************************************** - // Returns the modulo inverse of this. Throws ArithmeticException if - // the inverse does not exist. (i.e. gcd(this, modulus) != 1) - //*********************************************************************** - - public BigInteger modInverse(BigInteger modulus) - { - BigInteger[] p = { 0, 1 }; - BigInteger[] q = new BigInteger[2]; // quotients - BigInteger[] r = { 0, 0 }; // remainders - - int step = 0; - - BigInteger a = modulus; - BigInteger b = this; - - while (b.dataLength > 1 || (b.dataLength == 1 && b.data[0] != 0)) - { - BigInteger quotient = new BigInteger(); - BigInteger remainder = new BigInteger(); - - if (step > 1) - { - BigInteger pval = (p[0] - (p[1] * q[0])) % modulus; - p[0] = p[1]; - p[1] = pval; - } - - if (b.dataLength == 1) - singleByteDivide(a, b, quotient, remainder); - else - multiByteDivide(a, b, quotient, remainder); - - /* - Console.WriteLine(quotient.dataLength); - Console.WriteLine("{0} = {1}({2}) + {3} p = {4}", a.ToString(10), - b.ToString(10), quotient.ToString(10), remainder.ToString(10), - p[1].ToString(10)); - */ - - q[0] = q[1]; - r[0] = r[1]; - q[1] = quotient; r[1] = remainder; - - a = b; - b = remainder; - - step++; - } - - if (r[0].dataLength > 1 || (r[0].dataLength == 1 && r[0].data[0] != 1)) - throw (new ArithmeticException("No inverse!")); - - BigInteger result = ((p[0] - (p[1] * q[0])) % modulus); - - if ((result.data[maxLength - 1] & 0x80000000) != 0) - result += modulus; // get the least positive modulus - - return result; - } - - - //*********************************************************************** - // Returns the value of the BigInteger as a byte array. The lowest - // index contains the MSB. - //*********************************************************************** - - public byte[] getBytes() - { - int numBits = bitCount(); - - int numBytes = numBits >> 3; - if ((numBits & 0x7) != 0) - numBytes++; - - byte[] result = new byte[numBytes]; - - //Console.WriteLine(result.Length); - - int pos = 0; - uint tempVal, val = data[dataLength - 1]; - - if ((tempVal = (val >> 24 & 0xFF)) != 0) - result[pos++] = (byte)tempVal; - if ((tempVal = (val >> 16 & 0xFF)) != 0) - result[pos++] = (byte)tempVal; - if ((tempVal = (val >> 8 & 0xFF)) != 0) - result[pos++] = (byte)tempVal; - if ((tempVal = (val & 0xFF)) != 0) - result[pos++] = (byte)tempVal; - - for (int i = dataLength - 2; i >= 0; i--, pos += 4) - { - val = data[i]; - result[pos + 3] = (byte)(val & 0xFF); - val >>= 8; - result[pos + 2] = (byte)(val & 0xFF); - val >>= 8; - result[pos + 1] = (byte)(val & 0xFF); - val >>= 8; - result[pos] = (byte)(val & 0xFF); - } - - return result; - } - - - //*********************************************************************** - // Sets the value of the specified bit to 1 - // The Least Significant Bit position is 0. - //*********************************************************************** - - public void setBit(uint bitNum) - { - uint bytePos = bitNum >> 5; // divide by 32 - byte bitPos = (byte)(bitNum & 0x1F); // get the lowest 5 bits - - uint mask = (uint)1 << bitPos; - this.data[bytePos] |= mask; - - if (bytePos >= this.dataLength) - this.dataLength = (int)bytePos + 1; - } - - - //*********************************************************************** - // Sets the value of the specified bit to 0 - // The Least Significant Bit position is 0. - //*********************************************************************** - - public void unsetBit(uint bitNum) - { - uint bytePos = bitNum >> 5; - - if (bytePos < this.dataLength) - { - byte bitPos = (byte)(bitNum & 0x1F); - - uint mask = (uint)1 << bitPos; - uint mask2 = 0xFFFFFFFF ^ mask; - - this.data[bytePos] &= mask2; - - if (this.dataLength > 1 && this.data[this.dataLength - 1] == 0) - this.dataLength--; - } - } - - - //*********************************************************************** - // Returns a value that is equivalent to the integer square root - // of the BigInteger. - // - // The integer square root of "this" is defined as the largest integer n - // such that (n * n) <= this - // - //*********************************************************************** - - public BigInteger sqrt() - { - uint numBits = (uint)this.bitCount(); - - if ((numBits & 0x1) != 0) // odd number of bits - numBits = (numBits >> 1) + 1; - else - numBits = (numBits >> 1); - - uint bytePos = numBits >> 5; - byte bitPos = (byte)(numBits & 0x1F); - - uint mask; - - BigInteger result = new BigInteger(); - if (bitPos == 0) - mask = 0x80000000; - else - { - mask = (uint)1 << bitPos; - bytePos++; - } - result.dataLength = (int)bytePos; - - for (int i = (int)bytePos - 1; i >= 0; i--) - { - while (mask != 0) - { - // guess - result.data[i] ^= mask; - - // undo the guess if its square is larger than this - if ((result * result) > this) - result.data[i] ^= mask; - - mask >>= 1; - } - mask = 0x80000000; - } - return result; - } - - - //*********************************************************************** - // Returns the k_th number in the Lucas Sequence reduced modulo n. - // - // Uses index doubling to speed up the process. For example, to calculate V(k), - // we maintain two numbers in the sequence V(n) and V(n+1). - // - // To obtain V(2n), we use the identity - // V(2n) = (V(n) * V(n)) - (2 * Q^n) - // To obtain V(2n+1), we first write it as - // V(2n+1) = V((n+1) + n) - // and use the identity - // V(m+n) = V(m) * V(n) - Q * V(m-n) - // Hence, - // V((n+1) + n) = V(n+1) * V(n) - Q^n * V((n+1) - n) - // = V(n+1) * V(n) - Q^n * V(1) - // = V(n+1) * V(n) - Q^n * P - // - // We use k in its binary expansion and perform index doubling for each - // bit position. For each bit position that is set, we perform an - // index doubling followed by an index addition. This means that for V(n), - // we need to update it to V(2n+1). For V(n+1), we need to update it to - // V((2n+1)+1) = V(2*(n+1)) - // - // This function returns - // [0] = U(k) - // [1] = V(k) - // [2] = Q^n - // - // Where U(0) = 0 % n, U(1) = 1 % n - // V(0) = 2 % n, V(1) = P % n - //*********************************************************************** - - public static BigInteger[] LucasSequence(BigInteger P, BigInteger Q, - BigInteger k, BigInteger n) - { - if (k.dataLength == 1 && k.data[0] == 0) - { - BigInteger[] result = new BigInteger[3]; - - result[0] = 0; result[1] = 2 % n; result[2] = 1 % n; - return result; - } - - // calculate constant = b^(2k) / m - // for Barrett Reduction - BigInteger constant = new BigInteger(); - - int nLen = n.dataLength << 1; - constant.data[nLen] = 0x00000001; - constant.dataLength = nLen + 1; - - constant = constant / n; - - // calculate values of s and t - int s = 0; - - for (int index = 0; index < k.dataLength; index++) - { - uint mask = 0x01; - - for (int i = 0; i < 32; i++) - { - if ((k.data[index] & mask) != 0) - { - index = k.dataLength; // to break the outer loop - break; - } - mask <<= 1; - s++; - } - } - - BigInteger t = k >> s; - - //Console.WriteLine("s = " + s + " t = " + t); - return LucasSequenceHelper(P, Q, t, n, constant, s); - } - - - //*********************************************************************** - // Performs the calculation of the kth term in the Lucas Sequence. - // For details of the algorithm, see reference [9]. - // - // k must be odd. i.e LSB == 1 - //*********************************************************************** - - private static BigInteger[] LucasSequenceHelper(BigInteger P, BigInteger Q, - BigInteger k, BigInteger n, - BigInteger constant, int s) - { - BigInteger[] result = new BigInteger[3]; - - if ((k.data[0] & 0x00000001) == 0) - throw (new ArgumentException("Argument k must be odd.")); - - int numbits = k.bitCount(); - uint mask = (uint)0x1 << ((numbits & 0x1F) - 1); - - // v = v0, v1 = v1, u1 = u1, Q_k = Q^0 - - BigInteger v = 2 % n, Q_k = 1 % n, - v1 = P % n, u1 = Q_k; - bool flag = true; - - for (int i = k.dataLength - 1; i >= 0; i--) // iterate on the binary expansion of k - { - //Console.WriteLine("round"); - while (mask != 0) - { - if (i == 0 && mask == 0x00000001) // last bit - break; - - if ((k.data[i] & mask) != 0) // bit is set - { - // index doubling with addition - - u1 = (u1 * v1) % n; - - v = ((v * v1) - (P * Q_k)) % n; - v1 = n.BarrettReduction(v1 * v1, n, constant); - v1 = (v1 - ((Q_k * Q) << 1)) % n; - - if (flag) - flag = false; - else - Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); - - Q_k = (Q_k * Q) % n; - } - else - { - // index doubling - u1 = ((u1 * v) - Q_k) % n; - - v1 = ((v * v1) - (P * Q_k)) % n; - v = n.BarrettReduction(v * v, n, constant); - v = (v - (Q_k << 1)) % n; - - if (flag) - { - Q_k = Q % n; - flag = false; - } - else - Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); - } - - mask >>= 1; - } - mask = 0x80000000; - } - - // at this point u1 = u(n+1) and v = v(n) - // since the last bit always 1, we need to transform u1 to u(2n+1) and v to v(2n+1) - - u1 = ((u1 * v) - Q_k) % n; - v = ((v * v1) - (P * Q_k)) % n; - if (flag) - flag = false; - else - Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); - - Q_k = (Q_k * Q) % n; - - - for (int i = 0; i < s; i++) - { - // index doubling - u1 = (u1 * v) % n; - v = ((v * v) - (Q_k << 1)) % n; - - if (flag) - { - Q_k = Q % n; - flag = false; - } - else - Q_k = n.BarrettReduction(Q_k * Q_k, n, constant); - } - - result[0] = u1; - result[1] = v; - result[2] = Q_k; - - return result; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/Internal/CRC8.cs b/MuPDF.NET/Barcode/Encoders/Internal/CRC8.cs deleted file mode 100644 index 1682d81b..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/CRC8.cs +++ /dev/null @@ -1,63 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Class for calculating CRC8 checksums - /// - internal class CRC8 - { - private byte[] m_table; - - /// - /// Initializes a new instance of the class. - /// - /// The generator polinomial. - public CRC8(int polynomial) - { - this.m_table = this.GenerateTable(polynomial); - } - - - private byte[] GenerateTable(int polynomial) - { - byte[] table = new byte[256]; - - for (int i = 0; i < 256; ++i) - { - int r = i; - for (int j = 0; j < 8; ++j) - { - if ((r & 0x80) != 0) - r = (r << 1) ^ polynomial; - else - r <<= 1; - } - table[i] = (byte)r; - } - - return table; - } - - public byte Checksum(byte[] value) - { - if (value == null) - throw new BarcodeException("CRC8.Checksum argument is null"); - - byte crc = 0; - for (int i = 0; i < value.Length; i++) - { - crc = m_table[crc ^ value[i]]; - } - return crc; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/ImageStamper.cs b/MuPDF.NET/Barcode/Encoders/Internal/ImageStamper.cs deleted file mode 100644 index 5cdd5d3d..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/ImageStamper.cs +++ /dev/null @@ -1,237 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -using BitMiracle.LibTiff.Classic; -using SkiaSharp; -using System; -using System.IO; -using System.Runtime.InteropServices; - -namespace BarcodeWriter.Core.Internal -{ - internal class ImageStamper : IDisposable - { - public void Dispose() - { - } - - public void DrawToImage(Stream inputStream, SKBitmap barcodeImage, int pageIndex, int x, int y, Stream outputStream) - { - using (var managedStream = new SKManagedStream(inputStream)) - using (var codec = SKCodec.Create(managedStream)) - { - if (codec == null) - throw new Exception("Cannot read input image."); - - SKImageInfo info = codec.Info; - - // Create a bitmap for drawing - using (var bitmap = new SKBitmap(info.Width, info.Height, info.ColorType, info.AlphaType)) - { - SKCodecResult result = codec.GetPixels(bitmap.Info, bitmap.GetPixels()); - - if (result != SKCodecResult.Success && result != SKCodecResult.IncompleteInput) - throw new Exception("Failed to decode input image."); - - using (var canvas = new SKCanvas(bitmap)) - { - // Draw the barcode bitmap - canvas.DrawBitmap(barcodeImage, x, y); - } - - // Detect format by file extension (fallback to PNG) - SKEncodedImageFormat skFormat = SKEncodedImageFormat.Png; - if (outputStream is FileStream fs) - { - string ext = Path.GetExtension(fs.Name).ToLowerInvariant(); - switch (ext) - { - case ".bmp": - skFormat = SKEncodedImageFormat.Bmp; - break; - - case ".gif": - skFormat = SKEncodedImageFormat.Gif; - break; - - case ".jpg": - case ".jpeg": - skFormat = SKEncodedImageFormat.Jpeg; - break; - - case ".png": - skFormat = SKEncodedImageFormat.Png; - break; - - case ".webp": - skFormat = SKEncodedImageFormat.Webp; - break; - - default: - skFormat = SKEncodedImageFormat.Png; - break; - } - } - - using (var image = SKImage.FromBitmap(bitmap)) - using (var data = image.Encode(skFormat, 100)) - data.SaveTo(outputStream); - } - } - } - private void ConvertToBitonal(ref SKBitmap srcBitmap) - { - int width = srcBitmap.Width; - int height = srcBitmap.Height; - - // Create destination bitmap in 1-bit format (simulate with ALPHA_8 + manual threshold) - var dstBitmap = new SKBitmap(width, height, SKColorType.Gray8, SKAlphaType.Opaque); - - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - SKColor pixel = srcBitmap.GetPixel(x, y); - - // Compute brightness (simple luminance) - int brightness = pixel.Red + pixel.Green + pixel.Blue; - - // Threshold for 1-bit conversion - byte value = brightness < 500 ? (byte)0 : (byte)255; - - dstBitmap.SetPixel(x, y, new SKColor(value, value, value)); - } - } - - srcBitmap.Dispose(); - srcBitmap = dstBitmap; - } - - private static byte[] ExtractBytes(SKBitmap bitmap, out int rowBytes) - { - // Ensure bitmap is in 32-bit color (RGBA) - if (bitmap.ColorType != SKColorType.Rgba8888) - throw new ArgumentException("Bitmap must be in RGBA8888 format."); - - using (var pixmap = bitmap.PeekPixels()) - { - rowBytes = pixmap.RowBytes; - int totalBytes = rowBytes * pixmap.Height; - byte[] buffer = new byte[totalBytes]; - - unsafe - { - fixed (byte* dest = buffer) - { - System.Buffer.MemoryCopy( - (void*)pixmap.GetPixels(), // source pointer (IntPtr) - dest, // destination pointer - totalBytes, // destination size in bytes - totalBytes // copy size - ); - } - } - - return buffer; - } - } - - internal static SKEncodedImageFormat GetSkiaFormat(string mimeType) - { - switch (mimeType.ToLowerInvariant()) - { - case "image/bmp": - return SKEncodedImageFormat.Bmp; - case "image/gif": - return SKEncodedImageFormat.Gif; - case "image/jpeg": - case "image/jpg": - return SKEncodedImageFormat.Jpeg; - case "image/png": - return SKEncodedImageFormat.Png; - case "image/webp": - return SKEncodedImageFormat.Webp; - default: - throw new NotSupportedException($"MIME type '{mimeType}' is not supported in SkiaSharp."); - } - } - - public SKBitmap GetFrame(Stream imageStream, int index) - { - using (var codec = SKCodec.Create(imageStream)) - { - if (codec == null) - throw new Exception("Cannot create codec for image"); - - if (index < 0 || index >= codec.FrameCount) - throw new ArgumentOutOfRangeException(nameof(index)); - - SKBitmap bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height); - SKCodecOptions options = new SKCodecOptions(index); - codec.GetPixels(bitmap.Info, bitmap.GetPixels(), options); - - return bitmap; - } - } - - private static byte[] GetImageRasterBytes(SKBitmap bitmap) - { - if (bitmap == null) - return null; - - // Create a buffer to hold pixel data - int rowBytes = bitmap.RowBytes; - int height = bitmap.Height; - byte[] buffer = new byte[rowBytes * height]; - - // Copy pixels into the buffer - using (var pixmap = bitmap.PeekPixels()) - { - if (pixmap == null) - return null; - - IntPtr pixelsPtr = pixmap.GetPixels(); - if (pixelsPtr == IntPtr.Zero) - return null; - - System.Runtime.InteropServices.Marshal.Copy(pixelsPtr, buffer, 0, buffer.Length); - } - - return buffer; - } - - // Converts BGRA samples into RGBA samples - private static void ConvertSamples(byte[] data, int width, int height) - { - int stride = data.Length / height; - const int samplesPerPixel = 3; - - for (int y = 0; y < height; y++) - { - int offset = stride * y; - int strideEnd = offset + width * samplesPerPixel; - - for (int i = offset; i < strideEnd; i += samplesPerPixel) - { - byte temp = data[i + 2]; - data[i + 2] = data[i]; - data[i] = temp; - } - } - } - - private static void RemovePadding(byte[] raster, int width, int height) - { - int stride = raster.Length / height; - int rowLength = width * 3; - - for (int i = 1, readOffset = stride, writeOffset = rowLength; i < height; i++) - { - Buffer.BlockCopy(raster, readOffset, raster, writeOffset, rowLength); - readOffset += stride; - writeOffset += rowLength; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/Internal/OSInfo.cs b/MuPDF.NET/Barcode/Encoders/Internal/OSInfo.cs deleted file mode 100644 index bbd27e05..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/OSInfo.cs +++ /dev/null @@ -1,590 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Provides detailed information about the host operating system. - /// - internal static class OSInfo - { - public enum Platform - { - Unknown, - Windows, - Linux, - MacOSX, - } - - public static Platform GetPlatform() - { - string windir = Environment.GetEnvironmentVariable("windir"); - - if (!string.IsNullOrEmpty(windir) && windir.Contains(@"\") && Directory.Exists(windir)) - { - return Platform.Windows; - } - else if (File.Exists(@"/proc/sys/kernel/ostype")) - { - string osType = File.ReadAllText(@"/proc/sys/kernel/ostype"); - if (osType.StartsWith("Linux", StringComparison.OrdinalIgnoreCase)) - { - // Note: Android gets here too - return Platform.Linux; - } - else - { - return Platform.Unknown; - } - } - else if (File.Exists(@"/System/Library/CoreServices/SystemVersion.plist")) - { - // Note: iOS gets here too - return Platform.MacOSX; - } - else - { - return Platform.Unknown; - } - } - - public static bool IsServer - { - get - { - switch (GetPlatform()) - { - case Platform.Linux: - return true; - case Platform.MacOSX: - return false; - } - - OSVERSIONINFOEX osVersionInfoEx = new OSVERSIONINFOEX(); - osVersionInfoEx.dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX)); - - if (GetVersionEx(ref osVersionInfoEx)) - { - return osVersionInfoEx.wProductType == VER_NT_SERVER || - osVersionInfoEx.wProductType == VER_NT_DOMAIN_CONTROLLER; - } - - return true; - } - } - - /// - /// Gets the service pack information of the operating system running on this computer. - /// - public static string ServicePack - { - get - { - string servicePack = String.Empty; - OSVERSIONINFOEX osVersionInfo = new OSVERSIONINFOEX(); - - osVersionInfo.dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX)); - - if (GetVersionEx(ref osVersionInfo)) - { - servicePack = osVersionInfo.szCSDVersion; - } - - return servicePack; - } - } - - /// - /// Gets the build version number of the operating system running on this computer. - /// - public static int BuildVersion - { - get { return Environment.OSVersion.Version.Build; } - } - - /// - /// Gets the full version string of the operating system running on this computer. - /// - public static string VersionString - { - get { return Environment.OSVersion.Version.ToString(); } - } - - /// - /// Gets the full version of the operating system running on this computer. - /// - public static Version Version - { - get { return Environment.OSVersion.Version; } - } - - /// - /// Gets the major version number of the operating system running on this computer. - /// - public static int MajorVersion - { - get { return Environment.OSVersion.Version.Major; } - } - - /// - /// Gets the minor version number of the operating system running on this computer. - /// - public static int MinorVersion - { - get { return Environment.OSVersion.Version.Minor; } - } - - /// - /// Gets the revision version number of the operating system running on this computer. - /// - public static int RevisionVersion - { - get { return Environment.OSVersion.Version.Revision; } - } - - /*private static string _edition; - - /// - /// Gets the edition of the operating system running on this computer. - /// - public static string Edition - { - get - { - if (_edition != null) - return _edition; - - string edition = String.Empty; - - OperatingSystem osVersion = Environment.OSVersion; - OSVERSIONINFOEX osVersionInfo = new OSVERSIONINFOEX(); - osVersionInfo.dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX)); - - if (GetVersionEx(ref osVersionInfo)) - { - int majorVersion = osVersion.Version.Major; - int minorVersion = osVersion.Version.Minor; - byte productType = osVersionInfo.wProductType; - short suiteMask = osVersionInfo.wSuiteMask; - - #region VERSION 4 - - if (majorVersion == 4) - { - if (productType == VER_NT_WORKSTATION) - { - // Windows NT 4.0 Workstation - edition = "Workstation"; - } - else if (productType == VER_NT_SERVER) - { - if ((suiteMask & VER_SUITE_ENTERPRISE) != 0) - { - // Windows NT 4.0 Server Enterprise - edition = "Enterprise Server"; - } - else - { - // Windows NT 4.0 Server - edition = "Standard Server"; - } - } - } - - #endregion VERSION 4 - - #region VERSION 5 - - else if (majorVersion == 5) - { - if (productType == VER_NT_WORKSTATION) - { - if ((suiteMask & VER_SUITE_PERSONAL) != 0) - { - // Windows XP Home Edition - edition = "Home"; - } - else - { - // Windows XP / Windows 2000 Professional - edition = "Professional"; - } - } - else if (productType == VER_NT_SERVER) - { - if (minorVersion == 0) - { - if ((suiteMask & VER_SUITE_DATACENTER) != 0) - { - // Windows 2000 Datacenter Server - edition = "Datacenter Server"; - } - else if ((suiteMask & VER_SUITE_ENTERPRISE) != 0) - { - // Windows 2000 Advanced Server - edition = "Advanced Server"; - } - else - { - // Windows 2000 Server - edition = "Server"; - } - } - else - { - if ((suiteMask & VER_SUITE_DATACENTER) != 0) - { - // Windows Server 2003 Datacenter Edition - edition = "Datacenter"; - } - else if ((suiteMask & VER_SUITE_ENTERPRISE) != 0) - { - // Windows Server 2003 Enterprise Edition - edition = "Enterprise"; - } - else if ((suiteMask & VER_SUITE_BLADE) != 0) - { - // Windows Server 2003 Web Edition - edition = "Web Edition"; - } - else - { - // Windows Server 2003 Standard Edition - edition = "Standard"; - } - } - } - } - - #endregion VERSION 5 - - #region VERSION 6 - - else if (majorVersion == 6) - { - int ed; - if (GetProductInfo(majorVersion, minorVersion, - osVersionInfo.wServicePackMajor, osVersionInfo.wServicePackMinor, - out ed)) - { - switch (ed) - { - case PRODUCT_BUSINESS: - edition = "Business"; - break; - case PRODUCT_BUSINESS_N: - edition = "Business N"; - break; - case PRODUCT_CLUSTER_SERVER: - edition = "HPC Edition"; - break; - case PRODUCT_DATACENTER_SERVER: - edition = "Datacenter Server"; - break; - case PRODUCT_DATACENTER_SERVER_CORE: - edition = "Datacenter Server (core installation)"; - break; - case PRODUCT_ENTERPRISE: - edition = "Enterprise"; - break; - case PRODUCT_ENTERPRISE_N: - edition = "Enterprise N"; - break; - case PRODUCT_ENTERPRISE_SERVER: - edition = "Enterprise Server"; - break; - case PRODUCT_ENTERPRISE_SERVER_CORE: - edition = "Enterprise Server (core installation)"; - break; - case PRODUCT_ENTERPRISE_SERVER_CORE_V: - edition = "Enterprise Server without Hyper-V (core installation)"; - break; - case PRODUCT_ENTERPRISE_SERVER_IA64: - edition = "Enterprise Server for Itanium-based Systems"; - break; - case PRODUCT_ENTERPRISE_SERVER_V: - edition = "Enterprise Server without Hyper-V"; - break; - case PRODUCT_HOME_BASIC: - edition = "Home Basic"; - break; - case PRODUCT_HOME_BASIC_N: - edition = "Home Basic N"; - break; - case PRODUCT_HOME_PREMIUM: - edition = "Home Premium"; - break; - case PRODUCT_HOME_PREMIUM_N: - edition = "Home Premium N"; - break; - case PRODUCT_HYPERV: - edition = "Microsoft Hyper-V Server"; - break; - case PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT: - edition = "Windows Essential Business Management Server"; - break; - case PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING: - edition = "Windows Essential Business Messaging Server"; - break; - case PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY: - edition = "Windows Essential Business Security Server"; - break; - case PRODUCT_SERVER_FOR_SMALLBUSINESS: - edition = "Windows Essential Server Solutions"; - break; - case PRODUCT_SERVER_FOR_SMALLBUSINESS_V: - edition = "Windows Essential Server Solutions without Hyper-V"; - break; - case PRODUCT_SMALLBUSINESS_SERVER: - edition = "Windows Small Business Server"; - break; - case PRODUCT_STANDARD_SERVER: - edition = "Standard Server"; - break; - case PRODUCT_STANDARD_SERVER_CORE: - edition = "Standard Server (core installation)"; - break; - case PRODUCT_STANDARD_SERVER_CORE_V: - edition = "Standard Server without Hyper-V (core installation)"; - break; - case PRODUCT_STANDARD_SERVER_V: - edition = "Standard Server without Hyper-V"; - break; - case PRODUCT_STARTER: - edition = "Starter"; - break; - case PRODUCT_STORAGE_ENTERPRISE_SERVER: - edition = "Enterprise Storage Server"; - break; - case PRODUCT_STORAGE_EXPRESS_SERVER: - edition = "Express Storage Server"; - break; - case PRODUCT_STORAGE_STANDARD_SERVER: - edition = "Standard Storage Server"; - break; - case PRODUCT_STORAGE_WORKGROUP_SERVER: - edition = "Workgroup Storage Server"; - break; - case PRODUCT_UNDEFINED: - edition = "Unknown product"; - break; - case PRODUCT_ULTIMATE: - edition = "Ultimate"; - break; - case PRODUCT_ULTIMATE_N: - edition = "Ultimate N"; - break; - case PRODUCT_WEB_SERVER: - edition = "Web Server"; - break; - case PRODUCT_WEB_SERVER_CORE: - edition = "Web Server (core installation)"; - break; - } - } - } - - #endregion VERSION 6 - } - - _edition = edition; - return edition; - } - }*/ - - /*private static string _name; - - /// - /// Gets the name of the operating system running on this computer. - /// - public static string Name - { - get - { - if (_name != null) - return _name; //***** RETURN ****#1#/ - - string name = "unknown"; - - OperatingSystem osVersion = Environment.OSVersion; - OSVERSIONINFOEX osVersionInfo = new OSVERSIONINFOEX(); - osVersionInfo.dwOSVersionInfoSize = Marshal.SizeOf(typeof(OSVERSIONINFOEX)); - - if (GetVersionEx(ref osVersionInfo)) - { - int majorVersion = osVersion.Version.Major; - int minorVersion = osVersion.Version.Minor; - - switch (osVersion.Platform) - { - case PlatformID.Win32Windows: - { - if (majorVersion == 4) - { - string csdVersion = osVersionInfo.szCSDVersion; - switch (minorVersion) - { - case 0: - if (csdVersion == "B" || csdVersion == "C") - name = "Windows 95 OSR2"; - else - name = "Windows 95"; - break; - case 10: - if (csdVersion == "A") - name = "Windows 98 Second Edition"; - else - name = "Windows 98"; - break; - case 90: - name = "Windows Me"; - break; - } - } - - break; - } - - case PlatformID.Win32NT: - { - byte productType = osVersionInfo.wProductType; - - switch (majorVersion) - { - case 3: - name = "Windows NT 3.51"; - break; - case 4: - switch (productType) - { - case 1: - name = "Windows NT 4.0"; - break; - case 3: - name = "Windows NT 4.0 Server"; - break; - } - - break; - case 5: - switch (minorVersion) - { - case 0: - name = "Windows 2000"; - break; - case 1: - name = "Windows XP"; - break; - case 2: - name = "Windows Server 2003"; - break; - } - - break; - case 6: - switch (productType) - { - case 1: - name = "Windows Vista"; - break; - case 3: - name = "Windows Server 2008"; - break; - } - - break; - } - - break; - } - } - } - - _name = name; - return name; - } - }*/ - - [DllImport("Kernel32.dll")] - internal static extern bool GetProductInfo( - int osMajorVersion, - int osMinorVersion, - int spMajorVersion, - int spMinorVersion, - out int edition); - - [DllImport("kernel32.dll")] - private static extern bool GetVersionEx(ref OSVERSIONINFOEX osVersionInfo); - - [StructLayout(LayoutKind.Sequential)] - private struct OSVERSIONINFOEX - { - public int dwOSVersionInfoSize; - public int dwMajorVersion; - public int dwMinorVersion; - public int dwBuildNumber; - public int dwPlatformId; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] - public string szCSDVersion; - - public short wServicePackMajor; - public short wServicePackMinor; - public short wSuiteMask; - public byte wProductType; - public byte wReserved; - } - - private const int PRODUCT_UNDEFINED = 0x00000000; - private const int PRODUCT_ULTIMATE = 0x00000001; - private const int PRODUCT_HOME_BASIC = 0x00000002; - private const int PRODUCT_HOME_PREMIUM = 0x00000003; - private const int PRODUCT_ENTERPRISE = 0x00000004; - private const int PRODUCT_HOME_BASIC_N = 0x00000005; - private const int PRODUCT_BUSINESS = 0x00000006; - private const int PRODUCT_STANDARD_SERVER = 0x00000007; - private const int PRODUCT_DATACENTER_SERVER = 0x00000008; - private const int PRODUCT_SMALLBUSINESS_SERVER = 0x00000009; - private const int PRODUCT_ENTERPRISE_SERVER = 0x0000000A; - private const int PRODUCT_STARTER = 0x0000000B; - private const int PRODUCT_DATACENTER_SERVER_CORE = 0x0000000C; - private const int PRODUCT_STANDARD_SERVER_CORE = 0x0000000D; - private const int PRODUCT_ENTERPRISE_SERVER_CORE = 0x0000000E; - private const int PRODUCT_ENTERPRISE_SERVER_IA64 = 0x0000000F; - private const int PRODUCT_BUSINESS_N = 0x00000010; - private const int PRODUCT_WEB_SERVER = 0x00000011; - private const int PRODUCT_CLUSTER_SERVER = 0x00000012; - private const int PRODUCT_HOME_SERVER = 0x00000013; - private const int PRODUCT_STORAGE_EXPRESS_SERVER = 0x00000014; - private const int PRODUCT_STORAGE_STANDARD_SERVER = 0x00000015; - private const int PRODUCT_STORAGE_WORKGROUP_SERVER = 0x00000016; - private const int PRODUCT_STORAGE_ENTERPRISE_SERVER = 0x00000017; - private const int PRODUCT_SERVER_FOR_SMALLBUSINESS = 0x00000018; - private const int PRODUCT_SMALLBUSINESS_SERVER_PREMIUM = 0x00000019; - private const int PRODUCT_HOME_PREMIUM_N = 0x0000001A; - private const int PRODUCT_ENTERPRISE_N = 0x0000001B; - private const int PRODUCT_ULTIMATE_N = 0x0000001C; - private const int PRODUCT_WEB_SERVER_CORE = 0x0000001D; - private const int PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT = 0x0000001E; - private const int PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY = 0x0000001F; - private const int PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING = 0x00000020; - private const int PRODUCT_SERVER_FOR_SMALLBUSINESS_V = 0x00000023; - private const int PRODUCT_STANDARD_SERVER_V = 0x00000024; - private const int PRODUCT_ENTERPRISE_SERVER_V = 0x00000026; - private const int PRODUCT_STANDARD_SERVER_CORE_V = 0x00000028; - private const int PRODUCT_ENTERPRISE_SERVER_CORE_V = 0x00000029; - private const int PRODUCT_HYPERV = 0x0000002A; - - private const int VER_NT_WORKSTATION = 1; - private const int VER_NT_DOMAIN_CONTROLLER = 2; - private const int VER_NT_SERVER = 3; - private const int VER_SUITE_SMALLBUSINESS = 1; - private const int VER_SUITE_ENTERPRISE = 2; - private const int VER_SUITE_TERMINAL = 16; - private const int VER_SUITE_DATACENTER = 128; - private const int VER_SUITE_SINGLEUSERTS = 256; - private const int VER_SUITE_PERSONAL = 512; - private const int VER_SUITE_BLADE = 1024; - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/Internal/ProfileManager.cs b/MuPDF.NET/Barcode/Encoders/Internal/ProfileManager.cs deleted file mode 100644 index 3c04df39..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/ProfileManager.cs +++ /dev/null @@ -1,355 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Globalization; -using System.IO; -using System.Reflection; -using System.Text.RegularExpressions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace BarcodeWriter.Core.Internal -{ - internal class ProfileManager : IDisposable - { - private object _managedClass; - private readonly List _profiles = new List(); - - internal IEnumerable Profiles => _profiles; - - public ProfileManager(Object managedClass) - { - _managedClass = managedClass; - - // Load profiles from resources - try - { - Assembly assembly = Assembly.GetExecutingAssembly(); - string[] resourceNames = assembly.GetManifestResourceNames(); - foreach (string resourceName in resourceNames) - { - if (resourceName.EndsWith("profiles.json")) - { - using (Stream stream = assembly.GetManifestResourceStream(resourceName)) - if (stream != null) - using (StreamReader reader = new StreamReader(stream)) - ParseProfiles(reader.ReadToEnd()); - } - } - } - catch (Exception exception) - { - throw new BarcodeProfileException("Profiles parsing error.", exception); - } - } - - public void Dispose() - { - _managedClass = null; - _profiles.Clear(); - } - - public void LoadFromFile(string fileName) - { - try - { - ParseProfiles(File.ReadAllText(fileName)); - } - catch (Exception exception) - { - throw new BarcodeProfileException("Profiles parsing error.", exception); - } - } - - internal void LoadFromString(string jsonString) - { - try - { - ParseProfiles(jsonString); - } - catch (Exception exception) - { - throw new BarcodeProfileException("Profiles parsing error.", exception); - } - } - - internal void LoadAndApplyProfiles(string jsonString) - { - try - { - _profiles.Clear(); - ParseProfiles(jsonString); - foreach (Profile profile in _profiles) - ApplyProfile(profile.Name); - } - catch (Exception exception) - { - throw new BarcodeProfileException("Profiles parsing error.", exception); - } - } - - private void ParseProfiles(string jsonString) - { - void addProfile(IJEnumerable jEnumerable, string profileName) - { - Profile profile = new Profile { Name = profileName }; - - foreach (JToken jProp in jEnumerable) - { - string propertyName = ((JProperty) jProp).Name; - - if (propertyName.ToLower() == "keywords") - { - IJEnumerable jKeywords = ((JProperty) jProp).Value.AsJEnumerable(); - foreach (JToken jKeyword in jKeywords) - profile.Keywords.Add(jKeyword.ToString()); - - if (profile.Keywords.Count > 0) - profile.Deferred = true; - } - else - { - JToken valueToken = ((JProperty) jProp).Value; - string strValue; - - if (valueToken is JValue) - strValue = ((JValue) valueToken).ToString(CultureInfo.InvariantCulture); - else - strValue = valueToken.ToString(); - - profile.Properties.Add(new KeyValuePair(propertyName, strValue)); - } - } - - int existing = _profiles.FindIndex(p => p.Name == profile.Name); - if (existing > -1) - _profiles.RemoveAt(existing); - - _profiles.Add(profile); - } - - - JObject o = JObject.Parse(jsonString); - IJEnumerable jProfiles = o.GetValue("profiles").AsJEnumerable(); - - if (jProfiles != null) - { - foreach (JToken jProfile in jProfiles) - addProfile(((JProperty) jProfile.First).Value.AsJEnumerable(), ((JProperty) jProfile.First).Name); - } - else // simplified profile - { - addProfile(o.AsJEnumerable(), "profile1"); - } - } - - public void ApplyProfiles(string profiles) - { - string[] profileNames = profiles.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - foreach (string profile in profileNames) - ApplyProfile(profile.Trim()); - } - - internal void ApplyProfile(string profileName) - { - foreach (Profile profile in _profiles) - { - if (string.Compare(profileName, profile.Name, StringComparison.InvariantCultureIgnoreCase) == 0) - { - profile.Selected = true; - if (!profile.Deferred) - ApplyProfile(profile); - return; - } - } - - throw new BarcodeProfileException($"Unknown profile '{profileName}'."); - } - - internal void ApplyProfile(Profile profile) - { - foreach (KeyValuePair argument in profile.Properties) - { - if (string.Compare(argument.Key, "Description", StringComparison.InvariantCultureIgnoreCase) == 0) - continue; - - // Split by dot assuming we can have a chain, - // e.g. `MidProperty1.MidProperty2.Property` or `MidProperty1.MidProperty2.Method()` - string[] propertyChain = argument.Key.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries); - object @object = _managedClass; - Type type = _managedClass.GetType(); - - for (int i = 0; i < propertyChain.Length; i++) - { - string memberName = propertyChain[i]; - - // recurse mid properties - if (i < propertyChain.Length - 1) - { - PropertyInfo propertyInfo = type.GetProperty(memberName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy); - if (propertyInfo == null) - throw new BarcodeProfileException($"Property \"{memberName}\" does not exist in \"{type}\""); - - @object = propertyInfo.GetValue(@object, null); - type = @object.GetType(); - } - else // process the last part of the chain: a property or method - { - // .Method() - if (memberName.EndsWith("()", StringComparison.Ordinal)) - { - string methodName = memberName.Substring(0, memberName.Length - 2); - - if (string.IsNullOrEmpty(argument.Value)) - { - MethodInfo methodInfo = type.GetMethod(methodName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy, null, new Type[0], null); - if (methodInfo == null) - throw new BarcodeProfileException($"Method \"{methodName}\" does not exist in \"{type}\""); - - try - { - methodInfo.Invoke(@object, null); - } - catch (Exception exception) - { - throw new BarcodeProfileException($"Could not invoke method \"{argument.Key}\"", exception); - } - } - else - { - JArray argsArray = null; - MethodInfo methodInfo = null; - - try - { - argsArray = JArray.Parse(argument.Value.Trim()); - } - catch (JsonReaderException exception) - { - throw new BarcodeProfileException( - $"Could not parse arguments of the method \"{argument.Key}\"", exception); - } - - foreach (var mi in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy)) - { - if (string.Equals(mi.Name, methodName, StringComparison.InvariantCultureIgnoreCase) && - mi.GetParameters().Length == argsArray.Count) - { - methodInfo = mi; - break; - } - } - - if (methodInfo == null) - throw new BarcodeProfileException( - $"Could not find method \"{argument.Key}\" with {argsArray.Count} arguments."); - - var methodParameters = methodInfo.GetParameters(); - object[] arrParams = new object[argsArray.Count]; - - for (int p = 0; p < methodParameters.Length; p++) - { - var typeConverter = TypeDescriptor.GetConverter(methodParameters[p].ParameterType); - arrParams[p] = typeConverter.ConvertFromInvariantString(argsArray[p].Value()); - } - - try - { - methodInfo.Invoke(@object, arrParams); - } - catch (Exception exception) - { - throw new BarcodeProfileException($"Could not invoke method \"{argument.Key}\"", exception); - } - } - } - else // .Property=Value - { - PropertyInfo propertyInfo = type.GetProperty(memberName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy); - if (propertyInfo == null) - throw new BarcodeProfileException($"Property \"{memberName}\" does not exist in \"{type}\""); - - try - { - if (propertyInfo.PropertyType.BaseType == typeof(Array)) - { - string classTypeName = propertyInfo.PropertyType.FullName.Trim(new[] { '[', ']' }); - Type classType = Type.GetType(classTypeName); - TypeConverter typeConverter = TypeDescriptor.GetConverter(classType); - - if (!string.IsNullOrEmpty(argument.Value)) - { - string args = argument.Value.Trim(); - args = args.Trim(new[] { '[', ']' }); - string[] parts = Regex.Split(args, ", *"); - - Array array = Array.CreateInstance(classType, parts.Length); - - for (int v = 0; v < parts.Length; v++) - { - string piece = parts[v]; - array.SetValue(typeConverter.ConvertFromInvariantString(piece.Trim()), v); - } - - propertyInfo.SetValue(@object, array, BindingFlags.FlattenHierarchy, null, null, - CultureInfo.InvariantCulture); - } - else - propertyInfo.SetValue(@object, null, BindingFlags.FlattenHierarchy, null, null, - CultureInfo.InvariantCulture); - } - else - { - TypeConverter typeConverter = TypeDescriptor.GetConverter(propertyInfo.PropertyType); - object value = typeConverter.ConvertFromInvariantString(argument.Value); - propertyInfo.SetValue(@object, value, BindingFlags.FlattenHierarchy, null, null, - CultureInfo.InvariantCulture); - } - } - catch (Exception exception) - { - throw new BarcodeProfileException( - $"Could not set value \"{argument.Value}\" to property \"{argument.Key}\"", exception); - } - } - } - } - } - } - } - - internal class Profile - { - internal string Name { get; set; } = null; - - internal List> Properties { get; set; } = new List>(); - - internal bool Selected { get; set; } = false; - internal bool Deferred { get; set; } = false; - - internal List Keywords { get; set; } = new List(); - - public Profile() - { - } - } -} - -namespace BarcodeWriter.Core -{ -#if BARCODESDK_EMBEDDED_SOURCES - internal class BarcodeProfileException : BarcodeException -#else - public class BarcodeProfileException : BarcodeException -#endif - { - public BarcodeProfileException(string message) : base(message) - { - } - - public BarcodeProfileException(string message, Exception innerException) - : base(message, innerException) - { - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/RSCodec.cs b/MuPDF.NET/Barcode/Encoders/Internal/RSCodec.cs deleted file mode 100644 index 7bda4995..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/RSCodec.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core.Internal -{ - - - /// - /// - /// - internal class RSCodec - { - private int gf_mod; // module of the Galois Field - private int poly_degree; // // GF(2^m) maximum degree of RS-polynomial - private int log_mod; // 2^m - 1 size of Lock-up table - - private int[] index_of; // index table for fast multiplication - private int[] alpha_to; // table of powers of primitive polynomial - - /// - /// Initializes a new instance of the class. - /// - /// The mod. - public RSCodec(int mod) - { - gf_mod = mod; - Init(); - } - - private void Init() - { - poly_degree = 0; - int bin = 1; - while (bin <= gf_mod) - { - bin <<= 1; - poly_degree++; - } - bin >>= 1; - poly_degree--; - - log_mod = (1 << poly_degree) - 1; - index_of = new int[log_mod + 1]; - alpha_to = new int[log_mod + 1]; - - // Calculate Lock-up table - int mask = 1; - for (int i = 0; i < log_mod; i++) - { - alpha_to[i] = mask; - index_of[mask] = i; - mask <<= 1; - if ((mask & bin) != 0) - mask ^= gf_mod; - } - index_of[0] = -1; - } - - private int[] get_gpoly(int ec_length, int index) - { - int[] result = new int[ec_length + 1]; - result[0] = 1; - for (int i = 1; i < result.Length; i++) - { - result[i] = 1; - for (int k = i - 1; k > 0; k--) - { - if (result[k] != 0) - result[k] = alpha_to[(index_of[result[k]] + index) % log_mod]; - result[k] ^= result[k - 1]; - } - result[0] = alpha_to[(index_of[result[0]] + index) % log_mod]; - index++; - } - return result; - } - - /// - /// Encodes the specified data. - /// - /// The data. - /// The ec_length. - /// - public int[] Encode(byte[] data, int ec_length) - { - // calculating generator polynomial - int[] g_poly = get_gpoly(ec_length, 1); - - int[] result = new int[ec_length]; - int m; - - for (int i = 0; i < data.Length; i++) - { - m = result[ec_length - 1] ^ data[i]; - for (int k = ec_length - 1; k > 0; k--) - { - if ((m != 0) && (g_poly[k] != 0)) - result[k] = result[k - 1] ^ alpha_to[(index_of[m] + index_of[g_poly[k]]) % log_mod]; - else - result[k] = result[k - 1]; - } - if ((m != 0) && (g_poly[0] != 0)) - result[0] = alpha_to[(index_of[m] + index_of[g_poly[0]]) % log_mod]; - else - result[0] = 0; - } - - return result; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/SymbologyDrawing.cs b/MuPDF.NET/Barcode/Encoders/Internal/SymbologyDrawing.cs deleted file mode 100644 index 4021fa1a..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/SymbologyDrawing.cs +++ /dev/null @@ -1,710 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using SkiaSharp; -using System; -using System.Collections; -using System.ComponentModel; -using static System.Net.Mime.MediaTypeNames; - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Base class for symbology-specific drawings - /// - abstract class SymbologyDrawing : IDisposable - { - protected ArrayList m_rects = new ArrayList(); - protected SKSize m_drawingSize = new SKSize(); - protected TrueSymbologyType m_type = TrueSymbologyType.Code39; - private string m_value = ""; - private SKFont m_captionFont = new SKFont(SKTypeface.FromFamilyName( - "Arial", - SKFontStyleWeight.Normal, - SKFontStyleWidth.Normal, - SKFontStyleSlant.Upright), - 12); - private CaptionPosition m_captionPosition = CaptionPosition.Below; - - /// - /// Validates the value using current symbology rules. - /// If value is valid then it will be set as current value. - /// - /// The value. - /// Checksum is obligatory or not. - /// true if value is valid (can be encoded); otherwise, false. - public abstract bool ValueIsValid(string value, bool checksumIsMandatory); - - /// - /// Gets the value restrictions description string. - /// - /// The value restrictions description string. - public abstract string getValueRestrictions(); - - /// - /// Gets the barcode value encoded using current symbology rules. - /// - /// if set to true then encoded value is for caption. otherwise, for drawing - /// The barcode value encoded using current symbology rules. - public abstract string GetEncodedValue(bool forCaption); - - /// - /// Gets the encoding pattern for given character. - /// - /// The character to retrieve pattern for. - /// The encoding pattern for given character. - protected abstract string getCharPattern(char c); - - /// - /// Initializes a new instance of the class. - /// - public SymbologyDrawing() - { - } - - public SymbologyDrawing(TrueSymbologyType type) - { - m_type = type; - } - - /// - /// Initializes a new instance of the class. - /// - /// The existing SymbologyDrawing object to use as parameter prototype. - /// The new symbology drawing type. - public SymbologyDrawing(SymbologyDrawing prototype, TrueSymbologyType type) - { - m_type = type; - AddChecksum = prototype.AddChecksum; - AddChecksumToCaption = prototype.AddChecksumToCaption; - BarHeight = prototype.BarHeight; - CaptionFont = new SKFont( - prototype.CaptionFont.Typeface, - prototype.CaptionFont.Size, - prototype.CaptionFont.ScaleX, - prototype.CaptionFont.SkewX - ); - CaptionPosition = prototype.m_captionPosition; - CustomCaption = (string)prototype.CustomCaption.Clone(); - DrawCaption = prototype.DrawCaption; - Color = prototype.Color; - NarrowBarWidth = prototype.NarrowBarWidth; - Options = (SymbologyOptions)prototype.Options.Clone(); - WideToNarrowRatio = prototype.WideToNarrowRatio; - - if (ValueIsValid(prototype.Value, false)) - m_value = (string)prototype.Value.Clone(); - else - m_value = GetIncorrectValueSubstitution(); - } - - /// - /// Disposes all resources. - /// - public void Dispose() - { - m_captionFont.Dispose(); - } - - /// - /// Gets the incorrect value substitution. - /// - /// The incorrect value substitution. - protected virtual string GetIncorrectValueSubstitution() - { - return ""; - } - - /// - /// Gets or sets a value indicating whether checksum should be added to barcode. - /// - /// true if checksum should be added to barcode; otherwise, false. - public bool AddChecksum { get; set; } = true; - - /// - /// Gets or sets a value indicating whether checksum should be added to barcode caption. - /// - /// - /// true if checksum should be added to barcode caption; otherwise, false. - /// - public bool AddChecksumToCaption { get; set; } = true; - - /// - /// Gets the barcode encoded value. - /// - /// The barcode encoded value. - public string Caption - { - get - { - if (CustomCaption.Length != 0) - return CustomCaption; - - return GetEncodedValue(true); - } - } - - /// - /// Gets or sets the custom caption text to draw instead of the barcode encoded value. - /// - /// The custom caption text to draw instead of the barcode encoded value. - public string CustomCaption { get; set; } = ""; - - /// - /// Gets or sets a value indicating whether to draw the barcode encoded value. - /// - /// true if to draw the barcode encoded value; otherwise, false. - public bool DrawCaption { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to draw the barcode encoded - /// value for 2D barcodes. - /// - /// true if to draw the barcode encoded value for 2D - /// barcodes; otherwise, false. - public bool DrawCaption2D { get; set; } - - /// - /// Gets the barcode symbology type - /// - /// The barcode symbology. - public SymbologyType Symbology - { - get - { - try - { - TypeConverter tc = TypeDescriptor.GetConverter(typeof(SymbologyType)); - SymbologyType type = (SymbologyType)tc.ConvertFromString(m_type.ToString()); - return type; - } - catch (Exception) - { - } - - return SymbologyType.Code39; - } - } - - /// - /// Gets or sets the barcode symbology specific options. - /// - /// The barcode symbology specific options. - public SymbologyOptions Options { get; set; } = new SymbologyOptions(); - - /// - /// Gets or sets the barcode value to encode. - /// - /// The barcode value to encode. - public virtual string Value - { - get - { - return m_value; - } - set - { - if (ValueIsValid(value, false)) - { - m_value = value; - } - else - { - string generic = "Provided value can't be encoded by current symbology.\n"; - throw new BarcodeException(generic + getValueRestrictions()); - } - } - } - - /// - /// Resultion of result image - /// - internal SKSize BarcodeResolution { get; set; } - - /// - /// Gets or sets the height of the barcode bars in pixels. - /// - /// The height of the barcode bars in pixels. - public virtual int BarHeight { get; set; } = 50; - - /// - /// Gets or sets the barcode caption font. - /// - /// The barcode caption font. - public SKFont CaptionFont - { - get => m_captionFont; - set => m_captionFont = value; - } - - /// - /// Gets or sets the width of the narrow bar in pixels. - /// - /// The width of the narrow bar in pixels. - public virtual int NarrowBarWidth { get; set; } = 3; - - /// - /// Gets or sets the width of a wide bar relative to the narrow bar. - /// - /// The width of a wide bar relative to the narrow bar. - public int WideToNarrowRatio { get; set; } = 3; - - /// - /// Gets or sets the color used to draw the barcode bars. - /// - /// The color used to draw the barcode bars. - public SKColor Color { get; set; } = SKColors.Black; - - /// - /// Gets or sets the barcode caption position. - /// - /// The barcode caption position. - public virtual CaptionPosition CaptionPosition - { - get => m_captionPosition; - set => m_captionPosition = value; - } - - public virtual CaptionAlignment CaptionAlignment { get; set; } - - /// - /// Gets a value indicating whether this symbology can not have - /// a caption drawn. - /// - public virtual bool PreventCaptionDrawing => false; - - public bool DrawQuietZones { get; set; } = true; - - public int CustomCaptionGap { get; set; } = -1; - - public bool RoundDots { get; set; } = false; - - public int RoundDotsScale { get; set; } = 100; - - /// - /// Draws the barcode using current symbology rules. - /// - /// The Graphics object to draw the barcode on. - /// The position in pixels of the top left point of the barcode. - /// if set to true then barcode does not gets drawn, - /// only size calculatations performed. - /// - /// The size of the smallest rectangle in pixels that can accommodate the barcode with caption. - /// - public virtual SKSize Draw(SKCanvas canvas, SKPoint position, bool onlyCalculate) - { - // There is two main barcode image types: rectangular and EAN-like - // - // Rectangular (1D and 2D): - // - // above - // | | | || ||| | | | || | - // | | | || ||| | | | || | - // | | | || ||| | | | || | - // | | | || ||| | | | || | - // | | | || ||| | | | || | - // below - // - // EAN-like (EAN-13, EAN-8, ISBN, UPC-A, UPC-E): - // - // above - // | | | || ||| | | | || | - // | | | || ||| | | | || | - // | | | || ||| | | | || | - // | | | || ||| | | | || | - // before | | left || right | | after - - bool wasError = false; - - try - { - m_rects.Clear(); - // we shouldn't probide buildBars with graphics of font but, in fact, - // some symbologies contain parts whose size depends on font size. - m_drawingSize = buildBars(canvas, CaptionFont); - } - catch (BarcodeException e) - { - m_drawingSize = calculateOrDrawException(canvas, position, e.Message, onlyCalculate); - wasError = true; - } - - // we can't just use CaptionFont because sometimes caption font - // should be decreased to fit space available in EAN-like barcodes. - SKFont captionFontToUse = getFontForCaption(canvas, CaptionPosition); - - // calculate size of areas occupied by different caption parts - // of course, not all areas have non-zero size - SKSize captionAbove = occupiedByCaptionAbove(canvas, captionFontToUse); - SKSize captionBelow = occupiedByCaptionBelow(canvas, captionFontToUse); - int captionBeforeWidth = occupiedByCaptionBefore(canvas, captionFontToUse); - int captionAfterWidth = occupiedByCaptionAfter(canvas, captionFontToUse); - - // vertical quiet zone for 2D barcode - int quietZone2D = (this is SymbologyDrawing2D && DrawQuietZones) ? - NarrowBarWidth * 2 : 0; - - if (!onlyCalculate) - { - if (!wasError) - using (SKPaint paint = new SKPaint - { - Color = Color, // map from System.Drawing.Color - IsAntialias = true, - Style = SKPaintStyle.Fill - }) - { - // offset position and draw bars - drawBars(canvas, paint, new SKPoint(position.X + captionBeforeWidth, position.Y + captionAbove.Height + quietZone2D)); - - if (CaptionMayBeDrawn) - { - // draw all caption parts if needed - drawCaptionBeforePart(canvas, paint, captionFontToUse, position); - - // drawCaption should draw left and right caption parts in EAN-like symbologies - SKPoint drawingLeft = new SKPoint(position.X + captionBeforeWidth, - position.Y + (CaptionPosition == CaptionPosition.Below ? quietZone2D * 2 : 0)); - drawCaption(canvas, paint, captionFontToUse, drawingLeft); - - drawCaptionAfterPart(canvas, paint, captionFontToUse, drawingLeft); - } - } - } - - // calculate total image width (image is an area occupied by bars and captions) - float width = captionBeforeWidth + Math.Max(captionAbove.Width, Math.Max(captionBelow.Width, m_drawingSize.Width)) + captionAfterWidth; - float height = captionAbove.Height + quietZone2D + m_drawingSize.Height + quietZone2D + captionBelow.Height; - return new SKSize(width, height); - } - - private SKSize occupiedByCaptionAbove(SKCanvas canvas, SKFont font) - { - SKSize captionSize = new SKSize(); - if (CaptionMayBeDrawn && CaptionPosition == CaptionPosition.Above) - captionSize = calculateCaptionSize(canvas, font); - - return captionSize; - } - - protected virtual SKSize occupiedByCaptionBelow(SKCanvas canvas, SKFont font) - { - SKSize captionSize = new SKSize(); - if (CaptionMayBeDrawn && CaptionPosition == CaptionPosition.Below) - { - captionSize = calculateCaptionSize(canvas, font); - - if (CustomCaptionGap != -1) - { - captionSize.Height += CustomCaptionGap; - } - } - - return captionSize; - } - - protected virtual int occupiedByCaptionBefore(SKCanvas canvas, SKFont font) - { - return 0; - } - - protected virtual int occupiedByCaptionAfter(SKCanvas canvas, SKFont font) - { - return 0; - } - - protected virtual SKSize buildBars(SKCanvas canvas, SKFont font) - { - SKSize drawingSize = new SKSize(); - int x = 0; - int y = 0; - - string value = GetEncodedValue(false); - - bool drawBar = true; - bool firstChar = true; - - foreach (char c in value) - { - if (!firstChar) - { - // check if we are drawing Codabar symbology - // with this symbology we always should have - if (Symbology == SymbologyType.Codabar) - { // always draw intercharacter gap = narrow width for Codabar - // otherwise character patterns will merge together as there were no space between them - // this will cause unreadable barcodes (see UPN-997837) - - // by default Options.DrawIntercharacterGap == true, which causes incorrect drawing of Codabar - // I don't think this option is needed for Codabar symbology (Plisko) - //if (Options.DrawIntercharacterGap) // or if option has this enabled - // x += WideToNarrowRatio * NarrowBarWidth; - //else - x += NarrowBarWidth; - } - // other symbologies - else if (Options.DrawIntercharacterGap) // or if option has this enabled - x += NarrowBarWidth; - - - drawBar = true; - } - else - { - firstChar = false; - } - - string pattern = getCharPattern(c); - foreach (char patternChar in pattern) - { - int width = NarrowBarWidth; - if (patternChar == 'w') - width *= WideToNarrowRatio; - - if (drawBar) - m_rects.Add(new SKRect(x, y, x+width, y+BarHeight)); - - x += width; - drawBar = !drawBar; - } - } - - drawingSize.Width = x; - drawingSize.Height = BarHeight; - return drawingSize; - } - - private SKSize calculateOrDrawException(SKCanvas canvas, SKPoint position, string message, bool onlyCalculate) - { - string error = "Failed to encode: " + message; - - using (var paint = new SKPaint - { - IsAntialias = true, - Color = SKColors.Black // Or map from your Color property - }) - { - // Measure text bounds - SKRect bounds = new SKRect(); - this.CaptionFont.MeasureText(error, out bounds); - - if (!onlyCalculate) - { - canvas.DrawText(error, position.X, position.Y - bounds.Top, this.CaptionFont, paint); - // Note: subtracting bounds.Top ensures proper baseline alignment - } - - return new SKSize(bounds.Width + 1, bounds.Height + 1); - } - } - - - protected virtual void drawBars(SKCanvas canvas, SKPaint paint, SKPoint position) - { - if (RoundDots && - (Symbology == SymbologyType.QRCode || Symbology == SymbologyType.GS1_QRCode || - Symbology == SymbologyType.DataMatrix || Symbology == SymbologyType.GS1_DataMatrix || - Symbology == SymbologyType.Aztec)) - { - foreach (SKRect r in m_rects) - { - SKRect dotRect = new SKRect(r.Left + position.X, r.Top + position.Y, r.Left + position.X+r.Width, r.Top + position.Y+r.Height); - - if (RoundDotsScale != 100) - { - float scaledWidth = r.Width / 100f * RoundDotsScale; - float d = (scaledWidth - r.Width) / 2; - dotRect.Inflate(d, d); - } - - // Draw ellipse (circle/dot) - canvas.DrawOval(dotRect.Left, dotRect.Top, dotRect.Right, dotRect.Bottom, paint); - } - } - else - { - foreach (SKRect r in m_rects) - { - SKRect barRect = new SKRect( - r.Left + position.X, - r.Top + position.Y, - r.Right + position.X, - r.Bottom + position.Y - ); - - canvas.DrawRect(barRect, paint); - } - } - } - - protected virtual void drawCaptionBeforePart(SKCanvas canvas, SKPaint paint, SKFont font, SKPoint position) - { - } - - - protected virtual void drawCaption(SKCanvas canvas, SKPaint paint, SKFont font, SKPoint position) - { - // Measure caption size - SKSize size = calculateCaptionSize(canvas, font); - SKSizeI captionSize = new SKSizeI((int)size.Width, (int)size.Height); - SKRect captionRect = SKRect.Empty; - - // Resolve horizontal alignment - SKTextAlign hAlign; - - switch (CaptionAlignment) - { - case CaptionAlignment.Auto: - if (captionSize.Width <= m_drawingSize.Width) - hAlign = SKTextAlign.Center; - else - hAlign = SKTextAlign.Left; - break; - - case CaptionAlignment.Left: - hAlign = SKTextAlign.Left; - break; - - case CaptionAlignment.Center: - hAlign = SKTextAlign.Center; - break; - - case CaptionAlignment.Right: - hAlign = SKTextAlign.Right; - break; - - default: - throw new ArgumentOutOfRangeException(); - } - - // Get font metrics - var metrics = font.Metrics; - float fontHeight = metrics.Descent - metrics.Ascent + metrics.Leading; - - if (CaptionPosition == CaptionPosition.Above) - { - captionRect = new SKRect( - position.X, - position.Y, - position.X + Math.Max(captionSize.Width, m_drawingSize.Width), - position.Y + captionSize.Height - ); - - float textWidth = font.MeasureText(Caption); - - float x; - - switch (hAlign) - { - case SKTextAlign.Left: - x = captionRect.Left; - break; - - case SKTextAlign.Center: - x = captionRect.MidX - (textWidth / 2f); - break; - - case SKTextAlign.Right: - x = captionRect.Right - textWidth; - break; - - default: - x = captionRect.Left; - break; - } - - float y = captionRect.Top - metrics.Ascent; - - canvas.DrawText(Caption, x, y, font, paint); - } - else if (CaptionPosition == CaptionPosition.Below) - { - int captionGap = CustomCaptionGap == -1 ? Utils.CalculateCaptionGap(font) : CustomCaptionGap; - - captionRect = new SKRect( - position.X, - position.Y + m_drawingSize.Height + captionGap, - position.X + Math.Max(captionSize.Width, m_drawingSize.Width), - position.Y + m_drawingSize.Height + captionGap + captionSize.Height - ); - - if (CustomCaptionGap != -1) - { - // Equivalent of your interlineGap fix - float interlineGap = (metrics.Descent - metrics.Ascent + metrics.Leading) - fontHeight; - captionRect.Offset(0, -interlineGap); - } - - float textWidth = font.MeasureText(Caption); - - float x; - - switch (hAlign) - { - case SKTextAlign.Left: - x = captionRect.Left; - break; - - case SKTextAlign.Center: - x = captionRect.MidX - (textWidth / 2f); - break; - - case SKTextAlign.Right: - x = captionRect.Right - textWidth; - break; - - default: - x = captionRect.Left; - break; - } - - float y = captionRect.Bottom - metrics.Descent; - - canvas.DrawText(Caption, x, y, font, paint); - } - } - - protected virtual void drawCaptionAfterPart(SKCanvas canvas, SKPaint paint, SKFont font, SKPoint position) - { - } - - /// - /// Gets a value indicating whether a caption may be drawn. - /// - protected virtual bool CaptionMayBeDrawn - { - get - { - return (DrawCaption && !PreventCaptionDrawing && - (CaptionPosition == CaptionPosition.Above || CaptionPosition == CaptionPosition.Below)); - } - } - - /// - /// Calculates the size of the caption text string. - /// - /// The Graphics object used to measure caption text string. - /// The font. - /// The size of the caption text string. - protected SKSize calculateCaptionSize(SKCanvas canvas, SKFont font) - { - // Measure width directly from SKFont - float width = font.MeasureText(Caption); - - // Measure height using font metrics - var metrics = font.Metrics; - float height = metrics.Descent - metrics.Ascent + metrics.Leading; - - // +1 like in GDI+ version, return integer size - return new SKSize((width + 1), (height + 1)); - } - protected virtual SKFont getFontForCaption(SKCanvas canvas, CaptionPosition position) - { - return CaptionFont; - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/SymbologyDrawing2D.cs b/MuPDF.NET/Barcode/Encoders/Internal/SymbologyDrawing2D.cs deleted file mode 100644 index f1391174..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/SymbologyDrawing2D.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Text; -using System.Drawing; - -namespace BarcodeWriter.Core.Internal -{ - abstract class SymbologyDrawing2D : SymbologyDrawing - { - /// - /// Initializes a new instance of the class. - /// - public SymbologyDrawing2D() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The type of the new symbology drawing. - public SymbologyDrawing2D(TrueSymbologyType type) - : base(type) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The existing SymbologyDrawing object to - /// use as parameter prototype. - /// The new symbology drawing type. - public SymbologyDrawing2D(SymbologyDrawing prototype, TrueSymbologyType type) - : base(prototype, type) - { - } - - /// - /// Gets a value indicating whether a caption may be drawn. - /// - protected override bool CaptionMayBeDrawn - { - get - { - return (DrawCaption2D && !PreventCaptionDrawing); - } - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/TrueSymbologyType.cs b/MuPDF.NET/Barcode/Encoders/Internal/TrueSymbologyType.cs deleted file mode 100644 index 2a5bc98d..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/TrueSymbologyType.cs +++ /dev/null @@ -1,439 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -namespace BarcodeWriter.Core.Internal -{ - /// - /// Describes all supported barcode symbologies (types). - /// - enum TrueSymbologyType - { - /// - /// Codabar barcode (aka Ames Code/USD-4/NW-7/2 of 7 Code). Codabar - /// symbology allows only symbols from this string '0123456789-$:/.+' - /// to be encoded. - /// This symbology used for example in libraries and blood banks. - /// - Codabar, - - /// - /// Code 39 barcode (aka USD-3, 3 of 9). Code 39 symbology allows - /// all ASCII symbols to be encoded in extended mode or symbols from - /// this string "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%" in - /// standard mode. This symbology used for example by U.S. Government - /// and military, required for DoD applications. - /// - Code39, - - /// - /// Interleaved 2 of 5 barcode. Interleaved 2 of 5 symbology allows - /// only numeric values to be encoded. This symbology is used primarily in - /// the distribution and warehouse industry. - /// - I2of5, - - /// - /// Code 93 barcode (aka USS-93). Code 93 symbology allows - /// all ASCII symbols to be encoded in extended mode or symbols from - /// this string "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%" in - /// standard mode. This symbology was designed to - /// complement and improve upon Code 39 symbology. Code 93 - /// produces denser code than that of Code 39. - /// - Code93, - - /// - /// Code 128 barcode. It is a very effective, high-density symbology - /// which permits the encoding of alphanumeric (subject to alphabet - /// selection) data. Code 128 is a very dense code, used extensively worldwide. - /// - Code128, - - /// - /// EAN-13 barcode. Used with consumer products internationally. EAN-13 - /// symbology allows only numeric values to be encoded. - /// - EAN13, - - /// - /// JAN-13 barcode (aka JAN codes). This symbology is mostly the same - /// as EAN-13 symbology, but used in Japan. JAN stands for Japanese - /// Numbering Authority. First two digits of JAN-13 symbology are always "49". - /// - JAN13, - - /// - /// ISBN Number encoded as EAN-13 barcode. - /// - ISBN, - - /// - /// Bookland barcode. This symbology is mostly the same - /// as EAN-13 symbology, but used exclusively with books. First three - /// digits of Bookland symbology are always "978" the rest of code - /// is a ISBN number without embedded hyphens and ISBN check digit. - /// - Bookland, - - /// - /// UPC-A barcode. Used with consumer products in U.S. UPC-A symbology - /// allows only numeric values to be encoded. - /// - UPCA, - - /// - /// UPC-E barcode. This symbology is zero-suppression version - /// of UPC-A. It is intended to be used on packaging which would - /// be otherwise too small to use one of the other versions. The - /// code is smaller because it drops out zeros which would otherwise - /// occur in a symbol. Used with consumer products in U.S. - /// UPC-E symbology allows only numeric values to be encoded. - /// - UPCE, - - /// - /// EAN-8 barcode. This symbology is a short version of EAN-13 that - /// is intended to be used on packaging which would be otherwise - /// too small to use one of the other versions. Used with consumer - /// products internationally. EAN-8 symbology allows only numeric - /// values to be encoded. - /// - EAN8, - - /// - /// Postnet barcode. This symbology usually gets printed by U.S. Post - /// Office on envelopes. Postnet symbology allows only numeric - /// values to be encoded. The bar code itself can encode either a - /// standard 5-digit Zip Code, a Zip+4 code, or a full 11-point - /// delivery point code. - /// - Postnet, - - /// - /// PDF417 symbology. This symbology is heavily used in the parcel - /// industry. The PDF417 symbology can encode a vast amount of data - /// into a small space. This symbology allows a maximum data size of - /// 1850 text characters, or 2710 digits. - /// - PDF417, - - /// - /// PDF417 Truncated symbology. This symbology is a truncted (right column is missing) or - /// compact version of PDF417 symbology. - /// - PDF417Truncated, - - /// - /// Data Matrix symbology. The most popular application for Data - /// Matrix is marking small items. The Data Matrix can encode text - /// and raw data. Usual data size is from a few bytes up to 2 kilobytes. - /// - DataMatrix, - - /// - /// QR Code symbology. QR Code initially was used for tracking parts in vehicle - /// manufacturing, but now QR Codes used in a much broader context, - /// including both commercial tracking applications and convenience-oriented - /// applications aimed at mobile phone users (known as mobile tagging). - /// - QRCode, - - /// - /// Aztec Code symbology. An Aztec code barcode is used by Deutsche Bahn, - /// Trenitalia and by Swiss Federal Railways for tickets sold online and - /// printed out by customers. The Aztec Code has been selected by the airline - /// industry (IATA's BCBP standard) for the electronic boarding passes. - /// - Aztec, - - /// - /// PLANET (The Postal Alpha Numeric Encoding Technique) barcode is used by - /// the United States Postal Service to identify and track pieces of - /// mail during delivery - the Post Office's "CONFIRM" services. - /// - Planet, - - /// - /// EAN128 symbology (Also known as EAN-128, EAN-14, Shipping - /// Container Code, UCC-14, DUN-14 (Distribution Unit Number), - /// SSC-14, GS1-128, UCC-128, UCC/EAN-128. This symbology was developed - /// to provide a worldwide format and standard for exchanging common - /// data between companies. - /// - EAN128, - GS1_128, - - /// - /// USPS Sack Label symbology (Also known as USPS 25 Sack Label). This - /// is in fact Interleaved 2 of 5 symbology with exactly 8 digits - /// encoded: 5-digit Zip Code (the sack destination) and a 3-digit - /// content identifier number(CIN). - /// - USPSSackLabel, - - /// - /// USPS Tray Label symbology (Also known as USPS 25 Tray Label). This - /// is in fact Interleaved 2 of 5 symbology with exactly 10 digits - /// encoded: 5-digit Zip Code (the tray destination) and a 3-digit - /// content identifier number(CIN), and a 2-digit USPS processing code. - /// - USPSTrayLabel, - - /// - /// Deutsche Post Identcode (Also known as Deutsche Post AG IdentCode, - /// German Postal 2 of 5 IdentCode, Deutsche Frachtpost IdentCode, - /// IdentCode, Deutsche Post AG (DHL). This symbology is used by - /// German Post (Deutsche Post AG) (Deutsche Frachtpost). The barcode - /// contains a tracking number providing an identification of the - /// customer (sender) and the mail piece. The value to encode length - /// is fixed to 11 digits. The value to encode must have the following - /// structure: 2 digits for ID of primary distribution center, 3 digits - /// for Customer ID, and 6 digits for Mailing number. - /// - DeutschePostIdentcode, - - /// - /// Deutsche Post Leitcode (Also known as German Postal 2 of 5 LeitCode, - /// LeitCode, CodeLeitcode, Deutsche Post AG (DHL)). This symbology is used by - /// German Post (Deutsche Post AG) (Deutsche Frachtpost). The barcode - /// gives an indication of the destination. The value to encode length - /// is fixed to 13 digits. The value to encode must have the following - /// structure: 5 digits for Postal Code (Postleitzahl, PLZ), 3 digits - /// for Street ID/number, 3 digits for House number, and 2 digits for - /// Product code. - /// - DeutschePostLeitcode, - - /// - /// Numly barcode (Also known as ESN, Electronic Serial Number). This - /// barcode is a unique identifier that allows an author or publisher to assign - /// to content and track licensing of each id assignment. Numly Numbers - /// are useful if you wish to identify each electronic distributed copy - /// of any form of electronic media. Numly Numbers can also act a - /// third-party content submission time stamps to aid in copyright - /// proving instances and emails. The length of value to encode is - /// fixed to 19 digits. - /// - Numly, - - /// - /// PZN barcode (Also known as Pharma-Zentral-Nummer, Pharmazentralnummer, - /// Code PZN, CodePZN, Pharma Zentral Nummer). This symbology is - /// used for distribution of pharmaceutical / health care products - /// in Germany. The specification of this application is maintained - /// at Informationsstelle für Arzneispezialitäten GmbH (IFA). The - /// length of value to encode is 6 digits. - /// - PZN, - - /// - /// Optical Product Code (Also known as OPC, Vision Council of America - /// OPC, VCA BarCode, VCA OPC) symbology. This symbology is used for - /// marking retail optical products. The OPC is a 9-digit, numeric - /// code that identifies the product and the manufacturer. The - /// structure of the OPC code is the following: 5 digits for - /// Manufacturer Identification Number assigned by the Optical - /// Product Code Council, Inc., 4 digits for Item Identification Number - /// assigned and controlled by the optical manufacturer. - /// - OpticalProduct, - - /// - /// Swiss Post Parcel (Also known as SwissPost Parcel Barcode, - /// Switzerland Post Parcel Barcode, Swiss PostParcel Barcode) symbology. - /// This symbology is used by Swiss Post. It identifies each parcel - /// and serves as a means of verifying mailing and delivery and - /// checking the service offering. All parcels must have a unique - /// barcode. The barcode is the requirement for automated processing. - /// The barcode serves as a means of identifying the item. The - /// structure of the Swiss Post Parcel barcode is 18 numeric digits: - /// 2 digits for Swiss Post reference, 8 digits for Franking license - /// number, and 8 digits for Item number. - /// - SwissPostParcel, - - /// - /// Royal Mail barcode (Also known as RMS4CC, RoyalMail4SCC, - /// Royal Mail 4-State, British Royal Mail 4-State Customer Code, - /// 4-State). This symbology was created for automated mail sorting. - /// It normally codes the postcode and the house or mailbox number - /// in a machine readable format. The contents of the code may vary - /// in different countries. - /// - RoyalMail, - - /// - /// Dutch KIX barcode (Also known as Royal TNT Post Kix, Dutch - /// KIX 4-State Barcode, Kix Barcode, TPG KIX, Klantenindex Barcode, - /// TPGPOST KIX). This symbology is used by Royal Dutch TPG Post - /// (Netherlands) for Postal code and automatic mail sorting. It - /// provides information about the address of the receiver. This - /// symbology encodes alpha-numeric characters (0-9, A-Z). - /// - DutchKix, - - /// - /// Singapore 4-State Postal Code barcode (Also known as Singapore - /// 4-State Postal, SingPost 4-State, SingPost Barcode, Singapore - /// 4-State Code). This Symbology is used by Singapore Post - /// (SingPost) for Postal code and automatic mail sorting. Such - /// barcode provides information about the address of the receiver. - /// This symbology encodes alpha-numeric characters (0-9, A-Z). - /// - SingaporePostalCode, - - /// - /// The EAN-2 (Also known as EAN/2 and EAN 2) is a supplement to - /// the EAN-13 and UPC-A barcodes. It is often used on magazines - /// and periodicals to indicate an issue number. - /// - EAN2, - - /// - /// The EAN-5 (Also known as EAN/5 and EAN 5) is a supplement to - /// EAN-13 and UPC-A barcodes. It is used often used to give a - /// suggestion for the price of the book. - /// - EAN5, - - /// - /// The EAN14 symbology is used for traded goods. - /// - EAN14, - - /// - /// The Macro version of PDF417 Symbology. - /// - MacroPDF417, - - /// - /// The Micro version of PDF417 Symbology. - /// - MicroPDF417, - - /// - /// GS1 DataMatrix is a 2D (two-dimensional) barcode that holds - /// large amounts of data in a relatively small space. These barcodes - /// are used primarily in aerospace, pharmaceuticals, medical device - /// manufacturing, and by the U.S. Department of Defense to add - /// visibility to the value chain. GS1 DataMatrix can be used for parts - /// that need to be tracked in the manufacturing process because the - /// barcode allows users to encode a variety of information related - /// to the product, such as date or lot number. They are not intended - /// to be used on items that pass through retail point-of-sale (POS). - /// - GS1_DataMatrix, - - /// - /// The Telepen symbology. This symbology is used in many countries and - /// very widely in the UK. Most Universities and other academic libraries use - /// Telepen, as do many public libraries. Other users include the motor - /// industry, Ministry of Defence and innumerable well-known organisations - /// for many different applications. - /// - Telepen, - - /// - /// The Intelligent Mail Barcode symbology. - /// This symbology is used in the USPS mailstream. It is also known as - /// the USPS OneCode Solution or USPS 4-State Customer Barcode (abbreviated - /// 4CB, 4-CB, or USPS4CB) - /// - IntelligentMail, - - /// - /// The GS1 DataBar Omnidirectional symbology. - /// - GS1_DataBar_Omnidirectional, - - /// - /// The GS1 DataBar Truncated symbology. - /// - GS1_DataBar_Truncated, - - /// - /// The GS1 DataBar Stacked symbology. - /// - GS1_DataBar_Stacked, - - /// - /// The GS1 DataBar Stacked Omnidirectional symbology. - /// - GS1_DataBar_Stacked_Omnidirectional, - - /// - /// The GS1 DataBar Limited symbology. - /// - GS1_DataBar_Limited, - - /// - /// The GS1 DataBar Expanded symbology. - /// - GS1_DataBar_Expanded, - - /// - /// The GS1 DataBar Expanded Stacked symbology. - /// - GS1_DataBar_Expanded_Stacked, - - /// - /// The MaxiCode symbology. - /// - MaxiCode, - - /// - /// The Plessey Code symbology. - /// This symbology is used primarily in libraries and for retail grocery shelf marking. - /// - Plessey, - - /// - /// The MSI (also known as Modified Plessey) symbology. - /// This symbology is used primarily for inventory control, - /// marking storage containers and shelves in warehouse environments. - /// - MSI, - - /// - /// The ITF-14 (GTIN-14, UCC-14) symbology - /// ITF-14 is the GS1 implementation of an Interleaved 2 of 5 bar code to encode - /// a Global Trade Item Number. ITF-14 symbols are generally used on a packaging - /// step of products. The ITF-14 always encodes 14 digits. - /// - ITF14, - - /// - /// GTIN-12 is UPC-A with 12 digits. - /// - GTIN12, - - /// - /// GTIN-8 is EAN-8. - /// - GTIN8, - - /// - /// GTIN-13 is EAN-13. - /// - GTIN13, - - /// - /// GTIN-14 is ITF-14. - /// - GTIN14, - - /// - /// GS1 QR code symbology. - /// - GS1_QRCode, - - /// - /// PharmaCode symbology. - /// - PharmaCode, - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/Utils.cs b/MuPDF.NET/Barcode/Encoders/Internal/Utils.cs deleted file mode 100644 index d937f7e9..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/Utils.cs +++ /dev/null @@ -1,471 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using SkiaSharp; -using System; -using System.IO; - -namespace BarcodeWriter.Core.Internal -{ - class Utils - { - private Utils() - { - } - - public static SKCanvas GetScreenCompatibleGraphics(out SKBitmap dummy) - { - // Create a small dummy bitmap (10x10) with 32-bit color - dummy = new SKBitmap(10, 10, SKColorType.Bgra8888, SKAlphaType.Premul); - - // Create a canvas for drawing on the bitmap - var canvas = new SKCanvas(dummy); - - // Clear with transparent or white if needed - canvas.Clear(SKColors.Transparent); - - return canvas; - } - - /// - /// Calculates the draw position. - /// - /// Size of the barcode. - /// The total width. - /// The total height. - /// The leftmost draw position. - /// The topmost draw position. - /// The horizontal alignment. - /// The vertical alignment. - public static void CalculateDrawPosition(SKSize barcodeSize, - float totalWidth, float totalHeight, out float left, out float top, - BarcodeHorizontalAlignment hAlign, BarcodeVerticalAlignment vAlign) - { - left = 0; - top = 0; - - if (barcodeSize.Width < totalWidth) - { - switch (hAlign) - { - case BarcodeHorizontalAlignment.Right: - left = totalWidth - barcodeSize.Width; - break; - - case BarcodeHorizontalAlignment.Center: - left = (totalWidth - barcodeSize.Width) / 2; - break; - } - } - - if (barcodeSize.Height < totalHeight) - { - switch (vAlign) - { - case BarcodeVerticalAlignment.Bottom: - top = totalHeight - barcodeSize.Height; - break; - - case BarcodeVerticalAlignment.Middle: - top = (totalHeight - barcodeSize.Height) / 2; - break; - } - } - } - - public static int CalculateCaptionGap(SKFont captionFont) - { - SKFontMetrics metrics = captionFont.Metrics; - float height = metrics.Descent - metrics.Ascent + metrics.Leading; - return (int)System.Math.Ceiling(height); - } - - public static SKBitmap ConvertToBitonal(SKBitmap original) - { - int width = original.Width; - int height = original.Height; - - // Destination bitmap: 8-bit grayscale - var destination = new SKBitmap(width, height, SKColorType.Gray8, SKAlphaType.Opaque); - - // Manual thresholding (same as original GDI+ approach) - int threshold = 128; // ~500/3, like your original code - - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - SKColor color = original.GetPixel(x, y); - int brightness = color.Red + color.Green + color.Blue; - - byte bw = (brightness > 384) // (? 128 * 3 = 384, matches old threshold=500 logic) - ? (byte)255 - : (byte)0; - - destination.SetPixel(x, y, new SKColor(bw, bw, bw)); - } - } - - return destination; - } - - public static SKBitmap ConvertToBitonal_(SKBitmap original) - { - int width = original.Width; - int height = original.Height; - - // Calculate bytes per row (1-bit per pixel, padded to 8) - int stride = (width + 7) / 8; - byte[] bitonalData = new byte[stride * height]; - - int threshold = 500; // same as your GDI+ code - - for (int y = 0; y < height; y++) - { - int destIndex = y * stride; - byte destByte = 0; - int bitMask = 0x80; // start with MSB - - for (int x = 0; x < width; x++) - { - SKColor pixel = original.GetPixel(x, y); - int pixelTotal = pixel.Red + pixel.Green + pixel.Blue; - - if (pixelTotal > threshold) - destByte |= (byte)bitMask; - - if (bitMask == 1) - { - bitonalData[destIndex++] = destByte; - destByte = 0; - bitMask = 0x80; - } - else - { - bitMask >>= 1; - } - } - - if (bitMask != 0x80) // write last byte if row not multiple of 8 - bitonalData[destIndex] = destByte; - } - - // Create a 1-bit SKBitmap for output - SKImageInfo info = new SKImageInfo(width, height, SKColorType.Alpha8, SKAlphaType.Opaque); - SKBitmap bitonal = new SKBitmap(info); - IntPtr dstPixels = bitonal.GetPixels(); - - // Copy the bitonalData to SKBitmap memory - unsafe - { - fixed (byte* src = bitonalData) - { - Buffer.MemoryCopy(src, (void*)dstPixels, bitonalData.Length, bitonalData.Length); - } - } - - return bitonal; - } - - public static void SaveAsBitonalTiff(SKBitmap copy, Stream outputStream) - { - try - { - // Convert to 1bpp (threshold method) - using (var bitonal = new SKBitmap(copy.Width, copy.Height, SKColorType.Gray8, SKAlphaType.Opaque)) - { - for (int y = 0; y < copy.Height; y++) - { - for (int x = 0; x < copy.Width; x++) - { - var color = copy.GetPixel(x, y); - byte gray = (byte)(0.3 * color.Red + 0.59 * color.Green + 0.11 * color.Blue); - byte bw = gray > 127 ? (byte)255 : (byte)0; // Threshold - bitonal.SetPixel(x, y, new SKColor(bw, bw, bw)); - } - } - - using (var image = SKImage.FromBitmap(bitonal)) - using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) - data.SaveTo(outputStream); - } - } - catch (Exception ex) - { - throw new Exception("Can't save to 1-bit image.", ex); - } - } - /* - public static void SaveAsBitonalTiff(SKBitmap bitmap, MemoryStream ms) - { - int width = bitmap.Width; - int height = bitmap.Height; - - // Convert SKBitmap to 1-bit bitonal byte array (scanline by scanline) - byte[] bitonalData = new byte[(width + 7) / 8 * height]; - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - SKColor pixel = bitmap.GetPixel(x, y); - // simple threshold: black if intensity < 128 - bool isBlack = (pixel.Red + pixel.Green + pixel.Blue) / 3 < 128; - int byteIndex = y * ((width + 7) / 8) + (x / 8); - int bitIndex = 7 - (x % 8); - if (isBlack) - bitonalData[byteIndex] |= (byte)(1 << bitIndex); - } - } - - // Save using LibTiff.NET - using (Tiff tiff = Tiff.ClientOpen("in-memory", "w", ms, new TiffStream())) - { - if (tiff == null) - throw new Exception("Could not create TIFF"); - - tiff.SetField(TiffTag.IMAGEWIDTH, width); - tiff.SetField(TiffTag.IMAGELENGTH, height); - tiff.SetField(TiffTag.BITSPERSAMPLE, 1); - tiff.SetField(TiffTag.SAMPLESPERPIXEL, 1); - tiff.SetField(TiffTag.ROWSPERSTRIP, height); - tiff.SetField(TiffTag.COMPRESSION, Compression.CCITTFAX4); - tiff.SetField(TiffTag.PHOTOMETRIC, Photometric.MINISWHITE); - - tiff.WriteEncodedStrip(0, bitonalData, bitonalData.Length); - tiff.Close(); - } - } - */ - /// - /// Detects which format to use for image saved into a file with given name. - /// - /// Name of the file to save image to. - /// The format to use for image. - public static SKEncodedImageFormat FormatFromName(string fileName) - { - string extension = Path.GetExtension(fileName).ToLowerInvariant(); - - switch (extension) - { - case ".bmp": - return SKEncodedImageFormat.Bmp; - - case ".gif": // single-frame only - return SKEncodedImageFormat.Gif; - - case ".jpg": - case ".jpeg": - return SKEncodedImageFormat.Jpeg; - - case ".png": - return SKEncodedImageFormat.Png; - - case ".tif": - case ".tiff": - return SKEncodedImageFormat.Png; - - case ".wmf": - case ".emf": - throw new NotSupportedException("WMF/EMF is not supported in SkiaSharp."); - - default: - return SKEncodedImageFormat.Bmp; - } - } - - public static SKSize GetSizeInUnits(float dpiX, float dpiY, SKSize size, UnitOfMeasure unit) - { - float width = (float)size.Width; - float height = (float)size.Height; - - switch (unit) - { - case UnitOfMeasure.Document: - width = 300.0f * (float)size.Width / dpiX; - height = 300.0f * (float)size.Height / dpiY; - break; - - case UnitOfMeasure.Inch: - width = (float)size.Width / dpiX; - height = (float)size.Height / dpiY; - break; - - case UnitOfMeasure.Point: - width = 72.0f * (float)size.Width / dpiX; - height = 72.0f * (float)size.Height / dpiY; - break; - - case UnitOfMeasure.Millimeter: - width = 25.4f * (float)size.Width / dpiX; - height = 25.4f * (float)size.Height / dpiY; - break; - - case UnitOfMeasure.Centimeter: - width = 2.54f * (float)size.Width / dpiX; - height = 2.54f * (float)size.Height / dpiY; - break; - - case UnitOfMeasure.Twip: - width = 1440.0f * (float)size.Width / dpiX; - height = 1440.0f * (float)size.Height / dpiY; - break; - } - - return new SKSize(width, height); - } - - /// - /// Gets the size in pixels. - /// - /// The Graphics object (used to retrieve device properties). - /// The size in units. - /// The unit of the size. - /// - public static SKSize GetSizeInPixels(SKCanvas canvas, SKSize size, UnitOfMeasure unit) - { - return GetSizeInPixels(96.0f, 96.0f, size, unit); - } - - public static SKSize GetSizeInPixels(float dpiX, float dpiY, SKSize size, UnitOfMeasure unit) - { - int width = (int)size.Width; - int height = (int)size.Height; - - switch (unit) - { - case UnitOfMeasure.Document: - width = (int)(size.Width * dpiX / 300); - height = (int)(size.Height * dpiY / 300); - break; - - case UnitOfMeasure.Inch: - width = (int)(size.Width * dpiX); - height = (int)(size.Height * dpiY); - break; - - case UnitOfMeasure.Point: - width = (int)(size.Width * dpiX / 72); - height = (int)(size.Height * dpiY / 72); - break; - - case UnitOfMeasure.Millimeter: - width = (int)(size.Width * dpiX / 25.4); - height = (int)(size.Height * dpiY / 25.4); - break; - - case UnitOfMeasure.Centimeter: - width = (int)(size.Width * dpiX / 2.54); - height = (int)(size.Height * dpiY / 2.54); - break; - - case UnitOfMeasure.Twip: - width = (int)(size.Width * dpiX / 1440); - height = (int)(size.Height * dpiY / 1440); - break; - } - - return new SKSize(width, height); - } - - public static int UnitsToPixels(float resolution, float value, UnitOfMeasure unit) - { - int result = 0; - - switch (unit) - { - case UnitOfMeasure.Document: - result = (int) (value * resolution / 300); - break; - - case UnitOfMeasure.Inch: - result = (int) (value * resolution); - break; - - case UnitOfMeasure.Point: - result = (int) (value * resolution / 72); - break; - - case UnitOfMeasure.Millimeter: - result = (int) (value * resolution / 25.4); - break; - - case UnitOfMeasure.Centimeter: - result = (int) (value * resolution / 2.54); - break; - - case UnitOfMeasure.Twip: - result = (int) (value * resolution / 1440); - break; - - case UnitOfMeasure.Pixel: - result = (int) value; - break; - } - - return result; - } - - /// - /// Rotates the graphics using specified drawing angle. - /// - /// The Graphics object. - /// The angle. - /// The width of drawing. - /// The height of drawing. - public static void RotateGraphics(SKCanvas canvas, RotationAngle angle, float width, float height) - { - switch (angle) - { - case RotationAngle.Degrees90: - canvas.Translate(width, 0); - canvas.RotateDegrees(90); - break; - - case RotationAngle.Degrees180: - canvas.Translate(width, height); - canvas.RotateDegrees(180); - break; - - case RotationAngle.Degrees270: - canvas.Translate(0, height); - canvas.RotateDegrees(270); - break; - } - } - - public static SKBitmap CreateImage(SKSizeI imageSize, float dpiX, float dpiY) - { - // Create a 32-bit RGBA bitmap - var info = new SKImageInfo(imageSize.Width, imageSize.Height, SKColorType.Bgra8888, SKAlphaType.Premul); - var bitmap = new SKBitmap(info); - - // Note: SKBitmap does not store DPI directly; you may need to keep dpiX/dpiY separately - // if needed for export or rendering calculations. - - return bitmap; - } - - public static bool Is2DSymbology(SymbologyType symbology) - { - switch (symbology) - { - case SymbologyType.PDF417: - case SymbologyType.PDF417Truncated: - case SymbologyType.DataMatrix: - case SymbologyType.QRCode: - case SymbologyType.Aztec: - case SymbologyType.MacroPDF417: - case SymbologyType.MicroPDF417: - case SymbologyType.GS1_DataMatrix: - case SymbologyType.MaxiCode: - return true; - default: - return false; - } - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/byteList.cs b/MuPDF.NET/Barcode/Encoders/Internal/byteList.cs deleted file mode 100644 index c02a321b..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/byteList.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Text; -using System.Collections; - -namespace BarcodeWriter.Core.Internal -{ - class byteList - { - // Use of ArrayList slows down byteList, it is critical for large number of - // elements and intensive read/write. Slowdowns are caused by necessity of - // boxing/unboxing of value type byte when putting into/getting from ArrayList - private ArrayList m_list = new ArrayList(); - - /// - /// Gets the at the specified index. - /// - /// The zero-based index of the element to get - public byte this[int index] - { - get - { - return (byte)m_list[index]; - } - set - { - m_list[index] = value; - } - } - - /// - /// Gets the number of elements actually contained in the byteList. - /// - /// The number of elements actually contained in the byteList. - public int Count - { - get { return m_list.Count; } - } - - /// - /// Adds an to the end of the byteList. - /// - /// The to be added to the end of the byteList. - public void Add(byte item) - { - m_list.Add(item); - } - - public void AddRange(byte[] c) - { - m_list.AddRange(c); - } - - /// - /// Removes the element at the specified index of the byteList. - /// - /// The zero-based index of the element to remove. - public void RemoveAt(int index) - { - m_list.RemoveAt(index); - } - - /// - /// Removes all elements from the byteList. - /// - public void Clear() - { - m_list.Clear(); - } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - foreach (object o in m_list) - { - sb.Append((char)(byte)o); - } - - return sb.ToString(); - } - - /// - /// Copies the elements of the to a new array. - /// - /// An array containing copies of the elements of the byteList - public byte[] ToArray() - { - return (byte[])m_list.ToArray(typeof(byte)); - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Internal/intList.cs b/MuPDF.NET/Barcode/Encoders/Internal/intList.cs deleted file mode 100644 index 355bd4bd..00000000 --- a/MuPDF.NET/Barcode/Encoders/Internal/intList.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Text; -using System.Collections; - -namespace BarcodeWriter.Core.Internal -{ - class intList - { - private ArrayList m_list = new ArrayList(); - - /// - /// Gets the at the specified index. - /// - /// The zero-based index of the element to get - public int this[int index] - { - get - { - return (int)m_list[index]; - } - set - { - m_list[index] = value; - } - } - - /// - /// Gets the number of elements actually contained in the intList. - /// - /// The number of elements actually contained in the intList. - public int Count - { - get { return m_list.Count; } - } - - /// - /// Adds an to the end of the intList. - /// - /// The to be added to the end of the intList. - public void Add(int item) - { - m_list.Add(item); - } - - /// - /// Removes the element at the specified index of the intList. - /// - /// The zero-based index of the element to remove. - public void RemoveAt(int index) - { - m_list.RemoveAt(index); - } - - /// - /// Inserts an element into the intList at the specified index. - /// - /// The zero-based index at which value should be inserted. - /// The value to insert. - public void Insert(int index, int value) - { - m_list.Insert(index, value); - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/MSIChecksumAlgorithm.cs b/MuPDF.NET/Barcode/Encoders/MSIChecksumAlgorithm.cs deleted file mode 100644 index ff5acfca..00000000 --- a/MuPDF.NET/Barcode/Encoders/MSIChecksumAlgorithm.cs +++ /dev/null @@ -1,36 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -namespace BarcodeWriter.Core -{ - /// - /// Describes all supported MSI symbology checksum algorithms. - /// - public enum MSIChecksumAlgorithm - { - /// - /// No check digit (least common) - /// - NoCheckDigit = 0, - /// - /// The Modulo 10 check digit algorithm (most common) uses the Luhn algorithm. - /// - Modulo10 = 1, - /// - /// The Modulo 11 check digit algorithm. - /// - Modulo11 = 2, - /// - /// The Modulo 1010 check digit algorithm. - /// - Modulo1010 = 3, - /// - /// The Modulo 1110 check digit algorithm. - /// - Modulo1110 = 4, - } -} diff --git a/MuPDF.NET/Barcode/Encoders/Margins.cs b/MuPDF.NET/Barcode/Encoders/Margins.cs deleted file mode 100644 index 4165135b..00000000 --- a/MuPDF.NET/Barcode/Encoders/Margins.cs +++ /dev/null @@ -1,200 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Runtime.InteropServices; -using System.Text; -using System.ComponentModel; -using System.Globalization; - -namespace BarcodeWriter.Core -{ - /// - /// Class that describes barcode margins. - /// - [ClassInterface(ClassInterfaceType.AutoDual)] - [TypeConverter(typeof(ExpandableObjectConverter))] - public class Margins : ICloneable, IMargins - { - private int _left; - private int _top; - private int _right; - private int _bottom; - - /// - /// Occurs when margins get changed. - /// - public event EventHandler Changed; - - /// - /// Raises the event. - /// - /// The sender. - protected virtual void FireChanged(object sender) - { - Changed?.Invoke(sender, null); - } - - /// - /// Initializes a new instance of the class. - /// - public Margins() - { - } - - /// - /// Initializes a new instance of the struct. - /// - /// The left margin. - /// The top margin. - /// The right margin. - /// The bottom margin. - public Margins(int left, int top, int right, int bottom) - { - _left = left; - _top = top; - _right = right; - _bottom = bottom; - } - - /// - /// Initializes a new instance of the class. - /// - /// The string representing margins in the form "[0;0;0;0]". - public Margins(string value) - { - string[] tokens = value.Split(new char[] { '[', ']', ';' }); - if (tokens.Length != 6 || tokens[0].Length != 0 || tokens[5].Length != 0) - throw new BarcodeException("Invalid string representation of Margins object."); - - Left = toInt(tokens[1]); - Top = toInt(tokens[2]); - Right = toInt(tokens[3]); - Bottom = toInt(tokens[4]); - } - - /// - /// Converts string to an int value. - /// - /// The string value. - /// Conversion result. - private static int toInt(string value) - { - return int.Parse(value, CultureInfo.CurrentCulture); - } - - /// - /// Serves as a hash function for a particular type. - /// - /// - /// A hash code for the current . - /// - public override int GetHashCode() - { - return base.GetHashCode(); - } - - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare with the current . - /// - /// true if the specified is equal to the current ; otherwise, false. - /// - /// The parameter is null. - public override bool Equals(object obj) - { - Margins m = (Margins)obj; - - if (m.Bottom != Bottom || m.Left != Left || m.Right != Right || m.Top != Top) - return false; - - return true; - } - - /// - /// Returns a that represents the current . - /// - /// - /// A that represents the current . - /// - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - sb.Append("["); - sb.Append(Left.ToString()); - sb.Append(";"); - sb.Append(Top.ToString()); - sb.Append(";"); - sb.Append(Right.ToString()); - sb.Append(";"); - sb.Append(Bottom.ToString()); - sb.Append("]"); - - return sb.ToString(); - } - - /// - /// Creates a new object that is a copy of the current instance. - /// - /// - /// A new object that is a copy of this instance. - /// - public object Clone() - { - return MemberwiseClone(); - } - - /// - [Description("The left margin in pixels.")] - public int Left - { - get => _left; - set - { - _left = value; - FireChanged(this); - } - } - - /// - [Description("The top margin in pixels.")] - public int Top - { - get => _top; - set - { - _top = value; - FireChanged(this); - } - } - - /// - [Description("The right margin in pixels.")] - public int Right - { - get => _right; - set - { - _right = value; - FireChanged(this); - } - } - - /// - [Description("The bottom margin in pixels.")] - public int Bottom - { - get => _bottom; - set - { - _bottom = value; - FireChanged(this); - } - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/PDF417CompactionMode.cs b/MuPDF.NET/Barcode/Encoders/PDF417CompactionMode.cs deleted file mode 100644 index 379d042a..00000000 --- a/MuPDF.NET/Barcode/Encoders/PDF417CompactionMode.cs +++ /dev/null @@ -1,35 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -namespace BarcodeWriter.Core -{ - /// - /// Describes all possible compaction (encoding) modes for PDF417 symbology. - /// - public enum PDF417CompactionMode - { - /// - /// (-1) Library will choose best compaction mode (aim is to produce shortest encoded byte string). - /// - Auto = -1, - - /// - /// (0) Binary compaction mode. Can encode any ASCII symbol. - /// - Binary = 0, - - /// - /// (1) Text compaction mode (allows optimized text encoding) - /// - Text = 1, - - /// - /// (2) Numeric compaction mode (allows optimized encoding of numbers) - /// - Numeric = 2 - } -} diff --git a/MuPDF.NET/Barcode/Encoders/PDF417ErrorCorrectionLevel.cs b/MuPDF.NET/Barcode/Encoders/PDF417ErrorCorrectionLevel.cs deleted file mode 100644 index 6ab7c5d5..00000000 --- a/MuPDF.NET/Barcode/Encoders/PDF417ErrorCorrectionLevel.cs +++ /dev/null @@ -1,84 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; - -namespace BarcodeWriter.Core -{ - /// - /// Describes all possible error correction levels for PDF417 symbology. - /// - /// - /// When error correction level set to Auto, library will try to - /// use best possible error correction level. Error correction is done - /// by adding extra symbols to barcode. Higher error correction level - /// adds more extra symbols and thus shortens possible length of string - /// to encode. - /// - public enum PDF417ErrorCorrectionLevel - { - /// - /// (-1) Library will choose best possible error correction level. - /// - Auto = -1, - - /// - /// (0) Level 0 error correction. 2 extra symbols will be added to - /// encoded data. - /// - Level0 = 0, - - /// - /// (1) Level 1 error correction. 4 extra symbols will be added to - /// encoded data. - /// - Level1 = 1, - - /// - /// (2) Level 2 error correction. 8 extra symbols will be added to - /// encoded data. - /// - Level2 = 2, - - /// - /// (3) Level 3 error correction. 16 extra symbols will be added to - /// encoded data. - /// - Level3 = 3, - - /// - /// (4) Level 4 error correction. 32 extra symbols will be added to - /// encoded data. - /// - Level4 = 4, - - /// - /// (5) Level 5 error correction. 64 extra symbols will be added to - /// encoded data. - /// - Level5 = 5, - - /// - /// (6) Level 6 error correction. 128 extra symbols will be added to - /// encoded data. - /// - Level6 = 6, - - /// - /// (7) Level 7 error correction. 256 extra symbols will be added to - /// encoded data. - /// - Level7 = 7, - - /// - /// (8) Level 8 error correction. 512 extra symbols will be added to - /// encoded data. - /// - Level8 = 8 - } -} diff --git a/MuPDF.NET/Barcode/Encoders/PZNType.cs b/MuPDF.NET/Barcode/Encoders/PZNType.cs deleted file mode 100644 index 6ed6d37a..00000000 --- a/MuPDF.NET/Barcode/Encoders/PZNType.cs +++ /dev/null @@ -1,29 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; - -namespace BarcodeWriter.Core -{ - /// - /// Specifies the type of PZN symbology. - /// - public enum PZNType - { - /// - /// The original PZN barcode with 6 significant digits. PZN7 is obsolete since 2013-01-01, but can be used up to 2020-01-01. - /// - PZN7 = 0, - - /// - /// New PZN8 standard with 7 significant digits. Supercedes PZN7 since 2013-01-01. - /// Default. - /// - PZN8 = 1 - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/PageSize.cs b/MuPDF.NET/Barcode/Encoders/PageSize.cs deleted file mode 100644 index 0f0aabb7..00000000 --- a/MuPDF.NET/Barcode/Encoders/PageSize.cs +++ /dev/null @@ -1,338 +0,0 @@ -using SkiaSharp; -using System; -using System.Drawing; -using System.Text; - -namespace BarcodeWriter.Core -{ - /// - /// Describes standart page sizes. - /// - public enum PageSize - { - /// - /// - /// - LETTER, - /// - /// - /// - NOTE, - /// - /// - /// - LEGAL, - /// - /// - /// - TABLOID, - /// - /// - /// - EXECUTIVE, - /// - /// - /// - POSTCARD, - /// - /// - /// - A0, - /// - /// - /// - A1, - /// - /// - /// - A2, - /// - /// - /// - A3, - /// - /// - /// - A4, - /// - /// - /// - A5, - /// - /// - /// - A6, - /// - /// - /// - A7, - /// - /// - /// - A8, - /// - /// - /// - A9, - /// - /// - /// - A10, - /// - /// - /// - B0, - /// - /// - /// - B1, - /// - /// - /// - B2, - /// - /// - /// - B3, - /// - /// - /// - B4, - /// - /// - /// - B5, - /// - /// - /// - B6, - /// - /// - /// - B7, - /// - /// - /// - B8, - /// - /// - /// - B9, - /// - /// - /// - B10, - /// - /// - /// - ARCH_E, - /// - /// - /// - ARCH_D, - /// - /// - /// - ARCH_C, - /// - /// - /// - ARCH_B, - /// - /// - /// - ARCH_A, - /// - /// - /// - FLSA, - /// - /// - /// - FLSE, - /// - /// - /// - HALFLETTER, - /// - /// - /// - _11X17, - /// - /// - /// - ID_1, - /// - /// - /// - ID_2, - /// - /// - /// - ID_3, - /// - /// - /// - LEDGER, - /// - /// - /// - CROWN_QUARTO, - /// - /// - /// - LARGE_CROWN_QUARTO, - /// - /// - /// - DEMY_QUARTO, - /// - /// - /// - ROYAL_QUARTO, - /// - /// - /// - CROWN_OCTAVO, - /// - /// - /// - LARGE_CROWN_OCTAVO, - /// - /// - /// - DEMY_OCTAVO, - /// - /// - /// - ROYAL_OCTAVO, - /// - /// - /// - SMALL_PAPERBACK, - /// - /// - /// - PENGUIN_SMALL_PAPERBACK, - /// - /// - /// - PENGUIN_LARGE_PAPERBACK, - } - - internal static class PageSizeUtils - { - internal static SKSize GetPageSize(PageSize pageSize) - { - switch (pageSize) - { - case PageSize.LETTER: - return new SKSize(612, 792); - case PageSize.NOTE: - return new SKSize(540, 720); - case PageSize.LEGAL: - return new SKSize(612, 1008); - case PageSize.TABLOID: - return new SKSize(792, 1224); - case PageSize.EXECUTIVE: - return new SKSize(522, 756); - case PageSize.POSTCARD: - return new SKSize(283, 416); - case PageSize.A0: - return new SKSize(2384, 3370); - case PageSize.A1: - return new SKSize(1684, 2384); - case PageSize.A2: - return new SKSize(1191, 1684); - case PageSize.A3: - return new SKSize(842, 1191); - case PageSize.A4: - return new SKSize(595, 842); - case PageSize.A5: - return new SKSize(420, 595); - case PageSize.A6: - return new SKSize(297, 420); - case PageSize.A7: - return new SKSize(210, 297); - case PageSize.A8: - return new SKSize(148, 210); - case PageSize.A9: - return new SKSize(105, 148); - case PageSize.A10: - return new SKSize(73, 105); - case PageSize.B0: - return new SKSize(2834, 4008); - case PageSize.B1: - return new SKSize(2004, 2834); - case PageSize.B2: - return new SKSize(1417, 2004); - case PageSize.B3: - return new SKSize(1000, 1417); - case PageSize.B4: - return new SKSize(708, 1000); - case PageSize.B5: - return new SKSize(498, 708); - case PageSize.B6: - return new SKSize(354, 498); - case PageSize.B7: - return new SKSize(249, 354); - case PageSize.B8: - return new SKSize(175, 249); - case PageSize.B9: - return new SKSize(124, 175); - case PageSize.B10: - return new SKSize(87, 124); - case PageSize.ARCH_E: - return new SKSize(2592, 3456); - case PageSize.ARCH_D: - return new SKSize(1728, 2592); - case PageSize.ARCH_C: - return new SKSize(1296, 1728); - case PageSize.ARCH_B: - return new SKSize(864, 1296); - case PageSize.ARCH_A: - return new SKSize(648, 864); - case PageSize.FLSA: - return new SKSize(612, 936); - case PageSize.FLSE: - return new SKSize(648, 936); - case PageSize.HALFLETTER: - return new SKSize(396, 612); - case PageSize._11X17: - return new SKSize(792, 1224); - case PageSize.ID_1: - return new SKSize(242.65f, 153); - case PageSize.ID_2: - return new SKSize(297, 210); - case PageSize.ID_3: - return new SKSize(354, 249); - case PageSize.LEDGER: - return new SKSize(1224, 792); - case PageSize.CROWN_QUARTO: - return new SKSize(535, 697); - case PageSize.LARGE_CROWN_QUARTO: - return new SKSize(569, 731); - case PageSize.DEMY_QUARTO: - return new SKSize(620, 782); - case PageSize.ROYAL_QUARTO: - return new SKSize(671, 884); - case PageSize.CROWN_OCTAVO: - return new SKSize(348, 527); - case PageSize.LARGE_CROWN_OCTAVO: - return new SKSize(365, 561); - case PageSize.DEMY_OCTAVO: - return new SKSize(391, 612); - case PageSize.ROYAL_OCTAVO: - return new SKSize(442, 663); - case PageSize.SMALL_PAPERBACK: - return new SKSize(314, 504); - case PageSize.PENGUIN_SMALL_PAPERBACK: - return new SKSize(314, 513); - case PageSize.PENGUIN_LARGE_PAPERBACK: - return new SKSize(365, 561); - default: - return SKSize.Empty; - } - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/QREncodeHint.cs b/MuPDF.NET/Barcode/Encoders/QREncodeHint.cs deleted file mode 100644 index ec3e5fed..00000000 --- a/MuPDF.NET/Barcode/Encoders/QREncodeHint.cs +++ /dev/null @@ -1,28 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; - -namespace BarcodeWriter.Core -{ - /// - /// Describes the library how non-alphanumerical characters should be encoded. - /// - public enum QREncodeHint - { - /// - /// (0) All of non-alphanumerical characters will be encoded as is. This is default mode. - /// - Mode8 = 0, - - /// - /// (1) Kanji/Kana characters will be encoded as Shif-JIS characters. - /// - Kanji, - } -} diff --git a/MuPDF.NET/Barcode/Encoders/QRErrorCorrectionLevel.cs b/MuPDF.NET/Barcode/Encoders/QRErrorCorrectionLevel.cs deleted file mode 100644 index 121a5ebc..00000000 --- a/MuPDF.NET/Barcode/Encoders/QRErrorCorrectionLevel.cs +++ /dev/null @@ -1,38 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; - -namespace BarcodeWriter.Core -{ - /// - /// Level of error correction in QR Code symbols - /// - public enum QRErrorCorrectionLevel - { - /// - /// (0) Lowest error correction level (Approx. 7% of codewords can be restored). - /// - Low = 0, - - /// - /// (1) Medium error correction level (Approx. 15% of codewords can be restored). - /// - Medium, - - /// - /// (2) Quarter error correction level (Approx. 25% of codewords can be restored). - /// - Quarter, - - /// - /// (3) Highest error correction level (Approx. 30% of codewords can be restored). - /// - High - } -} diff --git a/MuPDF.NET/Barcode/Encoders/RotationAngle.cs b/MuPDF.NET/Barcode/Encoders/RotationAngle.cs deleted file mode 100644 index 09591d70..00000000 --- a/MuPDF.NET/Barcode/Encoders/RotationAngle.cs +++ /dev/null @@ -1,41 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -namespace BarcodeWriter.Core -{ - /// - /// Describes all supported barcode output rotation angles. - /// -#if QRCODESDK - internal enum RotationAngle -#else - public enum RotationAngle -#endif - { - /// - /// (0) 0 degrees clockwise - /// - Degrees0 = 0, - -#if !PocketPC && !WindowsCE - /// - /// (1) 90 degrees clockwise - /// - Degrees90 = 1, - - /// - /// (2) 180 degrees clockwise - /// - Degrees180 = 2, - - /// - /// (3) 270 degrees clockwise - /// - Degrees270 = 3, -#endif - } -} diff --git a/MuPDF.NET/Barcode/Encoders/SymbologyOptions.cs b/MuPDF.NET/Barcode/Encoders/SymbologyOptions.cs deleted file mode 100644 index 45d9e374..00000000 --- a/MuPDF.NET/Barcode/Encoders/SymbologyOptions.cs +++ /dev/null @@ -1,811 +0,0 @@ -/************************************************** - * - * - * - * -**************************************************/ - -using System; -using System.Text; -using System.ComponentModel; -using System.Drawing; -using System.Runtime.InteropServices; -using SkiaSharp; - -namespace BarcodeWriter.Core -{ - /// - /// Describes symbology specific options. - /// -#if QRCODESDK - internal class SymbologyOptions : ICloneable -#else - [TypeConverter(typeof(ExpandableObjectConverter)), ClassInterface(ClassInterfaceType.AutoDual)] - public class SymbologyOptions : ICloneable, ISymbologyOptions -#endif - { - /// - /// Occurs when options get changed. - /// - public event EventHandler Changed; - - /// - /// Raises the event. - /// - /// The sender. - protected virtual void FireChanged(object sender) - { - Changed?.Invoke(sender, null); - } - - /// - /// Creates a new object that is a copy of the current instance. - /// - /// - /// A new object that is a copy of this instance. - /// - public object Clone() - { - return MemberwiseClone(); - } - - /// - /// Gets or sets a value indicating whether to show start and stop symbology symbols in caption text. - /// - /// - /// true if to show start and stop symbology symbols in caption text; otherwise, false. - /// - /// - /// Note that not all symbologies support this option. Some of the symbologies simply do not - /// have printable start or stop symbol. - /// - [Description("A value indicating whether to show start and stop symbology symbols in caption text.")] - public bool ShowStartStop - { - get => _showStartStop; - set - { - _showStartStop = value; - FireChanged(this); - } - } - private bool _showStartStop; - - /// - /// Gets or sets a value indicating whether to draw intercharacter gaps. - /// - /// - /// true if to draw intercharacter gaps; otherwise, false. - /// - /// - /// Note that not all symbologies support this option. - /// - [Description("A value indicating whether to draw intercharacter gaps.")] - public bool DrawIntercharacterGap - { - get => _drawIntercharacterGap; - set - { - _drawIntercharacterGap = value; - FireChanged(this); - } - } - private bool _drawIntercharacterGap = true; - - /// - /// Gets or sets the space (in pixels) between main and supplemental barcode. - /// - /// The space (in pixels) between main and supplemental barcode. - [Description("The space (in pixels) between main and supplemental barcode.")] - public int SupplementSpace - { - get => _supplementSpace; - set - { - _supplementSpace = value; - FireChanged(this); - } - } - private int _supplementSpace = 5; - - /// - /// Gets or sets the character encoding of barcode value for 2D barcode types (QR Code, PDF417, DataMatrix, Aztec, MaxiCode). - /// Default is (ANSI). - /// - /// The character encoding. - public Encoding Encoding - { - get => _encoding; - set - { - _encoding = value; - FireChanged(this); - } - } - private Encoding _encoding = System.Text.Encoding.Default; - - /// - /// Gets or sets the character encoding of barcode value. - /// Default is - the default encoding on your computer. - /// You may override this value like: - /// - /// barcode.Options.TextEncodingCodePage = 1250; // to use German locale to decode text - /// - /// .NET interface: please use property instead - /// Some of available code pages are listed below for your reference: - /// - /// Name CodePage EncodingName - /// shift_jis 932 Japanese (Shift-JIS) - /// windows-1250 1250 Central European (Windows) - /// windows-1251 1251 Cyrillic (Windows) - /// Windows-1252 1252 Western European (Windows) - /// windows-1253 1253 Greek (Windows) - /// windows-1254 1254 Turkish (Windows) - /// csISO2022JP 50221 Japanese (JIS-Allow 1 byte Kana) - /// iso-2022-kr 50225 Korean (ISO) - /// - /// - public int TextEncodingCodePage - { - get => _encoding.CodePage; - set - { - _encoding = System.Text.Encoding.GetEncoding(value); - FireChanged(this); - } - } - - /// - /// ActiveX interface: Enables UTF8 text encoding for use for barcode value decoding - /// .NET interface: Use .TextEncoding property and set it to System.Text.Encoding.UTF8 if you need to - /// - public bool TextEncodingUseUTF8 - { - get => Equals(_encoding, System.Text.Encoding.UTF8); - set - { - _encoding = value ? System.Text.Encoding.UTF8 : System.Text.Encoding.Default; - FireChanged(this); - } - } - - /// - /// Gets or sets the hint to use when encoding non-alphanumeric data while - /// creating QR Code barcodes. - /// - /// The hint to use when encoding non-alphanumeric data while - /// creating QR Code barcodes. - [Description("A hint to use when encoding non-alphanumeric data while creating QR Code barcodes.")] - public QREncodeHint QREncodeHint - { - get => _qrHint; - set - { - _qrHint = value; - FireChanged(this); - } - } - private QREncodeHint _qrHint = QREncodeHint.Mode8; - - /// - /// Gets or sets the error correction level for QR Code barcodes. - /// - /// The error correction level for QR Code barcodes. - [Description("An error correction level for QR Code barcodes.")] - public QRErrorCorrectionLevel QRErrorCorrectionLevel - { - get => _qrErrorCorrectionLevel; - set - { - _qrErrorCorrectionLevel = value; - FireChanged(this); - } - } - private QRErrorCorrectionLevel _qrErrorCorrectionLevel = QRErrorCorrectionLevel.Low; - - /// - /// Gets or sets the minimum version (size) for QR Code barcodes. - /// - /// The minimum version (size) for QR Code barcodes. Should be in [0..40] range. - [Description("A minimum version (size) for QR Code barcodes. Should be in [0..40] range.")] - public int QRVersion - { - get => _qrVersion; - set - { - if (value < 0 || value > 40) - throw new BarcodeException("Minimum version should be in [0..40] range"); - - _qrVersion = value; - FireChanged(this); - } - } - private int _qrVersion; - -#if !QRCODESDK - /// - /// Gets or sets the algorithm to use for Codabar symbology checksum calculation. - /// - /// The algorithm to use for Codabar symbology checksum calculation. - [Description("The algorithm to use for Codabar symbology checksum calculation.")] - public CodabarChecksumAlgorithm CodabarChecksumAlgorithm - { - get => _codabarChecksumAlgorithm; - set - { - _codabarChecksumAlgorithm = value; - FireChanged(this); - } - } - private CodabarChecksumAlgorithm _codabarChecksumAlgorithm = CodabarChecksumAlgorithm.Modulo9; - - /// - /// Gets or sets the symbol to use as stop symbol in Codabar symbology. - /// - /// The symbol to use as stop symbol in Codabar symbology. - [Description("The symbol to use as stop symbol in Codabar symbology.")] - public CodabarSpecialSymbol CodabarStopSymbol - { - get => _codabarStopSymbol; - set - { - _codabarStopSymbol = value; - FireChanged(this); - } - } - private CodabarSpecialSymbol _codabarStopSymbol = CodabarSpecialSymbol.A; - - /// - /// Gets or sets the symbol to use as start symbol in Codabar symbology. - /// - /// The symbol to use as start symbol in Codabar symbology. - [Description("The symbol to use as start symbol in Codabar symbology.")] - public CodabarSpecialSymbol CodabarStartSymbol - { - get => _codabarStartSymbol; - set - { - _codabarStartSymbol = value; - FireChanged(this); - } - } - private CodabarSpecialSymbol _codabarStartSymbol = CodabarSpecialSymbol.A; - - /// - /// Gets or sets the alphabet to use for Code 128 symbology. - /// - /// The alphabet to use for Code 128 symbology. - [Description("The alphabet to use for Code 128 symbology.")] - public Code128Alphabet Code128Alphabet - { - get => _code128Alphabet; - set - { - _code128Alphabet = value; - FireChanged(this); - } - } - private Code128Alphabet _code128Alphabet = Code128Alphabet.Auto; - - /// - /// Gets or sets the alphabet to use for Telepen symbology. - /// - /// The alphabet to use for Telepen symbology. - [Description("The alphabet to use for Telepen symbology.")] - public TelepenAlphabet TelepenAlphabet - { - get => _telepenAlphabet; - set - { - _telepenAlphabet = value; - FireChanged(this); - } - } - private TelepenAlphabet _telepenAlphabet = TelepenAlphabet.Auto; - - /// - /// Gets or sets the minimum data column count for PDF417 barcodes. - /// - /// The minimum data column count for PDF417 barcodes. - [Description("A minimum data column count for PDF417 barcodes.")] - public int PDF417MinimumColumnCount - { - get => _pdf417MinColumnCount; - set - { - _pdf417MinColumnCount = value; - FireChanged(this); - } - } - private int _pdf417MinColumnCount; - - /// - /// This property is used only when PDF417UseManualSize == true. - /// Gets or sets the exact data column count for PDF417 barcodes. - /// - /// The exact data column count for PDF417 barcodes. - [Description("The exact data column count for PDF417 barcodes.")] - public int PDF417ColumnCount - { - get => _pdf417ColumnCount; - set - { - _pdf417ColumnCount = value; - FireChanged(this); - } - } - private int _pdf417ColumnCount; - - /// - /// This property is used only when PDF417UseManualSize == true. - /// Gets or sets the exact data row count for PDF417 barcodes. - /// Set this property to zero to automatically calculate it according to the given column count. - /// - /// The exact data row count for PDF417 barcodes. - [Description("The exact data row count for PDF417 barcodes.")] - public int PDF417RowCount - { - get => _pdf417RowCount; - set - { - _pdf417RowCount = value; - FireChanged(this); - } - } - private int _pdf417RowCount; - - /// - /// Gets a value indicating whether PDF417 barcodes should use exact row and column - /// count ( and ) - /// instead of minimal column count . - /// - /// A value indicating whether PDF417 barcodes should use exact row and column - /// count instead of minimal column count. - [Description("A value indicating whether PDF417 barcodes should use exact row and column count set by RowCount and ColumnCount or automatically calculated size (with limitation for a minimal column count).")] - public bool PDF417UseManualSize - { - get => _pdf417SizeIsManual; - set - { - _pdf417SizeIsManual = value; - FireChanged(this); - } - } - private bool _pdf417SizeIsManual; - - /// - /// Gets or sets the compaction mode to use while creating barcodes of PDF417 family. - /// - /// The compaction mode to use while creating barcodes of PDF417 family. - [Description("A compaction mode to use while creating barcodes of PDF417 family.")] - public PDF417CompactionMode PDF417CompactionMode - { - get => _pdf417CompactionMode; - set - { - _pdf417CompactionMode = value; - FireChanged(this); - } - } - private PDF417CompactionMode _pdf417CompactionMode = PDF417CompactionMode.Auto; - - /// - /// Gets or sets the error correction level for PDF417 barcodes. - /// - /// The error correction level for PDF417 barcodes. - [Description("An error correction level for PDF417 barcodes.")] - public PDF417ErrorCorrectionLevel PDF417ErrorCorrectionLevel - { - get => _pdf417ErrorCorrectionLevel; - set - { - _pdf417ErrorCorrectionLevel = value; - FireChanged(this); - } - } - private PDF417ErrorCorrectionLevel _pdf417ErrorCorrectionLevel = PDF417ErrorCorrectionLevel.Auto; - - /// - /// Gets or sets a value indicating whether to create PDF417 barcode as - /// part of Macro PDF417 sequence. - /// - /// true if PDF417 barcode should be created as part of - /// Macro PDF417 sequence; otherwise, false. - [Description("Whether to create PDF417 barcode as part of Macro PDF417 sequence.")] - public bool PDF417CreateMacro - { - get => _pdf417Macro; - set - { - _pdf417Macro = value; - FireChanged(this); - } - } - private bool _pdf417Macro; - - /// - /// Gets or sets File ID value for Macro PDF417 barcodes. - /// - /// The File ID value for Macro PDF417 barcodes. - [Description("File ID value for Macro PDF417 barcodes. Should be in [0..899] range.")] - public int PDF417FileID - { - get => _pdf417FileID; - set - { - var hi = value / 1000; - var lo = value % 1000; - - if (hi < 0 || hi > 899 || lo < 0 || lo > 899) - throw new BarcodeException("Improper PDF417 File ID value."); - - _pdf417FileID = value; - FireChanged(this); - } - } - private int _pdf417FileID; - - /*/// - /// Custom File ID for Macro PDF417 barcode in the form `\001\002\003`. - /// - /// Extended File ID value for Macro PDF417 barcodes. - public string PDF417ExtendedFileID - { - get => _pdf417ExtendedFileID; - set - { - _pdf417ExtendedFileID = value; - FireChanged(this); - } - } - private string _pdf417ExtendedFileID;*/ - - /// - /// Gets or sets Segment Index value for current Macro PDF417 barcode. - /// - /// The Segment Index value for current Macro PDF417 barcode. - [Description("Segment Index value for current Macro PDF417 barcode. Should be in [0..99998] range.")] - public int PDF417SegmentIndex - { - get => _pdf417SegmentIndex; - set - { - if (value < 0 || value > 99998) - throw new BarcodeException("PDF417 Segment Index should be in [0..99998] range."); - - _pdf417SegmentIndex = value; - FireChanged(this); - } - } - private int _pdf417SegmentIndex; - - /// - /// Gets or sets a value indicating if current Macro PDF 417 barcode - /// should be marked as last segment of sequence. - /// - /// true if current Macro PDF 417 barcode should be marked - /// as last segment of sequence.; otherwise, false. - [Description("Whether current Macro PDF 417 barcode should be marked as last segment of sequence.")] - public bool PDF417LastSegment - { - get => _pdf417MacroLastSegment; - set - { - _pdf417MacroLastSegment = value; - FireChanged(this); - } - } - private bool _pdf417MacroLastSegment = true; - - /// - /// Gets or sets the symbol size for Data Matrix barcodes. - /// - /// The symbol size for Data Matrix barcodes. - [Description("A the symbol size for Data Matrix barcodes.")] - public DataMatrixSize DataMatrixSize - { - get => _dataMatrixSize; - set - { - _dataMatrixSize = value; - FireChanged(this); - } - } - private DataMatrixSize _dataMatrixSize = DataMatrixSize.AutoSquareSize; - - /// - /// Gets or sets the compaction mode to use while creating Data Matrix barcodes. - /// - /// The compaction mode to use while creating Data Matrix barcodes. - [Description("A compaction mode to use while creating Data Matrix barcodes.")] - public DataMatrixCompactionMode DataMatrixCompactionMode - { - get => _dataMatrixCompactionMode; - set - { - _dataMatrixCompactionMode = value; - FireChanged(this); - } - } - private DataMatrixCompactionMode _dataMatrixCompactionMode = DataMatrixCompactionMode.Auto; - - /// - /// Alternative Reed-Solomon error correction for DataMatrix barcodes of 144x144 size, might be required for compatibility with some hardware barcode scanners. - /// - [Description("Alternative Reed-Solomon error correction for DataMatrix barcodes of 144x144 size, for compatibility with some hardware barcode scanners.")] - public bool DataMatrixAlternativeReedSolomonCorrectionFor144x144Size - { - get => _dataMatrixAlternativeReedSolomonCorrectionFor144x144Size; - set - { - _dataMatrixAlternativeReedSolomonCorrectionFor144x144Size = value; - FireChanged(this); - } - } - private bool _dataMatrixAlternativeReedSolomonCorrectionFor144x144Size = false; - - /// - /// Gets or sets the compaction mode to use while creating Aztec barcodes. - /// - /// The compaction mode to use while creating Aztec barcodes. - [Description("A compaction mode to use while creating Aztec barcodes.")] - public AztecCompactionMode AztecCompactionMode - { - get => _aztecCompactionMode; - set - { - _aztecCompactionMode = value; - FireChanged(this); - } - } - private AztecCompactionMode _aztecCompactionMode = AztecCompactionMode.Auto; - - /// - /// Gets or sets a value indicating whether to draw auto created additional caption when encoding barcodes using ISBN symbology.. - /// - /// true if auto created additional caption for ISBN barcodes should be drawn; otherwise, false. - [Description("A value indicating whether to draw auto created additional caption when encoding barcodes using ISBN symbology.")] - public bool ISBNAutoCaption - { - get => _ISBNAutoCaption; - set - { - _ISBNAutoCaption = value; - FireChanged(this); - } - } - private bool _ISBNAutoCaption = true; - - /// - /// Gets or sets the ISBN caption template (e.g. #-#######-#-#). - /// - /// The ISBN caption template. - [Description("The ISBN caption template.")] - public string ISBNCaptionTemplate - { - get => _ISBNCaptionTemplate; - set - { - _ISBNCaptionTemplate = value; - FireChanged(this); - } - } - private string _ISBNCaptionTemplate = string.Empty; - - /// - /// Gets or sets the error correction level for Aztec Code barcodes. - /// - /// The error correction level for Aztec Code barcodes. - [Description("An error correction level for Aztec Code barcodes.")] - public AztecErrorCorrectionLevel AztecErrorCorrectionLevel - { - get => _aztecErrorCorrectionLevel; - set - { - _aztecErrorCorrectionLevel = value; - FireChanged(this); - } - } - private AztecErrorCorrectionLevel _aztecErrorCorrectionLevel = AztecErrorCorrectionLevel.Auto; - - /// - /// Gets or sets the minimum size for Aztec Code barcodes. - /// - /// The version minimum size for Aztec Code barcodes. - [Description("A minimum size for Aztec Code barcodes. Should be in [0..36] range.")] - public int AztecSymbolSize - { - get => _aztecMinimumSymbolSize; - set - { - if (value < 0 || value > 36) - throw new BarcodeException("Minimum version should be in [0..36] range"); - - _aztecMinimumSymbolSize = value; - FireChanged(this); - } - } - private int _aztecMinimumSymbolSize; - - /// - /// Gets or sets the number of segments in line for GS1 DataBar Expanded Stacked symbology. - /// - /// The number of segments in line. - [Description("The number of segments in line for GS1 DataBar Expanded Stacked symbology.")] - public int GS1ExpandedStackedSegmentsNumber - { - get => _segmentsNumber; - set - { - if (value % 2 != 0) - value--; - if (value > 20) - value = 20; - else if (value < 2) - value = 2; - _segmentsNumber = value; - FireChanged(this); - } - } - private int _segmentsNumber = 4; - - /// - /// Gets or sets the MaxiCode mode. - /// - /// The MaxiCode mode. - [Description("The MaxiCode mode.")] - public byte MaxiCodeMode - { - // mode 2, 3, 4, 5 or 6 - get => _maxiCodeMode; - set - { - _maxiCodeMode = value; - FireChanged(this); - } - } - internal byte _maxiCodeMode = 4; - - /// - /// Gets or sets a value indicating whether MaxiCode enables the use of a custom width. - /// By default value is true and MaxiCode symbology size is determined as 30*NarrowBarWidth. - /// Set value to false if you want to use standard MaxiCode symbology size (calculated - /// as approximately one inch (2.54 cm) for the current resolution) - /// - /// - /// true if MaxiCode enable custom width; otherwise, false. - /// - public bool MaxiCodeEnableCustomWidth - { - get => _maxiCodeUsingBarWidth; - set - { - _maxiCodeUsingBarWidth = value; - FireChanged(this); - } - } - private bool _maxiCodeUsingBarWidth = true; - - /// - /// Gets or sets the algorithm to use for MSI symbology checksum calculation. - /// - /// The algorithm to use for MSI symbology checksum calculation. - [Description("The algorithm to use for MSI symbology checksum calculation.")] - public MSIChecksumAlgorithm MSIChecksumAlgorithm - { - get => _msiChecksumAlgorithm; - set - { - _msiChecksumAlgorithm = value; - FireChanged(this); - } - } - private MSIChecksumAlgorithm _msiChecksumAlgorithm = MSIChecksumAlgorithm.Modulo10; - - /// - /// Gets or sets a value indicating whether ITF-14 symbology has only horizontal bearer bar or surrounding bars. - /// - /// - /// true if only horizontal bearer bar; otherwise, false. - /// - [Description("A value indicating whether ITF-14 symbology has only horizontal bearer bar or surrounding bars.")] - public bool OnlyHorizontalBearerBar - { - get => _onlyHorizontalBearerBar; - set - { - _onlyHorizontalBearerBar = value; - FireChanged(this); - } - } - private bool _onlyHorizontalBearerBar = false; - - /// - /// Gets or sets the type of PZN symbology. - /// - /// The type of PZN symbology. - [Description("The type of PZN symbology.")] - public PZNType PZNType - { - get => _pznType; - set - { - _pznType = value; - FireChanged(this); - } - } - private PZNType _pznType = PZNType.PZN8; - - /// - /// Two-track variant of PharmaCode - /// - public bool PharmaCodeTwoTrack - { - get => _pharmaCodeTwoTrack; - set - { - _pharmaCodeTwoTrack = value; - FireChanged(this); - } - } - private bool _pharmaCodeTwoTrack; - - /// - /// Add colored Supplementary bar in PharmaCode - /// - public bool PharmaCodeSupplementaryCode - { - get => _pharmaCodeSupplementaryCode; - set - { - _pharmaCodeSupplementaryCode = value; - FireChanged(this); - } - } - private bool _pharmaCodeSupplementaryCode; - - /// - /// Color of Supplementary bar in PharmaCode - /// - public SKColor PharmaCodeSupplementaryBarColor - { - get => _pharmaСodeSupplementaryBarColor; - set - { - _pharmaСodeSupplementaryBarColor = value; - FireChanged(this); - } - } - private SKColor _pharmaСodeSupplementaryBarColor = SKColors.Magenta; - - /// - /// Miniature variant of PharmaCode - /// - public bool PharmaCodeMiniature - { - get => _pharmaCodeMiniature; - set - { - _pharmaCodeMiniature = value; - FireChanged(this); - } - } - private bool _pharmaCodeMiniature; - - /// - /// Gets or sets whether to draw the quite zone indicator on EAN-2 and EAN-5 supplementary barcodes. Default is false. - /// - public bool EANDrawQuietZoneIndicator - { - get => _eanDrawQuietZoneIndicator; - set - { - _eanDrawQuietZoneIndicator = value; - FireChanged(this); - } - } - private bool _eanDrawQuietZoneIndicator; - -#endif - } -} diff --git a/MuPDF.NET/Barcode/Encoders/SymbologyType.cs b/MuPDF.NET/Barcode/Encoders/SymbologyType.cs deleted file mode 100644 index f08da0ff..00000000 --- a/MuPDF.NET/Barcode/Encoders/SymbologyType.cs +++ /dev/null @@ -1,457 +0,0 @@ - -namespace BarcodeWriter.Core -{ - /// - /// Describes all supported barcode symbologies (types). - /// -#if QRCODESDK - internal enum SymbologyType -#else - public enum SymbologyType -#endif - { - Unknown = -1, - - /// - /// (0) Code 128 barcode (Also known as Code-128). It is a very effective, - /// high-density symbology which permits the encoding of alphanumeric - /// (subject to alphabet selection) data. Code 128 is a very dense - /// code, used extensively worldwide. - /// - Code128 = 0, - - /// - /// (1) Code 39 barcode (Also known as USD-3, Code 3 of 9, LOGMARS and - /// in extended mode also know as Code39Extended, Code 39 Full ASCII mode). - /// Code 39 symbology allows all ASCII symbols to be encoded in - /// extended mode or symbols from this string - /// "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%" in - /// standard mode. This symbology used for example by U.S. Government - /// and military, required for DoD applications. - /// - Code39 = 1, - - /// - /// (2) Postnet barcode. This symbology usually gets printed by U.S. Post - /// Office on envelopes. Postnet symbology allows only numeric - /// values to be encoded. The bar code itself can encode either a - /// standard 5-digit Zip Code, a Zip+4 code, or a full 11-point - /// delivery point code. - /// - Postnet = 2, - - /// - /// (3) UPC-A barcode (Also known as UPCA). Used with consumer products - /// in U.S. UPC-A symbology allows only numeric values to be encoded. - /// - UPCA = 3, - - /// - /// (4) EAN-8 barcode (GTIN-8). This symbology is a short version of EAN-13 that - /// is intended to be used on packaging which would be otherwise - /// too small to use one of the other versions. Used with consumer - /// products internationally. EAN-8 symbology allows only numeric - /// values to be encoded. - /// - EAN8 = 4, - - /// - /// (5) ISBN Number encoded as EAN-13 barcode. - /// - ISBN = 5, - - /// - /// (6) Codabar barcode (Also known as Ames Code, USD-4, NW-7, - /// Code 2 of 7). Codabar symbology allows only symbols from this - /// string '0123456789-$:/.+' to be encoded. This symbology used - /// for example in libraries and blood banks. - /// - Codabar = 6, - - /// - /// (7) Interleaved 2 of 5 barcode (Also know as Code 2 of 5 Interleaved). - /// Interleaved 2 of 5 symbology allows only numeric values to be - /// encoded. This symbology is used primarily in the distribution - /// and warehouse industry. - /// - I2of5 = 7, - - /// - /// (8) Code 93 barcode (Also known as USS-93, Code93 and in extended - /// mode also known as Code93Extended, Code 93 Full ASCII mode). - /// Code 93 symbology allows all ASCII symbols to be encoded in - /// extended mode or symbols from this string - /// "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%" in - /// standard mode. This symbology was designed to - /// complement and improve upon Code 39 symbology. Code 93 - /// produces denser code than that of Code 39. - /// - Code93 = 8, - - /// - /// (9) EAN-13 barcode (GTIN-13). Used with consumer - /// products internationally. EAN-13 symbology allows only numeric - /// values to be encoded. - /// - EAN13 = 9, - - /// - /// (10) JAN-13 barcode (Also known as JAN codes). This symbology is - /// mostly the same as EAN-13 symbology, but used in Japan. JAN - /// stands for Japanese Numbering Authority. First two digits of - /// JAN-13 symbology are always "49". - /// - JAN13 = 10, - - /// - /// (11) Bookland barcode. This symbology is mostly the same - /// as EAN-13 symbology, but used exclusively with books. First three - /// digits of Bookland symbology are always "978" the rest of code - /// is a ISBN number without embedded hyphens and ISBN check digit. - /// - Bookland = 11, - - /// - /// (12) UPC-E barcode (Also known as UPCE). This symbology is - /// zero-suppression version of UPC-A. It is intended to be used on - /// packaging which would be otherwise too small to use one of the - /// other versions. The code is smaller because it drops out zeros - /// which would otherwise occur in a symbol. Used with consumer - /// products in U.S. UPC-E symbology allows only numeric values - /// to be encoded. - /// - UPCE = 12, - - /// - /// (13) PDF417 symbology. This symbology is heavily used in the parcel - /// industry. The PDF417 symbology can encode a vast amount of data - /// into a small space. This symbology allows a maximum data size of - /// 1850 text characters, or 2710 digits. - /// - PDF417 = 13, - - /// - /// (14) PDF417 Truncated symbology. This symbology is a truncted (right - /// column is missing) or compact version of PDF417 symbology. - /// - PDF417Truncated = 14, - - /// - /// (15) Data Matrix symbology. The most popular application for Data - /// Matrix is marking small items. The Data Matrix can encode text - /// and raw data. Usual data size is from a few bytes up to 2 kilobytes. - /// - DataMatrix = 15, - - /// - /// (16) QR Code symbology. QR Code initially was used for tracking parts in vehicle - /// manufacturing, but now QR Codes used in a much broader context, - /// including both commercial tracking applications and convenience-oriented - /// applications aimed at mobile phone users (known as mobile tagging). - /// - QRCode = 16, - - /// - /// (17) Aztec Code symbology. An Aztec code barcode is used by Deutsche Bahn, - /// Trenitalia and by Swiss Federal Railways for tickets sold online and - /// printed out by customers. The Aztec Code has been selected by the airline - /// industry (IATA's BCBP standard) for the electronic boarding passes. - /// - Aztec = 17, - - /// - /// (18) PLANET (The Postal Alpha Numeric Encoding Technique) barcode is used by - /// the United States Postal Service to identify and track pieces of - /// mail during delivery - the Post Office's "CONFIRM" services. - /// - Planet = 18, - - /// - /// (19) EAN128 symbology (Also known as EAN-128, EAN-14, Shipping - /// Container Code, UCC-14, DUN-14 (Distribution Unit Number), - /// SSC-14, GS1-128, UCC-128, UCC/EAN-128. This symbology was developed - /// to provide a worldwide format and standard for exchanging common - /// data between companies. - /// - EAN128 = 19, - - /// - /// (20) GS1-128 symbology (new name for EAN128 symbology) - /// - GS1_128 = 20, - - /// - /// (21) USPS Sack Label symbology (Also known as USPS 25 Sack Label). This - /// is in fact Interleaved 2 of 5 symbology with exactly 8 digits - /// encoded: 5-digit Zip Code (the sack destination) and a 3-digit - /// content identifier number(CIN). - /// - USPSSackLabel = 21, - - /// - /// (22) USPS Tray Label symbology (Also known as USPS 25 Tray Label). This - /// is in fact Interleaved 2 of 5 symbology with exactly 10 digits - /// encoded: 5-digit Zip Code (the tray destination) and a 3-digit - /// content identifier number(CIN), and a 2-digit USPS processing code. - /// - USPSTrayLabel = 22, - - /// - /// (23) Deutsche Post Identcode (Also known as Deutsche Post AG IdentCode, - /// German Postal 2 of 5 IdentCode, Deutsche Frachtpost IdentCode, - /// IdentCode, Deutsche Post AG (DHL). This symbology is used by - /// German Post (Deutsche Post AG) (Deutsche Frachtpost). The barcode - /// contains a tracking number providing an identification of the - /// customer (sender) and the mail piece. The value to encode length - /// is fixed to 11 digits. The value to encode must have the following - /// structure: 2 digits for ID of primary distribution center, 3 digits - /// for Customer ID, and 6 digits for Mailing number. - /// - DeutschePostIdentcode = 23, - - /// - /// (24) Deutsche Post Leitcode (Also known as German Postal 2 of 5 LeitCode, - /// LeitCode, CodeLeitcode, Deutsche Post AG (DHL)). This symbology is used by - /// German Post (Deutsche Post AG) (Deutsche Frachtpost). The barcode - /// gives an indication of the destination. The value to encode length - /// is fixed to 13 digits. The value to encode must have the following - /// structure: 5 digits for Postal Code (Postleitzahl, PLZ), 3 digits - /// for Street ID/number, 3 digits for House number, and 2 digits for - /// Product code. - /// - DeutschePostLeitcode = 24, - - /// - /// (25) Numly barcode (Also known as ESN, Electronic Serial Number). This - /// barcode is a unique identifier that allows an author or publisher to assign - /// to content and track licensing of each id assignment. Numly Numbers - /// are useful if you wish to identify each electronic distributed copy - /// of any form of electronic media. Numly Numbers can also act a - /// third-party content submission time stamps to aid in copyright - /// proving instances and emails. The length of value to encode is - /// fixed to 19 digits. - /// - Numly = 25, - - /// - /// (26) PZN barcode (Also known as Pharma-Zentral-Nummer, Pharmazentralnummer, - /// Code PZN, CodePZN, Pharma Zentral Nummer). This symbology is - /// used for distribution of pharmaceutical / health care products - /// in Germany. The specification of this application is maintained - /// at Informationsstelle für Arzneispezialitäten GmbH (IFA). The - /// length of value to encode is 6 digits. - /// - PZN = 26, - - /// - /// (27) Optical Product Code (Also known as OPC, Vision Council of America - /// OPC, VCA BarCode, VCA OPC) symbology. This symbology is used for - /// marking retail optical products. The OPC is a 9-digit, numeric - /// code that identifies the product and the manufacturer. The - /// structure of the OPC code is the following: 5 digits for - /// Manufacturer Identification Number assigned by the Optical - /// Product Code Council, Inc., 4 digits for Item Identification Number - /// assigned and controlled by the optical manufacturer. - /// - OpticalProduct = 27, - - /// - /// (28) Swiss Post Parcel (Also known as SwissPost Parcel Barcode, - /// Switzerland Post Parcel Barcode, Swiss PostParcel Barcode) symbology. - /// This symbology is used by Swiss Post. It identifies each parcel - /// and serves as a means of verifying mailing and delivery and - /// checking the service offering. All parcels must have a unique - /// barcode. The barcode is the requirement for automated processing. - /// The barcode serves as a means of identifying the item. The - /// structure of the Swiss Post Parcel barcode is 18 numeric digits: - /// 2 digits for Swiss Post reference, 8 digits for Franking license - /// number, and 8 digits for Item number. - /// - SwissPostParcel = 28, - - /// - /// (29) Royal Mail barcode (Also known as RMS4CC, RoyalMail4SCC, - /// Royal Mail 4-State, British Royal Mail 4-State Customer Code, - /// 4-State). This symbology was created for automated mail sorting. - /// It normally codes the postcode and the house or mailbox number - /// in a machine readable format. The contents of the code may vary - /// in different countries. This symbology encodes alpha-numeric - /// characters (0-9, A-Z). - /// - RoyalMail = 29, - - /// - /// (30) Dutch KIX barcode (Also known as Royal TNT Post Kix, Dutch - /// KIX 4-State Barcode, Kix Barcode, TPG KIX, Klantenindex Barcode, - /// TPGPOST KIX). This symbology is used by Royal Dutch TPG Post - /// (Netherlands) for Postal code and automatic mail sorting. It - /// provides information about the address of the receiver. This - /// symbology encodes alpha-numeric characters (0-9, A-Z). - /// - DutchKix = 30, - - /// - /// (31) Singapore 4-State Postal Code barcode (Also known as Singapore - /// 4-State Postal, SingPost 4-State, SingPost Barcode, Singapore - /// 4-State Code). This Symbology is used by Singapore Post - /// (SingPost) for Postal code and automatic mail sorting. Such - /// barcode provides information about the address of the receiver. - /// This symbology encodes alpha-numeric characters (0-9, A-Z). - /// - SingaporePostalCode = 31, - - /// - /// (32) The EAN-2 (Also known as EAN/2 and EAN 2) is a supplement to - /// the EAN-13 and UPC-A barcodes. It is often used on magazines - /// and periodicals to indicate an issue number. - /// - EAN2 = 32, - - /// - /// (33) The EAN-5 (Also known as EAN/5 and EAN 5) is a supplement to - /// EAN-13 and UPC-A barcodes. It is often used to give a - /// suggestion for the price of the book. - /// - EAN5 = 33, - - /// - /// (34) The EAN14 symbology is used for traded goods. - /// - EAN14 = 34, - - /// - /// (35) The Macro version of PDF417 Symbology. - /// - MacroPDF417 = 35, - - /// - /// (36) The Micro version of PDF417 Symbology. - /// - MicroPDF417 = 36, - - /// - /// (37) GS1 DataMatrix is a 2D (two-dimensional) barcode that holds - /// large amounts of data in a relatively small space. These barcodes - /// are used primarily in aerospace, pharmaceuticals, medical device - /// manufacturing, and by the U.S. Department of Defense to add - /// visibility to the value chain. GS1 DataMatrix can be used for parts - /// that need to be tracked in the manufacturing process because the - /// barcode allows users to encode a variety of information related - /// to the product, such as date or lot number. They are not intended - /// to be used on items that pass through retail point-of-sale (POS). - /// - GS1_DataMatrix = 37, - - /// - /// (38) The Telepen symbology. This symbology is used in many countries and - /// very widely in the UK. Most Universities and other academic libraries use - /// Telepen, as do many public libraries. Other users include the motor - /// industry, Ministry of Defence and innumerable well-known organisations - /// for many different applications. - /// - Telepen = 38, - - /// - /// (39) The Intelligent Mail Barcode symbology. - /// This symbology is used in the USPS mailstream. It is also known as - /// the USPS OneCode Solution or USPS 4-State Customer Barcode (abbreviated - /// 4CB, 4-CB, or USPS4CB) - /// - IntelligentMail = 39, - - /// - /// (40) The GS1 DataBar Omnidirectional symbology. - /// - GS1_DataBar_Omnidirectional = 40, - - /// - /// (41) The GS1 DataBar Truncated symbology. - /// - GS1_DataBar_Truncated = 41, - - /// - /// (42) The GS1 DataBar Stacked symbology. - /// - GS1_DataBar_Stacked = 42, - - /// - /// (43) The GS1 DataBar Stacked Omnidirectional symbology. - /// - GS1_DataBar_Stacked_Omnidirectional = 43, - - /// - /// (44) The GS1 DataBar Limited symbology. - /// - GS1_DataBar_Limited = 44, - - /// - /// (45) The GS1 DataBar Expanded symbology. - /// - GS1_DataBar_Expanded = 45, - - /// - /// (46) The GS1 DataBar Expanded Stacked symbology. - /// - GS1_DataBar_Expanded_Stacked = 46, - - /// - /// (47) The MaxiCode symbology. - /// - MaxiCode = 47, - - /// - /// (48) The Plessey Code symbology. - /// This symbology is used primarily in libraries and for retail grocery shelf marking. - /// - Plessey = 48, - - /// - /// (49) The MSI (also known as Modified Plessey) symbology. - /// This symbology is used primarily for inventory control, - /// marking storage containers and shelves in warehouse environments. - /// - MSI = 49, - - /// - /// (50) The ITF-14 (GTIN-14, UCC-14) symbology. - /// ITF-14 is the GS1 implementation of an Interleaved 2 of 5 bar code to encode - /// a Global Trade Item Number. ITF-14 symbols are generally used on a packaging - /// step of products. The ITF-14 always encodes 14 digits. - /// - ITF14 = 50, - - /// - /// (51) The GTIN-12 (12-digit UPC-A) symbology. - /// GTIN-12 is a 12-digit number used primarily in North America. - /// - GTIN12 = 51, - - /// - /// (52) The GTIN-8 (EAN-8, UCC-8): this is an 8-digit number used predominately outside of North America. - /// - GTIN8 = 52, - - /// - /// (53) The GTIN-13 (EAN-13, UCC-13): this is an 13-digit number used predominately outside of North America. - /// - GTIN13 = 53, - - /// - /// (54) The GTIN-14 (ITF-14, EAN-14, UCC-14) symbology. - /// GTIN-14 is the GS1 implementation of an Interleaved 2 of 5 bar code to encode - /// a Global Trade Item Number. GTIN-14 symbols are generally used on a packaging - /// step of products. The GTIN-14 always encodes 14 digits. - /// - GTIN14 = 54, - - /// - /// (55) GS1 QRCode - /// - GS1_QRCode = 55, - - /// - /// (56) Pharma Code - /// - PharmaCode = 56, - } -} diff --git a/MuPDF.NET/Barcode/Encoders/UnitOfMeasure.cs b/MuPDF.NET/Barcode/Encoders/UnitOfMeasure.cs deleted file mode 100644 index 33af6c96..00000000 --- a/MuPDF.NET/Barcode/Encoders/UnitOfMeasure.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Text; - -namespace BarcodeWriter.Core -{ - /// - /// Specifies the unit of measure for the given data. - /// -#if QRCODESDK - internal enum UnitOfMeasure -#else - public enum UnitOfMeasure -#endif - { - /// - /// (0) Specifies a device pixel as the unit of measure. - /// - Pixel = 0, - - /// - /// (1) Specifies a printer's point (1/72 inch) as the unit of measure. It is also used in PDF documents. - /// - Point = 1, - - /// - /// (2) Specifies the inch as the unit of measure. - /// - Inch = 2, - - /// - /// (3) Specifies the document unit (1/300 inch) as the unit of measure. - /// - Document = 3, - - /// - /// (4) Specifies the millimeter as the unit of measure. - /// - Millimeter = 4, - - /// - /// (5) Specifies the centimeter as the unit of measure. - /// - Centimeter = 5, - - /// - /// (6) Specifies the twip unit (1/20 inch) as the unit of measure. - /// - Twip = 6, - } -} diff --git a/MuPDF.NET/Barcode/Encoders/ValueComposer.cs b/MuPDF.NET/Barcode/Encoders/ValueComposer.cs deleted file mode 100644 index 2b960522..00000000 --- a/MuPDF.NET/Barcode/Encoders/ValueComposer.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace BarcodeWriter.Core -{ - /// - /// Helps to create barcode values for vCard, e-mail, and SMS messages that should be correctly handled by mobile barcode readers. - /// - public static class ValueComposer - { - /// - /// Creates value that's correctly handled by barcode readers as SMS message. - /// - /// Phone number. - /// Message text. - /// String that contains composed SMS message. - public static string ComposeSMS(string phoneNumber, string messageText) - { - return $"smsto:{phoneNumber}:{messageText}"; - } - - /// - /// Creates value that's correctly handled by barcode readers as e-mail message. - /// - /// E-mail address. - /// Message subject. - /// Message body. - /// String that contains composed e-mail message. - public static string ComposeEmail(string emailAddress, string messageSubject, string messageBody) - { - return $@"MATMSG:TO:{emailAddress};SUB:{messageSubject}BODY:{messageBody};;"; - } - - /// - /// Creates value that's correctly handled by barcode readers as vCard message. - /// - /// First name. - /// Last name. - /// Phone number - /// Fax number - /// E-mail address. - /// Company name - /// Job. - /// Street. - /// City. - /// State. - /// ZIP (postal) code. - /// Country. - /// - public static string ComposeVCard(string firstName, string lastName, string phone, string fax, string email, string company, - string job, string street, string city, string state, string zipCode, string country) - { - return $@"BEGIN:VCARD -VERSION:2.1 -N:{lastName};{firstName};; -FN:{firstName} {lastName} -ORG:{company} -TITLE:{job} -TEL;WORK;VOICE:{phone} -TEL;FAX;VOICE:{fax} -ADR;WORK;PREF:;;{street};{city};{state};{zipCode};{country} -LABEL;WORK;PREF;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-8 -EMAIL:{email} -END:VCARD -"; - } - } -} \ No newline at end of file diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/CleanFaxData.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/CleanFaxData.cs deleted file mode 100644 index 895162ea..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/CleanFaxData.cs +++ /dev/null @@ -1,42 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Regenerated line info.
- /// Possible values for .CLEANFAXDATA tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum CleanFaxData - { - /// - /// No errors detected. - /// - CLEAN = 0, - - /// - /// Receiver regenerated lines. - /// - REGENERATED = 1, - - /// - /// Uncorrected errors exist. - /// - UNCLEAN = 2, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/ColorResponseUnit.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/ColorResponseUnit.cs deleted file mode 100644 index 2924141a..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/ColorResponseUnit.cs +++ /dev/null @@ -1,52 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Color curve accuracy.
- /// Possible values for .COLORRESPONSEUNIT tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum ColorResponseUnit - { - /// - /// Tenths of a unit. - /// - CRU10S = 1, - - /// - /// Hundredths of a unit. - /// - CRU100S = 2, - - /// - /// Thousandths of a unit. - /// - CRU1000S = 3, - - /// - /// Ten-thousandths of a unit. - /// - CRU10000S = 4, - - /// - /// Hundred-thousandths. - /// - CRU100000S = 5, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Compression.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Compression.cs deleted file mode 100644 index dc961c74..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Compression.cs +++ /dev/null @@ -1,158 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Compression scheme.
- /// Possible values for .COMPRESSION tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum Compression - { - /// - /// Dump mode. - /// - NONE = 1, - - /// - /// CCITT modified Huffman RLE. - /// - CCITTRLE = 2, - - /// - /// CCITT Group 3 fax encoding. - /// - CCITTFAX3 = 3, - - /// - /// CCITT T.4 (TIFF 6 name for CCITT Group 3 fax encoding). - /// - CCITT_T4 = 3, - - /// - /// CCITT Group 4 fax encoding. - /// - CCITTFAX4 = 4, - - /// - /// CCITT T.6 (TIFF 6 name for CCITT Group 4 fax encoding). - /// - CCITT_T6 = 4, - - /// - /// Lempel-Ziv & Welch. - /// - LZW = 5, - - /// - /// Original JPEG / Old-style JPEG (6.0). - /// - OJPEG = 6, - - /// - /// JPEG DCT compression. Introduced post TIFF rev 6.0. - /// - JPEG = 7, - - /// - /// NeXT 2-bit RLE. - /// - NEXT = 32766, - - /// - /// CCITT RLE. - /// - CCITTRLEW = 32771, - - /// - /// Macintosh RLE. - /// - PACKBITS = 32773, - - /// - /// ThunderScan RLE. - /// - THUNDERSCAN = 32809, - - /// - /// IT8 CT w/padding. Reserved for ANSI IT8 TIFF/IT. - /// - IT8CTPAD = 32895, - - /// - /// IT8 Linework RLE. Reserved for ANSI IT8 TIFF/IT. - /// - IT8LW = 32896, - - /// - /// IT8 Monochrome picture. Reserved for ANSI IT8 TIFF/IT. - /// - IT8MP = 32897, - - /// - /// IT8 Binary line art. Reserved for ANSI IT8 TIFF/IT. - /// - IT8BL = 32898, - - /// - /// Pixar companded 10bit LZW. Reserved for Pixar. - /// - PIXARFILM = 32908, - - /// - /// Pixar companded 11bit ZIP. Reserved for Pixar. - /// - PIXARLOG = 32909, - - /// - /// Deflate compression. - /// - DEFLATE = 32946, - - /// - /// Deflate compression, as recognized by Adobe. - /// - ADOBE_DEFLATE = 8, - - /// - /// Kodak DCS encoding. - /// Reserved for Oceana Matrix (dev@oceana.com). - /// - DCS = 32947, - - /// - /// ISO JBIG. - /// - JBIG = 34661, - - /// - /// SGI Log Luminance RLE. - /// - SGILOG = 34676, - - /// - /// SGI Log 24-bit packed. - /// - SGILOG24 = 34677, - - /// - /// Leadtools JPEG2000. - /// - JP2000 = 34712, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/ExtraSample.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/ExtraSample.cs deleted file mode 100644 index d69d1969..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/ExtraSample.cs +++ /dev/null @@ -1,42 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Information about extra samples.
- /// Possible values for .EXTRASAMPLES tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum ExtraSample - { - /// - /// Unspecified data. - /// - UNSPECIFIED = 0, - - /// - /// Associated alpha data. - /// - ASSOCALPHA = 1, - - /// - /// Unassociated alpha data. - /// - UNASSALPHA = 2, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/FaxMode.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/FaxMode.cs deleted file mode 100644 index 3a978adc..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/FaxMode.cs +++ /dev/null @@ -1,57 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Group 3/4 format control.
- /// Possible values for .FAXMODE tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum FaxMode - { - /// - /// Default, include RTC. - /// - CLASSIC = 0x0000, - - /// - /// No RTC at end of data. - /// - NORTC = 0x0001, - - /// - /// No EOL code at end of row. - /// - NOEOL = 0x0002, - - /// - /// Byte align row. - /// - BYTEALIGN = 0x0004, - - /// - /// Word align row. - /// - WORDALIGN = 0x0008, - - /// - /// TIFF Class F. - /// - CLASSF = NORTC, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/FileType.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/FileType.cs deleted file mode 100644 index 25eafb40..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/FileType.cs +++ /dev/null @@ -1,42 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Subfile data descriptor.
- /// Possible values for .SUBFILETYPE tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum FileType - { - /// - /// Reduced resolution version. - /// - REDUCEDIMAGE = 0x1, - - /// - /// One page of many. - /// - PAGE = 0x2, - - /// - /// Transparency mask. - /// - MASK = 0x4 - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/FillOrder.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/FillOrder.cs deleted file mode 100644 index 59747b43..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/FillOrder.cs +++ /dev/null @@ -1,37 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Data order within a byte.
- /// Possible values for .FILLORDER tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum FillOrder - { - /// - /// Most significant -> least. - /// - MSB2LSB = 1, - - /// - /// Least significant -> most. - /// - LSB2MSB = 2, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/GrayResponseUnit.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/GrayResponseUnit.cs deleted file mode 100644 index 056195bc..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/GrayResponseUnit.cs +++ /dev/null @@ -1,52 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Gray scale curve accuracy.
- /// Possible values for .GRAYRESPONSEUNIT tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum GrayResponseUnit - { - /// - /// Tenths of a unit. - /// - GRU10S = 1, - - /// - /// Hundredths of a unit. - /// - GRU100S = 2, - - /// - /// Thousandths of a unit. - /// - GRU1000S = 3, - - /// - /// Ten-thousandths of a unit. - /// - GRU10000S = 4, - - /// - /// Hundred-thousandths. - /// - GRU100000S = 5, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Group3Opt.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Group3Opt.cs deleted file mode 100644 index 8d06c52c..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Group3Opt.cs +++ /dev/null @@ -1,48 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Options for CCITT Group 3/4 fax encoding.
- /// Possible values for .GROUP3OPTIONS / TiffTag.T4OPTIONS and - /// TiffTag.GROUP4OPTIONS / TiffTag.T6OPTIONS tags. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum Group3Opt - { - /// - /// Unknown (uninitialized). - /// - UNKNOWN = -1, - - /// - /// 2-dimensional coding. - /// - ENCODING2D = 0x1, - - /// - /// Data not compressed. - /// - UNCOMPRESSED = 0x2, - - /// - /// Fill to byte boundary. - /// - FILLBITS = 0x4, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/InkSet.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/InkSet.cs deleted file mode 100644 index 21572fff..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/InkSet.cs +++ /dev/null @@ -1,37 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Inks in separated image.
- /// Possible values for .INKSET tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum InkSet - { - /// - /// Cyan-magenta-yellow-black color. - /// - CMYK = 1, - - /// - /// Multi-ink or hi-fi color. - /// - MULTIINK = 2, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/JpegColorMode.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/JpegColorMode.cs deleted file mode 100644 index cd1a9c7e..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/JpegColorMode.cs +++ /dev/null @@ -1,37 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Auto RGB<=>YCbCr convert.
- /// Possible values for .JPEGCOLORMODE tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum JpegColorMode - { - /// - /// No conversion (default). - /// - RAW = 0x0000, - - /// - /// Do auto conversion. - /// - RGB = 0x0001, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/JpegProc.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/JpegProc.cs deleted file mode 100644 index dd5860ff..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/JpegProc.cs +++ /dev/null @@ -1,37 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// JPEG processing algorithm.
- /// Possible values for .JPEGPROC tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum JpegProc - { - /// - /// Baseline sequential. - /// - BASELINE = 1, - - /// - /// Huffman coded lossless. - /// - LOSSLESS = 14, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/JpegTablesMode.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/JpegTablesMode.cs deleted file mode 100644 index bc483919..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/JpegTablesMode.cs +++ /dev/null @@ -1,42 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Jpeg Tables Mode.
- /// Possible values for .JPEGTABLESMODE tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum JpegTablesMode - { - /// - /// None. - /// - NONE = 0, - - /// - /// Include quantization tables. - /// - QUANT = 0x0001, - - /// - /// Include Huffman tables. - /// - HUFF = 0x0002, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/OFileType.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/OFileType.cs deleted file mode 100644 index efa616fa..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/OFileType.cs +++ /dev/null @@ -1,42 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Kind of data in subfile.
- /// Possible values for .OSUBFILETYPE tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum OFileType - { - /// - /// Full resolution image data. - /// - IMAGE = 1, - - /// - /// Reduced size image data. - /// - REDUCEDIMAGE = 2, - - /// - /// One page of many. - /// - PAGE = 3 - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Orientation.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Orientation.cs deleted file mode 100644 index b3e39878..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Orientation.cs +++ /dev/null @@ -1,67 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Image orientation.
- /// Possible values for .ORIENTATION tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum Orientation - { - /// - /// Row 0 top, Column 0 lhs. - /// - TOPLEFT = 1, - - /// - /// Row 0 top, Column 0 rhs. - /// - TOPRIGHT = 2, - - /// - /// Row 0 bottom, Column 0 rhs. - /// - BOTRIGHT = 3, - - /// - /// Row 0 bottom, Column 0 lhs. - /// - BOTLEFT = 4, - - /// - /// Row 0 lhs, Column 0 top. - /// - LEFTTOP = 5, - - /// - /// Row 0 rhs, Column 0 top. - /// - RIGHTTOP = 6, - - /// - /// Row 0 rhs, Column 0 bottom. - /// - RIGHTBOT = 7, - - /// - /// Row 0 lhs, Column 0 bottom. - /// - LEFTBOT = 8, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Photometric.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Photometric.cs deleted file mode 100644 index 8820a96a..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Photometric.cs +++ /dev/null @@ -1,87 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Photometric interpretation.
- /// Possible values for .PHOTOMETRIC tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum Photometric - { - /// - /// Min value is white. - /// - MINISWHITE = 0, - - /// - /// Min value is black. - /// - MINISBLACK = 1, - - /// - /// RGB color model. - /// - RGB = 2, - - /// - /// Color map indexed. - /// - PALETTE = 3, - - /// - /// [obsoleted by TIFF rev. 6.0] Holdout mask. - /// - MASK = 4, - - /// - /// Color separations. - /// - SEPARATED = 5, - - /// - /// CCIR 601. - /// - YCBCR = 6, - - /// - /// 1976 CIE L*a*b*. - /// - CIELAB = 8, - - /// - /// ICC L*a*b*. Introduced post TIFF rev 6.0 by Adobe TIFF Technote 4. - /// - ICCLAB = 9, - - /// - /// ITU L*a*b*. - /// - ITULAB = 10, - - /// - /// CIE Log2(L). - /// - LOGL = 32844, - - /// - /// CIE Log2(L) (u',v'). - /// - LOGLUV = 32845, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/PlanarConfig.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/PlanarConfig.cs deleted file mode 100644 index a833f53e..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/PlanarConfig.cs +++ /dev/null @@ -1,42 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Storage organization.
- /// Possible values for .PLANARCONFIG tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum PlanarConfig - { - /// - /// Unknown (uninitialized). - /// - UNKNOWN = 0, - - /// - /// Single image plane. - /// - CONTIG = 1, - - /// - /// Separate planes of data. - /// - SEPARATE = 2 - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Predictor.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Predictor.cs deleted file mode 100644 index 0a1723ef..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Predictor.cs +++ /dev/null @@ -1,42 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Prediction scheme w/ LZW.
- /// Possible values for .PREDICTOR tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum Predictor - { - /// - /// No prediction scheme used. - /// - NONE = 1, - - /// - /// Horizontal differencing. - /// - HORIZONTAL = 2, - - /// - /// Floating point predictor. - /// - FLOATINGPOINT = 3, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/ResUnit.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/ResUnit.cs deleted file mode 100644 index d650611b..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/ResUnit.cs +++ /dev/null @@ -1,42 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Units of resolutions.
- /// Possible values for .RESOLUTIONUNIT tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum ResUnit - { - /// - /// No meaningful units. - /// - NONE = 1, - - /// - /// English. - /// - INCH = 2, - - /// - /// Metric. - /// - CENTIMETER = 3, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/SampleFormat.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/SampleFormat.cs deleted file mode 100644 index 38d3e5fa..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/SampleFormat.cs +++ /dev/null @@ -1,57 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Data sample format.
- /// Possible values for .SAMPLEFORMAT tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum SampleFormat - { - /// - /// Unsigned integer data - /// - UINT = 1, - - /// - /// Signed integer data - /// - INT = 2, - - /// - /// IEEE floating point data - /// - IEEEFP = 3, - - /// - /// Untyped data - /// - VOID = 4, - - /// - /// Complex signed int - /// - COMPLEXINT = 5, - - /// - /// Complex ieee floating - /// - COMPLEXIEEEFP = 6, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Threshold.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Threshold.cs deleted file mode 100644 index 9ab331b7..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/Threshold.cs +++ /dev/null @@ -1,42 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Thresholding used on data.
- /// Possible values for .THRESHHOLDING tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum Threshold - { - /// - /// B&W art scan. - /// - BILEVEL = 1, - - /// - /// Dithered scan. - /// - HALFTONE = 2, - - /// - /// Usually Floyd-Steinberg. - /// - ERRORDIFFUSE = 3, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/TiffPrintFlags.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/TiffPrintFlags.cs deleted file mode 100644 index 52046d00..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/TiffPrintFlags.cs +++ /dev/null @@ -1,67 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System; - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Flags that can be passed to - /// method to control printing of data structures that are potentially very large. - /// - /// More than one flag can be used. Bit-or these flags to enable printing - /// multiple items. - [Flags] -#if EXPOSE_LIBTIFF - public -#endif - enum TiffPrintFlags - { - /// - /// no extra info - /// - NONE = 0x0, - - /// - /// strips/tiles info - /// - STRIPS = 0x1, - - /// - /// color/gray response curves - /// - CURVES = 0x2, - - /// - /// colormap - /// - COLORMAP = 0x4, - - /// - /// JPEG Q matrices - /// - JPEGQTABLES = 0x100, - - /// - /// JPEG AC tables - /// - JPEGACTABLES = 0x200, - - /// - /// JPEG DC tables - /// - JPEGDCTABLES = 0x200, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/TiffTag.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/TiffTag.cs deleted file mode 100644 index 2dddd12b..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/TiffTag.cs +++ /dev/null @@ -1,1418 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// TIFF tag definitions. - /// - /// - /// Joris Van Damme maintains - /// - /// TIFF Tag Reference, good source of tag information. It's an overview of known TIFF - /// Tags with properties, short description, and other useful information. - /// -#if EXPOSE_LIBTIFF - public -#endif - enum TiffTag - { - /// - /// Tag placeholder - /// - IGNORE = 0, - - /// - /// Subfile data descriptor. - /// For the list of possible values, see . - /// - SUBFILETYPE = 254, - - /// - /// [obsoleted by TIFF rev. 5.0]
- /// Kind of data in subfile. For the list of possible values, see . - ///
- OSUBFILETYPE = 255, - - /// - /// Image width in pixels. - /// - IMAGEWIDTH = 256, - - /// - /// Image height in pixels. - /// - IMAGELENGTH = 257, - - /// - /// Bits per channel (sample). - /// - BITSPERSAMPLE = 258, - - /// - /// Data compression technique. - /// For the list of possible values, see . - /// - COMPRESSION = 259, - - /// - /// Photometric interpretation. - /// For the list of possible values, see . - /// - PHOTOMETRIC = 262, - - /// - /// [obsoleted by TIFF rev. 5.0]
- /// Thresholding used on data. For the list of possible values, see . - ///
- THRESHHOLDING = 263, - - /// - /// [obsoleted by TIFF rev. 5.0]
- /// Dithering matrix width. - ///
- CELLWIDTH = 264, - - /// - /// [obsoleted by TIFF rev. 5.0]
- /// Dithering matrix height. - ///
- CELLLENGTH = 265, - - /// - /// Data order within a byte. - /// For the list of possible values, see . - /// - FILLORDER = 266, - - /// - /// Name of document which holds for image. - /// - DOCUMENTNAME = 269, - - /// - /// Information about image. - /// - IMAGEDESCRIPTION = 270, - - /// - /// Scanner manufacturer name. - /// - MAKE = 271, - - /// - /// Scanner model name/number. - /// - MODEL = 272, - - /// - /// Offsets to data strips. - /// - STRIPOFFSETS = 273, - - /// - /// [obsoleted by TIFF rev. 5.0]
- /// Image orientation. For the list of possible values, see . - ///
- ORIENTATION = 274, - - /// - /// Samples per pixel. - /// - SAMPLESPERPIXEL = 277, - - /// - /// Rows per strip of data. - /// - ROWSPERSTRIP = 278, - - /// - /// Bytes counts for strips. - /// - STRIPBYTECOUNTS = 279, - - /// - /// [obsoleted by TIFF rev. 5.0]
- /// Minimum sample value. - ///
- MINSAMPLEVALUE = 280, - - /// - /// [obsoleted by TIFF rev. 5.0]
- /// Maximum sample value. - ///
- MAXSAMPLEVALUE = 281, - - /// - /// Pixels/resolution in x. - /// - XRESOLUTION = 282, - - /// - /// Pixels/resolution in y. - /// - YRESOLUTION = 283, - - /// - /// Storage organization. - /// For the list of possible values, see . - /// - PLANARCONFIG = 284, - - /// - /// Page name image is from. - /// - PAGENAME = 285, - - /// - /// X page offset of image lhs. - /// - XPOSITION = 286, - - /// - /// Y page offset of image lhs. - /// - YPOSITION = 287, - - /// - /// [obsoleted by TIFF rev. 5.0]
- /// Byte offset to free block. - ///
- FREEOFFSETS = 288, - - /// - /// [obsoleted by TIFF rev. 5.0]
- /// Sizes of free blocks. - ///
- FREEBYTECOUNTS = 289, - - /// - /// [obsoleted by TIFF rev. 6.0]
- /// Gray scale curve accuracy. - /// For the list of possible values, see . - ///
- GRAYRESPONSEUNIT = 290, - - /// - /// [obsoleted by TIFF rev. 6.0]
- /// Gray scale response curve. - ///
- GRAYRESPONSECURVE = 291, - - /// - /// Options for CCITT Group 3 fax encoding. 32 flag bits. - /// For the list of possible values, see . - /// - GROUP3OPTIONS = 292, - - /// - /// TIFF 6.0 proper name alias for GROUP3OPTIONS. - /// - T4OPTIONS = 292, - - /// - /// Options for CCITT Group 4 fax encoding. 32 flag bits. - /// For the list of possible values, see . - /// - GROUP4OPTIONS = 293, - - /// - /// TIFF 6.0 proper name alias for GROUP4OPTIONS. - /// - T6OPTIONS = 293, - - /// - /// Units of resolutions. - /// For the list of possible values, see . - /// - RESOLUTIONUNIT = 296, - - /// - /// Page numbers of multi-page. - /// - PAGENUMBER = 297, - - /// - /// [obsoleted by TIFF rev. 6.0]
- /// Color curve accuracy. - /// For the list of possible values, see . - ///
- COLORRESPONSEUNIT = 300, - - /// - /// Colorimetry info. - /// - TRANSFERFUNCTION = 301, - - /// - /// Name & release. - /// - SOFTWARE = 305, - - /// - /// Creation date and time. - /// - DATETIME = 306, - - /// - /// Creator of image. - /// - ARTIST = 315, - - /// - /// Machine where created. - /// - HOSTCOMPUTER = 316, - - /// - /// Prediction scheme w/ LZW. - /// For the list of possible values, see . - /// - PREDICTOR = 317, - - /// - /// Image white point. - /// - WHITEPOINT = 318, - - /// - /// Primary chromaticities. - /// - PRIMARYCHROMATICITIES = 319, - - /// - /// RGB map for pallette image. - /// - COLORMAP = 320, - - /// - /// Highlight + shadow info. - /// - HALFTONEHINTS = 321, - - /// - /// Tile width in pixels. - /// - TILEWIDTH = 322, - - /// - /// Tile height in pixels. - /// - TILELENGTH = 323, - - /// - /// Offsets to data tiles. - /// - TILEOFFSETS = 324, - - /// - /// Byte counts for tiles. - /// - TILEBYTECOUNTS = 325, - - /// - /// Lines with wrong pixel count. - /// - BADFAXLINES = 326, - - /// - /// Regenerated line info. - /// For the list of possible values, see . - /// - CLEANFAXDATA = 327, - - /// - /// Max consecutive bad lines. - /// - CONSECUTIVEBADFAXLINES = 328, - - /// - /// Subimage descriptors. - /// - SUBIFD = 330, - - /// - /// Inks in separated image. - /// For the list of possible values, see . - /// - INKSET = 332, - - /// - /// ASCII names of inks. - /// - INKNAMES = 333, - - /// - /// Number of inks. - /// - NUMBEROFINKS = 334, - - /// - /// 0% and 100% dot codes. - /// - DOTRANGE = 336, - - /// - /// Separation target. - /// - TARGETPRINTER = 337, - - /// - /// Information about extra samples. - /// For the list of possible values, see . - /// - EXTRASAMPLES = 338, - - /// - /// Data sample format. - /// For the list of possible values, see . - /// - SAMPLEFORMAT = 339, - - /// - /// Variable MinSampleValue. - /// - SMINSAMPLEVALUE = 340, - - /// - /// Variable MaxSampleValue. - /// - SMAXSAMPLEVALUE = 341, - - /// - /// ClipPath. Introduced post TIFF rev 6.0 by Adobe TIFF technote 2. - /// - CLIPPATH = 343, - - /// - /// XClipPathUnits. Introduced post TIFF rev 6.0 by Adobe TIFF technote 2. - /// - XCLIPPATHUNITS = 344, - - /// - /// YClipPathUnits. Introduced post TIFF rev 6.0 by Adobe TIFF technote 2. - /// - YCLIPPATHUNITS = 345, - - /// - /// Indexed. Introduced post TIFF rev 6.0 by Adobe TIFF Technote 3. - /// - INDEXED = 346, - - /// - /// JPEG table stream. Introduced post TIFF rev 6.0. - /// - JPEGTABLES = 347, - - /// - /// OPI Proxy. Introduced post TIFF rev 6.0 by Adobe TIFF technote. - /// - OPIPROXY = 351, - - /// - /// [obsoleted by Technical Note #2 which specifies a revised JPEG-in-TIFF scheme]
- /// JPEG processing algorithm. - /// For the list of possible values, see . - ///
- JPEGPROC = 512, - - /// - /// [obsoleted by Technical Note #2 which specifies a revised JPEG-in-TIFF scheme]
- /// Pointer to SOI marker. - ///
- JPEGIFOFFSET = 513, - - /// - /// [obsoleted by Technical Note #2 which specifies a revised JPEG-in-TIFF scheme]
- /// JFIF stream length - ///
- JPEGIFBYTECOUNT = 514, - - /// - /// [obsoleted by Technical Note #2 which specifies a revised JPEG-in-TIFF scheme]
- /// Restart interval length. - ///
- JPEGRESTARTINTERVAL = 515, - - /// - /// [obsoleted by Technical Note #2 which specifies a revised JPEG-in-TIFF scheme]
- /// Lossless proc predictor. - ///
- JPEGLOSSLESSPREDICTORS = 517, - - /// - /// [obsoleted by Technical Note #2 which specifies a revised JPEG-in-TIFF scheme]
- /// Lossless point transform. - ///
- JPEGPOINTTRANSFORM = 518, - - /// - /// [obsoleted by Technical Note #2 which specifies a revised JPEG-in-TIFF scheme]
- /// Q matrice offsets. - ///
- JPEGQTABLES = 519, - - /// - /// [obsoleted by Technical Note #2 which specifies a revised JPEG-in-TIFF scheme]
- /// DCT table offsets. - ///
- JPEGDCTABLES = 520, - - /// - /// [obsoleted by Technical Note #2 which specifies a revised JPEG-in-TIFF scheme]
- /// AC coefficient offsets. - ///
- JPEGACTABLES = 521, - - /// - /// RGB -> YCbCr transform. - /// - YCBCRCOEFFICIENTS = 529, - - /// - /// YCbCr subsampling factors. - /// - YCBCRSUBSAMPLING = 530, - - /// - /// Subsample positioning. - /// For the list of possible values, see . - /// - YCBCRPOSITIONING = 531, - - /// - /// Colorimetry info. - /// - REFERENCEBLACKWHITE = 532, - - /// - /// XML packet. Introduced post TIFF rev 6.0 by Adobe XMP Specification, January 2004. - /// - XMLPACKET = 700, - - /// - /// OPI ImageID. Introduced post TIFF rev 6.0 by Adobe TIFF technote. - /// - OPIIMAGEID = 32781, - - /// - /// Image reference points. Private tag registered to Island Graphics. - /// - REFPTS = 32953, - - /// - /// Region-xform tack point. Private tag registered to Island Graphics. - /// - REGIONTACKPOINT = 32954, - - /// - /// Warp quadrilateral. Private tag registered to Island Graphics. - /// - REGIONWARPCORNERS = 32955, - - /// - /// Affine transformation matrix. Private tag registered to Island Graphics. - /// - REGIONAFFINE = 32956, - - /// - /// [obsoleted by TIFF rev. 6.0]
- /// Use EXTRASAMPLE tag. Private tag registered to SGI. - ///
- MATTEING = 32995, - - /// - /// [obsoleted by TIFF rev. 6.0]
- /// Use SAMPLEFORMAT tag. Private tag registered to SGI. - ///
- DATATYPE = 32996, - - /// - /// Z depth of image. Private tag registered to SGI. - /// - IMAGEDEPTH = 32997, - - /// - /// Z depth/data tile. Private tag registered to SGI. - /// - TILEDEPTH = 32998, - - /// - /// Full image size in X. This tag is set when an image has been cropped out of a larger - /// image. It reflect width of the original uncropped image. The XPOSITION tag can be used - /// to determine the position of the smaller image in the larger one. - /// Private tag registered to Pixar. - /// - PIXAR_IMAGEFULLWIDTH = 33300, - - /// - /// Full image size in Y. This tag is set when an image has been cropped out of a larger - /// image. It reflect height of the original uncropped image. The YPOSITION can be used - /// to determine the position of the smaller image in the larger one. - /// Private tag registered to Pixar. - /// - PIXAR_IMAGEFULLLENGTH = 33301, - - /// - /// Texture map format. Used to identify special image modes and data used by Pixar's - /// texture formats. Private tag registered to Pixar. - /// - PIXAR_TEXTUREFORMAT = 33302, /* t */ - - /// - /// S&T wrap modes. Used to identify special image modes and data used by Pixar's - /// texture formats. Private tag registered to Pixar. - /// - PIXAR_WRAPMODES = 33303, - - /// - /// Cotan(fov) for env. maps. Used to identify special image modes and data used by - /// Pixar's texture formats. Private tag registered to Pixar. - /// - PIXAR_FOVCOT = 33304, - - /// - /// Used to identify special image modes and data used by Pixar's texture formats. - /// Private tag registered to Pixar. - /// - PIXAR_MATRIX_WORLDTOSCREEN = 33305, - - /// - /// Used to identify special image modes and data used by Pixar's texture formats. - /// Private tag registered to Pixar. - /// - PIXAR_MATRIX_WORLDTOCAMERA = 33306, - - /// - /// Device serial number. Private tag registered to Eastman Kodak. - /// - WRITERSERIALNUMBER = 33405, - - /// - /// Copyright string. This tag is listed in the TIFF rev. 6.0 w/ unknown ownership. - /// - COPYRIGHT = 33432, - - /// - /// IPTC TAG from RichTIFF specifications. - /// - RICHTIFFIPTC = 33723, - - /// - /// Site name. Reserved for ANSI IT8 TIFF/IT. - /// - IT8SITE = 34016, - - /// - /// Color seq. [RGB, CMYK, etc]. Reserved for ANSI IT8 TIFF/IT. - /// - IT8COLORSEQUENCE = 34017, - - /// - /// DDES Header. Reserved for ANSI IT8 TIFF/IT. - /// - IT8HEADER = 34018, - - /// - /// Raster scanline padding. Reserved for ANSI IT8 TIFF/IT. - /// - IT8RASTERPADDING = 34019, - - /// - /// The number of bits in short run. Reserved for ANSI IT8 TIFF/IT. - /// - IT8BITSPERRUNLENGTH = 34020, - - /// - /// The number of bits in long run. Reserved for ANSI IT8 TIFF/IT. - /// - IT8BITSPEREXTENDEDRUNLENGTH = 34021, - - /// - /// LW colortable. Reserved for ANSI IT8 TIFF/IT. - /// - IT8COLORTABLE = 34022, - - /// - /// BP/BL image color switch. Reserved for ANSI IT8 TIFF/IT. - /// - IT8IMAGECOLORINDICATOR = 34023, - - /// - /// BP/BL bg color switch. Reserved for ANSI IT8 TIFF/IT. - /// - IT8BKGCOLORINDICATOR = 34024, - - /// - /// BP/BL image color value. Reserved for ANSI IT8 TIFF/IT. - /// - IT8IMAGECOLORVALUE = 34025, - - /// - /// BP/BL bg color value. Reserved for ANSI IT8 TIFF/IT. - /// - IT8BKGCOLORVALUE = 34026, - - /// - /// MP pixel intensity value. Reserved for ANSI IT8 TIFF/IT. - /// - IT8PIXELINTENSITYRANGE = 34027, - - /// - /// HC transparency switch. Reserved for ANSI IT8 TIFF/IT. - /// - IT8TRANSPARENCYINDICATOR = 34028, - - /// - /// Color characterization table. Reserved for ANSI IT8 TIFF/IT. - /// - IT8COLORCHARACTERIZATION = 34029, - - /// - /// HC usage indicator. Reserved for ANSI IT8 TIFF/IT. - /// - IT8HCUSAGE = 34030, - - /// - /// Trapping indicator (untrapped = 0, trapped = 1). Reserved for ANSI IT8 TIFF/IT. - /// - IT8TRAPINDICATOR = 34031, - - /// - /// CMYK color equivalents. - /// - IT8CMYKEQUIVALENT = 34032, - - /// - /// Sequence Frame Count. Private tag registered to Texas Instruments. - /// - FRAMECOUNT = 34232, - - /// - /// Private tag registered to Adobe for PhotoShop. - /// - PHOTOSHOP = 34377, - - /// - /// Pointer to EXIF private directory. This tag is documented in EXIF specification. - /// - EXIFIFD = 34665, - - /// - /// ICC profile data. ?? Private tag registered to Adobe. ?? - /// - ICCPROFILE = 34675, - - /// - /// JBIG options. Private tag registered to Pixel Magic. - /// - JBIGOPTIONS = 34750, - - /// - /// Pointer to GPS private directory. This tag is documented in EXIF specification. - /// - GPSIFD = 34853, - - /// - /// Encoded Class 2 ses. params. Private tag registered to SGI. - /// - FAXRECVPARAMS = 34908, - - /// - /// Received SubAddr string. Private tag registered to SGI. - /// - FAXSUBADDRESS = 34909, - - /// - /// Receive time (secs). Private tag registered to SGI. - /// - FAXRECVTIME = 34910, - - /// - /// Encoded fax ses. params, Table 2/T.30. Private tag registered to SGI. - /// - FAXDCS = 34911, - - /// - /// Sample value to Nits. Private tag registered to SGI. - /// - STONITS = 37439, - - /// - /// Private tag registered to FedEx. - /// - FEDEX_EDR = 34929, - - /// - /// Pointer to Interoperability private directory. - /// This tag is documented in EXIF specification. - /// - INTEROPERABILITYIFD = 40965, - - /// - /// DNG version number. Introduced by Adobe DNG specification. - /// - DNGVERSION = 50706, - - /// - /// DNG compatibility version. Introduced by Adobe DNG specification. - /// - DNGBACKWARDVERSION = 50707, - - /// - /// Name for the camera model. Introduced by Adobe DNG specification. - /// - UNIQUECAMERAMODEL = 50708, - - /// - /// Localized camera model name. Introduced by Adobe DNG specification. - /// - LOCALIZEDCAMERAMODEL = 50709, - - /// - /// CFAPattern->LinearRaw space mapping. Introduced by Adobe DNG specification. - /// - CFAPLANECOLOR = 50710, - - /// - /// Spatial layout of the CFA. Introduced by Adobe DNG specification. - /// - CFALAYOUT = 50711, - - /// - /// Lookup table description. Introduced by Adobe DNG specification. - /// - LINEARIZATIONTABLE = 50712, - - /// - /// Repeat pattern size for the BlackLevel tag. Introduced by Adobe DNG specification. - /// - BLACKLEVELREPEATDIM = 50713, - - /// - /// Zero light encoding level. Introduced by Adobe DNG specification. - /// - BLACKLEVEL = 50714, - - /// - /// Zero light encoding level differences (columns). Introduced by Adobe DNG specification. - /// - BLACKLEVELDELTAH = 50715, - - /// - /// Zero light encoding level differences (rows). Introduced by Adobe DNG specification. - /// - BLACKLEVELDELTAV = 50716, - - /// - /// Fully saturated encoding level. Introduced by Adobe DNG specification. - /// - WHITELEVEL = 50717, - - /// - /// Default scale factors. Introduced by Adobe DNG specification. - /// - DEFAULTSCALE = 50718, - - /// - /// Origin of the final image area. Introduced by Adobe DNG specification. - /// - DEFAULTCROPORIGIN = 50719, - - /// - /// Size of the final image area. Introduced by Adobe DNG specification. - /// - DEFAULTCROPSIZE = 50720, - - /// - /// XYZ->reference color space transformation matrix 1. - /// Introduced by Adobe DNG specification. - /// - COLORMATRIX1 = 50721, - - /// - /// XYZ->reference color space transformation matrix 2. - /// Introduced by Adobe DNG specification. - /// - COLORMATRIX2 = 50722, - - /// - /// Calibration matrix 1. Introduced by Adobe DNG specification. - /// - CAMERACALIBRATION1 = 50723, - - /// - /// Calibration matrix 2. Introduced by Adobe DNG specification. - /// - CAMERACALIBRATION2 = 50724, - - /// - /// Dimensionality reduction matrix 1. Introduced by Adobe DNG specification. - /// - REDUCTIONMATRIX1 = 50725, - - /// - /// Dimensionality reduction matrix 2. Introduced by Adobe DNG specification. - /// - REDUCTIONMATRIX2 = 50726, - - /// - /// Gain applied the stored raw values. Introduced by Adobe DNG specification. - /// - ANALOGBALANCE = 50727, - - /// - /// Selected white balance in linear reference space. - /// Introduced by Adobe DNG specification. - /// - ASSHOTNEUTRAL = 50728, - - /// - /// Selected white balance in x-y chromaticity coordinates. - /// Introduced by Adobe DNG specification. - /// - ASSHOTWHITEXY = 50729, - - /// - /// How much to move the zero point. Introduced by Adobe DNG specification. - /// - BASELINEEXPOSURE = 50730, - - /// - /// Relative noise level. Introduced by Adobe DNG specification. - /// - BASELINENOISE = 50731, - - /// - /// Relative amount of sharpening. Introduced by Adobe DNG specification. - /// - BASELINESHARPNESS = 50732, - - /// - /// How closely the values of the green pixels in the blue/green rows - /// track the values of the green pixels in the red/green rows. - /// Introduced by Adobe DNG specification. - /// - BAYERGREENSPLIT = 50733, - - /// - /// Non-linear encoding range. Introduced by Adobe DNG specification. - /// - LINEARRESPONSELIMIT = 50734, - - /// - /// Camera's serial number. Introduced by Adobe DNG specification. - /// - CAMERASERIALNUMBER = 50735, - - /// - /// Information about the lens. - /// - LENSINFO = 50736, - - /// - /// Chroma blur radius. Introduced by Adobe DNG specification. - /// - CHROMABLURRADIUS = 50737, - - /// - /// Relative strength of the camera's anti-alias filter. - /// Introduced by Adobe DNG specification. - /// - ANTIALIASSTRENGTH = 50738, - - /// - /// Used by Adobe Camera Raw. Introduced by Adobe DNG specification. - /// - SHADOWSCALE = 50739, - - /// - /// Manufacturer's private data. Introduced by Adobe DNG specification. - /// - DNGPRIVATEDATA = 50740, - - /// - /// Whether the EXIF MakerNote tag is safe to preserve along with the rest of the EXIF data. - /// Introduced by Adobe DNG specification. - /// - MAKERNOTESAFETY = 50741, - - /// - /// Illuminant 1. Introduced by Adobe DNG specification. - /// - CALIBRATIONILLUMINANT1 = 50778, - - /// - /// Illuminant 2. Introduced by Adobe DNG specification. - /// - CALIBRATIONILLUMINANT2 = 50779, - - /// - /// Best quality multiplier. Introduced by Adobe DNG specification. - /// - BESTQUALITYSCALE = 50780, - - /// - /// Unique identifier for the raw image data. Introduced by Adobe DNG specification. - /// - RAWDATAUNIQUEID = 50781, - - /// - /// File name of the original raw file. Introduced by Adobe DNG specification. - /// - ORIGINALRAWFILENAME = 50827, - - /// - /// Contents of the original raw file. Introduced by Adobe DNG specification. - /// - ORIGINALRAWFILEDATA = 50828, - - /// - /// Active (non-masked) pixels of the sensor. Introduced by Adobe DNG specification. - /// - ACTIVEAREA = 50829, - - /// - /// List of coordinates of fully masked pixels. Introduced by Adobe DNG specification. - /// - MASKEDAREAS = 50830, - - /// - /// Used to map cameras's color space into ICC profile space. - /// Introduced by Adobe DNG specification. - /// - ASSHOTICCPROFILE = 50831, - - /// - /// Used to map cameras's color space into ICC profile space. - /// Introduced by Adobe DNG specification. - /// - ASSHOTPREPROFILEMATRIX = 50832, - - /// - /// Introduced by Adobe DNG specification. - /// - CURRENTICCPROFILE = 50833, - - /// - /// Introduced by Adobe DNG specification. - /// - CURRENTPREPROFILEMATRIX = 50834, - - /// - /// Undefined tag used by Eastman Kodak, hue shift correction data. - /// - DCSHUESHIFTVALUES = 65535, - - /// - /// [pseudo tag. not written to file]
- /// Group 3/4 format control. - /// For the list of possible values, see . - ///
- FAXMODE = 65536, - - /// - /// [pseudo tag. not written to file]
- /// Compression quality level. Quality level is on the IJG 0-100 scale. Default value is 75. - ///
- JPEGQUALITY = 65537, - - /// - /// [pseudo tag. not written to file]
- /// Auto RGB<=>YCbCr convert. - /// For the list of possible values, see . - ///
- JPEGCOLORMODE = 65538, - - /// - /// [pseudo tag. not written to file]
- /// For the list of possible values, see . - /// Default is | . - ///
- JPEGTABLESMODE = 65539, - - /// - /// [pseudo tag. not written to file]
- /// G3/G4 fill function. - ///
- FAXFILLFUNC = 65540, - - /// - /// [pseudo tag. not written to file]
- /// PixarLogCodec I/O data sz. - ///
- PIXARLOGDATAFMT = 65549, - - /// - /// [pseudo tag. not written to file]
- /// Imager mode & filter. - /// Allocated to Oceana Matrix (dev@oceana.com). - ///
- DCSIMAGERTYPE = 65550, - - /// - /// [pseudo tag. not written to file]
- /// Interpolation mode. - /// Allocated to Oceana Matrix (dev@oceana.com). - ///
- DCSINTERPMODE = 65551, - - /// - /// [pseudo tag. not written to file]
- /// Color balance values. - /// Allocated to Oceana Matrix (dev@oceana.com). - ///
- DCSBALANCEARRAY = 65552, - - /// - /// [pseudo tag. not written to file]
- /// Color correction values. - /// Allocated to Oceana Matrix (dev@oceana.com). - ///
- DCSCORRECTMATRIX = 65553, - - /// - /// [pseudo tag. not written to file]
- /// Gamma value. - /// Allocated to Oceana Matrix (dev@oceana.com). - ///
- DCSGAMMA = 65554, - - /// - /// [pseudo tag. not written to file]
- /// Toe & shoulder points. - /// Allocated to Oceana Matrix (dev@oceana.com). - ///
- DCSTOESHOULDERPTS = 65555, - - /// - /// [pseudo tag. not written to file]
- /// Calibration file description. - ///
- DCSCALIBRATIONFD = 65556, - - /// - /// [pseudo tag. not written to file]
- /// Compression quality level. - /// Quality level is on the ZLIB 1-9 scale. Default value is -1. - ///
- ZIPQUALITY = 65557, - - /// - /// [pseudo tag. not written to file]
- /// PixarLog uses same scale. - ///
- PIXARLOGQUALITY = 65558, - - /// - /// [pseudo tag. not written to file]
- /// Area of image to acquire. - /// Allocated to Oceana Matrix (dev@oceana.com). - ///
- DCSCLIPRECTANGLE = 65559, - - /// - /// [pseudo tag. not written to file]
- /// SGILog user data format. - ///
- SGILOGDATAFMT = 65560, - - /// - /// [pseudo tag. not written to file]
- /// SGILog data encoding control. - ///
- SGILOGENCODE = 65561, - - /// - /// Exposure time. - /// - EXIF_EXPOSURETIME = 33434, - - /// - /// F number. - /// - EXIF_FNUMBER = 33437, - - /// - /// Exposure program. - /// - EXIF_EXPOSUREPROGRAM = 34850, - - /// - /// Spectral sensitivity. - /// - EXIF_SPECTRALSENSITIVITY = 34852, - - /// - /// ISO speed rating. - /// - EXIF_ISOSPEEDRATINGS = 34855, - - /// - /// Optoelectric conversion factor. - /// - EXIF_OECF = 34856, - - /// - /// Exif version. - /// - EXIF_EXIFVERSION = 36864, - - /// - /// Date and time of original data generation. - /// - EXIF_DATETIMEORIGINAL = 36867, - - /// - /// Date and time of digital data generation. - /// - EXIF_DATETIMEDIGITIZED = 36868, - - /// - /// Meaning of each component. - /// - EXIF_COMPONENTSCONFIGURATION = 37121, - - /// - /// Image compression mode. - /// - EXIF_COMPRESSEDBITSPERPIXEL = 37122, - - /// - /// Shutter speed. - /// - EXIF_SHUTTERSPEEDVALUE = 37377, - - /// - /// Aperture. - /// - EXIF_APERTUREVALUE = 37378, - - /// - /// Brightness. - /// - EXIF_BRIGHTNESSVALUE = 37379, - - /// - /// Exposure bias. - /// - EXIF_EXPOSUREBIASVALUE = 37380, - - /// - /// Maximum lens aperture. - /// - EXIF_MAXAPERTUREVALUE = 37381, - - /// - /// Subject distance. - /// - EXIF_SUBJECTDISTANCE = 37382, - - /// - /// Metering mode. - /// - EXIF_METERINGMODE = 37383, - - /// - /// Light source. - /// - EXIF_LIGHTSOURCE = 37384, - - /// - /// Flash. - /// - EXIF_FLASH = 37385, - - /// - /// Lens focal length. - /// - EXIF_FOCALLENGTH = 37386, - - /// - /// Subject area. - /// - EXIF_SUBJECTAREA = 37396, - - /// - /// Manufacturer notes. - /// - EXIF_MAKERNOTE = 37500, - - /// - /// User comments. - /// - EXIF_USERCOMMENT = 37510, - - /// - /// DateTime subseconds. - /// - EXIF_SUBSECTIME = 37520, - - /// - /// DateTimeOriginal subseconds. - /// - EXIF_SUBSECTIMEORIGINAL = 37521, - - /// - /// DateTimeDigitized subseconds. - /// - EXIF_SUBSECTIMEDIGITIZED = 37522, - - /// - /// Supported Flashpix version. - /// - EXIF_FLASHPIXVERSION = 40960, - - /// - /// Color space information. - /// - EXIF_COLORSPACE = 40961, - - /// - /// Valid image width. - /// - EXIF_PIXELXDIMENSION = 40962, - - /// - /// Valid image height. - /// - EXIF_PIXELYDIMENSION = 40963, - - /// - /// Related audio file. - /// - EXIF_RELATEDSOUNDFILE = 40964, - - /// - /// Flash energy. - /// - EXIF_FLASHENERGY = 41483, - - /// - /// Spatial frequency response. - /// - EXIF_SPATIALFREQUENCYRESPONSE = 41484, - - /// - /// Focal plane X resolution. - /// - EXIF_FOCALPLANEXRESOLUTION = 41486, - - /// - /// Focal plane Y resolution. - /// - EXIF_FOCALPLANEYRESOLUTION = 41487, - - /// - /// Focal plane resolution unit. - /// - EXIF_FOCALPLANERESOLUTIONUNIT = 41488, - - /// - /// Subject location. - /// - EXIF_SUBJECTLOCATION = 41492, - - /// - /// Exposure index. - /// - EXIF_EXPOSUREINDEX = 41493, - - /// - /// Sensing method. - /// - EXIF_SENSINGMETHOD = 41495, - - /// - /// File source. - /// - EXIF_FILESOURCE = 41728, - - /// - /// Scene type. - /// - EXIF_SCENETYPE = 41729, - - /// - /// CFA pattern. - /// - EXIF_CFAPATTERN = 41730, - - /// - /// Custom image processing. - /// - EXIF_CUSTOMRENDERED = 41985, - - /// - /// Exposure mode. - /// - EXIF_EXPOSUREMODE = 41986, - - /// - /// White balance. - /// - EXIF_WHITEBALANCE = 41987, - - /// - /// Digital zoom ratio. - /// - EXIF_DIGITALZOOMRATIO = 41988, - - /// - /// Focal length in 35 mm film. - /// - EXIF_FOCALLENGTHIN35MMFILM = 41989, - - /// - /// Scene capture type. - /// - EXIF_SCENECAPTURETYPE = 41990, - - /// - /// Gain control. - /// - EXIF_GAINCONTROL = 41991, - - /// - /// Contrast. - /// - EXIF_CONTRAST = 41992, - - /// - /// Saturation. - /// - EXIF_SATURATION = 41993, - - /// - /// Sharpness. - /// - EXIF_SHARPNESS = 41994, - - /// - /// Device settings description. - /// - EXIF_DEVICESETTINGDESCRIPTION = 41995, - - /// - /// Subject distance range. - /// - EXIF_SUBJECTDISTANCERANGE = 41996, - - /// - /// Unique image ID. - /// - EXIF_IMAGEUNIQUEID = 42016, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/TiffType.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/TiffType.cs deleted file mode 100644 index edd87843..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/TiffType.cs +++ /dev/null @@ -1,102 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Tag data type. - /// - /// Note: RATIONALs are the ratio of two 32-bit integer values. -#if EXPOSE_LIBTIFF - public -#endif - enum TiffType : short - { - /// - /// Placeholder. - /// - NOTYPE = 0, - - /// - /// For field descriptor searching. - /// - ANY = NOTYPE, - - /// - /// 8-bit unsigned integer. - /// - BYTE = 1, - - /// - /// 8-bit bytes with last byte null. - /// - ASCII = 2, - - /// - /// 16-bit unsigned integer. - /// - SHORT = 3, - - /// - /// 32-bit unsigned integer. - /// - LONG = 4, - - /// - /// 64-bit unsigned fraction. - /// - RATIONAL = 5, - - /// - /// 8-bit signed integer. - /// - SBYTE = 6, - - /// - /// 8-bit untyped data. - /// - UNDEFINED = 7, - - /// - /// 16-bit signed integer. - /// - SSHORT = 8, - - /// - /// 32-bit signed integer. - /// - SLONG = 9, - - /// - /// 64-bit signed fraction. - /// - SRATIONAL = 10, - - /// - /// 32-bit IEEE floating point. - /// - FLOAT = 11, - - /// - /// 64-bit IEEE floating point. - /// - DOUBLE = 12, - - /// - /// 32-bit unsigned integer (offset) - /// - IFD = 13 - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/YCbCrPosition.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/YCbCrPosition.cs deleted file mode 100644 index fa76a99d..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Enums/YCbCrPosition.cs +++ /dev/null @@ -1,37 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Subsample positioning.
- /// Possible values for .YCBCRPOSITIONING tag. - ///
-#if EXPOSE_LIBTIFF - public -#endif - enum YCbCrPosition - { - /// - /// As in PostScript Level 2 - /// - CENTERED = 1, - - /// - /// As in CCIR 601-1 - /// - COSITED = 2, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/FieldBit.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/FieldBit.cs deleted file mode 100644 index 6fbe38b6..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/FieldBit.cs +++ /dev/null @@ -1,111 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Field bits (flags) for tags. - /// - /// Field bits used to indicate fields that have been set in a directory, and to - /// reference fields when manipulating a directory. -#if EXPOSE_LIBTIFF - public -#endif - static class FieldBit - { - internal const int SetLongs = 4; - - ///////////////////////////////////////////////////////////////////////// - // multi-item fields - - internal const short ImageDimensions = 1; - internal const short TileDimensions = 2; - internal const short Resolution = 3; - internal const short Position = 4; - - ///////////////////////////////////////////////////////////////////////// - // single-item fields - - internal const short SubFileType = 5; - internal const short BitsPerSample = 6; - internal const short Compression = 7; - internal const short Photometric = 8; - internal const short Thresholding = 9; - internal const short FillOrder = 10; - internal const short Orientation = 15; - internal const short SamplesPerPixel = 16; - internal const short RowsPerStrip = 17; - internal const short MinSampleValue = 18; - internal const short MaxSampleValue = 19; - internal const short PlanarConfig = 20; - internal const short ResolutionUnit = 22; - internal const short PageNumber = 23; - internal const short StripByteCounts = 24; - internal const short StripOffsets = 25; - internal const short ColorMap = 26; - internal const short ExtraSamples = 31; - internal const short SampleFormat = 32; - internal const short SMinSampleValue = 33; - internal const short SMaxSampleValue = 34; - internal const short ImageDepth = 35; - internal const short TileDepth = 36; - internal const short HalftoneHints = 37; - internal const short YCbCrSubsampling = 39; - internal const short YCbCrPositioning = 40; - internal const short RefBlackWhite = 41; - internal const short TransferFunction = 44; - internal const short InkNames = 46; - internal const short SubIFD = 49; - - ///////////////////////////////////////////////////////////////////////// - // end of support for well-known tags; codec-private tags follow - - /// - /// This value is used to signify tags that are to be processed - /// but otherwise ignored.
- /// This permits antiquated tags to be quietly read and discarded. Note that - /// a bit is allocated for ignored tags; this is understood by the - /// directory reading logic which uses this fact to avoid special-case handling. - ///
- public const short Ignore = 0; - - /// - /// This value is used to signify pseudo-tags.
- /// Pseudo-tags don't normally need field bits since they are not - /// written to an output file (by definition). The library also has - /// express logic to always query a codec for a pseudo-tag so allocating - /// a field bit for one is a waste. If codec wants to promote the notion - /// of a pseudo-tag being set or unset then it can do using - /// internal state flags without polluting the field bit space defined - /// for real tags. - ///
- public const short Pseudo = 0; - - /// - /// This value is used to signify custom tags. - /// - public const short Custom = 65; - - /// - /// This value is used as a base (starting) value for codec-private tags. - /// - public const short Codec = 66; - - /// - /// Last usable value for field bit. All tags values should be less than this value. - /// - public const short Last = (32 * SetLongs - 1); - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/FieldValue.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/FieldValue.cs deleted file mode 100644 index 1356031f..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/FieldValue.cs +++ /dev/null @@ -1,693 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* - * Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - */ -using System; - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Holds a value of a Tiff tag. - /// - /// - /// Simply put, it is a wrapper around System.Object, that helps to deal with - /// unboxing and conversion of types a bit easier. - /// - /// Please take a look at: - /// http://blogs.msdn.com/ericlippert/archive/2009/03/19/representation-and-identity.aspx - /// -#if EXPOSE_LIBTIFF - public -#endif - struct FieldValue - { - private object m_value; - - internal FieldValue(object o) - { - m_value = o; - } - - static internal FieldValue[] FromParams(params object[] list) - { - FieldValue[] values = new FieldValue[list.Length]; - for (int i = 0; i < list.Length; i++) - { - if (list[i] is FieldValue) - values[i] = new FieldValue(((FieldValue)(list[i])).Value); - else - values[i] = new FieldValue(list[i]); - } - - return values; - } - - internal void Set(object o) - { - m_value = o; - } - - /// - /// Gets the value. - /// - /// The value. - public object Value - { - get { return m_value; } - } - - /// - /// Retrieves value converted to byte. - /// - /// The value converted to byte. - public byte ToByte() - { - return Convert.ToByte(m_value); - } - - /// - /// Retrieves value converted to short. - /// - /// The value converted to short. - public short ToShort() - { - return Convert.ToInt16(m_value); - } - - /// - /// Retrieves value converted to ushort. - /// - /// The value converted to ushort. -#if EXPOSE_LIBTIFF - [CLSCompliant(false)] -#endif - public ushort ToUShort() - { - return Convert.ToUInt16(m_value); - } - - /// - /// Retrieves value converted to int. - /// - /// The value converted to int. - public int ToInt() - { - return Convert.ToInt32(m_value); - } - - /// - /// Retrieves value converted to uint. - /// - /// The value converted to uint. -#if EXPOSE_LIBTIFF - [CLSCompliant(false)] -#endif - public uint ToUInt() - { - return Convert.ToUInt32(m_value); - } - - /// - /// Retrieves value converted to float. - /// - /// The value converted to float. - public float ToFloat() - { - return Convert.ToSingle(m_value); - } - - /// - /// Retrieves value converted to double. - /// - /// The value converted to double. - public double ToDouble() - { - return Convert.ToDouble(m_value); - } - - /// - /// Retrieves value converted to string. - /// - /// - /// A that represents this instance. - /// - /// If value is a byte array, then it gets converted to string using - /// Latin1 encoding encoder. - public override string ToString() - { - if (m_value is byte[]) - return Tiff.Latin1Encoding.GetString(m_value as byte[]); - - return Convert.ToString(m_value); - } - - /// - /// Retrieves value converted to byte array. - /// - /// Value converted to byte array. - /// - /// If value is byte array then it retrieved unaltered. - /// If value is array of short, ushort, int, uint, float or double values then this - /// array is converted to byte array - /// If value is a string then it gets converted to byte array using Latin1 encoding - /// encoder. - /// If value is of any other type then null is returned. - /// - public byte[] GetBytes() - { - if (m_value == null) - return null; - - Type t = m_value.GetType(); - if (t.IsArray) - { - if (m_value is byte[]) - return m_value as byte[]; - else if (m_value is short[]) - { - short[] temp = m_value as short[]; - byte[] result = new byte[temp.Length * sizeof(short)]; - Buffer.BlockCopy(temp, 0, result, 0, result.Length); - return result; - } - else if (m_value is ushort[]) - { - ushort[] temp = m_value as ushort[]; - byte[] result = new byte[temp.Length * sizeof(ushort)]; - Buffer.BlockCopy(temp, 0, result, 0, result.Length); - return result; - } - else if (m_value is int[]) - { - int[] temp = m_value as int[]; - byte[] result = new byte[temp.Length * sizeof(int)]; - Buffer.BlockCopy(temp, 0, result, 0, result.Length); - return result; - } - else if (m_value is uint[]) - { - uint[] temp = m_value as uint[]; - byte[] result = new byte[temp.Length * sizeof(uint)]; - Buffer.BlockCopy(temp, 0, result, 0, result.Length); - return result; - } - else if (m_value is float[]) - { - float[] temp = m_value as float[]; - byte[] result = new byte[temp.Length * sizeof(float)]; - Buffer.BlockCopy(temp, 0, result, 0, result.Length); - return result; - } - else if (m_value is double[]) - { - double[] temp = m_value as double[]; - byte[] result = new byte[temp.Length * sizeof(double)]; - Buffer.BlockCopy(temp, 0, result, 0, result.Length); - return result; - } - } - else if (m_value is string) - { - return Tiff.Latin1Encoding.GetBytes(m_value as string); - } - else if (m_value is int) - { - return BitConverter.GetBytes((int)m_value); - } - - return null; - } - - /// - /// Retrieves value converted to array of bytes. - /// - /// Value converted to array of bytes. - /// If value is array of bytes then it retrieved unaltered. - /// If value is array of short, ushort, int or uint values then each element of - /// field value gets converted to byte and added to resulting array. - /// If value is string then it gets converted to byte[] using Latin1 encoding - /// encoder. - /// If value is of any other type then null is returned. - public byte[] ToByteArray() - { - if (m_value == null) - return null; - - Type t = m_value.GetType(); - if (t.IsArray) - { - if (m_value is byte[]) - return m_value as byte[]; - else if (m_value is short[]) - { - short[] temp = m_value as short[]; - byte[] result = new byte[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (byte)temp[i]; - - return result; - } - else if (m_value is ushort[]) - { - ushort[] temp = m_value as ushort[]; - byte[] result = new byte[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (byte)temp[i]; - - return result; - } - else if (m_value is int[]) - { - int[] temp = m_value as int[]; - byte[] result = new byte[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (byte)temp[i]; - - return result; - } - else if (m_value is uint[]) - { - uint[] temp = m_value as uint[]; - byte[] result = new byte[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (byte)temp[i]; - - return result; - } - } - else if (m_value is string) - return Tiff.Latin1Encoding.GetBytes(m_value as string); - - return null; - } - - /// - /// Retrieves value converted to array of short values. - /// - /// Value converted to array of short values. - /// If value is array of short values then it retrieved unaltered. - /// If value is array of bytes then each pair of bytes is converted to short and - /// added to resulting array. If value contains odd amount of bytes, then null is - /// returned. - /// If value is array of ushort, int or uint values then each element of field value gets - /// converted to short and added to resulting array. - /// If value is of any other type then null is returned. - public short[] ToShortArray() - { - if (m_value == null) - return null; - - Type t = m_value.GetType(); - if (t.IsArray) - { - if (m_value is short[]) - return m_value as short[]; - else if (m_value is byte[]) - { - byte[] temp = m_value as byte[]; - if (temp.Length % sizeof(short) != 0) - return null; - - int totalShorts = temp.Length / sizeof(short); - short[] result = new short[totalShorts]; - - int byteOffset = 0; - for (int i = 0; i < totalShorts; i++) - { - short s = BitConverter.ToInt16(temp, byteOffset); - result[i] = s; - byteOffset += sizeof(short); - } - - return result; - } - else if (m_value is ushort[]) - { - ushort[] temp = m_value as ushort[]; - short[] result = new short[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (short)temp[i]; - - return result; - } - else if (m_value is int[]) - { - int[] temp = m_value as int[]; - short[] result = new short[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (short)temp[i]; - - return result; - } - else if (m_value is uint[]) - { - uint[] temp = m_value as uint[]; - short[] result = new short[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (short)temp[i]; - - return result; - } - } - - return null; - } - - /// - /// Retrieves value converted to array of ushort values. - /// - /// Value converted to array of ushort values. - /// If value is array of ushort values then it retrieved unaltered. - /// If value is array of bytes then each pair of bytes is converted to ushort and - /// added to resulting array. If value contains odd amount of bytes, then null is - /// returned. - /// If value is array of short, int or uint values then each element of field value gets - /// converted to ushort and added to resulting array. - /// If value is of any other type then null is returned. -#if EXPOSE_LIBTIFF - [CLSCompliant(false)] -#endif - public ushort[] ToUShortArray() - { - if (m_value == null) - return null; - - Type t = m_value.GetType(); - if (t.IsArray) - { - if (m_value is ushort[]) - return m_value as ushort[]; - else if (m_value is byte[]) - { - byte[] temp = m_value as byte[]; - if (temp.Length % sizeof(ushort) != 0) - return null; - - int totalUShorts = temp.Length / sizeof(ushort); - ushort[] result = new ushort[totalUShorts]; - - int byteOffset = 0; - for (int i = 0; i < totalUShorts; i++) - { - ushort s = BitConverter.ToUInt16(temp, byteOffset); - result[i] = s; - byteOffset += sizeof(ushort); - } - - return result; - } - else if (m_value is short[]) - { - short[] temp = m_value as short[]; - ushort[] result = new ushort[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (ushort)temp[i]; - - return result; - } - else if (m_value is int[]) - { - int[] temp = m_value as int[]; - ushort[] result = new ushort[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (ushort)temp[i]; - - return result; - } - else if (m_value is uint[]) - { - uint[] temp = m_value as uint[]; - ushort[] result = new ushort[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (ushort)temp[i]; - - return result; - } - } - - return null; - } - - /// - /// Retrieves value converted to array of int values. - /// - /// Value converted to array of int values. - /// If value is array of int values then it retrieved unaltered. - /// If value is array of bytes then each 4 bytes are converted to int and added to - /// resulting array. If value contains amount of bytes that can't be divided by 4 without - /// remainder, then null is returned. - /// If value is array of short, ushort or uint values then each element of - /// field value gets converted to int and added to resulting array. - /// If value is of any other type then null is returned. - public int[] ToIntArray() - { - if (m_value == null) - return null; - - Type t = m_value.GetType(); - if (t.IsArray) - { - if (m_value is int[]) - return m_value as int[]; - else if (m_value is byte[]) - { - byte[] temp = m_value as byte[]; - if (temp.Length % sizeof(int) != 0) - return null; - - int totalInts = temp.Length / sizeof(int); - int[] result = new int[totalInts]; - - int byteOffset = 0; - for (int i = 0; i < totalInts; i++) - { - int s = BitConverter.ToInt32(temp, byteOffset); - result[i] = s; - byteOffset += sizeof(int); - } - - return result; - } - else if (m_value is short[]) - { - short[] temp = m_value as short[]; - int[] result = new int[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (int)temp[i]; - - return result; - } - else if (m_value is ushort[]) - { - ushort[] temp = m_value as ushort[]; - int[] result = new int[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (int)temp[i]; - - return result; - } - else if (m_value is uint[]) - { - uint[] temp = m_value as uint[]; - int[] result = new int[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (int)temp[i]; - - return result; - } - } - - return null; - } - - /// - /// Retrieves value converted to array of uint values. - /// - /// Value converted to array of uint values. - /// If value is array of uint values then it retrieved unaltered. - /// If value is array of bytes then each 4 bytes are converted to uint and added to - /// resulting array. If value contains amount of bytes that can't be divided by 4 without - /// remainder, then null is returned. - /// If value is array of short, ushort or int values then each element of - /// field value gets converted to uint and added to resulting array. - /// If value is of any other type then null is returned. -#if EXPOSE_LIBTIFF - [CLSCompliant(false)] -#endif - public uint[] ToUIntArray() - { - if (m_value == null) - return null; - - Type t = m_value.GetType(); - if (t.IsArray) - { - if (m_value is uint[]) - return m_value as uint[]; - else if (m_value is byte[]) - { - byte[] temp = m_value as byte[]; - if (temp.Length % sizeof(uint) != 0) - return null; - - int totalUInts = temp.Length / sizeof(uint); - uint[] result = new uint[totalUInts]; - - int byteOffset = 0; - for (int i = 0; i < totalUInts; i++) - { - uint s = BitConverter.ToUInt32(temp, byteOffset); - result[i] = s; - byteOffset += sizeof(uint); - } - - return result; - } - else if (m_value is short[]) - { - short[] temp = m_value as short[]; - uint[] result = new uint[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (uint)temp[i]; - - return result; - } - else if (m_value is ushort[]) - { - ushort[] temp = m_value as ushort[]; - uint[] result = new uint[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (uint)temp[i]; - - return result; - } - else if (m_value is int[]) - { - int[] temp = m_value as int[]; - uint[] result = new uint[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (uint)temp[i]; - - return result; - } - } - - return null; - } - - /// - /// Retrieves value converted to array of float values. - /// - /// Value converted to array of float values. - /// If value is array of float values then it retrieved unaltered. - /// If value is array of bytes then each 4 bytes are converted to float and added to - /// resulting array. If value contains amount of bytes that can't be divided by 4 without - /// remainder, then null is returned. - /// If value is array of double values then each element of field value gets - /// converted to float and added to resulting array. - /// If value is of any other type then null is returned. - public float[] ToFloatArray() - { - if (m_value == null) - return null; - - Type t = m_value.GetType(); - if (t.IsArray) - { - if (m_value is float[]) - return m_value as float[]; - else if (m_value is double[]) - { - double[] temp = m_value as double[]; - float[] result = new float[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (float)temp[i]; - - return result; - } - else if (m_value is byte[]) - { - byte[] temp = m_value as byte[]; - if (temp.Length % sizeof(float) != 0) - return null; - - int tempPos = 0; - - int floatCount = temp.Length / sizeof(float); - float[] result = new float[floatCount]; - - for (int i = 0; i < floatCount; i++) - { - float f = BitConverter.ToSingle(temp, tempPos); - result[i] = f; - tempPos += sizeof(float); - } - - return result; - } - } - - return null; - } - - /// - /// Retrieves value converted to array of double values. - /// - /// Value converted to array of double values. - /// If value is array of double values then it retrieved unaltered. - /// If value is array of bytes then each 8 bytes are converted to double and added to - /// resulting array. If value contains amount of bytes that can't be divided by 8 without - /// remainder, then null is returned. - /// If value is array of float values then each element of field value gets - /// converted to double and added to resulting array. - /// If value is of any other type then null is returned. - public double[] ToDoubleArray() - { - if (m_value == null) - return null; - - Type t = m_value.GetType(); - if (t.IsArray) - { - if (m_value is double[]) - return m_value as double[]; - else if (m_value is float[]) - { - float[] temp = m_value as float[]; - double[] result = new double[temp.Length]; - for (int i = 0; i < temp.Length; i++) - result[i] = (double)temp[i]; - - return result; - } - else if (m_value is byte[]) - { - byte[] temp = m_value as byte[]; - if (temp.Length % sizeof(double) != 0) - return null; - - int tempPos = 0; - - int floatCount = temp.Length / sizeof(double); - double[] result = new double[floatCount]; - - for (int i = 0; i < floatCount; i++) - { - double d = BitConverter.ToDouble(temp, tempPos); - result[i] = d; - tempPos += sizeof(double); - } - - return result; - } - } - - return null; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CCITTCodec.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CCITTCodec.cs deleted file mode 100644 index 07b9ed53..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CCITTCodec.cs +++ /dev/null @@ -1,2397 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * CCITT Group 3 (T.4) and Group 4 (T.6) Compression Support. - * - * This file contains support for decoding and encoding TIFF - * compression algorithms 2, 3, 4, and 32771. - * - * Decoder support is derived, with permission, from the code - * in Frank Cringle's viewfax program; - * Copyright (C) 1990, 1995 Frank D. Cringle. - */ - -using System; -using System.Diagnostics; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - partial class CCITTCodec : TiffCodec - { - public const int FIELD_BADFAXLINES = (FieldBit.Codec + 0); - public const int FIELD_CLEANFAXDATA = (FieldBit.Codec + 1); - public const int FIELD_BADFAXRUN = (FieldBit.Codec + 2); - public const int FIELD_RECVPARAMS = (FieldBit.Codec + 3); - public const int FIELD_SUBADDRESS = (FieldBit.Codec + 4); - public const int FIELD_RECVTIME = (FieldBit.Codec + 5); - public const int FIELD_FAXDCS = (FieldBit.Codec + 6); - public const int FIELD_OPTIONS = (FieldBit.Codec + 7); - - internal FaxMode m_mode; /* operating mode */ - internal Group3Opt m_groupoptions; /* Group 3/4 options tag */ - internal CleanFaxData m_cleanfaxdata; /* CleanFaxData tag */ - internal int m_badfaxlines; /* BadFaxLines tag */ - internal int m_badfaxrun; /* BadFaxRun tag */ - internal int m_recvparams; /* encoded Class 2 session params */ - internal string m_subaddress; /* subaddress string */ - internal int m_recvtime; /* time spent receiving (secs) */ - internal string m_faxdcs; /* Table 2/T.30 encoded session params */ - - /* Decoder state info */ - internal Tiff.FaxFillFunc fill; /* fill routine */ - - private const int EOL_CODE = 0x001; /* EOL code value - 0000 0000 0000 1 */ - - /* finite state machine codes */ - private const byte S_Null = 0; - private const byte S_Pass = 1; - private const byte S_Horiz = 2; - private const byte S_V0 = 3; - private const byte S_VR = 4; - private const byte S_VL = 5; - private const byte S_Ext = 6; - private const byte S_TermW = 7; - private const byte S_TermB = 8; - private const byte S_MakeUpW = 9; - private const byte S_MakeUpB = 10; - private const byte S_MakeUp = 11; - private const byte S_EOL = 12; - - /* status values returned instead of a run length */ - private const short G3CODE_EOL = -1; /* NB: ACT_EOL - ACT_WRUNT */ - private const short G3CODE_INVALID = -2; /* NB: ACT_INVALID - ACT_WRUNT */ - private const short G3CODE_EOF = -3; /* end of input data */ - private const short G3CODE_INCOMP = -4; /* incomplete run code */ - - /* - * CCITT T.4 1D Huffman runlength codes and - * related definitions. Given the small sizes - * of these tables it does not seem - * worthwhile to make code & length 8 bits. - */ - private struct tableEntry - { - public short length; /* bit length of g3 code */ - public short code; /* g3 code */ - public short runlen; /* run length in bits */ - - public tableEntry(short _length, short _code, short _runlen) - { - length = _length; - code = _code; - runlen = _runlen; - } - - public static tableEntry FromArray(short[] array, int entryNumber) - { - int offset = entryNumber * 3; // we have 3 elements in entry - return new tableEntry(array[offset], array[offset + 1], array[offset + 2]); - } - }; - - private struct faxTableEntry - { - public faxTableEntry(byte _State, byte _Width, int _Param) - { - State = _State; - Width = _Width; - Param = _Param; - } - - public static faxTableEntry FromArray(int[] array, int entryNumber) - { - int offset = entryNumber * 3; // we have 3 elements in entry - return new faxTableEntry((byte)array[offset], (byte)array[offset + 1], array[offset + 2]); - } - - /* state table entry */ - public byte State; /* see above */ - public byte Width; /* width of code in bits */ - public int Param; /* unsigned 32-bit run length in bits */ - }; - - private enum Decoder - { - useFax3_1DDecoder, - useFax3_2DDecoder, - useFax4Decoder, - useFax3RLEDecoder - }; - - private enum Fax3Encoder - { - useFax1DEncoder, - useFax2DEncoder - }; - - private static readonly TiffFieldInfo[] m_faxFieldInfo = - { - new TiffFieldInfo(TiffTag.FAXMODE, 0, 0, TiffType.ANY, FieldBit.Pseudo, false, false, "FaxMode"), - new TiffFieldInfo(TiffTag.FAXFILLFUNC, 0, 0, TiffType.ANY, FieldBit.Pseudo, false, false, "FaxFillFunc"), - new TiffFieldInfo(TiffTag.BADFAXLINES, 1, 1, TiffType.LONG, FIELD_BADFAXLINES, true, false, "BadFaxLines"), - new TiffFieldInfo(TiffTag.BADFAXLINES, 1, 1, TiffType.SHORT, FIELD_BADFAXLINES, true, false, "BadFaxLines"), - new TiffFieldInfo(TiffTag.CLEANFAXDATA, 1, 1, TiffType.SHORT, FIELD_CLEANFAXDATA, true, false, "CleanFaxData"), - new TiffFieldInfo(TiffTag.CONSECUTIVEBADFAXLINES, 1, 1, TiffType.LONG, FIELD_BADFAXRUN, true, false, "ConsecutiveBadFaxLines"), - new TiffFieldInfo(TiffTag.CONSECUTIVEBADFAXLINES, 1, 1, TiffType.SHORT, FIELD_BADFAXRUN, true, false, "ConsecutiveBadFaxLines"), - new TiffFieldInfo(TiffTag.FAXRECVPARAMS, 1, 1, TiffType.LONG, FIELD_RECVPARAMS, true, false, "FaxRecvParams"), - new TiffFieldInfo(TiffTag.FAXSUBADDRESS, -1, -1, TiffType.ASCII, FIELD_SUBADDRESS, true, false, "FaxSubAddress"), - new TiffFieldInfo(TiffTag.FAXRECVTIME, 1, 1, TiffType.LONG, FIELD_RECVTIME, true, false, "FaxRecvTime"), - new TiffFieldInfo(TiffTag.FAXDCS, -1, -1, TiffType.ASCII, FIELD_FAXDCS, true, false, "FaxDcs"), - }; - - private static readonly TiffFieldInfo[] m_fax3FieldInfo = - { - new TiffFieldInfo(TiffTag.GROUP3OPTIONS, 1, 1, TiffType.LONG, FIELD_OPTIONS, false, false, "Group3Options"), - }; - - private static readonly TiffFieldInfo[] m_fax4FieldInfo = - { - new TiffFieldInfo(TiffTag.GROUP4OPTIONS, 1, 1, TiffType.LONG, FIELD_OPTIONS, false, false, "Group4Options"), - }; - - private TiffTagMethods m_parentTagMethods; - private TiffTagMethods m_tagMethods; - - private int m_rw_mode; /* O_RDONLY for decode, else encode */ - private int m_rowbytes; /* bytes in a decoded scanline */ - private int m_rowpixels; /* pixels in a scanline */ - - /* Decoder state info */ - private Decoder m_decoder; - private byte[] m_bitmap; /* bit reversal table */ - private int m_data; /* current i/o byte/word */ - private int m_bit; /* current i/o bit in byte */ - private int m_EOLcnt; /* count of EOL codes recognized */ - private int[] m_runs; /* b&w runs for current/previous row */ - private int m_refruns; /* runs for reference line (index in m_runs) */ - private int m_curruns; /* runs for current line (index in m_runs) */ - - private int m_a0; /* reference element */ - private int m_RunLength; /* length of current run */ - private int m_thisrun; /* current row's run array (index in m_runs) */ - private int m_pa; /* place to stuff next run (index in m_runs) */ - private int m_pb; /* next run in reference line (index in m_runs) */ - - /* Encoder state info */ - private Fax3Encoder m_encoder; /* encoding state */ - private bool m_encodingFax4; // if false, G3 will be used - private byte[] m_refline; /* reference line for 2d decoding */ - private int m_k; /* #rows left that can be 2d encoded */ - private int m_maxk; /* max #rows that can be 2d encoded */ - private int m_line; - - private byte[] m_buffer; // buffer with data to encode - private int m_offset; // current position in m_buffer - - public CCITTCodec(Tiff tif, Compression scheme, string name) - : base(tif, scheme, name) - { - m_tagMethods = new CCITTCodecTagMethods(); - } - - private void cleanState() - { - m_mode = FaxMode.CLASSIC; - m_groupoptions = Group3Opt.UNKNOWN; - m_cleanfaxdata = CleanFaxData.CLEAN; - m_badfaxlines = 0; - m_badfaxrun = 0; - m_recvparams = 0; - m_subaddress = null; - m_recvtime = 0; - m_faxdcs = null; - - fill = null; - m_rw_mode = 0; - m_rowbytes = 0; - m_rowpixels = 0; - - m_decoder = 0; - m_bitmap = null; - m_data = 0; - m_bit = 0; - m_EOLcnt = 0; - m_runs = null; - m_refruns = 0; - m_curruns = 0; - - m_a0 = 0; - m_RunLength = 0; - m_thisrun = 0; - m_pa = 0; - m_pb = 0; - - m_encoder = 0; - m_encodingFax4 = false; - m_refline = null; - m_k = 0; - m_maxk = 0; - m_line = 0; - - m_buffer = null; - m_offset = 0; - } - - public override bool Init() - { - switch (m_scheme) - { - case Compression.CCITTRLE: - return TIFFInitCCITTRLE(); - case Compression.CCITTRLEW: - return TIFFInitCCITTRLEW(); - case Compression.CCITTFAX3: - return TIFFInitCCITTFax3(); - case Compression.CCITTFAX4: - return TIFFInitCCITTFax4(); - } - - return false; - } - - /// - /// Gets a value indicating whether this codec can encode data. - /// - /// - /// true if this codec can encode data; otherwise, false. - /// - public override bool CanEncode - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether this codec can decode data. - /// - /// - /// true if this codec can decode data; otherwise, false. - /// - public override bool CanDecode - { - get - { - return true; - } - } - - public override bool SetupDecode() - { - // same for all types - return setupState(); - } - - /// - /// Prepares the decoder part of the codec for a decoding. - /// - /// The zero-based sample plane index. - /// - /// true if this codec successfully prepared its decoder part and ready - /// to decode data; otherwise, false. - /// - /// - /// PreDecode is called after and before decoding. - /// - public override bool PreDecode(short plane) - { - m_bit = 0; // force initial read - m_data = 0; - m_EOLcnt = 0; // force initial scan for EOL - - // Decoder assumes lsb-to-msb bit order. Note that we select this here rather than in - // setupState so that viewers can hold the image open, fiddle with the FillOrder tag - // value, and then re-decode the image. Otherwise they'd need to close and open the - // image to get the state reset. - m_bitmap = Tiff.GetBitRevTable(m_tif.m_dir.td_fillorder != FillOrder.LSB2MSB); - if (m_refruns >= 0) - { - // init reference line to white - m_runs[m_refruns] = m_rowpixels; - m_runs[m_refruns + 1] = 0; - } - - m_line = 0; - return true; - } - - /// - /// Decodes one row of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeRow(byte[] buffer, int offset, int count, short plane) - { - switch (m_decoder) - { - case Decoder.useFax3_1DDecoder: - return Fax3Decode1D(buffer, offset, count); - case Decoder.useFax3_2DDecoder: - return Fax3Decode2D(buffer, offset, count); - case Decoder.useFax4Decoder: - return Fax4Decode(buffer, offset, count); - case Decoder.useFax3RLEDecoder: - return Fax3DecodeRLE(buffer, offset, count); - } - - return false; - } - - /// - /// Decodes one strip of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeStrip(byte[] buffer, int offset, int count, short plane) - { - return DecodeRow(buffer, offset, count, plane); - } - - /// - /// Decodes one tile of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeTile(byte[] buffer, int offset, int count, short plane) - { - return DecodeRow(buffer, offset, count, plane); - } - - /// - /// Setups the encoder part of the codec. - /// - /// - /// true if this codec successfully setup its encoder part and can encode data; - /// otherwise, false. - /// - /// - /// SetupEncode is called once before - /// . - public override bool SetupEncode() - { - // same for all types - return setupState(); - } - - /// - /// Prepares the encoder part of the codec for a encoding. - /// - /// The zero-based sample plane index. - /// - /// true if this codec successfully prepared its encoder part and ready - /// to encode data; otherwise, false. - /// - /// - /// PreEncode is called after and before encoding. - /// - public override bool PreEncode(short plane) - { - m_bit = 8; - m_data = 0; - m_encoder = Fax3Encoder.useFax1DEncoder; - - /* - * This is necessary for Group 4; otherwise it isn't - * needed because the first scanline of each strip ends - * up being copied into the refline. - */ - if (m_refline != null) - Array.Clear(m_refline, 0, m_refline.Length); - - if (is2DEncoding()) - { - float res = m_tif.m_dir.td_yresolution; - /* - * The CCITT spec says that when doing 2d encoding, you - * should only do it on K consecutive scanlines, where K - * depends on the resolution of the image being encoded - * (2 for <= 200 lpi, 4 for > 200 lpi). Since the directory - * code initializes td_yresolution to 0, this code will - * select a K of 2 unless the YResolution tag is set - * appropriately. (Note also that we fudge a little here - * and use 150 lpi to avoid problems with units conversion.) - */ - if (m_tif.m_dir.td_resolutionunit == ResUnit.CENTIMETER) - { - /* convert to inches */ - res *= 2.54f; - } - - m_maxk = (res > 150 ? 4 : 2); - m_k = m_maxk - 1; - } - else - { - m_maxk = 0; - m_k = 0; - } - - m_line = 0; - return true; - } - - /// - /// Performs any actions after encoding required by the codec. - /// - /// - /// true if all post-encode actions succeeded; otherwise, false - /// - /// - /// PostEncode is called after encoding and can be used to release any external - /// resources needed during encoding. - /// - public override bool PostEncode() - { - if (m_encodingFax4) - return Fax4PostEncode(); - - return Fax3PostEncode(); - } - - /// - /// Encodes one row of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeRow(byte[] buffer, int offset, int count, short plane) - { - if (m_encodingFax4) - return Fax4Encode(buffer, offset, count); - - return Fax3Encode(buffer, offset, count); - } - - /// - /// Encodes one strip of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeStrip(byte[] buffer, int offset, int count, short plane) - { - return EncodeRow(buffer, offset, count, plane); - } - - /// - /// Encodes one tile of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeTile(byte[] buffer, int offset, int count, short plane) - { - return EncodeRow(buffer, offset, count, plane); - } - - /// - /// Flushes any internal data buffers and terminates current operation. - /// - public override void Close() - { - if ((m_mode & FaxMode.NORTC) == 0) - { - int code = EOL_CODE; - int length = 12; - if (is2DEncoding()) - { - bool b = ((code << 1) != 0) | (m_encoder == Fax3Encoder.useFax1DEncoder); - if (b) - code = 1; - else - code = 0; - - length++; - } - - for (int i = 0; i < 6; i++) - putBits(code, length); - - flushBits(); - } - } - - /// - /// Cleanups the state of the codec. - /// - /// - /// Cleanup is called when codec is no longer needed (won't be used) and can be - /// used for example to restore tag methods that were substituted. - public override void Cleanup() - { - m_tif.m_tagmethods = m_parentTagMethods; - } - - private bool is2DEncoding() - { - return (m_groupoptions & Group3Opt.ENCODING2D) != 0; - } - - /* - * Update the value of b1 using the array - * of runs for the reference line. - */ - private void CHECK_b1(ref int b1) - { - if (m_pa != m_thisrun) - { - while (b1 <= m_a0 && b1 < m_rowpixels) - { - b1 += m_runs[m_pb] + m_runs[m_pb + 1]; - m_pb += 2; - } - } - } - - private static void SWAP(ref int a, ref int b) - { - int x = a; - a = b; - b = x; - } - - private static bool isLongAligned(int offset) - { - return (offset % sizeof(int) == 0); - } - - private static bool isShortAligned(int offset) - { - return (offset % sizeof(short) == 0); - } - - /* - * The FILL macro must handle spans < 2*sizeof(int) bytes. - * This is <8 bytes. - */ - private static void FILL(int n, byte[] cp, ref int offset, byte value) - { - const int max = 7; - - if (n <= max && n > 0) - { - for (int i = n; i > 0; i--) - cp[offset + i - 1] = value; - - offset += n; - } - } - - /* - * Bit-fill a row according to the white/black - * runs generated during G3/G4 decoding. - * The default run filler; made public for other decoders. - */ - private static void fax3FillRuns(byte[] buffer, int offset, int[] runs, - int thisRunOffset, int nextRunOffset, int width) - { - if (((nextRunOffset - thisRunOffset) & 1) != 0) - { - runs[nextRunOffset] = 0; - nextRunOffset++; - } - - int x = 0; - for (; thisRunOffset < nextRunOffset; thisRunOffset += 2) - { - int run = runs[thisRunOffset]; - - // should cast 'run' to unsigned in order to discard values bigger than int.MaxValue - // for such value 'run' become negative and following condition is not met - if ((uint)x + (uint)run > (uint)width || (uint)run > (uint)width) - { - runs[thisRunOffset] = width - x; - run = runs[thisRunOffset]; - } - - if (run != 0) - { - int cp = offset + (x >> 3); - int bx = x & 7; - if (run > 8 - bx) - { - if (bx != 0) - { - // align to byte boundary - buffer[cp] &= (byte)(0xff << (8 - bx)); - cp++; - run -= 8 - bx; - } - - int n = run >> 3; - if (n != 0) - { - // multiple bytes to fill - if ((n / sizeof(int)) > 1) - { - // Align to longword boundary and fill. - for ( ; n != 0 && !isLongAligned(cp); n--) - { - buffer[cp] = 0x00; - cp++; - } - - int bytesToFill = n - (n % sizeof(int)); - n -= bytesToFill; - - int stop = bytesToFill + cp; - for ( ; cp < stop; cp++) - buffer[cp] = 0; - } - - FILL(n, buffer, ref cp, 0); - run &= 7; - } - - if (run != 0) - buffer[cp] &= (byte)(0xff >> run); - } - else - buffer[cp] &= (byte)(~(fillMasks[run] >> bx)); - - x += runs[thisRunOffset]; - } - - run = runs[thisRunOffset + 1]; - - // should cast 'run' to unsigned in order to discard values bigger than int.MaxValue - // for such value 'run' become negative and following condition is not met - if ((uint)x + (uint)run > (uint)width || (uint)run > (uint)width) - { - runs[thisRunOffset + 1] = width - x; - run = runs[thisRunOffset + 1]; - } - - if (run != 0) - { - int cp = offset + (x >> 3); - int bx = x & 7; - if (run > 8 - bx) - { - if (bx != 0) - { - // align to byte boundary - buffer[cp] |= (byte)(0xff >> bx); - cp++; - run -= 8 - bx; - } - - int n = run >> 3; - if (n != 0) - { - // multiple bytes to fill - if ((n / sizeof(int)) > 1) - { - // Align to longword boundary and fill. - for ( ; n != 0 && !isLongAligned(cp); n--) - { - buffer[cp] = 0xff; - cp++; - } - - int bytesToFill = n - (n % sizeof(int)); - n -= bytesToFill; - - int stop = bytesToFill + cp; - for ( ; cp < stop; cp++) - buffer[cp] = 0xff; - } - - FILL(n, buffer, ref cp, 0xff); - run &= 7; - } - - if (run != 0) - buffer[cp] |= (byte)(0xff00 >> run); - } - else - buffer[cp] |= (byte)(fillMasks[run] >> bx); - - x += runs[thisRunOffset + 1]; - } - } - - Debug.Assert(x == width); - } - - /* - * Find a span of ones or zeros using the supplied - * table. The ``base'' of the bit string is supplied - * along with the start+end bit indices. - */ - private static int find0span(byte[] bp, int bpOffset, int bs, int be) - { - int offset = bpOffset + (bs >> 3); - - /* - * Check partial byte on lhs. - */ - int bits = be - bs; - int n = bs & 7; - int span = 0; - if (bits > 0 && n != 0) - { - span = m_zeroruns[(bp[offset] << n) & 0xff]; - - if (span > 8 - n) - { - /* table value too generous */ - span = 8 - n; - } - - if (span > bits) - { - /* constrain span to bit range */ - span = bits; - } - - if (n + span < 8) - { - /* doesn't extend to edge of byte */ - return span; - } - - bits -= span; - offset++; - } - - if (bits >= (2 * 8 * sizeof(int))) - { - /* - * Align to longword boundary and check longwords. - */ - while (!isLongAligned(offset)) - { - if (bp[offset] != 0x00) - return (span + m_zeroruns[bp[offset]]); - - span += 8; - bits -= 8; - offset++; - } - - while (bits >= 8 * sizeof(int)) - { - bool allZeros = true; - for (int i = 0; i < sizeof(int); i++) - { - if (bp[offset + i] != 0) - { - allZeros = false; - break; - } - } - - if (allZeros) - { - span += 8 * sizeof(int); - bits -= 8 * sizeof(int); - offset += sizeof(int); - } - else - { - break; - } - } - } - - /* - * Scan full bytes for all 0's. - */ - while (bits >= 8) - { - if (bp[offset] != 0x00) - { - /* end of run */ - return (span + m_zeroruns[bp[offset]]); - } - - span += 8; - bits -= 8; - offset++; - } - - /* - * Check partial byte on rhs. - */ - if (bits > 0) - { - n = m_zeroruns[bp[offset]]; - span += (n > bits ? bits : n); - } - - return span; - } - - private static int find1span(byte[] bp, int bpOffset, int bs, int be) - { - int offset = bpOffset + (bs >> 3); - - /* - * Check partial byte on lhs. - */ - int n = bs & 7; - int span = 0; - int bits = be - bs; - if (bits > 0 && n != 0) - { - span = m_oneruns[(bp[offset] << n) & 0xff]; - if (span > 8 - n) - { - /* table value too generous */ - span = 8 - n; - } - - if (span > bits) - { - /* constrain span to bit range */ - span = bits; - } - - if (n + span < 8) - { - /* doesn't extend to edge of byte */ - return (span); - } - - bits -= span; - offset++; - } - - if (bits >= (2 * 8 * sizeof(int))) - { - /* - * Align to longword boundary and check longwords. - */ - while (!isLongAligned(offset)) - { - if (bp[offset] != 0xff) - return (span + m_oneruns[bp[offset]]); - - span += 8; - bits -= 8; - offset++; - } - - while (bits >= 8 * sizeof(int)) - { - bool allOnes = true; - for (int i = 0; i < sizeof(int); i++) - { - if (bp[offset + i] != 0xff) - { - allOnes = false; - break; - } - } - - if (allOnes) - { - span += 8 * sizeof(int); - bits -= 8 * sizeof(int); - offset += sizeof(int); - } - else - { - break; - } - } - } - - /* - * Scan full bytes for all 1's. - */ - while (bits >= 8) - { - if (bp[offset] != 0xff) - { - /* end of run */ - return (span + m_oneruns[bp[offset]]); - } - - span += 8; - bits -= 8; - offset++; - } - - /* - * Check partial byte on rhs. - */ - if (bits > 0) - { - n = m_oneruns[bp[offset]]; - span += (n > bits ? bits : n); - } - - return span; - } - - /* - * Return the offset of the next bit in the range - * [bs..be] that is different from the specified - * color. The end, be, is returned if no such bit - * exists. - */ - private static int finddiff(byte[] bp, int bpOffset, int _bs, int _be, int _color) - { - if (_color != 0) - return (_bs + find1span(bp, bpOffset, _bs, _be)); - - return (_bs + find0span(bp, bpOffset, _bs, _be)); - } - - /* - * Like finddiff, but also check the starting bit - * against the end in case start > end. - */ - private static int finddiff2(byte[] bp, int bpOffset, int _bs, int _be, int _color) - { - if (_bs < _be) - return finddiff(bp, bpOffset, _bs, _be, _color); - - return _be; - } - - /* - * Group 3 and Group 4 Decoding. - */ - - /* - * The following macros define the majority of the G3/G4 decoder - * algorithm using the state tables defined elsewhere. To build - * a decoder you need some setup code and some glue code. Note - * that you may also need/want to change the way the NeedBits* - * macros get input data if, for example, you know the data to be - * decoded is properly aligned and oriented (doing so before running - * the decoder can be a big performance win). - * - * Consult the decoder in the TIFF library for an idea of what you - * need to define and setup to make use of these definitions. - * - * NB: to enable a debugging version of these macros define FAX3_DEBUG - * before including this file. Trace output goes to stdout. - */ - - private bool EndOfData() - { - return (m_tif.m_rawcp >= m_tif.m_rawcc); - } - - private int GetBits(int n) - { - return (m_data & ((1 << n) - 1)); - } - - private void ClrBits(int n) - { - m_bit -= n; - m_data >>= n; - } - - /* - * Need <=8 or <=16 bits of input data. Unlike viewfax we - * cannot use/assume a word-aligned, properly bit swizzled - * input data set because data may come from an arbitrarily - * aligned, read-only source such as a memory-mapped file. - * Note also that the viewfax decoder does not check for - * running off the end of the input data buffer. This is - * possible for G3-encoded data because it prescans the input - * data to count EOL markers, but can cause problems for G4 - * data. In any event, we don't prescan and must watch for - * running out of data since we can't permit the library to - * scan past the end of the input data buffer. - * - * Finally, note that we must handle remaindered data at the end - * of a strip specially. The coder asks for a fixed number of - * bits when scanning for the next code. This may be more bits - * than are actually present in the data stream. If we appear - * to run out of data but still have some number of valid bits - * remaining then we makeup the requested amount with zeros and - * return successfully. If the returned data is incorrect then - * we should be called again and get a premature EOF error; - * otherwise we should get the right answer. - */ - private bool NeedBits8(int n) - { - if (m_bit < n) - { - if (EndOfData()) - { - if (m_bit == 0) - { - /* no valid bits */ - return false; - } - - m_bit = n; /* pad with zeros */ - } - else - { - m_data |= m_bitmap[m_tif.m_rawdata[m_tif.m_rawcp]] << m_bit; - m_tif.m_rawcp++; - m_bit += 8; - } - } - - return true; - } - - private bool NeedBits16(int n) - { - if (m_bit < n) - { - if (EndOfData()) - { - if (m_bit == 0) - { - /* no valid bits */ - return false; - } - - m_bit = n; /* pad with zeros */ - } - else - { - m_data |= m_bitmap[m_tif.m_rawdata[m_tif.m_rawcp]] << m_bit; - m_tif.m_rawcp++; - m_bit += 8; - if (m_bit < n) - { - if (EndOfData()) - { - /* NB: we know BitsAvail is non-zero here */ - m_bit = n; /* pad with zeros */ - } - else - { - m_data |= m_bitmap[m_tif.m_rawdata[m_tif.m_rawcp]] << m_bit; - m_tif.m_rawcp++; - m_bit += 8; - } - } - } - } - - return true; - } - - private bool LOOKUP8(out faxTableEntry TabEnt, int wid) - { - if (!NeedBits8(wid)) - { - TabEnt = new faxTableEntry(); - return false; - } - - TabEnt = faxTableEntry.FromArray(m_faxMainTable, GetBits(wid)); - ClrBits(TabEnt.Width); - - return true; - } - - private bool LOOKUP16(out faxTableEntry TabEnt, int wid, bool useBlack) - { - if (!NeedBits16(wid)) - { - TabEnt = new faxTableEntry(); - return false; - } - - if (useBlack) - TabEnt = faxTableEntry.FromArray(m_faxBlackTable, GetBits(wid)); - else - TabEnt = faxTableEntry.FromArray(m_faxWhiteTable, GetBits(wid)); - - ClrBits(TabEnt.Width); - - return true; - } - - /* - * Synchronize input decoding at the start of each - * row by scanning for an EOL (if appropriate) and - * skipping any trash data that might be present - * after a decoding error. Note that the decoding - * done elsewhere that recognizes an EOL only consumes - * 11 consecutive zero bits. This means that if EOLcnt - * is non-zero then we still need to scan for the final flag - * bit that is part of the EOL code. - */ - private bool SYNC_EOL() - { - if (m_EOLcnt == 0) - { - for ( ; ; ) - { - if (!NeedBits16(11)) - return false; - - if (GetBits(11) == 0) - break; - - ClrBits(1); - } - } - - for ( ; ; ) - { - if (!NeedBits8(8)) - return false; - - if (GetBits(8) != 0) - break; - - ClrBits(8); - } - - while (GetBits(1) == 0) - ClrBits(1); - - ClrBits(1); /* EOL bit */ - m_EOLcnt = 0; /* reset EOL counter/flag */ - - return true; - } - - /* - * Setup G3/G4-related compression/decompression state - * before data is processed. This routine is called once - * per image -- it sets up different state based on whether - * or not decoding or encoding is being done and whether - * 1D- or 2D-encoded data is involved. - */ - private bool setupState() - { - if (m_tif.m_dir.td_bitspersample != 1) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "Bits/sample must be 1 for Group 3/4 encoding/decoding"); - return false; - } - - /* - * Calculate the scanline/tile widths. - */ - int rowbytes = 0; - int rowpixels = 0; - if (m_tif.IsTiled()) - { - rowbytes = m_tif.TileRowSize(); - rowpixels = m_tif.m_dir.td_tilewidth; - } - else - { - rowbytes = m_tif.ScanlineSize(); - rowpixels = m_tif.m_dir.td_imagewidth; - } - - m_rowbytes = rowbytes; - m_rowpixels = rowpixels; - - /* - * Allocate any additional space required for decoding/encoding. - */ - bool needsRefLine = ((m_groupoptions & Group3Opt.ENCODING2D) != 0 || - m_tif.m_dir.td_compression == Compression.CCITTFAX4); - - // Assure that allocation computations do not overflow. - m_runs = null; - int nruns = Tiff.roundUp(rowpixels, 32); - if (needsRefLine) - { - long multiplied = (long)nruns * 2; - if (multiplied > int.MaxValue) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "Row pixels integer overflow (rowpixels {0})", rowpixels); - return false; - } - else - { - nruns = (int)multiplied; - } - } - - if (nruns == 0 || ((long)nruns * 2) > int.MaxValue) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "Row pixels integer overflow (rowpixels {0})", rowpixels); - return false; - } - - m_runs = new int[2 * nruns]; - m_curruns = 0; - - if (needsRefLine) - m_refruns = nruns; - else - m_refruns = -1; - - if (m_tif.m_dir.td_compression == Compression.CCITTFAX3 && is2DEncoding()) - { - /* NB: default is 1D routine */ - m_decoder = Decoder.useFax3_2DDecoder; - } - - if (needsRefLine) - { - /* 2d encoding */ - /* - * 2d encoding requires a scanline - * buffer for the "reference line"; the - * scanline against which delta encoding - * is referenced. The reference line must - * be initialized to be "white" (done elsewhere). - */ - m_refline = new byte [rowbytes + 1]; - } - else - { - /* 1d encoding */ - m_refline = null; - } - - return true; - } - - /* - * Routine for handling various errors/conditions. - * Note how they are "glued into the decoder" by - * overriding the definitions used by the decoder. - */ - private void Fax3Unexpected(string module) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "{0}: Bad code word at line {1} of {2} {3} (x {4})", - m_tif.m_name, m_line, m_tif.IsTiled() ? "tile" : "strip", - (m_tif.IsTiled() ? m_tif.m_curtile : m_tif.m_curstrip), m_a0); - } - - private void Fax3Extension(string module) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "{0}: Uncompressed data (not supported) at line {1} of {2} {3} (x {4})", - m_tif.m_name, m_line, m_tif.IsTiled() ? "tile" : "strip", - (m_tif.IsTiled() ? m_tif.m_curtile : m_tif.m_curstrip), m_a0); - } - - private void Fax3BadLength(string module) - { - Tiff.WarningExt(m_tif, m_tif.m_clientdata, module, - "{0}: {1} at line {2} of {3} {4} (got {5}, expected {6})", - m_tif.m_name, m_a0 < m_rowpixels ? "Premature EOL" : "Line length mismatch", - m_line, m_tif.IsTiled() ? "tile" : "strip", - (m_tif.IsTiled() ? m_tif.m_curtile : m_tif.m_curstrip), m_a0, m_rowpixels); - } - - private void Fax3PrematureEOF(string module) - { - Tiff.WarningExt(m_tif, m_tif.m_clientdata, module, - "{0}: Premature EOF at line {1} of {2} {3} (x {4})", - m_tif.m_name, m_line, m_tif.IsTiled() ? "tile" : "strip", - (m_tif.IsTiled() ? m_tif.m_curtile : m_tif.m_curstrip), m_a0); - } - - /// - /// Decode the requested amount of G3 1D-encoded data. - /// - private bool Fax3Decode1D(byte[] buffer, int offset, int count) - { - const string module = "Fax3Decode1D"; - - // current row's run array - m_thisrun = m_curruns; - while (count > 0) - { - m_a0 = 0; - m_RunLength = 0; - m_pa = m_thisrun; - - if (!SYNC_EOL()) - { - // premature EOF - CLEANUP_RUNS(module); - fill(buffer, offset, m_runs, m_thisrun, m_pa, m_rowpixels); - return false; - } - - bool expandSucceeded = EXPAND1D(module); - if (!expandSucceeded) - { - fill(buffer, offset, m_runs, m_thisrun, m_pa, m_rowpixels); - return false; - } - - fill(buffer, offset, m_runs, m_thisrun, m_pa, m_rowpixels); - offset += m_rowbytes; - count -= m_rowbytes; - m_line++; - } - - return true; - } - - /// - /// Decode the requested amount of G3 2D-encoded data. - /// - private bool Fax3Decode2D(byte[] buffer, int offset, int count) - { - const string module = "Fax3Decode2D"; - - while (count > 0) - { - m_a0 = 0; - m_RunLength = 0; - m_pa = m_curruns; - m_thisrun = m_curruns; - - bool prematureEOF = false; - if (!SYNC_EOL()) - prematureEOF = true; - - if (!prematureEOF && !NeedBits8(1)) - prematureEOF = true; - - if (!prematureEOF) - { - int is1D = GetBits(1); // 1D/2D-encoding tag bit - ClrBits(1); - m_pb = m_refruns; - int b1 = m_runs[m_pb]; - m_pb++; // next change on prev line - - bool expandSucceeded = false; - if (is1D != 0) - expandSucceeded = EXPAND1D(module); - else - expandSucceeded = EXPAND2D(module, b1); - - if (expandSucceeded) - { - fill(buffer, offset, m_runs, m_thisrun, m_pa, m_rowpixels); - SETVALUE(0); // imaginary change for reference - SWAP(ref m_curruns, ref m_refruns); - offset += m_rowbytes; - count -= m_rowbytes; - m_line++; - continue; - } - } - else - { - // premature EOF - CLEANUP_RUNS(module); - } - - // premature EOF - fill(buffer, offset, m_runs, m_thisrun, m_pa, m_rowpixels); - return false; - } - - return true; - } - - /* - * 1d-encode a row of pixels. The encoding is - * a sequence of all-white or all-black spans - * of pixels encoded with Huffman codes. - */ - private bool Fax3Encode1DRow() - { - int bs = 0; - for ( ; ; ) - { - int span = find0span(m_buffer, m_offset, bs, m_rowpixels); /* white span */ - putspan(span, false); - bs += span; - if (bs >= m_rowpixels) - break; - - span = find1span(m_buffer, m_offset, bs, m_rowpixels); /* black span */ - putspan(span, true); - bs += span; - if (bs >= m_rowpixels) - break; - } - - if ((m_mode & (FaxMode.BYTEALIGN | FaxMode.WORDALIGN)) != 0) - { - if (m_bit != 8) - { - /* byte-align */ - flushBits(); - } - - if ((m_mode & FaxMode.WORDALIGN) != 0 && !isShortAligned(m_tif.m_rawcp)) - flushBits(); - } - - return true; - } - - /* - * 2d-encode a row of pixels. Consult the CCITT - * documentation for the algorithm. - */ - private bool Fax3Encode2DRow() - { - int a0 = 0; - int a1 = (Fax3Encode2DRow_Pixel(m_buffer, m_offset, 0) != 0 ? 0 : finddiff(m_buffer, m_offset, 0, m_rowpixels, 0)); - int b1 = (Fax3Encode2DRow_Pixel(m_refline, 0, 0) != 0 ? 0 : finddiff(m_refline, 0, 0, m_rowpixels, 0)); - - for (; ; ) - { - int b2 = finddiff2(m_refline, 0, b1, m_rowpixels, Fax3Encode2DRow_Pixel(m_refline, 0, b1)); - if (b2 >= a1) - { - int d = b1 - a1; - if (!(-3 <= d && d <= 3)) - { - /* horizontal mode */ - int a2 = finddiff2(m_buffer, m_offset, a1, m_rowpixels, Fax3Encode2DRow_Pixel(m_buffer, m_offset, a1)); - putcode(m_horizcode); - - if (a0 + a1 == 0 || Fax3Encode2DRow_Pixel(m_buffer, m_offset, a0) == 0) - { - putspan(a1 - a0, false); - putspan(a2 - a1, true); - } - else - { - putspan(a1 - a0, true); - putspan(a2 - a1, false); - } - - a0 = a2; - } - else - { - /* vertical mode */ - putcode(m_vcodes[d + 3]); - a0 = a1; - } - } - else - { - /* pass mode */ - putcode(m_passcode); - a0 = b2; - } - - if (a0 >= m_rowpixels) - break; - - a1 = finddiff(m_buffer, m_offset, a0, m_rowpixels, Fax3Encode2DRow_Pixel(m_buffer, m_offset, a0)); - - int color = Fax3Encode2DRow_Pixel(m_buffer, m_offset, a0); - if (color == 0) - color = 1; - else - color = 0; - - b1 = finddiff(m_refline, 0, a0, m_rowpixels, color); - b1 = finddiff(m_refline, 0, b1, m_rowpixels, Fax3Encode2DRow_Pixel(m_buffer, m_offset, a0)); - } - - return true; - } - - private static int Fax3Encode2DRow_Pixel(byte[] buf, int bufOffset, int ix) - { - // some images caused out-of-bounds exception here. not sure why. maybe the images are - // malformed or implementation is buggy. original libtiff does not produce exceptions - // here. it's just read after the end of the buffer. - - // it's a fast fix (use last byte when requested any byte beyond buffer end) for - // the problem that possibly should be reviewed. - // (it's weird but produced output is byte-to-byte equal to libtiff's one) - return (((buf[Math.Min(bufOffset + (ix >> 3), buf.Length - 1)]) >> (7 - (ix & 7))) & 1); - } - - /// - /// Encode a buffer of pixels. - /// - private bool Fax3Encode(byte[] buffer, int offset, int count) - { - m_buffer = buffer; - m_offset = offset; - - while (count > 0) - { - if ((m_mode & FaxMode.NOEOL) == 0) - Fax3PutEOL(); - - if (is2DEncoding()) - { - if (m_encoder == Fax3Encoder.useFax1DEncoder) - { - if (!Fax3Encode1DRow()) - return false; - - m_encoder = Fax3Encoder.useFax2DEncoder; - } - else - { - if (!Fax3Encode2DRow()) - return false; - - m_k--; - } - - if (m_k == 0) - { - m_encoder = Fax3Encoder.useFax1DEncoder; - m_k = m_maxk - 1; - } - else - { - Buffer.BlockCopy(m_buffer, m_offset, m_refline, 0, m_rowbytes); - } - } - else - { - if (!Fax3Encode1DRow()) - return false; - } - - m_offset += m_rowbytes; - count -= m_rowbytes; - } - - return true; - } - - private bool Fax3PostEncode() - { - if (m_bit != 8) - flushBits(); - - return true; - } - - private void InitCCITTFax3() - { - /* - * Merge codec-specific tag information and - * override parent get/set field methods. - */ - m_tif.MergeFieldInfo(m_faxFieldInfo, m_faxFieldInfo.Length); - - /* - * Allocate state block so tag methods have storage to record values. - */ - cleanState(); - m_rw_mode = m_tif.m_mode; - - m_parentTagMethods = m_tif.m_tagmethods; - m_tif.m_tagmethods = m_tagMethods; - - m_groupoptions = 0; - m_recvparams = 0; - m_subaddress = null; - m_faxdcs = null; - - if (m_rw_mode == Tiff.O_RDONLY) - { - // FIXME: improve for in place update - m_tif.m_flags |= TiffFlags.NOBITREV; - // decoder does bit reversal - } - - m_runs = null; - m_tif.SetField(TiffTag.FAXFILLFUNC, new Tiff.FaxFillFunc(fax3FillRuns)); - m_refline = null; - - m_decoder = Decoder.useFax3_1DDecoder; - m_encodingFax4 = false; - } - - private bool TIFFInitCCITTFax3() - { - InitCCITTFax3(); - m_tif.MergeFieldInfo(m_fax3FieldInfo, m_fax3FieldInfo.Length); - - /* - * The default format is Class/F-style w/o RTC. - */ - return m_tif.SetField(TiffTag.FAXMODE, FaxMode.CLASSF); - } - - /* - * CCITT Group 3 FAX Encoding. - */ - private void flushBits() - { - if (m_tif.m_rawcc >= m_tif.m_rawdatasize) - m_tif.flushData1(); - - m_tif.m_rawdata[m_tif.m_rawcp] = (byte)m_data; - m_tif.m_rawcp++; - m_tif.m_rawcc++; - m_data = 0; - m_bit = 8; - } - - /* - * Write a variable-length bit-value to - * the output stream. Values are - * assumed to be at most 16 bits. - */ - private void putBits(int bits, int length) - { - while (length > m_bit) - { - m_data |= bits >> (length - m_bit); - length -= m_bit; - flushBits(); - } - - m_data |= (bits & m_msbmask[length]) << (m_bit - length); - m_bit -= length; - if (m_bit == 0) - flushBits(); - } - - /* - * Write a code to the output stream. - */ - private void putcode(tableEntry te) - { - putBits(te.code, te.length); - } - - /* - * Write the sequence of codes that describes - * the specified span of zero's or one's. The - * appropriate table that holds the make-up and - * terminating codes is supplied. - */ - private void putspan(int span, bool useBlack) - { - short[] entries = null; - if (useBlack) - entries = m_faxBlackCodes; - else - entries = m_faxWhiteCodes; - - tableEntry te = tableEntry.FromArray(entries, 63 + (2560 >> 6)); - while (span >= 2624) - { - putBits(te.code, te.length); - span -= te.runlen; - } - - if (span >= 64) - { - te = tableEntry.FromArray(entries, 63 + (span >> 6)); - Debug.Assert(te.runlen == 64 * (span >> 6)); - putBits(te.code, te.length); - span -= te.runlen; - } - - te = tableEntry.FromArray(entries, span); - putBits(te.code, te.length); - } - - /* - * Write an EOL code to the output stream. The zero-fill - * logic for byte-aligning encoded scanlines is handled - * here. We also handle writing the tag bit for the next - * scanline when doing 2d encoding. - */ - private void Fax3PutEOL() - { - if ((m_groupoptions & Group3Opt.FILLBITS) != 0) - { - /* - * Force bit alignment so EOL will terminate on - * a byte boundary. That is, force the bit alignment - * to 16-12 = 4 before putting out the EOL code. - */ - int align = 8 - 4; - if (align != m_bit) - { - if (align > m_bit) - align = m_bit + (8 - align); - else - align = m_bit - align; - - putBits(0, align); - } - } - - int code = EOL_CODE; - int length = 12; - if (is2DEncoding()) - { - code = (code << 1); - if (m_encoder == Fax3Encoder.useFax1DEncoder) - code++; - - length++; - } - - putBits(code, length); - } - - /* - * Append a run to the run length array for the - * current row and reset decoding state. - */ - private void SETVALUE(int x) - { - m_runs[m_pa] = m_RunLength + x; - m_pa++; - m_a0 += x; - m_RunLength = 0; - } - - /* - * Cleanup the array of runs after decoding a row. - * We adjust final runs to insure the user buffer is not - * overwritten and/or undecoded area is white filled. - */ - private void CLEANUP_RUNS(string module) - { - if (m_RunLength != 0) - SETVALUE(0); - - if (m_a0 != m_rowpixels) - { - Fax3BadLength(module); - - while (m_a0 > m_rowpixels && m_pa > m_thisrun) - { - m_pa--; - m_a0 -= m_runs[m_pa]; - } - - if (m_a0 < m_rowpixels) - { - if (m_a0 < 0) - m_a0 = 0; - - if (((m_pa - m_thisrun) & 1) != 0) - SETVALUE(0); - - SETVALUE(m_rowpixels - m_a0); - } - else if (m_a0 > m_rowpixels) - { - SETVALUE(m_rowpixels); - SETVALUE(0); - } - } - } - - private void handlePrematureEOFinExpand2D(string module) - { - Fax3PrematureEOF(module); - CLEANUP_RUNS(module); - } - - /* - * Decode a line of 1D-encoded data. - */ - private bool EXPAND1D(string module) - { - faxTableEntry TabEnt; - - for ( ; ; ) - { - for ( ; ; ) - { - if (!LOOKUP16(out TabEnt, 12, false)) - { - Fax3PrematureEOF(module); - CLEANUP_RUNS(module); - return false; - } - - bool whiteDecodingDone = false; - switch (TabEnt.State) - { - case S_EOL: - m_EOLcnt = 1; - CLEANUP_RUNS(module); - return true; - - case S_TermW: - SETVALUE(TabEnt.Param); - whiteDecodingDone = true; - break; - - case S_MakeUpW: - case S_MakeUp: - m_a0 += TabEnt.Param; - m_RunLength += TabEnt.Param; - break; - - default: - /* "WhiteTable" */ - Fax3Unexpected(module); - CLEANUP_RUNS(module); - return true; - } - - if (whiteDecodingDone) - break; - } - - if (m_a0 >= m_rowpixels) - { - CLEANUP_RUNS(module); - return true; - } - - for ( ; ; ) - { - if (!LOOKUP16(out TabEnt, 13, true)) - { - Fax3PrematureEOF(module); - CLEANUP_RUNS(module); - return false; - } - - bool blackDecodingDone = false; - switch (TabEnt.State) - { - case S_EOL: - m_EOLcnt = 1; - CLEANUP_RUNS(module); - return true; - - case S_TermB: - SETVALUE(TabEnt.Param); - blackDecodingDone = true; - break; - - case S_MakeUpB: - case S_MakeUp: - m_a0 += TabEnt.Param; - m_RunLength += TabEnt.Param; - break; - - default: - /* "BlackTable" */ - Fax3Unexpected(module); - CLEANUP_RUNS(module); - return true; - } - - if (blackDecodingDone) - break; - } - - if (m_a0 >= m_rowpixels) - { - CLEANUP_RUNS(module); - return true; - } - - if (m_runs[m_pa - 1] == 0 && m_runs[m_pa - 2] == 0) - m_pa -= 2; - } - } - - /* - * Expand a row of 2D-encoded data. - */ - private bool EXPAND2D(string module, int b1) - { - faxTableEntry TabEnt; - bool decodingDone = false; - - while (m_a0 < m_rowpixels) - { - if (!LOOKUP8(out TabEnt, 7)) - { - handlePrematureEOFinExpand2D(module); - return false; - } - - switch (TabEnt.State) - { - case S_Pass: - CHECK_b1(ref b1); - b1 += m_runs[m_pb]; - m_pb++; - m_RunLength += b1 - m_a0; - m_a0 = b1; - b1 += m_runs[m_pb]; - m_pb++; - break; - - case S_Horiz: - if (((m_pa - m_thisrun) & 1) != 0) - { - for ( ; ; ) - { - /* black first */ - if (!LOOKUP16(out TabEnt, 13, true)) - { - handlePrematureEOFinExpand2D(module); - return false; - } - - bool doneWhite2d = false; - switch (TabEnt.State) - { - case S_TermB: - SETVALUE(TabEnt.Param); - doneWhite2d = true; - break; - - case S_MakeUpB: - case S_MakeUp: - m_a0 += TabEnt.Param; - m_RunLength += TabEnt.Param; - break; - - default: - /* "BlackTable" */ - Fax3Unexpected(module); - decodingDone = true; - break; - } - - if (doneWhite2d || decodingDone) - break; - } - - if (decodingDone) - break; - - for ( ; ; ) - { - /* then white */ - if (!LOOKUP16(out TabEnt, 12, false)) - { - handlePrematureEOFinExpand2D(module); - return false; - } - - bool doneBlack2d = false; - switch (TabEnt.State) - { - case S_TermW: - SETVALUE(TabEnt.Param); - doneBlack2d = true; - break; - - case S_MakeUpW: - case S_MakeUp: - m_a0 += TabEnt.Param; - m_RunLength += TabEnt.Param; - break; - - default: - /* "WhiteTable" */ - Fax3Unexpected(module); - decodingDone = true; - break; - } - - if (doneBlack2d || decodingDone) - break; - } - - if (decodingDone) - break; - } - else - { - for ( ; ; ) - { - /* white first */ - if (!LOOKUP16(out TabEnt, 12, false)) - { - handlePrematureEOFinExpand2D(module); - return false; - } - - bool doneWhite2d = false; - switch (TabEnt.State) - { - case S_TermW: - SETVALUE(TabEnt.Param); - doneWhite2d = true; - break; - - case S_MakeUpW: - case S_MakeUp: - m_a0 += TabEnt.Param; - m_RunLength += TabEnt.Param; - break; - - default: - /* "WhiteTable" */ - Fax3Unexpected(module); - decodingDone = true; - break; - } - - if (doneWhite2d || decodingDone) - break; - } - - if (decodingDone) - break; - - for ( ; ; ) - { - /* then black */ - if (!LOOKUP16(out TabEnt, 13, true)) - { - handlePrematureEOFinExpand2D(module); - return false; - } - - bool doneBlack2d = false; - switch (TabEnt.State) - { - case S_TermB: - SETVALUE(TabEnt.Param); - doneBlack2d = true; - break; - - case S_MakeUpB: - case S_MakeUp: - m_a0 += TabEnt.Param; - m_RunLength += TabEnt.Param; - break; - - default: - /* "BlackTable" */ - Fax3Unexpected(module); - decodingDone = true; - break; - } - - if (doneBlack2d || decodingDone) - break; - } - } - - if (decodingDone) - break; - - CHECK_b1(ref b1); - break; - - case S_V0: - CHECK_b1(ref b1); - SETVALUE(b1 - m_a0); - b1 += m_runs[m_pb]; - m_pb++; - break; - - case S_VR: - CHECK_b1(ref b1); - SETVALUE(b1 - m_a0 + TabEnt.Param); - b1 += m_runs[m_pb]; - m_pb++; - break; - - case S_VL: - CHECK_b1(ref b1); - SETVALUE(b1 - m_a0 - TabEnt.Param); - m_pb--; - b1 -= m_runs[m_pb]; - break; - - case S_Ext: - m_runs[m_pa] = m_rowpixels - m_a0; - m_pa++; - Fax3Extension(module); - decodingDone = true; - break; - - case S_EOL: - m_runs[m_pa] = m_rowpixels - m_a0; - m_pa++; - - if (!NeedBits8(4)) - { - handlePrematureEOFinExpand2D(module); - return false; - } - - if (GetBits(4) != 0) - { - /* "EOL" */ - Fax3Unexpected(module); - } - - ClrBits(4); - m_EOLcnt = 1; - decodingDone = true; - break; - - default: - Fax3Unexpected(module); - decodingDone = true; - break; - } - } - - if (!decodingDone && m_RunLength != 0) - { - if (m_RunLength + m_a0 < m_rowpixels) - { - /* expect a final V0 */ - if (!NeedBits8(1)) - { - handlePrematureEOFinExpand2D(module); - return false; - } - - if (GetBits(1) == 0) - { - /* "MainTable" */ - Fax3Unexpected(module); - decodingDone = true; - } - - if (!decodingDone) - ClrBits(1); - } - - if (!decodingDone) - SETVALUE(0); - } - - CLEANUP_RUNS(module); - return true; - } - - /* - * CCITT Group 3 1-D Modified Huffman RLE Compression Support. - * (Compression algorithms 2 and 32771) - */ - - private bool TIFFInitCCITTRLE() - { - /* reuse G3 support */ - InitCCITTFax3(); - - m_decoder = Decoder.useFax3RLEDecoder; - - /* - * Suppress RTC+EOLs when encoding and byte-align data. - */ - return m_tif.SetField(TiffTag.FAXMODE, - FaxMode.NORTC | FaxMode.NOEOL | FaxMode.BYTEALIGN); - } - - private bool TIFFInitCCITTRLEW() - { - /* reuse G3 support */ - InitCCITTFax3(); - - m_decoder = Decoder.useFax3RLEDecoder; - - /* - * Suppress RTC+EOLs when encoding and word-align data. - */ - return m_tif.SetField(TiffTag.FAXMODE, - FaxMode.NORTC | FaxMode.NOEOL | FaxMode.WORDALIGN); - } - - /// - /// Decode the requested amount of RLE-encoded data. - /// - private bool Fax3DecodeRLE(byte[] buffer, int offset, int count) - { - const string module = "Fax3DecodeRLE"; - - int thisrun = m_curruns; // current row's run array - - while (count > 0) - { - m_a0 = 0; - m_RunLength = 0; - m_pa = thisrun; - - bool expandSucceeded = EXPAND1D(module); - if (expandSucceeded) - { - fill(buffer, offset, m_runs, thisrun, m_pa, m_rowpixels); - - // Cleanup at the end of the row. - if ((m_mode & FaxMode.BYTEALIGN) != 0) - { - int n = m_bit - (m_bit & ~7); - ClrBits(n); - } - else if ((m_mode & FaxMode.WORDALIGN) != 0) - { - int n = m_bit - (m_bit & ~15); - ClrBits(n); - if (m_bit == 0 && !isShortAligned(m_tif.m_rawcp)) - m_tif.m_rawcp++; - } - - offset += m_rowbytes; - count -= m_rowbytes; - m_line++; - continue; - } - - // premature EOF - fill(buffer, offset, m_runs, thisrun, m_pa, m_rowpixels); - return false; - } - - return true; - } - - /* - * CCITT Group 4 (T.6) Facsimile-compatible - * Compression Scheme Support. - */ - - private bool TIFFInitCCITTFax4() - { - /* reuse G3 support */ - InitCCITTFax3(); - - m_tif.MergeFieldInfo(m_fax4FieldInfo, m_fax4FieldInfo.Length); - - m_decoder = Decoder.useFax4Decoder; - m_encodingFax4 = true; - - /* - * Suppress RTC at the end of each strip. - */ - return m_tif.SetField(TiffTag.FAXMODE, FaxMode.NORTC); - } - - /// - /// Decode the requested amount of G4-encoded data. - /// - private bool Fax4Decode(byte[] buffer, int offset, int count) - { - const string module = "Fax4Decode"; - - while (count > 0) - { - m_a0 = 0; - m_RunLength = 0; - m_thisrun = m_curruns; - m_pa = m_curruns; - m_pb = m_refruns; - int b1 = m_runs[m_pb]; - m_pb++; // next change on prev line - - bool expandSucceeded = EXPAND2D(module, b1); - if (expandSucceeded && m_EOLcnt != 0) - expandSucceeded = false; - - if (expandSucceeded) - { - fill(buffer, offset, m_runs, m_thisrun, m_pa, m_rowpixels); - SETVALUE(0); // imaginary change for reference - SWAP(ref m_curruns, ref m_refruns); - offset += m_rowbytes; - count -= m_rowbytes; - m_line++; - continue; - } - - NeedBits16(13); - ClrBits(13); - fill(buffer, offset, m_runs, m_thisrun, m_pa, m_rowpixels); - return false; - } - - return true; - } - - /// - /// Encode the requested amount of data. - /// - private bool Fax4Encode(byte[] buffer, int offset, int count) - { - m_buffer = buffer; - m_offset = offset; - - while (count > 0) - { - if (!Fax3Encode2DRow()) - return false; - - Buffer.BlockCopy(m_buffer, m_offset, m_refline, 0, m_rowbytes); - m_offset += m_rowbytes; - count -= m_rowbytes; - } - - return true; - } - - private bool Fax4PostEncode() - { - // terminate strip w/ EOFB - putBits(EOL_CODE, 12); - putBits(EOL_CODE, 12); - - if (m_bit != 8) - flushBits(); - - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CCITTCodecTagMethods.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CCITTCodecTagMethods.cs deleted file mode 100644 index e884c626..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CCITTCodecTagMethods.cs +++ /dev/null @@ -1,203 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System.Diagnostics; -using System.IO; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - class CCITTCodecTagMethods : TiffTagMethods - { - public override bool SetField(Tiff tif, TiffTag tag, FieldValue[] ap) - { - CCITTCodec sp = tif.m_currentCodec as CCITTCodec; - Debug.Assert(sp != null); - - switch (tag) - { - case TiffTag.FAXMODE: - sp.m_mode = (FaxMode)ap[0].ToShort(); - return true; /* NB: pseudo tag */ - case TiffTag.FAXFILLFUNC: - sp.fill = ap[0].Value as Tiff.FaxFillFunc; - return true; /* NB: pseudo tag */ - case TiffTag.GROUP3OPTIONS: - /* XXX: avoid reading options if compression mismatches. */ - if (tif.m_dir.td_compression == Compression.CCITTFAX3) - sp.m_groupoptions = (Group3Opt)ap[0].ToShort(); - break; - case TiffTag.GROUP4OPTIONS: - /* XXX: avoid reading options if compression mismatches. */ - if (tif.m_dir.td_compression == Compression.CCITTFAX4) - sp.m_groupoptions = (Group3Opt)ap[0].ToShort(); - break; - case TiffTag.BADFAXLINES: - sp.m_badfaxlines = ap[0].ToInt(); - break; - case TiffTag.CLEANFAXDATA: - sp.m_cleanfaxdata = (CleanFaxData)ap[0].ToByte(); - break; - case TiffTag.CONSECUTIVEBADFAXLINES: - sp.m_badfaxrun = ap[0].ToInt(); - break; - case TiffTag.FAXRECVPARAMS: - sp.m_recvparams = ap[0].ToInt(); - break; - case TiffTag.FAXSUBADDRESS: - Tiff.setString(out sp.m_subaddress, ap[0].ToString()); - break; - case TiffTag.FAXRECVTIME: - sp.m_recvtime = ap[0].ToInt(); - break; - case TiffTag.FAXDCS: - Tiff.setString(out sp.m_faxdcs, ap[0].ToString()); - break; - default: - return base.SetField(tif, tag, ap); - } - - TiffFieldInfo fip = tif.FieldWithTag(tag); - if (fip != null) - tif.setFieldBit(fip.Bit); - else - return false; - - tif.m_flags |= TiffFlags.DIRTYDIRECT; - return true; - } - - public override FieldValue[] GetField(Tiff tif, TiffTag tag) - { - CCITTCodec sp = tif.m_currentCodec as CCITTCodec; - Debug.Assert(sp != null); - - FieldValue[] result = new FieldValue[1]; - - switch (tag) - { - case TiffTag.FAXMODE: - result[0].Set(sp.m_mode); - break; - case TiffTag.FAXFILLFUNC: - result[0].Set(sp.fill); - break; - case TiffTag.GROUP3OPTIONS: - case TiffTag.GROUP4OPTIONS: - result[0].Set(sp.m_groupoptions); - break; - case TiffTag.BADFAXLINES: - result[0].Set(sp.m_badfaxlines); - break; - case TiffTag.CLEANFAXDATA: - result[0].Set(sp.m_cleanfaxdata); - break; - case TiffTag.CONSECUTIVEBADFAXLINES: - result[0].Set(sp.m_badfaxrun); - break; - case TiffTag.FAXRECVPARAMS: - result[0].Set(sp.m_recvparams); - break; - case TiffTag.FAXSUBADDRESS: - result[0].Set(sp.m_subaddress); - break; - case TiffTag.FAXRECVTIME: - result[0].Set(sp.m_recvtime); - break; - case TiffTag.FAXDCS: - result[0].Set(sp.m_faxdcs); - break; - default: - return base.GetField(tif, tag); - } - - return result; - } - - public override void PrintDir(Tiff tif, Stream fd, TiffPrintFlags flags) - { - CCITTCodec sp = tif.m_currentCodec as CCITTCodec; - Debug.Assert(sp != null); - - if (tif.fieldSet(CCITTCodec.FIELD_OPTIONS)) - { - string sep = " "; - if (tif.m_dir.td_compression == Compression.CCITTFAX4) - { - Tiff.fprintf(fd, " Group 4 Options:"); - if ((sp.m_groupoptions & Group3Opt.UNCOMPRESSED) != 0) - Tiff.fprintf(fd, "{0}uncompressed data", sep); - } - else - { - Tiff.fprintf(fd, " Group 3 Options:"); - if ((sp.m_groupoptions & Group3Opt.ENCODING2D) != 0) - { - Tiff.fprintf(fd, "{0}2-d encoding", sep); - sep = "+"; - } - - if ((sp.m_groupoptions & Group3Opt.FILLBITS) != 0) - { - Tiff.fprintf(fd, "{0}EOL padding", sep); - sep = "+"; - } - - if ((sp.m_groupoptions & Group3Opt.UNCOMPRESSED) != 0) - Tiff.fprintf(fd, "{0}uncompressed data", sep); - } - - Tiff.fprintf(fd, " ({0} = 0x{1:x})\n", sp.m_groupoptions, sp.m_groupoptions); - } - - if (tif.fieldSet(CCITTCodec.FIELD_CLEANFAXDATA)) - { - Tiff.fprintf(fd, " Fax Data:"); - - switch (sp.m_cleanfaxdata) - { - case CleanFaxData.CLEAN: - Tiff.fprintf(fd, " clean"); - break; - case CleanFaxData.REGENERATED: - Tiff.fprintf(fd, " receiver regenerated"); - break; - case CleanFaxData.UNCLEAN: - Tiff.fprintf(fd, " uncorrected errors"); - break; - } - - Tiff.fprintf(fd, " ({0} = 0x{1:x})\n", sp.m_cleanfaxdata, sp.m_cleanfaxdata); - } - - if (tif.fieldSet(CCITTCodec.FIELD_BADFAXLINES)) - Tiff.fprintf(fd, " Bad Fax Lines: {0}\n", sp.m_badfaxlines); - - if (tif.fieldSet(CCITTCodec.FIELD_BADFAXRUN)) - Tiff.fprintf(fd, " Consecutive Bad Fax Lines: {0}\n", sp.m_badfaxrun); - - if (tif.fieldSet(CCITTCodec.FIELD_RECVPARAMS)) - Tiff.fprintf(fd, " Fax Receive Parameters: {0,8:x}\n", sp.m_recvparams); - - if (tif.fieldSet(CCITTCodec.FIELD_SUBADDRESS)) - Tiff.fprintf(fd, " Fax SubAddress: {0}\n", sp.m_subaddress); - - if (tif.fieldSet(CCITTCodec.FIELD_RECVTIME)) - Tiff.fprintf(fd, " Fax Receive Time: {0} secs\n", sp.m_recvtime); - - if (tif.fieldSet(CCITTCodec.FIELD_FAXDCS)) - Tiff.fprintf(fd, " Fax DCS: {0}\n", sp.m_faxdcs); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CCITTCodec_Data.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CCITTCodec_Data.cs deleted file mode 100644 index ccd6e250..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CCITTCodec_Data.cs +++ /dev/null @@ -1,1572 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic.Internal -{ - partial class CCITTCodec - { - private static readonly int[] m_faxMainTable = - { - 12,7,0,3,1,0,5,3,1,3,1,0,2,3,0,3,1,0,4,3,1,3,1,0,1,4,0,3,1,0, - 5,3,1,3,1,0,2,3,0,3,1,0,4,3,1,3,1,0,5,6,2,3,1,0,5,3,1,3,1,0, - 2,3,0,3,1,0,4,3,1,3,1,0,1,4,0,3,1,0,5,3,1,3,1,0,2,3,0,3,1,0, - 4,3,1,3,1,0,5,7,3,3,1,0,5,3,1,3,1,0,2,3,0,3,1,0,4,3,1,3,1,0, - 1,4,0,3,1,0,5,3,1,3,1,0,2,3,0,3,1,0,4,3,1,3,1,0,4,6,2,3,1,0, - 5,3,1,3,1,0,2,3,0,3,1,0,4,3,1,3,1,0,1,4,0,3,1,0,5,3,1,3,1,0, - 2,3,0,3,1,0,4,3,1,3,1,0,6,7,0,3,1,0,5,3,1,3,1,0,2,3,0,3,1,0, - 4,3,1,3,1,0,1,4,0,3,1,0,5,3,1,3,1,0,2,3,0,3,1,0,4,3,1,3,1,0, - 5,6,2,3,1,0,5,3,1,3,1,0,2,3,0,3,1,0,4,3,1,3,1,0,1,4,0,3,1,0, - 5,3,1,3,1,0,2,3,0,3,1,0,4,3,1,3,1,0,4,7,3,3,1,0,5,3,1,3,1,0, - 2,3,0,3,1,0,4,3,1,3,1,0,1,4,0,3,1,0,5,3,1,3,1,0,2,3,0,3,1,0, - 4,3,1,3,1,0,4,6,2,3,1,0,5,3,1,3,1,0,2,3,0,3,1,0,4,3,1,3,1,0, - 1,4,0,3,1,0,5,3,1,3,1,0,2,3,0,3,1,0,4,3,1,3,1,0 - }; - - private static readonly int[] m_faxWhiteTable = - { - 12,11,0,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128, - 7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5, - 7,8,39,7,6,16,9,8,576,7,4,6,7,7,19,7,5,8,7,8,55,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,45,7,4,3,7,5,11,7,4,5,7,8,53,7,5,9,9,8,448,7,4,6, - 7,8,35,9,5,128,7,8,51,7,6,15,7,8,63,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3, - 9,9,1472,7,4,5,7,8,43,7,6,17,9,9,1216,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,8,29,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9, - 9,6,1664,7,4,6,7,8,33,9,5,128,7,8,49,7,6,14,7,8,61,7,4,4,7,4,2,7,4,7, - 7,8,47,7,4,3,7,8,59,7,4,5,7,8,41,7,6,16,9,9,960,7,4,6,7,8,31,7,5,8, - 7,8,57,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5, - 7,7,26,7,5,9,9,9,704,7,4,6,7,8,37,9,5,128,7,7,25,7,6,15,9,8,320,7,4,4, - 7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6, - 7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,11,11,1792,7,4,3, - 7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14, - 7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5,7,8,40,7,6,16, - 9,9,832,7,4,6,7,7,19,7,5,8,7,8,56,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,8,46,7,4,3,7,5,11,7,4,5,7,8,54,7,5,9,9,8,512,7,4,6,7,8,36,9,5,128, - 7,8,52,7,6,15,7,8,0,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,9,9,1600,7,4,5, - 7,8,44,7,6,17,9,9,1344,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,30,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6, - 7,8,34,9,5,128,7,8,50,7,6,14,7,8,62,7,4,4,7,4,2,7,4,7,7,8,48,7,4,3, - 7,8,60,7,4,5,7,8,42,7,6,16,9,9,1088,7,4,6,7,8,32,7,5,8,7,8,58,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9, - 9,8,640,7,4,6,7,8,38,9,5,128,7,7,25,7,6,15,9,8,384,7,4,4,7,4,2,7,4,7, - 7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8, - 9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,0,0,0,7,4,3,7,5,11,7,4,5, - 7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4, - 7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5,7,8,39,7,6,16,9,8,576,7,4,6, - 7,7,19,7,5,8,7,8,55,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,45,7,4,3, - 7,5,11,7,4,5,7,8,53,7,5,9,9,8,448,7,4,6,7,8,35,9,5,128,7,8,51,7,6,15, - 7,8,63,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,9,9,1536,7,4,5,7,8,43,7,6,17, - 9,9,1280,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,8,29,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,8,33,9,5,128, - 7,8,49,7,6,14,7,8,61,7,4,4,7,4,2,7,4,7,7,8,47,7,4,3,7,8,59,7,4,5, - 7,8,41,7,6,16,9,9,1024,7,4,6,7,8,31,7,5,8,7,8,57,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9,9,9,768,7,4,6, - 7,8,37,9,5,128,7,7,25,7,6,15,9,8,320,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3, - 7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,11,11,1856,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9, - 9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7, - 7,7,23,7,4,3,7,7,27,7,4,5,7,8,40,7,6,16,9,9,896,7,4,6,7,7,19,7,5,8, - 7,8,56,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,46,7,4,3,7,5,11,7,4,5, - 7,8,54,7,5,9,9,8,512,7,4,6,7,8,36,9,5,128,7,8,52,7,6,15,7,8,0,7,4,4, - 7,4,2,7,4,7,7,6,13,7,4,3,9,9,1728,7,4,5,7,8,44,7,6,17,9,9,1408,7,4,6, - 7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,30,7,4,3, - 7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,8,34,9,5,128,7,8,50,7,6,14, - 7,8,62,7,4,4,7,4,2,7,4,7,7,8,48,7,4,3,7,8,60,7,4,5,7,8,42,7,6,16, - 9,9,1152,7,4,6,7,8,32,7,5,8,7,8,58,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9,9,8,640,7,4,6,7,8,38,9,5,128, - 7,7,25,7,6,15,9,8,384,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5, - 7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,0,0,0,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6, - 7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3, - 7,7,27,7,4,5,7,8,39,7,6,16,9,8,576,7,4,6,7,7,19,7,5,8,7,8,55,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,8,45,7,4,3,7,5,11,7,4,5,7,8,53,7,5,9, - 9,8,448,7,4,6,7,8,35,9,5,128,7,8,51,7,6,15,7,8,63,7,4,4,7,4,2,7,4,7, - 7,6,13,7,4,3,9,9,1472,7,4,5,7,8,43,7,6,17,9,9,1216,7,4,6,7,6,1,7,5,8, - 9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,29,7,4,3,7,5,11,7,4,5, - 7,6,12,7,5,9,9,6,1664,7,4,6,7,8,33,9,5,128,7,8,49,7,6,14,7,8,61,7,4,4, - 7,4,2,7,4,7,7,8,47,7,4,3,7,8,59,7,4,5,7,8,41,7,6,16,9,9,960,7,4,6, - 7,8,31,7,5,8,7,8,57,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3, - 7,5,11,7,4,5,7,7,26,7,5,9,9,9,704,7,4,6,7,8,37,9,5,128,7,7,25,7,6,15, - 9,8,320,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17, - 9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 11,12,2112,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128, - 7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5, - 7,8,40,7,6,16,9,9,832,7,4,6,7,7,19,7,5,8,7,8,56,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,46,7,4,3,7,5,11,7,4,5,7,8,54,7,5,9,9,8,512,7,4,6, - 7,8,36,9,5,128,7,8,52,7,6,15,7,8,0,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3, - 9,9,1600,7,4,5,7,8,44,7,6,17,9,9,1344,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,8,30,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9, - 9,6,1664,7,4,6,7,8,34,9,5,128,7,8,50,7,6,14,7,8,62,7,4,4,7,4,2,7,4,7, - 7,8,48,7,4,3,7,8,60,7,4,5,7,8,42,7,6,16,9,9,1088,7,4,6,7,8,32,7,5,8, - 7,8,58,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5, - 7,7,26,7,5,9,9,8,640,7,4,6,7,8,38,9,5,128,7,7,25,7,6,15,9,8,384,7,4,4, - 7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6, - 7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,0,0,0,7,4,3, - 7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14, - 7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5,7,8,39,7,6,16, - 9,8,576,7,4,6,7,7,19,7,5,8,7,8,55,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,8,45,7,4,3,7,5,11,7,4,5,7,8,53,7,5,9,9,8,448,7,4,6,7,8,35,9,5,128, - 7,8,51,7,6,15,7,8,63,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,9,9,1536,7,4,5, - 7,8,43,7,6,17,9,9,1280,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,29,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6, - 7,8,33,9,5,128,7,8,49,7,6,14,7,8,61,7,4,4,7,4,2,7,4,7,7,8,47,7,4,3, - 7,8,59,7,4,5,7,8,41,7,6,16,9,9,1024,7,4,6,7,8,31,7,5,8,7,8,57,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9, - 9,9,768,7,4,6,7,8,37,9,5,128,7,7,25,7,6,15,9,8,320,7,4,4,7,4,2,7,4,7, - 7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8, - 9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,11,12,2368,7,4,3,7,5,11,7,4,5, - 7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4, - 7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5,7,8,40,7,6,16,9,9,896,7,4,6, - 7,7,19,7,5,8,7,8,56,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,46,7,4,3, - 7,5,11,7,4,5,7,8,54,7,5,9,9,8,512,7,4,6,7,8,36,9,5,128,7,8,52,7,6,15, - 7,8,0,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,9,9,1728,7,4,5,7,8,44,7,6,17, - 9,9,1408,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,8,30,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,8,34,9,5,128, - 7,8,50,7,6,14,7,8,62,7,4,4,7,4,2,7,4,7,7,8,48,7,4,3,7,8,60,7,4,5, - 7,8,42,7,6,16,9,9,1152,7,4,6,7,8,32,7,5,8,7,8,58,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9,9,8,640,7,4,6, - 7,8,38,9,5,128,7,7,25,7,6,15,9,8,384,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3, - 7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,0,0,0,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9, - 9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7, - 7,7,23,7,4,3,7,7,27,7,4,5,7,8,39,7,6,16,9,8,576,7,4,6,7,7,19,7,5,8, - 7,8,55,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,45,7,4,3,7,5,11,7,4,5, - 7,8,53,7,5,9,9,8,448,7,4,6,7,8,35,9,5,128,7,8,51,7,6,15,7,8,63,7,4,4, - 7,4,2,7,4,7,7,6,13,7,4,3,9,9,1472,7,4,5,7,8,43,7,6,17,9,9,1216,7,4,6, - 7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,29,7,4,3, - 7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,8,33,9,5,128,7,8,49,7,6,14, - 7,8,61,7,4,4,7,4,2,7,4,7,7,8,47,7,4,3,7,8,59,7,4,5,7,8,41,7,6,16, - 9,9,960,7,4,6,7,8,31,7,5,8,7,8,57,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9,9,9,704,7,4,6,7,8,37,9,5,128, - 7,7,25,7,6,15,9,8,320,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5, - 7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,11,12,1984,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6, - 7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3, - 7,7,27,7,4,5,7,8,40,7,6,16,9,9,832,7,4,6,7,7,19,7,5,8,7,8,56,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,8,46,7,4,3,7,5,11,7,4,5,7,8,54,7,5,9, - 9,8,512,7,4,6,7,8,36,9,5,128,7,8,52,7,6,15,7,8,0,7,4,4,7,4,2,7,4,7, - 7,6,13,7,4,3,9,9,1600,7,4,5,7,8,44,7,6,17,9,9,1344,7,4,6,7,6,1,7,5,8, - 9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,30,7,4,3,7,5,11,7,4,5, - 7,6,12,7,5,9,9,6,1664,7,4,6,7,8,34,9,5,128,7,8,50,7,6,14,7,8,62,7,4,4, - 7,4,2,7,4,7,7,8,48,7,4,3,7,8,60,7,4,5,7,8,42,7,6,16,9,9,1088,7,4,6, - 7,8,32,7,5,8,7,8,58,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3, - 7,5,11,7,4,5,7,7,26,7,5,9,9,8,640,7,4,6,7,8,38,9,5,128,7,7,25,7,6,15, - 9,8,384,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17, - 9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 0,0,0,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128, - 7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5, - 7,8,39,7,6,16,9,8,576,7,4,6,7,7,19,7,5,8,7,8,55,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,45,7,4,3,7,5,11,7,4,5,7,8,53,7,5,9,9,8,448,7,4,6, - 7,8,35,9,5,128,7,8,51,7,6,15,7,8,63,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3, - 9,9,1536,7,4,5,7,8,43,7,6,17,9,9,1280,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,8,29,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9, - 9,6,1664,7,4,6,7,8,33,9,5,128,7,8,49,7,6,14,7,8,61,7,4,4,7,4,2,7,4,7, - 7,8,47,7,4,3,7,8,59,7,4,5,7,8,41,7,6,16,9,9,1024,7,4,6,7,8,31,7,5,8, - 7,8,57,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5, - 7,7,26,7,5,9,9,9,768,7,4,6,7,8,37,9,5,128,7,7,25,7,6,15,9,8,320,7,4,4, - 7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6, - 7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,11,11,1920,7,4,3, - 7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14, - 7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5,7,8,40,7,6,16, - 9,9,896,7,4,6,7,7,19,7,5,8,7,8,56,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,8,46,7,4,3,7,5,11,7,4,5,7,8,54,7,5,9,9,8,512,7,4,6,7,8,36,9,5,128, - 7,8,52,7,6,15,7,8,0,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,9,9,1728,7,4,5, - 7,8,44,7,6,17,9,9,1408,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,30,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6, - 7,8,34,9,5,128,7,8,50,7,6,14,7,8,62,7,4,4,7,4,2,7,4,7,7,8,48,7,4,3, - 7,8,60,7,4,5,7,8,42,7,6,16,9,9,1152,7,4,6,7,8,32,7,5,8,7,8,58,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9, - 9,8,640,7,4,6,7,8,38,9,5,128,7,7,25,7,6,15,9,8,384,7,4,4,7,4,2,7,4,7, - 7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8, - 9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,0,0,0,7,4,3,7,5,11,7,4,5, - 7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4, - 7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5,7,8,39,7,6,16,9,8,576,7,4,6, - 7,7,19,7,5,8,7,8,55,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,45,7,4,3, - 7,5,11,7,4,5,7,8,53,7,5,9,9,8,448,7,4,6,7,8,35,9,5,128,7,8,51,7,6,15, - 7,8,63,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,9,9,1472,7,4,5,7,8,43,7,6,17, - 9,9,1216,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,8,29,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,8,33,9,5,128, - 7,8,49,7,6,14,7,8,61,7,4,4,7,4,2,7,4,7,7,8,47,7,4,3,7,8,59,7,4,5, - 7,8,41,7,6,16,9,9,960,7,4,6,7,8,31,7,5,8,7,8,57,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9,9,9,704,7,4,6, - 7,8,37,9,5,128,7,7,25,7,6,15,9,8,320,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3, - 7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,11,12,2240,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9, - 9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7, - 7,7,23,7,4,3,7,7,27,7,4,5,7,8,40,7,6,16,9,9,832,7,4,6,7,7,19,7,5,8, - 7,8,56,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,46,7,4,3,7,5,11,7,4,5, - 7,8,54,7,5,9,9,8,512,7,4,6,7,8,36,9,5,128,7,8,52,7,6,15,7,8,0,7,4,4, - 7,4,2,7,4,7,7,6,13,7,4,3,9,9,1600,7,4,5,7,8,44,7,6,17,9,9,1344,7,4,6, - 7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,30,7,4,3, - 7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,8,34,9,5,128,7,8,50,7,6,14, - 7,8,62,7,4,4,7,4,2,7,4,7,7,8,48,7,4,3,7,8,60,7,4,5,7,8,42,7,6,16, - 9,9,1088,7,4,6,7,8,32,7,5,8,7,8,58,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9,9,8,640,7,4,6,7,8,38,9,5,128, - 7,7,25,7,6,15,9,8,384,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5, - 7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,0,0,0,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6, - 7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3, - 7,7,27,7,4,5,7,8,39,7,6,16,9,8,576,7,4,6,7,7,19,7,5,8,7,8,55,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,8,45,7,4,3,7,5,11,7,4,5,7,8,53,7,5,9, - 9,8,448,7,4,6,7,8,35,9,5,128,7,8,51,7,6,15,7,8,63,7,4,4,7,4,2,7,4,7, - 7,6,13,7,4,3,9,9,1536,7,4,5,7,8,43,7,6,17,9,9,1280,7,4,6,7,6,1,7,5,8, - 9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,29,7,4,3,7,5,11,7,4,5, - 7,6,12,7,5,9,9,6,1664,7,4,6,7,8,33,9,5,128,7,8,49,7,6,14,7,8,61,7,4,4, - 7,4,2,7,4,7,7,8,47,7,4,3,7,8,59,7,4,5,7,8,41,7,6,16,9,9,1024,7,4,6, - 7,8,31,7,5,8,7,8,57,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3, - 7,5,11,7,4,5,7,7,26,7,5,9,9,9,768,7,4,6,7,8,37,9,5,128,7,7,25,7,6,15, - 9,8,320,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17, - 9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 11,12,2496,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128, - 7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5, - 7,8,40,7,6,16,9,9,896,7,4,6,7,7,19,7,5,8,7,8,56,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,46,7,4,3,7,5,11,7,4,5,7,8,54,7,5,9,9,8,512,7,4,6, - 7,8,36,9,5,128,7,8,52,7,6,15,7,8,0,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3, - 9,9,1728,7,4,5,7,8,44,7,6,17,9,9,1408,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,8,30,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9, - 9,6,1664,7,4,6,7,8,34,9,5,128,7,8,50,7,6,14,7,8,62,7,4,4,7,4,2,7,4,7, - 7,8,48,7,4,3,7,8,60,7,4,5,7,8,42,7,6,16,9,9,1152,7,4,6,7,8,32,7,5,8, - 7,8,58,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5, - 7,7,26,7,5,9,9,8,640,7,4,6,7,8,38,9,5,128,7,7,25,7,6,15,9,8,384,7,4,4, - 7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6, - 7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,12,11,0,7,4,3, - 7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14, - 7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5,7,8,39,7,6,16, - 9,8,576,7,4,6,7,7,19,7,5,8,7,8,55,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,8,45,7,4,3,7,5,11,7,4,5,7,8,53,7,5,9,9,8,448,7,4,6,7,8,35,9,5,128, - 7,8,51,7,6,15,7,8,63,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,9,9,1472,7,4,5, - 7,8,43,7,6,17,9,9,1216,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,29,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6, - 7,8,33,9,5,128,7,8,49,7,6,14,7,8,61,7,4,4,7,4,2,7,4,7,7,8,47,7,4,3, - 7,8,59,7,4,5,7,8,41,7,6,16,9,9,960,7,4,6,7,8,31,7,5,8,7,8,57,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9, - 9,9,704,7,4,6,7,8,37,9,5,128,7,7,25,7,6,15,9,8,320,7,4,4,7,4,2,7,4,7, - 7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8, - 9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,11,11,1792,7,4,3,7,5,11,7,4,5, - 7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4, - 7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5,7,8,40,7,6,16,9,9,832,7,4,6, - 7,7,19,7,5,8,7,8,56,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,46,7,4,3, - 7,5,11,7,4,5,7,8,54,7,5,9,9,8,512,7,4,6,7,8,36,9,5,128,7,8,52,7,6,15, - 7,8,0,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,9,9,1600,7,4,5,7,8,44,7,6,17, - 9,9,1344,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,8,30,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,8,34,9,5,128, - 7,8,50,7,6,14,7,8,62,7,4,4,7,4,2,7,4,7,7,8,48,7,4,3,7,8,60,7,4,5, - 7,8,42,7,6,16,9,9,1088,7,4,6,7,8,32,7,5,8,7,8,58,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9,9,8,640,7,4,6, - 7,8,38,9,5,128,7,7,25,7,6,15,9,8,384,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3, - 7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,0,0,0,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9, - 9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7, - 7,7,23,7,4,3,7,7,27,7,4,5,7,8,39,7,6,16,9,8,576,7,4,6,7,7,19,7,5,8, - 7,8,55,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,45,7,4,3,7,5,11,7,4,5, - 7,8,53,7,5,9,9,8,448,7,4,6,7,8,35,9,5,128,7,8,51,7,6,15,7,8,63,7,4,4, - 7,4,2,7,4,7,7,6,13,7,4,3,9,9,1536,7,4,5,7,8,43,7,6,17,9,9,1280,7,4,6, - 7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,29,7,4,3, - 7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,8,33,9,5,128,7,8,49,7,6,14, - 7,8,61,7,4,4,7,4,2,7,4,7,7,8,47,7,4,3,7,8,59,7,4,5,7,8,41,7,6,16, - 9,9,1024,7,4,6,7,8,31,7,5,8,7,8,57,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9,9,9,768,7,4,6,7,8,37,9,5,128, - 7,7,25,7,6,15,9,8,320,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5, - 7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,11,11,1856,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6, - 7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3, - 7,7,27,7,4,5,7,8,40,7,6,16,9,9,896,7,4,6,7,7,19,7,5,8,7,8,56,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,8,46,7,4,3,7,5,11,7,4,5,7,8,54,7,5,9, - 9,8,512,7,4,6,7,8,36,9,5,128,7,8,52,7,6,15,7,8,0,7,4,4,7,4,2,7,4,7, - 7,6,13,7,4,3,9,9,1728,7,4,5,7,8,44,7,6,17,9,9,1408,7,4,6,7,6,1,7,5,8, - 9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,30,7,4,3,7,5,11,7,4,5, - 7,6,12,7,5,9,9,6,1664,7,4,6,7,8,34,9,5,128,7,8,50,7,6,14,7,8,62,7,4,4, - 7,4,2,7,4,7,7,8,48,7,4,3,7,8,60,7,4,5,7,8,42,7,6,16,9,9,1152,7,4,6, - 7,8,32,7,5,8,7,8,58,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3, - 7,5,11,7,4,5,7,7,26,7,5,9,9,8,640,7,4,6,7,8,38,9,5,128,7,7,25,7,6,15, - 9,8,384,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17, - 9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 0,0,0,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128, - 7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5, - 7,8,39,7,6,16,9,8,576,7,4,6,7,7,19,7,5,8,7,8,55,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,45,7,4,3,7,5,11,7,4,5,7,8,53,7,5,9,9,8,448,7,4,6, - 7,8,35,9,5,128,7,8,51,7,6,15,7,8,63,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3, - 9,9,1472,7,4,5,7,8,43,7,6,17,9,9,1216,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,8,29,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9, - 9,6,1664,7,4,6,7,8,33,9,5,128,7,8,49,7,6,14,7,8,61,7,4,4,7,4,2,7,4,7, - 7,8,47,7,4,3,7,8,59,7,4,5,7,8,41,7,6,16,9,9,960,7,4,6,7,8,31,7,5,8, - 7,8,57,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5, - 7,7,26,7,5,9,9,9,704,7,4,6,7,8,37,9,5,128,7,7,25,7,6,15,9,8,320,7,4,4, - 7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6, - 7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,11,12,2176,7,4,3, - 7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14, - 7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5,7,8,40,7,6,16, - 9,9,832,7,4,6,7,7,19,7,5,8,7,8,56,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,8,46,7,4,3,7,5,11,7,4,5,7,8,54,7,5,9,9,8,512,7,4,6,7,8,36,9,5,128, - 7,8,52,7,6,15,7,8,0,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,9,9,1600,7,4,5, - 7,8,44,7,6,17,9,9,1344,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,30,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6, - 7,8,34,9,5,128,7,8,50,7,6,14,7,8,62,7,4,4,7,4,2,7,4,7,7,8,48,7,4,3, - 7,8,60,7,4,5,7,8,42,7,6,16,9,9,1088,7,4,6,7,8,32,7,5,8,7,8,58,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9, - 9,8,640,7,4,6,7,8,38,9,5,128,7,7,25,7,6,15,9,8,384,7,4,4,7,4,2,7,4,7, - 7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8, - 9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,0,0,0,7,4,3,7,5,11,7,4,5, - 7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4, - 7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5,7,8,39,7,6,16,9,8,576,7,4,6, - 7,7,19,7,5,8,7,8,55,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,45,7,4,3, - 7,5,11,7,4,5,7,8,53,7,5,9,9,8,448,7,4,6,7,8,35,9,5,128,7,8,51,7,6,15, - 7,8,63,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,9,9,1536,7,4,5,7,8,43,7,6,17, - 9,9,1280,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,8,29,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,8,33,9,5,128, - 7,8,49,7,6,14,7,8,61,7,4,4,7,4,2,7,4,7,7,8,47,7,4,3,7,8,59,7,4,5, - 7,8,41,7,6,16,9,9,1024,7,4,6,7,8,31,7,5,8,7,8,57,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9,9,9,768,7,4,6, - 7,8,37,9,5,128,7,7,25,7,6,15,9,8,320,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3, - 7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,11,12,2432,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9, - 9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7, - 7,7,23,7,4,3,7,7,27,7,4,5,7,8,40,7,6,16,9,9,896,7,4,6,7,7,19,7,5,8, - 7,8,56,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,46,7,4,3,7,5,11,7,4,5, - 7,8,54,7,5,9,9,8,512,7,4,6,7,8,36,9,5,128,7,8,52,7,6,15,7,8,0,7,4,4, - 7,4,2,7,4,7,7,6,13,7,4,3,9,9,1728,7,4,5,7,8,44,7,6,17,9,9,1408,7,4,6, - 7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,30,7,4,3, - 7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,8,34,9,5,128,7,8,50,7,6,14, - 7,8,62,7,4,4,7,4,2,7,4,7,7,8,48,7,4,3,7,8,60,7,4,5,7,8,42,7,6,16, - 9,9,1152,7,4,6,7,8,32,7,5,8,7,8,58,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9,9,8,640,7,4,6,7,8,38,9,5,128, - 7,7,25,7,6,15,9,8,384,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5, - 7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,0,0,0,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6, - 7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3, - 7,7,27,7,4,5,7,8,39,7,6,16,9,8,576,7,4,6,7,7,19,7,5,8,7,8,55,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,8,45,7,4,3,7,5,11,7,4,5,7,8,53,7,5,9, - 9,8,448,7,4,6,7,8,35,9,5,128,7,8,51,7,6,15,7,8,63,7,4,4,7,4,2,7,4,7, - 7,6,13,7,4,3,9,9,1472,7,4,5,7,8,43,7,6,17,9,9,1216,7,4,6,7,6,1,7,5,8, - 9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,29,7,4,3,7,5,11,7,4,5, - 7,6,12,7,5,9,9,6,1664,7,4,6,7,8,33,9,5,128,7,8,49,7,6,14,7,8,61,7,4,4, - 7,4,2,7,4,7,7,8,47,7,4,3,7,8,59,7,4,5,7,8,41,7,6,16,9,9,960,7,4,6, - 7,8,31,7,5,8,7,8,57,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3, - 7,5,11,7,4,5,7,7,26,7,5,9,9,9,704,7,4,6,7,8,37,9,5,128,7,7,25,7,6,15, - 9,8,320,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17, - 9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 11,12,2048,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128, - 7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5, - 7,8,40,7,6,16,9,9,832,7,4,6,7,7,19,7,5,8,7,8,56,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,46,7,4,3,7,5,11,7,4,5,7,8,54,7,5,9,9,8,512,7,4,6, - 7,8,36,9,5,128,7,8,52,7,6,15,7,8,0,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3, - 9,9,1600,7,4,5,7,8,44,7,6,17,9,9,1344,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,8,30,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9, - 9,6,1664,7,4,6,7,8,34,9,5,128,7,8,50,7,6,14,7,8,62,7,4,4,7,4,2,7,4,7, - 7,8,48,7,4,3,7,8,60,7,4,5,7,8,42,7,6,16,9,9,1088,7,4,6,7,8,32,7,5,8, - 7,8,58,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5, - 7,7,26,7,5,9,9,8,640,7,4,6,7,8,38,9,5,128,7,7,25,7,6,15,9,8,384,7,4,4, - 7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6, - 7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,0,0,0,7,4,3, - 7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14, - 7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5,7,8,39,7,6,16, - 9,8,576,7,4,6,7,7,19,7,5,8,7,8,55,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,8,45,7,4,3,7,5,11,7,4,5,7,8,53,7,5,9,9,8,448,7,4,6,7,8,35,9,5,128, - 7,8,51,7,6,15,7,8,63,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,9,9,1536,7,4,5, - 7,8,43,7,6,17,9,9,1280,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,29,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6, - 7,8,33,9,5,128,7,8,49,7,6,14,7,8,61,7,4,4,7,4,2,7,4,7,7,8,47,7,4,3, - 7,8,59,7,4,5,7,8,41,7,6,16,9,9,1024,7,4,6,7,8,31,7,5,8,7,8,57,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9, - 9,9,768,7,4,6,7,8,37,9,5,128,7,7,25,7,6,15,9,8,320,7,4,4,7,4,2,7,4,7, - 7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8, - 9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,11,11,1920,7,4,3,7,5,11,7,4,5, - 7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4, - 7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5,7,8,40,7,6,16,9,9,896,7,4,6, - 7,7,19,7,5,8,7,8,56,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,46,7,4,3, - 7,5,11,7,4,5,7,8,54,7,5,9,9,8,512,7,4,6,7,8,36,9,5,128,7,8,52,7,6,15, - 7,8,0,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,9,9,1728,7,4,5,7,8,44,7,6,17, - 9,9,1408,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,8,30,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,8,34,9,5,128, - 7,8,50,7,6,14,7,8,62,7,4,4,7,4,2,7,4,7,7,8,48,7,4,3,7,8,60,7,4,5, - 7,8,42,7,6,16,9,9,1152,7,4,6,7,8,32,7,5,8,7,8,58,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9,9,8,640,7,4,6, - 7,8,38,9,5,128,7,7,25,7,6,15,9,8,384,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3, - 7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,0,0,0,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9, - 9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7, - 7,7,23,7,4,3,7,7,27,7,4,5,7,8,39,7,6,16,9,8,576,7,4,6,7,7,19,7,5,8, - 7,8,55,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,45,7,4,3,7,5,11,7,4,5, - 7,8,53,7,5,9,9,8,448,7,4,6,7,8,35,9,5,128,7,8,51,7,6,15,7,8,63,7,4,4, - 7,4,2,7,4,7,7,6,13,7,4,3,9,9,1472,7,4,5,7,8,43,7,6,17,9,9,1216,7,4,6, - 7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,29,7,4,3, - 7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,8,33,9,5,128,7,8,49,7,6,14, - 7,8,61,7,4,4,7,4,2,7,4,7,7,8,47,7,4,3,7,8,59,7,4,5,7,8,41,7,6,16, - 9,9,960,7,4,6,7,8,31,7,5,8,7,8,57,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9,9,9,704,7,4,6,7,8,37,9,5,128, - 7,7,25,7,6,15,9,8,320,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5, - 7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,11,12,2304,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6, - 7,7,20,9,5,128,7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3, - 7,7,27,7,4,5,7,8,40,7,6,16,9,9,832,7,4,6,7,7,19,7,5,8,7,8,56,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,8,46,7,4,3,7,5,11,7,4,5,7,8,54,7,5,9, - 9,8,512,7,4,6,7,8,36,9,5,128,7,8,52,7,6,15,7,8,0,7,4,4,7,4,2,7,4,7, - 7,6,13,7,4,3,9,9,1600,7,4,5,7,8,44,7,6,17,9,9,1344,7,4,6,7,6,1,7,5,8, - 9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,8,30,7,4,3,7,5,11,7,4,5, - 7,6,12,7,5,9,9,6,1664,7,4,6,7,8,34,9,5,128,7,8,50,7,6,14,7,8,62,7,4,4, - 7,4,2,7,4,7,7,8,48,7,4,3,7,8,60,7,4,5,7,8,42,7,6,16,9,9,1088,7,4,6, - 7,8,32,7,5,8,7,8,58,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3, - 7,5,11,7,4,5,7,7,26,7,5,9,9,8,640,7,4,6,7,8,38,9,5,128,7,7,25,7,6,15, - 9,8,384,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17, - 9,7,256,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 0,0,0,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128, - 7,7,24,7,6,14,7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5, - 7,8,39,7,6,16,9,8,576,7,4,6,7,7,19,7,5,8,7,8,55,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,45,7,4,3,7,5,11,7,4,5,7,8,53,7,5,9,9,8,448,7,4,6, - 7,8,35,9,5,128,7,8,51,7,6,15,7,8,63,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3, - 9,9,1536,7,4,5,7,8,43,7,6,17,9,9,1280,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,8,29,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9, - 9,6,1664,7,4,6,7,8,33,9,5,128,7,8,49,7,6,14,7,8,61,7,4,4,7,4,2,7,4,7, - 7,8,47,7,4,3,7,8,59,7,4,5,7,8,41,7,6,16,9,9,1024,7,4,6,7,8,31,7,5,8, - 7,8,57,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5, - 7,7,26,7,5,9,9,9,768,7,4,6,7,8,37,9,5,128,7,7,25,7,6,15,9,8,320,7,4,4, - 7,4,2,7,4,7,7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6, - 7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7,11,12,2560,7,4,3, - 7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6,7,7,20,9,5,128,7,7,24,7,6,14, - 7,7,28,7,4,4,7,4,2,7,4,7,7,7,23,7,4,3,7,7,27,7,4,5,7,8,40,7,6,16, - 9,9,896,7,4,6,7,7,19,7,5,8,7,8,56,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7, - 7,8,46,7,4,3,7,5,11,7,4,5,7,8,54,7,5,9,9,8,512,7,4,6,7,8,36,9,5,128, - 7,8,52,7,6,15,7,8,0,7,4,4,7,4,2,7,4,7,7,6,13,7,4,3,9,9,1728,7,4,5, - 7,8,44,7,6,17,9,9,1408,7,4,6,7,6,1,7,5,8,9,6,192,9,5,64,7,5,10,7,4,4, - 7,4,2,7,4,7,7,8,30,7,4,3,7,5,11,7,4,5,7,6,12,7,5,9,9,6,1664,7,4,6, - 7,8,34,9,5,128,7,8,50,7,6,14,7,8,62,7,4,4,7,4,2,7,4,7,7,8,48,7,4,3, - 7,8,60,7,4,5,7,8,42,7,6,16,9,9,1152,7,4,6,7,8,32,7,5,8,7,8,58,9,5,64, - 7,5,10,7,4,4,7,4,2,7,4,7,7,7,22,7,4,3,7,5,11,7,4,5,7,7,26,7,5,9, - 9,8,640,7,4,6,7,8,38,9,5,128,7,7,25,7,6,15,9,8,384,7,4,4,7,4,2,7,4,7, - 7,6,13,7,4,3,7,7,18,7,4,5,7,7,21,7,6,17,9,7,256,7,4,6,7,6,1,7,5,8, - 9,6,192,9,5,64,7,5,10,7,4,4,7,4,2,7,4,7 - }; - - private static readonly int[] m_faxBlackTable = - { - 12,11,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,9,15,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,10,18,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,10,17,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,11,11,1792,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,11,23,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,11,20,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,11,25,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,10,12,128,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,56,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,30,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,11,11,1856,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,57,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,11,21,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,54,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,9,15,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,52,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,48,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 11,12,2112,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,44,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,36,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,10,12,384,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,28,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,60,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,12,40,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,11,12,2368,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,10,16,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,10,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 10,10,64,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,9,15,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,10,18,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,10,17,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,11,12,1984,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,12,50,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,34,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,13,1664,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,26,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,10,13,1408,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,32,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,11,11,1920,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,61,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,42,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,10,13,1024,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,9,15,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 10,13,768,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,62,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,11,12,2240,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,46,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,38,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,13,512,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,11,19,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,11,24,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,11,22,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 11,12,2496,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,10,16,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,10,0,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,10,10,64,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,12,11,0,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,9,15,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,10,18,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,10,17,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,11,11,1792,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,11,23,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,11,20,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,11,25,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,10,12,192,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,13,1280,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,31,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,11,11,1856,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,12,58,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,11,21,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,13,896,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,9,15,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,10,13,640,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,49,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,11,12,2176,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,45,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,37,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,10,12,448,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,12,29,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 10,13,1536,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,41,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,11,12,2432,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,10,16,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,10,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,10,64,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,9,15,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,10,18,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,10,17,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 11,12,2048,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,51,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,35,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,10,12,320,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,27,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,59,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,12,33,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,11,11,1920,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,12,256,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,12,43,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 10,13,1152,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,9,15,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,55,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,63,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,11,12,2304,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,12,47,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,39,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,53,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,11,19,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,11,24,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,11,22,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,11,12,2560,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,10,16,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,10,0,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,10,10,64,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,12,11,0,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,9,15,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,10,18,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,10,17,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,11,11,1792,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,11,23,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,11,20,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,11,25,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 10,12,128,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,56,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,30,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 11,11,1856,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,57,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,11,21,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,12,54,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,9,15,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,52,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,12,48,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,11,12,2112,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,44,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,12,36,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 10,12,384,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,28,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,60,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,40,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,11,12,2368,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,10,16,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,10,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,10,64,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,9,15,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,10,18,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,10,17,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,11,12,1984,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,50,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,34,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,10,13,1728,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,12,26,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 10,13,1472,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,32,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,11,11,1920,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,61,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,42,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,13,1088,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,9,15,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,13,832,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,62,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 11,12,2240,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,46,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,38,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,10,13,576,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,11,19,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,11,24,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,11,22,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,11,12,2496,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,10,16,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,10,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 10,10,64,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,12,11,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,9,15,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,10,18,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,10,17,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,11,11,1792,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,11,23,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,11,20,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,11,25,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,12,192,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,10,13,1344,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,31,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,11,11,1856,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,58,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,11,21,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,10,13,960,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,9,15,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 10,13,704,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,49,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,11,12,2176,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,45,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,37,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,12,448,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,29,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,13,1600,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,41,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 11,12,2432,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,10,16,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,10,0,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,10,10,64,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,9,15,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,10,18,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,10,17,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,11,12,2048,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,51,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,12,35,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 10,12,320,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,27,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,59,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,33,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,11,11,1920,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,10,12,256,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,43,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,13,1216,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 0,0,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,8,13,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,9,15,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,12,55,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,63,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,11,12,2304,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,12,47,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,12,39,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,12,53,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,12,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,0,0,0,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,8,13,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,11,19,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,11,24,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,11,22,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,11,12,2560,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,7,10,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,10,16,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2,8,10,0,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2, - 8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,10,10,64,8,2,3, - 8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,9,8,2,3,8,3,1,8,2,2, - 8,4,5,8,2,3,8,3,4,8,2,2,8,7,11,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3, - 8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2, - 8,8,14,8,2,3,8,3,1,8,2,2,8,4,6,8,2,3,8,3,4,8,2,2,8,6,8,8,2,3, - 8,3,1,8,2,2,8,4,5,8,2,3,8,3,4,8,2,2,8,7,12,8,2,3,8,3,1,8,2,2, - 8,4,6,8,2,3,8,3,4,8,2,2,8,5,7,8,2,3,8,3,1,8,2,2,8,4,5,8,2,3, - 8,3,4,8,2,2 - }; - - /* - * Note that these tables are ordered such that the - * index into the table is known to be either the - * run length, or (run length / 64) + a fixed offset. - * - * NB: The G3CODE_INVALID entries are only used - * during state generation (see mkg3states.c). - */ - private static readonly short[] m_faxWhiteCodes = - { - 8, 0x35, 0, /* 0011 0101 */ - 6, 0x7, 1, /* 0001 11 */ - 4, 0x7, 2, /* 0111 */ - 4, 0x8, 3, /* 1000 */ - 4, 0xB, 4, /* 1011 */ - 4, 0xC, 5, /* 1100 */ - 4, 0xE, 6, /* 1110 */ - 4, 0xF, 7, /* 1111 */ - 5, 0x13, 8, /* 1001 1 */ - 5, 0x14, 9, /* 1010 0 */ - 5, 0x7, 10, /* 0011 1 */ - 5, 0x8, 11, /* 0100 0 */ - 6, 0x8, 12, /* 0010 00 */ - 6, 0x3, 13, /* 0000 11 */ - 6, 0x34, 14, /* 1101 00 */ - 6, 0x35, 15, /* 1101 01 */ - 6, 0x2A, 16, /* 1010 10 */ - 6, 0x2B, 17, /* 1010 11 */ - 7, 0x27, 18, /* 0100 111 */ - 7, 0xC, 19, /* 0001 100 */ - 7, 0x8, 20, /* 0001 000 */ - 7, 0x17, 21, /* 0010 111 */ - 7, 0x3, 22, /* 0000 011 */ - 7, 0x4, 23, /* 0000 100 */ - 7, 0x28, 24, /* 0101 000 */ - 7, 0x2B, 25, /* 0101 011 */ - 7, 0x13, 26, /* 0010 011 */ - 7, 0x24, 27, /* 0100 100 */ - 7, 0x18, 28, /* 0011 000 */ - 8, 0x2, 29, /* 0000 0010 */ - 8, 0x3, 30, /* 0000 0011 */ - 8, 0x1A, 31, /* 0001 1010 */ - 8, 0x1B, 32, /* 0001 1011 */ - 8, 0x12, 33, /* 0001 0010 */ - 8, 0x13, 34, /* 0001 0011 */ - 8, 0x14, 35, /* 0001 0100 */ - 8, 0x15, 36, /* 0001 0101 */ - 8, 0x16, 37, /* 0001 0110 */ - 8, 0x17, 38, /* 0001 0111 */ - 8, 0x28, 39, /* 0010 1000 */ - 8, 0x29, 40, /* 0010 1001 */ - 8, 0x2A, 41, /* 0010 1010 */ - 8, 0x2B, 42, /* 0010 1011 */ - 8, 0x2C, 43, /* 0010 1100 */ - 8, 0x2D, 44, /* 0010 1101 */ - 8, 0x4, 45, /* 0000 0100 */ - 8, 0x5, 46, /* 0000 0101 */ - 8, 0xA, 47, /* 0000 1010 */ - 8, 0xB, 48, /* 0000 1011 */ - 8, 0x52, 49, /* 0101 0010 */ - 8, 0x53, 50, /* 0101 0011 */ - 8, 0x54, 51, /* 0101 0100 */ - 8, 0x55, 52, /* 0101 0101 */ - 8, 0x24, 53, /* 0010 0100 */ - 8, 0x25, 54, /* 0010 0101 */ - 8, 0x58, 55, /* 0101 1000 */ - 8, 0x59, 56, /* 0101 1001 */ - 8, 0x5A, 57, /* 0101 1010 */ - 8, 0x5B, 58, /* 0101 1011 */ - 8, 0x4A, 59, /* 0100 1010 */ - 8, 0x4B, 60, /* 0100 1011 */ - 8, 0x32, 61, /* 0011 0010 */ - 8, 0x33, 62, /* 0011 0011 */ - 8, 0x34, 63, /* 0011 0100 */ - 5, 0x1B, 64, /* 1101 1 */ - 5, 0x12, 128, /* 1001 0 */ - 6, 0x17, 192, /* 0101 11 */ - 7, 0x37, 256, /* 0110 111 */ - 8, 0x36, 320, /* 0011 0110 */ - 8, 0x37, 384, /* 0011 0111 */ - 8, 0x64, 448, /* 0110 0100 */ - 8, 0x65, 512, /* 0110 0101 */ - 8, 0x68, 576, /* 0110 1000 */ - 8, 0x67, 640, /* 0110 0111 */ - 9, 0xCC, 704, /* 0110 0110 0 */ - 9, 0xCD, 768, /* 0110 0110 1 */ - 9, 0xD2, 832, /* 0110 1001 0 */ - 9, 0xD3, 896, /* 0110 1001 1 */ - 9, 0xD4, 960, /* 0110 1010 0 */ - 9, 0xD5, 1024, /* 0110 1010 1 */ - 9, 0xD6, 1088, /* 0110 1011 0 */ - 9, 0xD7, 1152, /* 0110 1011 1 */ - 9, 0xD8, 1216, /* 0110 1100 0 */ - 9, 0xD9, 1280, /* 0110 1100 1 */ - 9, 0xDA, 1344, /* 0110 1101 0 */ - 9, 0xDB, 1408, /* 0110 1101 1 */ - 9, 0x98, 1472, /* 0100 1100 0 */ - 9, 0x99, 1536, /* 0100 1100 1 */ - 9, 0x9A, 1600, /* 0100 1101 0 */ - 6, 0x18, 1664, /* 0110 00 */ - 9, 0x9B, 1728, /* 0100 1101 1 */ - 11, 0x8, 1792, /* 0000 0001 000 */ - 11, 0xC, 1856, /* 0000 0001 100 */ - 11, 0xD, 1920, /* 0000 0001 101 */ - 12, 0x12, 1984, /* 0000 0001 0010 */ - 12, 0x13, 2048, /* 0000 0001 0011 */ - 12, 0x14, 2112, /* 0000 0001 0100 */ - 12, 0x15, 2176, /* 0000 0001 0101 */ - 12, 0x16, 2240, /* 0000 0001 0110 */ - 12, 0x17, 2304, /* 0000 0001 0111 */ - 12, 0x1C, 2368, /* 0000 0001 1100 */ - 12, 0x1D, 2432, /* 0000 0001 1101 */ - 12, 0x1E, 2496, /* 0000 0001 1110 */ - 12, 0x1F, 2560, /* 0000 0001 1111 */ - 12, 0x1, G3CODE_EOL, /* 0000 0000 0001 */ - 9, 0x1, G3CODE_INVALID, /* 0000 0000 1 */ - 10, 0x1, G3CODE_INVALID, /* 0000 0000 01 */ - 11, 0x1, G3CODE_INVALID, /* 0000 0000 001 */ - 12, 0x0, G3CODE_INVALID, /* 0000 0000 0000 */ - }; - - private static readonly short[] m_faxBlackCodes = - { - 10, 0x37, 0, /* 0000 1101 11 */ - 3, 0x2, 1, /* 010 */ - 2, 0x3, 2, /* 11 */ - 2, 0x2, 3, /* 10 */ - 3, 0x3, 4, /* 011 */ - 4, 0x3, 5, /* 0011 */ - 4, 0x2, 6, /* 0010 */ - 5, 0x3, 7, /* 0001 1 */ - 6, 0x5, 8, /* 0001 01 */ - 6, 0x4, 9, /* 0001 00 */ - 7, 0x4, 10, /* 0000 100 */ - 7, 0x5, 11, /* 0000 101 */ - 7, 0x7, 12, /* 0000 111 */ - 8, 0x4, 13, /* 0000 0100 */ - 8, 0x7, 14, /* 0000 0111 */ - 9, 0x18, 15, /* 0000 1100 0 */ - 10, 0x17, 16, /* 0000 0101 11 */ - 10, 0x18, 17, /* 0000 0110 00 */ - 10, 0x8, 18, /* 0000 0010 00 */ - 11, 0x67, 19, /* 0000 1100 111 */ - 11, 0x68, 20, /* 0000 1101 000 */ - 11, 0x6C, 21, /* 0000 1101 100 */ - 11, 0x37, 22, /* 0000 0110 111 */ - 11, 0x28, 23, /* 0000 0101 000 */ - 11, 0x17, 24, /* 0000 0010 111 */ - 11, 0x18, 25, /* 0000 0011 000 */ - 12, 0xCA, 26, /* 0000 1100 1010 */ - 12, 0xCB, 27, /* 0000 1100 1011 */ - 12, 0xCC, 28, /* 0000 1100 1100 */ - 12, 0xCD, 29, /* 0000 1100 1101 */ - 12, 0x68, 30, /* 0000 0110 1000 */ - 12, 0x69, 31, /* 0000 0110 1001 */ - 12, 0x6A, 32, /* 0000 0110 1010 */ - 12, 0x6B, 33, /* 0000 0110 1011 */ - 12, 0xD2, 34, /* 0000 1101 0010 */ - 12, 0xD3, 35, /* 0000 1101 0011 */ - 12, 0xD4, 36, /* 0000 1101 0100 */ - 12, 0xD5, 37, /* 0000 1101 0101 */ - 12, 0xD6, 38, /* 0000 1101 0110 */ - 12, 0xD7, 39, /* 0000 1101 0111 */ - 12, 0x6C, 40, /* 0000 0110 1100 */ - 12, 0x6D, 41, /* 0000 0110 1101 */ - 12, 0xDA, 42, /* 0000 1101 1010 */ - 12, 0xDB, 43, /* 0000 1101 1011 */ - 12, 0x54, 44, /* 0000 0101 0100 */ - 12, 0x55, 45, /* 0000 0101 0101 */ - 12, 0x56, 46, /* 0000 0101 0110 */ - 12, 0x57, 47, /* 0000 0101 0111 */ - 12, 0x64, 48, /* 0000 0110 0100 */ - 12, 0x65, 49, /* 0000 0110 0101 */ - 12, 0x52, 50, /* 0000 0101 0010 */ - 12, 0x53, 51, /* 0000 0101 0011 */ - 12, 0x24, 52, /* 0000 0010 0100 */ - 12, 0x37, 53, /* 0000 0011 0111 */ - 12, 0x38, 54, /* 0000 0011 1000 */ - 12, 0x27, 55, /* 0000 0010 0111 */ - 12, 0x28, 56, /* 0000 0010 1000 */ - 12, 0x58, 57, /* 0000 0101 1000 */ - 12, 0x59, 58, /* 0000 0101 1001 */ - 12, 0x2B, 59, /* 0000 0010 1011 */ - 12, 0x2C, 60, /* 0000 0010 1100 */ - 12, 0x5A, 61, /* 0000 0101 1010 */ - 12, 0x66, 62, /* 0000 0110 0110 */ - 12, 0x67, 63, /* 0000 0110 0111 */ - 10, 0xF, 64, /* 0000 0011 11 */ - 12, 0xC8, 128, /* 0000 1100 1000 */ - 12, 0xC9, 192, /* 0000 1100 1001 */ - 12, 0x5B, 256, /* 0000 0101 1011 */ - 12, 0x33, 320, /* 0000 0011 0011 */ - 12, 0x34, 384, /* 0000 0011 0100 */ - 12, 0x35, 448, /* 0000 0011 0101 */ - 13, 0x6C, 512, /* 0000 0011 0110 0 */ - 13, 0x6D, 576, /* 0000 0011 0110 1 */ - 13, 0x4A, 640, /* 0000 0010 0101 0 */ - 13, 0x4B, 704, /* 0000 0010 0101 1 */ - 13, 0x4C, 768, /* 0000 0010 0110 0 */ - 13, 0x4D, 832, /* 0000 0010 0110 1 */ - 13, 0x72, 896, /* 0000 0011 1001 0 */ - 13, 0x73, 960, /* 0000 0011 1001 1 */ - 13, 0x74, 1024, /* 0000 0011 1010 0 */ - 13, 0x75, 1088, /* 0000 0011 1010 1 */ - 13, 0x76, 1152, /* 0000 0011 1011 0 */ - 13, 0x77, 1216, /* 0000 0011 1011 1 */ - 13, 0x52, 1280, /* 0000 0010 1001 0 */ - 13, 0x53, 1344, /* 0000 0010 1001 1 */ - 13, 0x54, 1408, /* 0000 0010 1010 0 */ - 13, 0x55, 1472, /* 0000 0010 1010 1 */ - 13, 0x5A, 1536, /* 0000 0010 1101 0 */ - 13, 0x5B, 1600, /* 0000 0010 1101 1 */ - 13, 0x64, 1664, /* 0000 0011 0010 0 */ - 13, 0x65, 1728, /* 0000 0011 0010 1 */ - 11, 0x8, 1792, /* 0000 0001 000 */ - 11, 0xC, 1856, /* 0000 0001 100 */ - 11, 0xD, 1920, /* 0000 0001 101 */ - 12, 0x12, 1984, /* 0000 0001 0010 */ - 12, 0x13, 2048, /* 0000 0001 0011 */ - 12, 0x14, 2112, /* 0000 0001 0100 */ - 12, 0x15, 2176, /* 0000 0001 0101 */ - 12, 0x16, 2240, /* 0000 0001 0110 */ - 12, 0x17, 2304, /* 0000 0001 0111 */ - 12, 0x1C, 2368, /* 0000 0001 1100 */ - 12, 0x1D, 2432, /* 0000 0001 1101 */ - 12, 0x1E, 2496, /* 0000 0001 1110 */ - 12, 0x1F, 2560, /* 0000 0001 1111 */ - 12, 0x1, G3CODE_EOL, /* 0000 0000 0001 */ - 9, 0x1, G3CODE_INVALID, /* 0000 0000 1 */ - 10, 0x1, G3CODE_INVALID, /* 0000 0000 01 */ - 11, 0x1, G3CODE_INVALID, /* 0000 0000 001 */ - 12, 0x0, G3CODE_INVALID, /* 0000 0000 0000 */ - }; - - private static readonly tableEntry m_horizcode = new tableEntry(3, 0x1, 0); /* 001 */ - private static readonly tableEntry m_passcode = new tableEntry(4, 0x1, 0); /* 0001 */ - private static readonly tableEntry[] m_vcodes = - { - new tableEntry(7, 0x03, 0), /* 0000 011 */ - new tableEntry(6, 0x03, 0), /* 0000 11 */ - new tableEntry(3, 0x03, 0), /* 011 */ - new tableEntry(1, 0x1, 0), /* 1 */ - new tableEntry(3, 0x2, 0), /* 010 */ - new tableEntry(6, 0x02, 0), /* 0000 10 */ - new tableEntry(7, 0x02, 0) /* 0000 010 */ - }; - - private static readonly int[] m_msbmask = - { - 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff - }; - - private static readonly byte[] m_zeroruns = - { - 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */ - }; - - private static readonly byte[] m_oneruns = - { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */ - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */ - 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */ - }; - - private static readonly byte[] fillMasks = - { - 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff - }; - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CodecWithPredictor.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CodecWithPredictor.cs deleted file mode 100644 index c0e9196f..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CodecWithPredictor.cs +++ /dev/null @@ -1,938 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Predictor Tag Support (used by multiple codecs). - */ - -using System; -using System.Diagnostics; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - /// - /// Codecs that want to support the Predictor tag should inherit from - /// this class instead of TiffCodec. - /// - /// Such codecs should not override default TiffCodec's methods for - /// decode|encode setup and encoding|decoding of row|tile|strip. - /// Codecs with predictor support should override equivalent methods - /// provided by this class. - /// - /// If codec wants to provide custom tag get|set|print methods, then - /// it should pass pointer to a object derived from TiffTagMethods - /// as parameter to TIFFPredictorInit - /// - class CodecWithPredictor : TiffCodec - { - public const int FIELD_PREDICTOR = (FieldBit.Codec + 0); - - private enum PredictorType - { - ptNone, - ptHorAcc8, - ptHorAcc16, - ptHorAcc32, - ptSwabHorAcc16, - ptSwabHorAcc32, - ptHorDiff8, - ptHorDiff16, - ptHorDiff32, - ptFpAcc, - ptFpDiff, - }; - - private static readonly TiffFieldInfo[] m_predictFieldInfo = - { - new TiffFieldInfo(TiffTag.PREDICTOR, 1, 1, TiffType.SHORT, CodecWithPredictor.FIELD_PREDICTOR, false, false, "Predictor"), - }; - - /// - /// predictor tag value - /// - private Predictor m_predictor; - - /// - /// sample stride over data - /// - private int m_stride; - - /// - /// tile/strip row size - /// - private int m_rowSize; - - private TiffTagMethods m_parentTagMethods; - private TiffTagMethods m_tagMethods; - private TiffTagMethods m_childTagMethods; // could be null - - private bool m_passThruDecode; - private bool m_passThruEncode; - - /// - /// horizontal differencer/accumulator - /// - private PredictorType m_predictorType; - - public CodecWithPredictor(Tiff tif, Compression scheme, string name) - : base(tif, scheme, name) - { - m_tagMethods = new CodecWithPredictorTagMethods(); - } - - // tagMethods can be null - public void TIFFPredictorInit(TiffTagMethods tagMethods) - { - // Merge codec-specific tag information and override parent get/set field methods. - m_tif.MergeFieldInfo(m_predictFieldInfo, m_predictFieldInfo.Length); - m_childTagMethods = tagMethods; - m_parentTagMethods = m_tif.m_tagmethods; - m_tif.m_tagmethods = m_tagMethods; - - m_predictor = Predictor.NONE; // default value - m_predictorType = PredictorType.ptNone; // no predictor method - } - - public void TIFFPredictorCleanup() - { - m_tif.m_tagmethods = m_parentTagMethods; - } - - ////////////////////////////////////////////////////////////////////////// - // WARNING: do not override this methods! - // please override their equivalents listed below - - /// - /// Setups the decoder part of the codec. - /// - /// - /// true if this codec successfully setup its decoder part and can decode data; - /// otherwise, false. - /// - /// - /// SetupDecode is called once before - /// . - public override bool SetupDecode() - { - return PredictorSetupDecode(); - } - - /// - /// Decodes one row of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeRow(byte[] buffer, int offset, int count, short plane) - { - if (!m_passThruDecode) - return PredictorDecodeRow(buffer, offset, count, plane); - - return predictor_decoderow(buffer, offset, count, plane); - } - - /// - /// Decodes one strip of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeStrip(byte[] buffer, int offset, int count, short plane) - { - if (!m_passThruDecode) - return PredictorDecodeTile(buffer, offset, count, plane); - - return predictor_decodestrip(buffer, offset, count, plane); - } - - /// - /// Decodes one tile of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeTile(byte[] buffer, int offset, int count, short plane) - { - if (!m_passThruDecode) - return PredictorDecodeTile(buffer, offset, count, plane); - - return predictor_decodetile(buffer, offset, count, plane); - } - - /// - /// Setups the encoder part of the codec. - /// - /// - /// true if this codec successfully setup its encoder part and can encode data; - /// otherwise, false. - /// - /// - /// SetupEncode is called once before - /// . - public override bool SetupEncode() - { - return PredictorSetupEncode(); - } - - /// - /// Encodes one row of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeRow(byte[] buffer, int offset, int count, short plane) - { - if (!m_passThruEncode) - return PredictorEncodeRow(buffer, offset, count, plane); - - return predictor_encoderow(buffer, offset, count, plane); - } - - /// - /// Encodes one strip of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeStrip(byte[] buffer, int offset, int count, short plane) - { - if (!m_passThruEncode) - return PredictorEncodeTile(buffer, offset, count, plane); - - return predictor_encodestrip(buffer, offset, count, plane); - } - - /// - /// Encodes one tile of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeTile(byte[] buffer, int offset, int count, short plane) - { - if (!m_passThruEncode) - return PredictorEncodeTile(buffer, offset, count, plane); - - return predictor_encodetile(buffer, offset, count, plane); - } - - ////////////////////////////////////////////////////////////////////////// - // derived class should override methods below instead of - // TiffCodec's methods - - public virtual bool predictor_setupdecode() - { - return base.SetupDecode(); - } - - public virtual bool predictor_decoderow(byte[] buffer, int offset, int count, short plane) - { - return base.DecodeRow(buffer, offset, count, plane); - } - - public virtual bool predictor_decodestrip(byte[] buffer, int offset, int count, short plane) - { - return base.DecodeStrip(buffer, offset, count, plane); - } - - public virtual bool predictor_decodetile(byte[] buffer, int offset, int count, short plane) - { - return base.DecodeTile(buffer, offset, count, plane); - } - - public virtual bool predictor_setupencode() - { - return base.SetupEncode(); - } - - public virtual bool predictor_encoderow(byte[] buffer, int offset, int count, short plane) - { - return base.EncodeRow(buffer, offset, count, plane); - } - - public virtual bool predictor_encodestrip(byte[] buffer, int offset, int count, short plane) - { - return base.EncodeStrip(buffer, offset, count, plane); - } - - public virtual bool predictor_encodetile(byte[] buffer, int offset, int count, short plane) - { - return base.EncodeTile(buffer, offset, count, plane); - } - - public Predictor GetPredictorValue() - { - return m_predictor; - } - - public void SetPredictorValue(Predictor value) - { - m_predictor = value; - } - - // retrieves child object's tag methods (could be null) - public TiffTagMethods GetChildTagMethods() - { - return m_childTagMethods; - } - - private void predictorFunc(byte[] buffer, int offset, int count) - { - switch (m_predictorType) - { - case PredictorType.ptHorAcc8: - horAcc8(buffer, offset, count); - break; - case PredictorType.ptHorAcc16: - horAcc16(buffer, offset, count); - break; - case PredictorType.ptHorAcc32: - horAcc32(buffer, offset, count); - break; - case PredictorType.ptSwabHorAcc16: - swabHorAcc16(buffer, offset, count); - break; - case PredictorType.ptSwabHorAcc32: - swabHorAcc32(buffer, offset, count); - break; - case PredictorType.ptHorDiff8: - horDiff8(buffer, offset, count); - break; - case PredictorType.ptHorDiff16: - horDiff16(buffer, offset, count); - break; - case PredictorType.ptHorDiff32: - horDiff32(buffer, offset, count); - break; - case PredictorType.ptFpAcc: - fpAcc(buffer, offset, count); - break; - case PredictorType.ptFpDiff: - fpDiff(buffer, offset, count); - break; - } - } - - private void horAcc8(byte[] buffer, int offset, int count) - { - int cp = offset; - if (count > m_stride) - { - count -= m_stride; - - // Pipeline the most common cases. - - if (m_stride == 3) - { - int cr = buffer[cp]; - int cg = buffer[cp + 1]; - int cb = buffer[cp + 2]; - do - { - count -= 3; - cp += 3; - - cr += buffer[cp]; - buffer[cp] = (byte)cr; - - cg += buffer[cp + 1]; - buffer[cp + 1] = (byte)cg; - - cb += buffer[cp + 2]; - buffer[cp + 2] = (byte)cb; - } - while (count > 0); - } - else if (m_stride == 4) - { - int cr = buffer[cp]; - int cg = buffer[cp + 1]; - int cb = buffer[cp + 2]; - int ca = buffer[cp + 3]; - do - { - count -= 4; - cp += 4; - - cr += buffer[cp]; - buffer[cp] = (byte)cr; - - cg += buffer[cp + 1]; - buffer[cp + 1] = (byte)cg; - - cb += buffer[cp + 2]; - buffer[cp + 2] = (byte)cb; - - ca += buffer[cp + 3]; - buffer[cp + 3] = (byte)ca; - } - while (count > 0); - } - else - { - do - { - for (int i = m_stride; i > 0; i--) - { - buffer[cp + m_stride] = (byte)(buffer[cp + m_stride] + buffer[cp]); - cp++; - } - - count -= m_stride; - } - while (count > 0); - } - } - } - - private void horAcc16(byte[] buffer, int offset, int count) - { - short[] wBuffer = Tiff.ByteArrayToShorts(buffer, offset, count); - int wOffset = 0; - - int wCount = count / 2; - if (wCount > m_stride) - { - wCount -= m_stride; - do - { - for (int i = m_stride; i > 0; i--) - { - wBuffer[wOffset + m_stride] += wBuffer[wOffset]; - wOffset++; - } - - wCount -= m_stride; - } - while (wCount > 0); - } - - Tiff.ShortsToByteArray(wBuffer, 0, count / 2, buffer, offset); - } - - private void horAcc32(byte[] buffer, int offset, int count) - { - int[] wBuffer = Tiff.ByteArrayToInts(buffer, offset, count); - int wOffset = 0; - - int wCount = count / 4; - if (wCount > m_stride) - { - wCount -= m_stride; - do - { - for (int i = m_stride; i > 0; i--) - { - wBuffer[wOffset + m_stride] += wBuffer[wOffset]; - wOffset++; - } - - wCount -= m_stride; - } while (wCount > 0); - } - - Tiff.IntsToByteArray(wBuffer, 0, count / 4, buffer, offset); - } - - private void swabHorAcc16(byte[] buffer, int offset, int count) - { - short[] wBuffer = Tiff.ByteArrayToShorts(buffer, offset, count); - int wOffset= 0; - - int wCount = count / 2; - if (wCount > m_stride) - { - Tiff.SwabArrayOfShort(wBuffer, wCount); - wCount -= m_stride; - do - { - for (int i = m_stride; i > 0; i--) - { - wBuffer[wOffset + m_stride] += wBuffer[wOffset]; - wOffset++; - } - - wCount -= m_stride; - } - while (wCount > 0); - } - - Tiff.ShortsToByteArray(wBuffer, 0, count / 2, buffer, offset); - } - - private void swabHorAcc32(byte[] buffer, int offset, int count) - { - int[] wBuffer = Tiff.ByteArrayToInts(buffer, offset, count); - int wOffset = 0; - - int wCount = count / 4; - if (wCount > m_stride) - { - Tiff.SwabArrayOfLong(wBuffer, wCount); - wCount -= m_stride; - do - { - for (int i = m_stride; i > 0; i--) - { - wBuffer[wOffset + m_stride] += wBuffer[wOffset]; - wOffset++; - } - - wCount -= m_stride; - } while (wCount > 0); - } - - Tiff.IntsToByteArray(wBuffer, 0, count / 4, buffer, offset); - } - - private void horDiff8(byte[] buffer, int offset, int count) - { - if (count > m_stride) - { - count -= m_stride; - int cp = offset; - - // Pipeline the most common cases. - - if (m_stride == 3) - { - int r2 = buffer[cp]; - int g2 = buffer[cp + 1]; - int b2 = buffer[cp + 2]; - do - { - int r1 = buffer[cp + 3]; - buffer[cp + 3] = (byte)(r1 - r2); - r2 = r1; - - int g1 = buffer[cp + 4]; - buffer[cp + 4] = (byte)(g1 - g2); - g2 = g1; - - int b1 = buffer[cp + 5]; - buffer[cp + 5] = (byte)(b1 - b2); - b2 = b1; - - cp += 3; - } - while ((count -= 3) > 0); - } - else if (m_stride == 4) - { - int r2 = buffer[cp]; - int g2 = buffer[cp + 1]; - int b2 = buffer[cp + 2]; - int a2 = buffer[cp + 3]; - do - { - int r1 = buffer[cp + 4]; - buffer[cp + 4] = (byte)(r1 - r2); - r2 = r1; - - int g1 = buffer[cp + 5]; - buffer[cp + 5] = (byte)(g1 - g2); - g2 = g1; - - int b1 = buffer[cp + 6]; - buffer[cp + 6] = (byte)(b1 - b2); - b2 = b1; - - int a1 = buffer[cp + 7]; - buffer[cp + 7] = (byte)(a1 - a2); - a2 = a1; - - cp += 4; - } - while ((count -= 4) > 0); - } - else - { - cp += count - 1; - do - { - for (int i = m_stride; i > 0; i--) - { - buffer[cp + m_stride] -= buffer[cp]; - cp--; - } - } - while ((count -= m_stride) > 0); - } - } - } - - private void horDiff16(byte[] buffer, int offset, int count) - { - short[] wBuffer = Tiff.ByteArrayToShorts(buffer, offset, count); - int wOffset = 0; - - int wCount = count / 2; - if (wCount > m_stride) - { - wCount -= m_stride; - wOffset += wCount - 1; - do - { - for (int i = m_stride; i > 0; i--) - { - wBuffer[wOffset + m_stride] -= wBuffer[wOffset]; - wOffset--; - } - - wCount -= m_stride; - } - while (wCount > 0); - } - - Tiff.ShortsToByteArray(wBuffer, 0, count / 2, buffer, offset); - } - - private void horDiff32(byte[] buffer, int offset, int count) - { - int[] wBuffer = Tiff.ByteArrayToInts(buffer, offset, count); - int wOffset = 0; - - int wCount = count / 4; - if (wCount > m_stride) - { - wCount -= m_stride; - wOffset += wCount - 1; - do - { - for (int i = m_stride; i > 0; i--) - { - wBuffer[wOffset + m_stride] -= wBuffer[wOffset]; - wOffset--; - } - - wCount -= m_stride; - } while (wCount > 0); - } - - Tiff.IntsToByteArray(wBuffer, 0, count / 4, buffer, offset); - } - - /// - /// Floating point predictor accumulation routine. - /// - private void fpAcc(byte[] buffer, int offset, int count) - { - int bps = m_tif.m_dir.td_bitspersample / 8; - int wCount = count / bps; - int left = count; - int cp = offset; - - while (left > m_stride) - { - for (int i = m_stride; i > 0; i--) - { - buffer[cp + m_stride] += buffer[cp]; - cp++; - } - - left -= m_stride; - } - - byte[] tmp = new byte[count]; - Buffer.BlockCopy(buffer, offset, tmp, 0, count); - for (int i = 0; i < wCount; i++) - { - for (int b = 0; b < bps; b++) - buffer[offset + bps * i + b] = tmp[(bps - b - 1) * wCount + i]; - } - } - - /// - /// Floating point predictor differencing routine. - /// - private void fpDiff(byte[] buffer, int offset, int count) - { - byte[] tmp = new byte [count]; - Buffer.BlockCopy(buffer, offset, tmp, 0, count); - - int bps = m_tif.m_dir.td_bitspersample / 8; - int wCount = count / bps; - for (int c = 0; c < wCount; c++) - { - for (int b = 0; b < bps; b++) - buffer[offset + (bps - b - 1) * wCount + c] = tmp[bps * c + b]; - } - - int cp = offset + count - m_stride - 1; - for (int c = count; c > m_stride; c -= m_stride) - { - for (int i = m_stride; i > 0; i--) - { - buffer[cp + m_stride] -= buffer[cp]; - cp--; - } - } - } - - /// - /// Decode a scanline and apply the predictor routine. - /// - private bool PredictorDecodeRow(byte[] buffer, int offset, int count, short plane) - { - Debug.Assert(m_predictorType != PredictorType.ptNone); - - if (predictor_decoderow(buffer, offset, count, plane)) - { - predictorFunc(buffer, offset, count); - return true; - } - - return false; - } - - /// - /// Decode a tile/strip and apply the predictor routine. Note that horizontal differencing - /// must be done on a row-by-row basis. The width of a "row" has already been calculated - /// at pre-decode time according to the strip/tile dimensions. - /// - private bool PredictorDecodeTile(byte[] buffer, int offset, int count, short plane) - { - if (predictor_decodetile(buffer, offset, count, plane)) - { - Debug.Assert(m_rowSize > 0); - Debug.Assert(m_predictorType != PredictorType.ptNone); - - while (count > 0) - { - predictorFunc(buffer, offset, m_rowSize); - count -= m_rowSize; - offset += m_rowSize; - } - - return true; - } - - return false; - } - - private bool PredictorEncodeRow(byte[] buffer, int offset, int count, short plane) - { - Debug.Assert(m_predictorType != PredictorType.ptNone); - - // XXX horizontal differencing alters user's data XXX - predictorFunc(buffer, offset, count); - return predictor_encoderow(buffer, offset, count, plane); - } - - private bool PredictorEncodeTile(byte[] buffer, int offset, int count, short plane) - { - Debug.Assert(m_predictorType != PredictorType.ptNone); - - // Do predictor manipulation in a working buffer to avoid altering - // the callers buffer. http://trac.osgeo.org/gdal/ticket/1965 - byte[] working_copy = new byte[count]; - Buffer.BlockCopy(buffer, 0, working_copy, 0, count); - - Debug.Assert(m_rowSize > 0); - Debug.Assert((count % m_rowSize) == 0); - - int cc = count; - while (cc > 0) - { - predictorFunc(working_copy, offset, m_rowSize); - cc -= m_rowSize; - offset += m_rowSize; - } - - return predictor_encodetile(working_copy, 0, count, plane); - } - - private bool PredictorSetupDecode() - { - if (!predictor_setupdecode() || !PredictorSetup()) - return false; - - m_passThruDecode = true; - if (m_predictor == Predictor.HORIZONTAL) - { - switch (m_tif.m_dir.td_bitspersample) - { - case 8: - m_predictorType = PredictorType.ptHorAcc8; - break; - case 16: - m_predictorType = PredictorType.ptHorAcc16; - break; - case 32: - m_predictorType = PredictorType.ptHorAcc32; - break; - } - - // Override default decoding method with one that does the predictor stuff. - m_passThruDecode = false; - - // If the data is horizontally differenced 16-bit data that requires byte-swapping, - // then it must be byte swapped before the accumulation step. We do this with a - // special-purpose method and override the normal post decoding logic that the - // library setup when the directory was read. - if ((m_tif.m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - { - if (m_predictorType == PredictorType.ptHorAcc16) - { - m_predictorType = PredictorType.ptSwabHorAcc16; - m_tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmNone; - } - else if (m_predictorType == PredictorType.ptHorAcc32) - { - m_predictorType = PredictorType.ptSwabHorAcc32; - m_tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmNone; - } - } - } - else if (m_predictor == Predictor.FLOATINGPOINT) - { - m_predictorType = PredictorType.ptFpAcc; - - // Override default decoding method with one that does the predictor stuff. - m_passThruDecode = false; - - // The data should not be swapped outside of the floating point predictor, the - // accumulation method should return bytes in the native order. - if ((m_tif.m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - m_tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmNone; - - // Allocate buffer to keep the decoded bytes before rearranging in the right order - } - - return true; - } - - private bool PredictorSetupEncode() - { - if (!predictor_setupencode() || !PredictorSetup()) - return false; - - m_passThruEncode = true; - if (m_predictor == Predictor.HORIZONTAL) - { - switch (m_tif.m_dir.td_bitspersample) - { - case 8: - m_predictorType = PredictorType.ptHorDiff8; - break; - case 16: - m_predictorType = PredictorType.ptHorDiff16; - break; - case 32: - m_predictorType = PredictorType.ptHorDiff32; - break; - } - - // Override default encoding method with one that does the predictor stuff. - m_passThruEncode = false; - } - else if (m_predictor == Predictor.FLOATINGPOINT) - { - m_predictorType = PredictorType.ptFpDiff; - - // Override default encoding method with one that does the predictor stuff. - m_passThruEncode = false; - } - - return true; - } - - private bool PredictorSetup() - { - const string module = "PredictorSetup"; - TiffDirectory td = m_tif.m_dir; - - switch (m_predictor) - { - case Predictor.NONE: - // no differencing - return true; - - case Predictor.HORIZONTAL: - if (td.td_bitspersample != 8 && - td.td_bitspersample != 16 && - td.td_bitspersample != 32) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "Horizontal differencing \"Predictor\" not supported with {0}-bit samples", - td.td_bitspersample); - return false; - } - break; - - case Predictor.FLOATINGPOINT: - if (td.td_sampleformat != SampleFormat.IEEEFP) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "Floating point \"Predictor\" not supported with {0} data format", - td.td_sampleformat); - return false; - } - break; - - default: - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "\"Predictor\" value {0} not supported", m_predictor); - return false; - } - - m_stride = (td.td_planarconfig == PlanarConfig.CONTIG ? (int)td.td_samplesperpixel : 1); - - // Calculate the scanline/tile-width size in bytes. - if (m_tif.IsTiled()) - m_rowSize = m_tif.TileRowSize(); - else - m_rowSize = m_tif.ScanlineSize(); - - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CodecWithPredictorTagMethods.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CodecWithPredictorTagMethods.cs deleted file mode 100644 index 94fab4ba..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/CodecWithPredictorTagMethods.cs +++ /dev/null @@ -1,96 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System.Diagnostics; -using System.IO; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - class CodecWithPredictorTagMethods : TiffTagMethods - { - public override bool SetField(Tiff tif, TiffTag tag, FieldValue[] ap) - { - CodecWithPredictor sp = tif.m_currentCodec as CodecWithPredictor; - Debug.Assert(sp != null); - - switch (tag) - { - case TiffTag.PREDICTOR: - sp.SetPredictorValue((Predictor)ap[0].ToByte()); - tif.setFieldBit(CodecWithPredictor.FIELD_PREDICTOR); - tif.m_flags |= TiffFlags.DIRTYDIRECT; - return true; - } - - TiffTagMethods childMethods = sp.GetChildTagMethods(); - if (childMethods != null) - return childMethods.SetField(tif, tag, ap); - - return base.SetField(tif, tag, ap); - } - - public override FieldValue[] GetField(Tiff tif, TiffTag tag) - { - CodecWithPredictor sp = tif.m_currentCodec as CodecWithPredictor; - Debug.Assert(sp != null); - - switch (tag) - { - case TiffTag.PREDICTOR: - FieldValue[] result = new FieldValue[1]; - result[0].Set(sp.GetPredictorValue()); - return result; - } - - TiffTagMethods childMethods = sp.GetChildTagMethods(); - if (childMethods != null) - return childMethods.GetField(tif, tag); - - return base.GetField(tif, tag); - } - - public override void PrintDir(Tiff tif, Stream fd, TiffPrintFlags flags) - { - CodecWithPredictor sp = tif.m_currentCodec as CodecWithPredictor; - Debug.Assert(sp != null); - - if (tif.fieldSet(CodecWithPredictor.FIELD_PREDICTOR)) - { - Tiff.fprintf(fd, " Predictor: "); - Predictor predictor = sp.GetPredictorValue(); - switch (predictor) - { - case Predictor.NONE: - Tiff.fprintf(fd, "none "); - break; - case Predictor.HORIZONTAL: - Tiff.fprintf(fd, "horizontal differencing "); - break; - case Predictor.FLOATINGPOINT: - Tiff.fprintf(fd, "floating point predictor "); - break; - } - - Tiff.fprintf(fd, "{0} (0x{1:x})\r\n", predictor, predictor); - } - - TiffTagMethods childMethods = sp.GetChildTagMethods(); - if (childMethods != null) - childMethods.PrintDir(tif, fd, flags); - else - base.PrintDir(tif, fd, flags); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/DeflateCodec.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/DeflateCodec.cs deleted file mode 100644 index 8ceda833..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/DeflateCodec.cs +++ /dev/null @@ -1,419 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * ZIP (aka Deflate) Compression Support - * - * This file is simply an interface to the zlib library written by - * Jean-loup Gailly and Mark Adler. You must use version 1.0 or later - * of the library: this code assumes the 1.0 API and also depends on - * the ability to write the zlib header multiple times (one per strip) - * which was not possible with versions prior to 0.95. Note also that - * older versions of this codec avoided this bug by supressing the header - * entirely. This means that files written with the old library cannot - * be read; they should be converted to a different compression scheme - * and then reconverted. - * - * The data format used by the zlib library is described in the files - * zlib-3.1.doc, deflate-1.1.doc and gzip-4.1.doc, available in the - * directory ftp://ftp.uu.net/pub/archiving/zip/doc. The library was - * last found at ftp://ftp.uu.net/pub/archiving/zip/zlib/zlib-0.99.tar.gz. - */ - -using System.Diagnostics; - -using ComponentAce.Compression.Libs.zlib; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - class DeflateCodec : CodecWithPredictor - { - public const int ZSTATE_INIT_DECODE = 0x01; - public const int ZSTATE_INIT_ENCODE = 0x02; - - public ZStream m_stream = new ZStream(); - public int m_zipquality; /* compression level */ - public int m_state; /* state flags */ - - private static readonly TiffFieldInfo[] zipFieldInfo = - { - new TiffFieldInfo(TiffTag.ZIPQUALITY, 0, 0, TiffType.ANY, FieldBit.Pseudo, true, false, ""), - }; - - private TiffTagMethods m_tagMethods; - - public DeflateCodec(Tiff tif, Compression scheme, string name) - : base(tif, scheme, name) - { - m_tagMethods = new DeflateCodecTagMethods(); - } - - public override bool Init() - { - Debug.Assert((m_scheme == Compression.DEFLATE) || - (m_scheme == Compression.ADOBE_DEFLATE)); - - /* - * Merge codec-specific tag information and - * override parent get/set field methods. - */ - m_tif.MergeFieldInfo(zipFieldInfo, zipFieldInfo.Length); - - /* Default values for codec-specific fields */ - m_zipquality = zlibConst.Z_DEFAULT_COMPRESSION; /* default comp. level */ - m_state = 0; - - /* - * Setup predictor setup. - */ - TIFFPredictorInit(m_tagMethods); - return true; - } - - - /// - /// Gets a value indicating whether this codec can encode data. - /// - /// - /// true if this codec can encode data; otherwise, false. - /// - public override bool CanEncode - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether this codec can decode data. - /// - /// - /// true if this codec can decode data; otherwise, false. - /// - public override bool CanDecode - { - get - { - return true; - } - } - - /// - /// Prepares the decoder part of the codec for a decoding. - /// - /// The zero-based sample plane index. - /// - /// true if this codec successfully prepared its decoder part and ready - /// to decode data; otherwise, false. - /// - /// - /// PreDecode is called after and before decoding. - /// - public override bool PreDecode(short plane) - { - return ZIPPreDecode(plane); - } - - /// - /// Prepares the encoder part of the codec for a encoding. - /// - /// The zero-based sample plane index. - /// - /// true if this codec successfully prepared its encoder part and ready - /// to encode data; otherwise, false. - /// - /// - /// PreEncode is called after and before encoding. - /// - public override bool PreEncode(short plane) - { - return ZIPPreEncode(plane); - } - - /// - /// Performs any actions after encoding required by the codec. - /// - /// - /// true if all post-encode actions succeeded; otherwise, false - /// - /// - /// PostEncode is called after encoding and can be used to release any external - /// resources needed during encoding. - /// - public override bool PostEncode() - { - return ZIPPostEncode(); - } - - /// - /// Cleanups the state of the codec. - /// - /// - /// Cleanup is called when codec is no longer needed (won't be used) and can be - /// used for example to restore tag methods that were substituted. - public override void Cleanup() - { - ZIPCleanup(); - } - - // CodecWithPredictor overrides - - public override bool predictor_setupdecode() - { - return ZIPSetupDecode(); - } - - public override bool predictor_decoderow(byte[] buffer, int offset, int count, short plane) - { - return ZIPDecode(buffer, offset, count, plane); - } - - public override bool predictor_decodestrip(byte[] buffer, int offset, int count, short plane) - { - return ZIPDecode(buffer, offset, count, plane); - } - - public override bool predictor_decodetile(byte[] buffer, int offset, int count, short plane) - { - return ZIPDecode(buffer, offset, count, plane); - } - - public override bool predictor_setupencode() - { - return ZIPSetupEncode(); - } - - public override bool predictor_encoderow(byte[] buffer, int offset, int count, short plane) - { - return ZIPEncode(buffer, offset, count, plane); - } - - public override bool predictor_encodestrip(byte[] buffer, int offset, int count, short plane) - { - return ZIPEncode(buffer, offset, count, plane); - } - - public override bool predictor_encodetile(byte[] buffer, int offset, int count, short plane) - { - return ZIPEncode(buffer, offset, count, plane); - } - - private void ZIPCleanup() - { - base.TIFFPredictorCleanup(); - - if ((m_state & ZSTATE_INIT_ENCODE) != 0) - { - m_stream.deflateEnd(); - m_state = 0; - } - else if ((m_state & ZSTATE_INIT_DECODE) != 0) - { - m_stream.inflateEnd(); - m_state = 0; - } - } - - private bool ZIPDecode(byte[] buffer, int offset, int count, short plane) - { - const string module = "ZIPDecode"; - - Debug.Assert(m_state == ZSTATE_INIT_DECODE); - m_stream.next_out = buffer; - m_stream.next_out_index = offset; - m_stream.avail_out = count; - do - { - int state = m_stream.inflate(zlibConst.Z_PARTIAL_FLUSH); - if (state == zlibConst.Z_STREAM_END) - break; - - if (state == zlibConst.Z_DATA_ERROR) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "{0}: Decoding error at scanline {1}, {2}", - m_tif.m_name, m_tif.m_row, m_stream.msg); - - if (m_stream.inflateSync() != zlibConst.Z_OK) - return false; - - continue; - } - - if (state != zlibConst.Z_OK) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "{0}: zlib error: {1}", m_tif.m_name, m_stream.msg); - return false; - } - } - while (m_stream.avail_out > 0); - - if (m_stream.avail_out != 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "{0}: Not enough data at scanline {1} (short {2} bytes)", - m_tif.m_name, m_tif.m_row, m_stream.avail_out); - return false; - } - - return true; - } - - /// - /// Encode a chunk of pixels. - /// - private bool ZIPEncode(byte[] buffer, int offset, int count, short plane) - { - const string module = "ZIPEncode"; - - Debug.Assert(m_state == ZSTATE_INIT_ENCODE); - - m_stream.next_in = buffer; - m_stream.next_in_index = offset; - m_stream.avail_in = count; - do - { - if (m_stream.deflate(zlibConst.Z_NO_FLUSH) != zlibConst.Z_OK) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "{0}: Encoder error: {1}", m_tif.m_name, m_stream.msg); - return false; - } - - if (m_stream.avail_out == 0) - { - m_tif.m_rawcc = m_tif.m_rawdatasize; - m_tif.flushData1(); - m_stream.next_out = m_tif.m_rawdata; - m_stream.next_out_index = 0; - m_stream.avail_out = m_tif.m_rawdatasize; - } - } - while (m_stream.avail_in > 0); - - return true; - } - - /* - * Finish off an encoded strip by flushing the last - * string and tacking on an End Of Information code. - */ - private bool ZIPPostEncode() - { - const string module = "ZIPPostEncode"; - int state; - - m_stream.avail_in = 0; - do - { - state = m_stream.deflate(zlibConst.Z_FINISH); - switch (state) - { - case zlibConst.Z_STREAM_END: - case zlibConst.Z_OK: - if (m_stream.avail_out != m_tif.m_rawdatasize) - { - m_tif.m_rawcc = m_tif.m_rawdatasize - m_stream.avail_out; - m_tif.flushData1(); - m_stream.next_out = m_tif.m_rawdata; - m_stream.next_out_index = 0; - m_stream.avail_out = m_tif.m_rawdatasize; - } - break; - default: - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "{0}: zlib error: {1}", m_tif.m_name, m_stream.msg); - return false; - } - } - while (state != zlibConst.Z_STREAM_END); - - return true; - } - - /* - * Setup state for decoding a strip. - */ - private bool ZIPPreDecode(short s) - { - if ((m_state & ZSTATE_INIT_DECODE) == 0) - SetupDecode(); - - m_stream.next_in = m_tif.m_rawdata; - m_stream.next_in_index = 0; - m_stream.avail_in = m_tif.m_rawcc; - return (m_stream.inflateInit() == zlibConst.Z_OK); - } - - /* - * Reset encoding state at the start of a strip. - */ - private bool ZIPPreEncode(short s) - { - if (m_state != ZSTATE_INIT_ENCODE) - SetupEncode(); - - m_stream.next_out = m_tif.m_rawdata; - m_stream.next_out_index = 0; - m_stream.avail_out = m_tif.m_rawdatasize; - return (m_stream.deflateInit(m_zipquality) == zlibConst.Z_OK); - } - - private bool ZIPSetupDecode() - { - const string module = "ZIPSetupDecode"; - - /* if we were last encoding, terminate this mode */ - if ((m_state & ZSTATE_INIT_ENCODE) != 0) - { - m_stream.deflateEnd(); - m_state = 0; - } - - if (m_stream.inflateInit() != zlibConst.Z_OK) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "{0}: {1}", - m_tif.m_name, m_stream.msg); - return false; - } - - m_state |= ZSTATE_INIT_DECODE; - return true; - } - - private bool ZIPSetupEncode() - { - const string module = "ZIPSetupEncode"; - - if ((m_state & ZSTATE_INIT_DECODE) != 0) - { - m_stream.inflateEnd(); - m_state = 0; - } - - if (m_stream.deflateInit(m_zipquality) != zlibConst.Z_OK) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "{0}: {1}", - m_tif.m_name, m_stream.msg); - return false; - } - - m_state |= ZSTATE_INIT_ENCODE; - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/DeflateCodecTagMethods.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/DeflateCodecTagMethods.cs deleted file mode 100644 index 2c90e85a..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/DeflateCodecTagMethods.cs +++ /dev/null @@ -1,67 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System.Diagnostics; - -using ComponentAce.Compression.Libs.zlib; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - class DeflateCodecTagMethods : TiffTagMethods - { - public override bool SetField(Tiff tif, TiffTag tag, FieldValue[] ap) - { - DeflateCodec sp = tif.m_currentCodec as DeflateCodec; - Debug.Assert(sp != null); - - const string module = "ZIPVSetField"; - - switch (tag) - { - case TiffTag.ZIPQUALITY: - sp.m_zipquality = ap[0].ToInt(); - if ((sp.m_state & DeflateCodec.ZSTATE_INIT_ENCODE) != 0) - { - if (sp.m_stream.deflateParams(sp.m_zipquality, zlibConst.Z_DEFAULT_STRATEGY) != zlibConst.Z_OK) - { - Tiff.ErrorExt(tif, tif.m_clientdata, module, - "{0}: zlib error: {0}", tif.m_name, sp.m_stream.msg); - return false; - } - } - - return true; - } - - return base.SetField(tif, tag, ap); - } - - public override FieldValue[] GetField(Tiff tif, TiffTag tag) - { - DeflateCodec sp = tif.m_currentCodec as DeflateCodec; - Debug.Assert(sp != null); - - switch (tag) - { - case TiffTag.ZIPQUALITY: - FieldValue[] result = new FieldValue[1]; - result[0].Set(sp.m_zipquality); - return result; - } - - return base.GetField(tif, tag); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/DumpModeCodec.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/DumpModeCodec.cs deleted file mode 100644 index 8e0a1b2a..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/DumpModeCodec.cs +++ /dev/null @@ -1,225 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * "Null" Compression Algorithm Support. - */ - -using System; -using System.Diagnostics; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - class DumpModeCodec : TiffCodec - { - public DumpModeCodec(Tiff tif, Compression scheme, string name) - : base(tif, scheme, name) - { - } - - public override bool Init() - { - return true; - } - - /// - /// Gets a value indicating whether this codec can encode data. - /// - /// - /// true if this codec can encode data; otherwise, false. - /// - public override bool CanEncode - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether this codec can decode data. - /// - /// - /// true if this codec can decode data; otherwise, false. - /// - public override bool CanDecode - { - get - { - return true; - } - } - - /// - /// Decodes one row of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeRow(byte[] buffer, int offset, int count, short plane) - { - return DumpModeDecode(buffer, offset, count, plane); - } - - /// - /// Decodes one strip of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeStrip(byte[] buffer, int offset, int count, short plane) - { - return DumpModeDecode(buffer, offset, count, plane); - } - - /// - /// Decodes one tile of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeTile(byte[] buffer, int offset, int count, short plane) - { - return DumpModeDecode(buffer, offset, count, plane); - } - - /// - /// Encodes one row of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeRow(byte[] buffer, int offset, int count, short plane) - { - return DumpModeEncode(buffer, offset, count, plane); - } - - /// - /// Encodes one strip of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeStrip(byte[] buffer, int offset, int count, short plane) - { - return DumpModeEncode(buffer, offset, count, plane); - } - - /// - /// Encodes one tile of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeTile(byte[] buffer, int offset, int count, short plane) - { - return DumpModeEncode(buffer, offset, count, plane); - } - - /// - /// Seeks the specified row in the strip being processed. - /// - /// The row to seek. - /// - /// true if specified row was successfully found; otherwise, false - /// - public override bool Seek(int row) - { - m_tif.m_rawcp += row * m_tif.m_scanlinesize; - m_tif.m_rawcc -= row * m_tif.m_scanlinesize; - return true; - } - - /// - /// Encode a hunk of pixels. - /// - private bool DumpModeEncode(byte[] buffer, int offset, int count, short plane) - { - while (count > 0) - { - int n = count; - if (m_tif.m_rawcc + n > m_tif.m_rawdatasize) - n = m_tif.m_rawdatasize - m_tif.m_rawcc; - - Debug.Assert(n > 0); - - Buffer.BlockCopy(buffer, offset, m_tif.m_rawdata, m_tif.m_rawcp, n); - m_tif.m_rawcp += n; - m_tif.m_rawcc += n; - - offset += n; - count -= n; - if (m_tif.m_rawcc >= m_tif.m_rawdatasize && !m_tif.flushData1()) - return false; - } - - return true; - } - - /// - /// Decode a hunk of pixels. - /// - private bool DumpModeDecode(byte[] buffer, int offset, int count, short plane) - { - if (m_tif.m_rawcc < count) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "DumpModeDecode: Not enough data for scanline {0}", m_tif.m_row); - return false; - } - - Buffer.BlockCopy(m_tif.m_rawdata, m_tif.m_rawcp, buffer, offset, count); - m_tif.m_rawcp += count; - m_tif.m_rawcc -= count; - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/ExtenderAndErrorHandler.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/ExtenderAndErrorHandler.cs deleted file mode 100644 index 80b52e37..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/ExtenderAndErrorHandler.cs +++ /dev/null @@ -1,332 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -using System; -using System.IO; -using BitMiracle.LibTiff.Classic.Internal; - -namespace BitMiracle.LibTiff.Classic -{ - partial class Tiff - { -#if THREAD_SAFE_LIBTIFF - private TiffErrorHandler m_errorHandler; -#else - private static TiffErrorHandler m_errorHandler; -#endif - - /// - /// Client Tag extension support (from Niles Ritter). - /// -#if THREAD_SAFE_LIBTIFF - private TiffExtendProc m_extender; -#else - private static TiffExtendProc m_extender; -#endif - -#if THREAD_SAFE_LIBTIFF - public -#else - private -#endif - static Tiff Open(string fileName, string mode, TiffErrorHandler errorHandler) - { - return Tiff.Open(fileName, mode, errorHandler, null); - } - -#if THREAD_SAFE_LIBTIFF - public -#else - private -#endif - static Tiff Open(string fileName, string mode, TiffErrorHandler errorHandler, TiffExtendProc extender) - { - const string module = "Open"; - - FileMode fileMode; - FileAccess fileAccess; - getMode(mode, module, out fileMode, out fileAccess); - - FileStream stream = null; - try - { - if (fileAccess == FileAccess.Read) - stream = File.Open(fileName, fileMode, fileAccess, FileShare.Read); - else - stream = File.Open(fileName, fileMode, fileAccess); - } - catch (Exception e) - { - Error(module, "Failed to open '{0}'. {1}", fileName, e.Message); - return null; - } - - Tiff tif = ClientOpen(fileName, mode, stream, new TiffStream(), errorHandler, extender); - if (tif == null) - stream.Dispose(); - else - tif.m_fileStream = stream; - - return tif; - } - -#if THREAD_SAFE_LIBTIFF - public -#else - private -#endif - static Tiff ClientOpen(string name, string mode, object clientData, TiffStream stream, TiffErrorHandler errorHandler) - { - return ClientOpen(name, mode, clientData, stream, errorHandler, null); - } - -#if THREAD_SAFE_LIBTIFF - public -#else - private -#endif - static Tiff ClientOpen(string name, string mode, object clientData, TiffStream stream, TiffErrorHandler errorHandler, TiffExtendProc extender) - { - const string module = "ClientOpen"; - - if (mode == null || mode.Length == 0) - { - ErrorExt(null, clientData, module, "{0}: mode string should contain at least one char", name); - return null; - } - - FileMode fileMode; - FileAccess fileAccess; - int m = getMode(mode, module, out fileMode, out fileAccess); - - Tiff tif = new Tiff(); -#if THREAD_SAFE_LIBTIFF - if (errorHandler != null) - tif.m_errorHandler = errorHandler; - if (extender != null) - tif.m_extender = extender; -#endif - tif.m_name = name; - - tif.m_mode = m & ~(O_CREAT | O_TRUNC); - tif.m_curdir = -1; // non-existent directory - tif.m_curoff = 0; - tif.m_curstrip = -1; // invalid strip - tif.m_row = -1; // read/write pre-increment - tif.m_clientdata = clientData; - - if (stream == null) - { - ErrorExt(tif, clientData, module, "TiffStream is null pointer."); - return null; - } - - tif.m_stream = stream; - - // setup default state - tif.m_currentCodec = tif.m_builtInCodecs[0]; - - // Default is to return data MSB2LSB and enable the use of - // strip chopping when a file is opened read-only. - tif.m_flags = TiffFlags.MSB2LSB; - - if (m == O_RDONLY || m == O_RDWR) - tif.m_flags |= STRIPCHOP_DEFAULT; - - // Process library-specific flags in the open mode string. - // See remarks for Open method for the list of supported flags. - int modelength = mode.Length; - for (int i = 0; i < modelength; i++) - { - switch (mode[i]) - { - case 'b': - if ((m & O_CREAT) != 0) - tif.m_flags |= TiffFlags.SWAB; - break; - case 'l': - break; - case 'B': - tif.m_flags = (tif.m_flags & ~TiffFlags.FILLORDER) | TiffFlags.MSB2LSB; - break; - case 'L': - tif.m_flags = (tif.m_flags & ~TiffFlags.FILLORDER) | TiffFlags.LSB2MSB; - break; - case 'H': - tif.m_flags = (tif.m_flags & ~TiffFlags.FILLORDER) | TiffFlags.LSB2MSB; - break; - case 'C': - if (m == O_RDONLY) - tif.m_flags |= TiffFlags.STRIPCHOP; - break; - case 'c': - if (m == O_RDONLY) - tif.m_flags &= ~TiffFlags.STRIPCHOP; - break; - case 'h': - tif.m_flags |= TiffFlags.HEADERONLY; - break; - } - } - - // Read in TIFF header. - - if ((tif.m_mode & O_TRUNC) != 0 || !tif.readHeaderOk(ref tif.m_header)) - { - if (tif.m_mode == O_RDONLY) - { - ErrorExt(tif, tif.m_clientdata, name, "Cannot read TIFF header"); - return null; - } - - // Setup header and write. - - if ((tif.m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - tif.m_header.tiff_magic = TIFF_BIGENDIAN; - else - tif.m_header.tiff_magic = TIFF_LITTLEENDIAN; - - tif.m_header.tiff_version = TIFF_VERSION; - if ((tif.m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabShort(ref tif.m_header.tiff_version); - - tif.m_header.tiff_diroff = 0; // filled in later - - tif.seekFile(0, SeekOrigin.Begin); - - if (!tif.writeHeaderOK(tif.m_header)) - { - ErrorExt(tif, tif.m_clientdata, name, "Error writing TIFF header"); - tif.m_mode = O_RDONLY; - return null; - } - - // Setup the byte order handling. - tif.initOrder(tif.m_header.tiff_magic); - - // Setup default directory. - tif.setupDefaultDirectory(); - tif.m_diroff = 0; - tif.m_dirlist = null; - tif.m_dirlistsize = 0; - tif.m_dirnumber = 0; - return tif; - } - - // Setup the byte order handling. - if (tif.m_header.tiff_magic != TIFF_BIGENDIAN && - tif.m_header.tiff_magic != TIFF_LITTLEENDIAN && - tif.m_header.tiff_magic != MDI_LITTLEENDIAN) - { - ErrorExt(tif, tif.m_clientdata, name, - "Not a TIFF or MDI file, bad magic number {0} (0x{1:x})", - tif.m_header.tiff_magic, tif.m_header.tiff_magic); - tif.m_mode = O_RDONLY; - return null; - } - - tif.initOrder(tif.m_header.tiff_magic); - - // Swap header if required. - if ((tif.m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - { - SwabShort(ref tif.m_header.tiff_version); - SwabUInt(ref tif.m_header.tiff_diroff); - } - - // Now check version (if needed, it's been byte-swapped). - // Note that this isn't actually a version number, it's a - // magic number that doesn't change (stupid). - if (tif.m_header.tiff_version == TIFF_BIGTIFF_VERSION) - { - ErrorExt(tif, tif.m_clientdata, name, - "This is a BigTIFF file. This format not supported\nby this version of LibTiff.Net."); - tif.m_mode = O_RDONLY; - return null; - } - - if (tif.m_header.tiff_version != TIFF_VERSION) - { - ErrorExt(tif, tif.m_clientdata, name, - "Not a TIFF file, bad version number {0} (0x{1:x})", - tif.m_header.tiff_version, tif.m_header.tiff_version); - tif.m_mode = O_RDONLY; - return null; - } - - tif.m_flags |= TiffFlags.MYBUFFER; - tif.m_rawcp = 0; - tif.m_rawdata = null; - tif.m_rawdatasize = 0; - - // Sometimes we do not want to read the first directory (for example, - // it may be broken) and want to proceed to other directories. I this - // case we use the HEADERONLY flag to open file and return - // immediately after reading TIFF header. - if ((tif.m_flags & TiffFlags.HEADERONLY) == TiffFlags.HEADERONLY) - return tif; - - // Setup initial directory. - switch (mode[0]) - { - case 'r': - tif.m_nextdiroff = tif.m_header.tiff_diroff; - - if (tif.ReadDirectory()) - { - tif.m_rawcc = -1; - tif.m_flags |= TiffFlags.BUFFERSETUP; - return tif; - } - break; - case 'a': - // New directories are automatically append to the end of - // the directory chain when they are written out (see WriteDirectory). - tif.setupDefaultDirectory(); - return tif; - } - - tif.m_mode = O_RDONLY; - return null; - } - - private static TiffErrorHandler setErrorHandlerImpl(TiffErrorHandler errorHandler) - { -#if THREAD_SAFE_LIBTIFF - throw new InvalidOperationException("Do not use SetErrorHandler method (it's not thread-safe).\n" + - "Use overloads for Open and ClientOpen methods to achieve the same."); -#else - TiffErrorHandler prev = m_errorHandler; - m_errorHandler = errorHandler; - return prev; -#endif - } - - private static TiffExtendProc setTagExtenderImpl(TiffExtendProc extender) - { -#if THREAD_SAFE_LIBTIFF - throw new InvalidOperationException("Do not use SetTagExtender method (it's not thread-safe).\n" + - "Use overloads for Open and ClientOpen methods to achieve the same."); -#else - TiffExtendProc prev = m_extender; - m_extender = extender; - return prev; -#endif - } - - private static TiffErrorHandler getErrorHandler(Tiff tif) - { - TiffErrorHandler errorHandler = null; -#if THREAD_SAFE_LIBTIFF - if (tif != null) - { - errorHandler = tif.m_errorHandler; - } -#else - errorHandler = m_errorHandler; -#endif - return errorHandler; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegCodec.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegCodec.cs deleted file mode 100644 index 83d3adf6..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegCodec.cs +++ /dev/null @@ -1,1779 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * JPEG Compression support per TIFF Technical Note #2 - * (*not* per the original TIFF 6.0 spec). - * - * This file is simply an interface to the libjpeg library written by - * the Independent JPEG Group. You need release 5 or later of the IJG - * code, which you can find on the Internet at ftp.uu.net:/graphics/jpeg/. - * - * Contributed by Tom Lane . - */ - -using System; -using System.Diagnostics; - -using BitMiracle.LibJpeg.Classic; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - class JpegCodec : TiffCodec - { - public const int FIELD_JPEGTABLES = (FieldBit.Codec + 0); - public const int FIELD_RECVPARAMS = (FieldBit.Codec + 1); - public const int FIELD_SUBADDRESS = (FieldBit.Codec + 2); - public const int FIELD_RECVTIME = (FieldBit.Codec + 3); - public const int FIELD_FAXDCS = (FieldBit.Codec + 4); - - internal jpeg_compress_struct m_compression; - internal jpeg_decompress_struct m_decompression; - internal jpeg_common_struct m_common; - - internal int m_h_sampling; /* luminance sampling factors */ - internal int m_v_sampling; - - /* pseudo-tag fields */ - internal byte[] m_jpegtables; /* JPEGTables tag value, or null */ - internal int m_jpegtables_length; /* number of bytes in same */ - internal int m_jpegquality; /* Compression quality level */ - internal JpegColorMode m_jpegcolormode; /* Auto RGB<=>YCbCr convert? */ - internal JpegTablesMode m_jpegtablesmode; /* What to put in JPEGTables */ - - internal bool m_ycbcrsampling_fetched; - - internal int m_recvparams; /* encoded Class 2 session params */ - internal string m_subaddress; /* subaddress string */ - internal int m_recvtime; /* time spent receiving (secs) */ - internal string m_faxdcs; /* encoded fax parameters (DCS, Table 2/T.30) */ - - private static readonly TiffFieldInfo[] jpegFieldInfo = - { - new TiffFieldInfo(TiffTag.JPEGTABLES, -3, -3, TiffType.UNDEFINED, FIELD_JPEGTABLES, false, true, "JPEGTables"), - new TiffFieldInfo(TiffTag.JPEGQUALITY, 0, 0, TiffType.ANY, FieldBit.Pseudo, true, false, ""), - new TiffFieldInfo(TiffTag.JPEGCOLORMODE, 0, 0, TiffType.ANY, FieldBit.Pseudo, false, false, ""), - new TiffFieldInfo(TiffTag.JPEGTABLESMODE, 0, 0, TiffType.ANY, FieldBit.Pseudo, false, false, ""), - /* Specific for JPEG in faxes */ - new TiffFieldInfo(TiffTag.FAXRECVPARAMS, 1, 1, TiffType.LONG, FIELD_RECVPARAMS, true, false, "FaxRecvParams"), - new TiffFieldInfo(TiffTag.FAXSUBADDRESS, -1, -1, TiffType.ASCII, FIELD_SUBADDRESS, true, false, "FaxSubAddress"), - new TiffFieldInfo(TiffTag.FAXRECVTIME, 1, 1, TiffType.LONG, FIELD_RECVTIME, true, false, "FaxRecvTime"), - new TiffFieldInfo(TiffTag.FAXDCS, -1, -1, TiffType.ASCII, FIELD_FAXDCS, true, false, "FaxDcs"), - }; - - private bool m_rawDecode; - private bool m_rawEncode; - - private TiffTagMethods m_tagMethods; - private TiffTagMethods m_parentTagMethods; - - private bool m_cinfo_initialized; - - internal jpeg_error_mgr m_err; /* LibJpeg.Net error manager */ - private Photometric m_photometric; /* copy of PhotometricInterpretation */ - - private int m_bytesperline; /* decompressed bytes per scanline */ - /* pointers to intermediate buffers when processing downsampled data */ - private byte[][][] m_ds_buffer = new byte[JpegConstants.MAX_COMPONENTS][][]; - private int m_scancount; /* number of "scanlines" accumulated */ - private int m_samplesperclump; - - public JpegCodec(Tiff tif, Compression scheme, string name) - : base(tif, scheme, name) - { - m_tagMethods = new JpegCodecTagMethods(); - } - - private void cleanState() - { - m_compression = null; - m_decompression = null; - m_common = null; - - m_h_sampling = 0; - m_v_sampling = 0; - - m_jpegtables = null; - m_jpegtables_length = 0; - m_jpegquality = 0; - m_jpegcolormode = 0; - m_jpegtablesmode = 0; - - m_ycbcrsampling_fetched = false; - - m_recvparams = 0; - m_subaddress = null; - m_recvtime = 0; - m_faxdcs = null; - m_rawDecode = false; - m_rawEncode = false; - - m_cinfo_initialized = false; - - m_err = null; - m_photometric = 0; - - m_bytesperline = 0; - m_ds_buffer = new byte[JpegConstants.MAX_COMPONENTS][][]; - m_scancount = 0; - m_samplesperclump = 0; - } - - public override bool Init() - { - Debug.Assert(m_scheme == Compression.JPEG); - - /* - * Merge codec-specific tag information and override parent get/set - * field methods. - */ - m_tif.MergeFieldInfo(jpegFieldInfo, jpegFieldInfo.Length); - - /* - * Allocate state block so tag methods have storage to record values. - */ - cleanState(); - m_err = new JpegErrorManager(this); - - m_parentTagMethods = m_tif.m_tagmethods; - m_tif.m_tagmethods = m_tagMethods; - - /* Default values for codec-specific fields */ - m_jpegquality = 75; /* Default IJG quality */ - m_jpegcolormode = JpegColorMode.RGB; - m_jpegtablesmode = JpegTablesMode.QUANT | JpegTablesMode.HUFF; - - m_tif.m_flags |= TiffFlags.NOBITREV; // no bit reversal, please - - /* - ** Create a JPEGTables field if no directory has yet been created. - ** We do this just to ensure that sufficient space is reserved for - ** the JPEGTables field. It will be properly created the right - ** size later. - */ - if (m_tif.m_diroff == 0) - { - const int SIZE_OF_JPEGTABLES = 2000; - - // The following line assumes incorrectly that all JPEG-in-TIFF - // files will have a JPEGTABLES tag generated and causes - // null-filled JPEGTABLES tags to be written when the JPEG data - // is placed with WriteRawStrip. The field bit should be - // set, anyway, later when actual JPEGTABLES header is - // generated, so removing it here hopefully is harmless. - // - // m_tif.setFieldBit(FIELD_JPEGTABLES); - // - - m_jpegtables_length = SIZE_OF_JPEGTABLES; - m_jpegtables = new byte[m_jpegtables_length]; - } - - /* - * Mark the YCBCRSAMPLES as present even if it is not - * see: JPEGFixupTestSubsampling(). - */ - m_tif.setFieldBit(FieldBit.YCbCrSubsampling); - return true; - } - - /// - /// Gets a value indicating whether this codec can encode data. - /// - /// - /// true if this codec can encode data; otherwise, false. - /// - public override bool CanEncode - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether this codec can decode data. - /// - /// - /// true if this codec can decode data; otherwise, false. - /// - public override bool CanDecode - { - get - { - return true; - } - } - - /// - /// Setups the decoder part of the codec. - /// - /// - /// true if this codec successfully setup its decoder part and can decode data; - /// otherwise, false. - /// - /// - /// SetupDecode is called once before - /// . - public override bool SetupDecode() - { - return JPEGSetupDecode(); - } - - /// - /// Prepares the decoder part of the codec for a decoding. - /// - /// The zero-based sample plane index. - /// - /// true if this codec successfully prepared its decoder part and ready - /// to decode data; otherwise, false. - /// - /// - /// PreDecode is called after and before decoding. - /// - public override bool PreDecode(short plane) - { - return JPEGPreDecode(plane); - } - - /// - /// Decodes one row of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeRow(byte[] buffer, int offset, int count, short plane) - { - if (m_rawDecode) - return JPEGDecodeRaw(buffer, offset, count, plane); - - return JPEGDecode(buffer, offset, count, plane); - } - - /// - /// Decodes one strip of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeStrip(byte[] buffer, int offset, int count, short plane) - { - if (m_rawDecode) - return JPEGDecodeRaw(buffer, offset, count, plane); - - return JPEGDecode(buffer, offset, count, plane); - } - - /// - /// Decodes one tile of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeTile(byte[] buffer, int offset, int count, short plane) - { - if (m_rawDecode) - return JPEGDecodeRaw(buffer, offset, count, plane); - - return JPEGDecode(buffer, offset, count, plane); - } - - /// - /// Setups the encoder part of the codec. - /// - /// - /// true if this codec successfully setup its encoder part and can encode data; - /// otherwise, false. - /// - /// - /// SetupEncode is called once before - /// . - public override bool SetupEncode() - { - return JPEGSetupEncode(); - } - - /// - /// Prepares the encoder part of the codec for a encoding. - /// - /// The zero-based sample plane index. - /// - /// true if this codec successfully prepared its encoder part and ready - /// to encode data; otherwise, false. - /// - /// - /// PreEncode is called after and before encoding. - /// - public override bool PreEncode(short plane) - { - return JPEGPreEncode(plane); - } - - /// - /// Performs any actions after encoding required by the codec. - /// - /// - /// true if all post-encode actions succeeded; otherwise, false - /// - /// - /// PostEncode is called after encoding and can be used to release any external - /// resources needed during encoding. - /// - public override bool PostEncode() - { - return JPEGPostEncode(); - } - - /// - /// Encodes one row of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeRow(byte[] buffer, int offset, int count, short plane) - { - if (m_rawEncode) - return JPEGEncodeRaw(buffer, offset, count, plane); - - return JPEGEncode(buffer, offset, count, plane); - } - - /// - /// Encodes one strip of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeStrip(byte[] buffer, int offset, int count, short plane) - { - if (m_rawEncode) - return JPEGEncodeRaw(buffer, offset, count, plane); - - return JPEGEncode(buffer, offset, count, plane); - } - - /// - /// Encodes one tile of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeTile(byte[] buffer, int offset, int count, short plane) - { - if (m_rawEncode) - return JPEGEncodeRaw(buffer, offset, count, plane); - - return JPEGEncode(buffer, offset, count, plane); - } - - /// - /// Cleanups the state of the codec. - /// - /// - /// Cleanup is called when codec is no longer needed (won't be used) and can be - /// used for example to restore tag methods that were substituted. - public override void Cleanup() - { - JPEGCleanup(); - } - - /// - /// Calculates and/or constrains a strip size. - /// - /// The proposed strip size (may be zero or negative). - /// A strip size to use. - public override int DefStripSize(int size) - { - return JPEGDefaultStripSize(size); - } - - /// - /// Calculate and/or constrains a tile size - /// - /// The proposed tile width upon the call / tile width to use after the call. - /// The proposed tile height upon the call / tile height to use after the call. - public override void DefTileSize(ref int width, ref int height) - { - JPEGDefaultTileSize(ref width, ref height); - } - - /* - * The JPEG library initialized used to be done in TIFFInitJPEG(), but - * now that we allow a TIFF file to be opened in update mode it is necessary - * to have some way of deciding whether compression or decompression is - * desired other than looking at tif.tif_mode. We accomplish this by - * examining {TILE/STRIP}BYTECOUNTS to see if there is a non-zero entry. - * If so, we assume decompression is desired. - * - * This is tricky, because TIFFInitJPEG() is called while the directory is - * being read, and generally speaking the BYTECOUNTS tag won't have been read - * at that point. So we try to defer jpeg library initialization till we - * do have that tag ... basically any access that might require the compressor - * or decompressor that occurs after the reading of the directory. - * - * In an ideal world compressors or decompressors would be setup - * at the point where a single tile or strip was accessed (for read or write) - * so that stuff like update of missing tiles, or replacement of tiles could - * be done. However, we aren't trying to crack that nut just yet ... - * - * NFW, Feb 3rd, 2003. - */ - public bool InitializeLibJPEG(bool force_encode, bool force_decode) - { - int[] byte_counts = null; - bool data_is_empty = true; - bool decompress; - - if (m_cinfo_initialized) - { - if (force_encode && m_common.IsDecompressor) - TIFFjpeg_destroy(); - else if (force_decode && !m_common.IsDecompressor) - TIFFjpeg_destroy(); - else - return true; - - m_cinfo_initialized = false; - } - - /* - * Do we have tile data already? Make sure we initialize the - * the state in decompressor mode if we have tile data, even if we - * are not in read-only file access mode. - */ - FieldValue[] result = m_tif.GetField(TiffTag.TILEBYTECOUNTS); - if (m_tif.IsTiled() && result != null) - { - byte_counts = result[0].ToIntArray(); - if (byte_counts != null) - data_is_empty = byte_counts[0] == 0; - } - - result = m_tif.GetField(TiffTag.STRIPBYTECOUNTS); - if (!m_tif.IsTiled() && result != null) - { - byte_counts = result[0].ToIntArray(); - if (byte_counts != null) - data_is_empty = byte_counts[0] == 0; - } - - if (force_decode) - decompress = true; - else if (force_encode) - decompress = false; - else if (m_tif.m_mode == Tiff.O_RDONLY) - decompress = true; - else if (data_is_empty) - decompress = false; - else - decompress = true; - - // Initialize LibJpeg.Net - if (decompress) - { - if (!TIFFjpeg_create_decompress()) - return false; - } - else - { - if (!TIFFjpeg_create_compress()) - return false; - } - - m_cinfo_initialized = true; - return true; - } - - public Tiff GetTiff() - { - return m_tif; - } - - public void JPEGResetUpsampled() - { - /* - * Mark whether returned data is up-sampled or not so TIFFStripSize - * and TIFFTileSize return values that reflect the true amount of - * data. - */ - m_tif.m_flags &= ~TiffFlags.UPSAMPLED; - if (m_tif.m_dir.td_planarconfig == PlanarConfig.CONTIG) - { - if (m_tif.m_dir.td_photometric == Photometric.YCBCR && m_jpegcolormode == JpegColorMode.RGB) - m_tif.m_flags |= TiffFlags.UPSAMPLED; - } - - /* - * Must recalculate cached tile size in case sampling state changed. - * Should we really be doing this now if image size isn't set? - */ - if (m_tif.m_tilesize > 0) - m_tif.m_tilesize = m_tif.IsTiled() ? m_tif.TileSize() : -1; - - if (m_tif.m_scanlinesize > 0) - m_tif.m_scanlinesize = m_tif.ScanlineSize(); - } - - /// - /// Set encoding state at the start of a strip or tile. - /// - private bool JPEGPreEncode(short s) - { - const string module = "JPEGPreEncode"; - int segment_width; - int segment_height; - bool downsampled_input; - - Debug.Assert(!m_common.IsDecompressor); - /* - * Set encoding parameters for this strip/tile. - */ - if (m_tif.IsTiled()) - { - segment_width = m_tif.m_dir.td_tilewidth; - segment_height = m_tif.m_dir.td_tilelength; - m_bytesperline = m_tif.TileRowSize(); - } - else - { - segment_width = m_tif.m_dir.td_imagewidth; - segment_height = m_tif.m_dir.td_imagelength - m_tif.m_row; - if (segment_height > m_tif.m_dir.td_rowsperstrip) - segment_height = m_tif.m_dir.td_rowsperstrip; - m_bytesperline = m_tif.oldScanlineSize(); - } - if (m_tif.m_dir.td_planarconfig == PlanarConfig.SEPARATE && s > 0) - { - /* for PC 2, scale down the strip/tile size - * to match a downsampled component - */ - segment_width = Tiff.howMany(segment_width, m_h_sampling); - segment_height = Tiff.howMany(segment_height, m_v_sampling); - } - - if (segment_width > 65535 || segment_height > 65535) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Strip/tile too large for JPEG"); - return false; - } - - m_compression.Image_width = segment_width; - m_compression.Image_height = segment_height; - downsampled_input = false; - - if (m_tif.m_dir.td_planarconfig == PlanarConfig.CONTIG) - { - m_compression.Input_components = m_tif.m_dir.td_samplesperpixel; - if (m_photometric == Photometric.YCBCR) - { - if (m_jpegcolormode == JpegColorMode.RGB) - { - m_compression.In_color_space = J_COLOR_SPACE.JCS_RGB; - } - else - { - m_compression.In_color_space = J_COLOR_SPACE.JCS_YCbCr; - if (m_h_sampling != 1 || m_v_sampling != 1) - downsampled_input = true; - } - - if (!TIFFjpeg_set_colorspace(J_COLOR_SPACE.JCS_YCbCr)) - return false; - - /* - * Set Y sampling factors; - * we assume jpeg_set_colorspace() set the rest to 1 - */ - m_compression.Component_info[0].H_samp_factor = m_h_sampling; - m_compression.Component_info[0].V_samp_factor = m_v_sampling; - } - else - { - m_compression.In_color_space = J_COLOR_SPACE.JCS_UNKNOWN; - if (!TIFFjpeg_set_colorspace(J_COLOR_SPACE.JCS_UNKNOWN)) - return false; - /* jpeg_set_colorspace set all sampling factors to 1 */ - } - } - else - { - m_compression.Input_components = 1; - m_compression.In_color_space = J_COLOR_SPACE.JCS_UNKNOWN; - if (!TIFFjpeg_set_colorspace(J_COLOR_SPACE.JCS_UNKNOWN)) - return false; - - m_compression.Component_info[0].Component_id = s; - /* jpeg_set_colorspace() set sampling factors to 1 */ - if (m_photometric == Photometric.YCBCR && s > 0) - { - m_compression.Component_info[0].Quant_tbl_no = 1; - m_compression.Component_info[0].Dc_tbl_no = 1; - m_compression.Component_info[0].Ac_tbl_no = 1; - } - } - - // ensure LibJpeg.Net won't write any extraneous markers - m_compression.Write_JFIF_header = false; - m_compression.Write_Adobe_marker = false; - - /* set up table handling correctly */ - if (!TIFFjpeg_set_quality(m_jpegquality, false)) - return false; - - if ((m_jpegtablesmode & JpegTablesMode.QUANT) == 0) - { - unsuppress_quant_table(0); - unsuppress_quant_table(1); - } - - if ((m_jpegtablesmode & JpegTablesMode.HUFF) != 0) - m_compression.Optimize_coding = false; - else - m_compression.Optimize_coding = true; - - if (downsampled_input) - { - // Need to use raw-data interface to LibJpeg.Net - m_compression.Raw_data_in = true; - m_rawEncode = true; - } - else - { - // Use normal interface to LibJpeg.Net - m_compression.Raw_data_in = false; - m_rawEncode = false; - } - - /* Start JPEG compressor */ - if (!TIFFjpeg_start_compress(false)) - return false; - - /* Allocate downsampled-data buffers if needed */ - if (downsampled_input) - { - if (!alloc_downsampled_buffers(m_compression.Component_info, m_compression.Num_components)) - return false; - } - - m_scancount = 0; - return true; - } - - private bool JPEGSetupEncode() - { - const string module = "JPEGSetupEncode"; - - InitializeLibJPEG(true, false); - - Debug.Assert(!m_common.IsDecompressor); - - /* - * Initialize all JPEG parameters to default values. - * Note that jpeg_set_defaults needs legal values for - * in_color_space and input_components. - */ - m_compression.In_color_space = J_COLOR_SPACE.JCS_UNKNOWN; - m_compression.Input_components = 1; - if (!TIFFjpeg_set_defaults()) - return false; - - /* Set per-file parameters */ - m_photometric = m_tif.m_dir.td_photometric; - switch (m_photometric) - { - case Photometric.YCBCR: - m_h_sampling = m_tif.m_dir.td_ycbcrsubsampling[0]; - m_v_sampling = m_tif.m_dir.td_ycbcrsubsampling[1]; - /* - * A ReferenceBlackWhite field *must* be present since the - * default value is inappropriate for YCbCr. Fill in the - * proper value if application didn't set it. - */ - FieldValue[] result = m_tif.GetField(TiffTag.REFERENCEBLACKWHITE); - if (result == null) - { - float[] refbw = new float[6]; - int top = 1 << m_tif.m_dir.td_bitspersample; - refbw[0] = 0; - refbw[1] = (float)(top - 1L); - refbw[2] = (float)(top >> 1); - refbw[3] = refbw[1]; - refbw[4] = refbw[2]; - refbw[5] = refbw[1]; - m_tif.SetField(TiffTag.REFERENCEBLACKWHITE, refbw); - } - break; - - /* disallowed by Tech Note */ - case Photometric.PALETTE: - case Photometric.MASK: - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "PhotometricInterpretation {0} not allowed for JPEG", m_photometric); - return false; - - default: - /* TIFF 6.0 forbids subsampling of all other color spaces */ - m_h_sampling = 1; - m_v_sampling = 1; - break; - } - - /* Verify miscellaneous parameters */ - - // This would need work if LibTiff.Net ever supports different - // depths for different components, or if LibJpeg.Net ever supports - // run-time selection of depth. Neither is imminent. - if (m_tif.m_dir.td_bitspersample != JpegConstants.BITS_IN_JSAMPLE) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "BitsPerSample {0} not allowed for JPEG", m_tif.m_dir.td_bitspersample); - return false; - } - - m_compression.Data_precision = m_tif.m_dir.td_bitspersample; - if (m_tif.IsTiled()) - { - if ((m_tif.m_dir.td_tilelength % (m_v_sampling * JpegConstants.DCTSIZE)) != 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "JPEG tile height must be multiple of {0}", m_v_sampling * JpegConstants.DCTSIZE); - return false; - } - - if ((m_tif.m_dir.td_tilewidth % (m_h_sampling * JpegConstants.DCTSIZE)) != 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "JPEG tile width must be multiple of {0}", m_h_sampling * JpegConstants.DCTSIZE); - return false; - } - } - else - { - if (m_tif.m_dir.td_rowsperstrip < m_tif.m_dir.td_imagelength && - (m_tif.m_dir.td_rowsperstrip % (m_v_sampling * JpegConstants.DCTSIZE)) != 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "RowsPerStrip must be multiple of {0} for JPEG", m_v_sampling * JpegConstants.DCTSIZE); - return false; - } - } - - /* Create a JPEGTables field if appropriate */ - if ((m_jpegtablesmode & (JpegTablesMode.QUANT | JpegTablesMode.HUFF)) != 0) - { - bool startsWithZeroes = true; - if (m_jpegtables != null) - { - for (int i = 0; i < 8; i++) - { - if (m_jpegtables[i] != 0) - { - startsWithZeroes = false; - break; - } - } - } - else - { - startsWithZeroes = false; - } - - if (m_jpegtables == null || startsWithZeroes) - { - if (!prepare_JPEGTables()) - return false; - - /* Mark the field present */ - /* Can't use TIFFSetField since BEENWRITING is already set! */ - m_tif.m_flags |= TiffFlags.DIRTYDIRECT; - m_tif.setFieldBit(FIELD_JPEGTABLES); - } - } - else - { - /* We do not support application-supplied JPEGTables, */ - /* so mark the field not present */ - m_tif.clearFieldBit(FIELD_JPEGTABLES); - } - - /* Direct LibJpeg.Net output to LibTiff.Net's output buffer */ - TIFFjpeg_data_dest(); - - return true; - } - - /// - /// Finish up at the end of a strip or tile. - /// - /// - private bool JPEGPostEncode() - { - if (m_scancount > 0) - { - // Need to emit a partial bufferload of downsampled data. Pad the data vertically. - for (int ci = 0; ci < m_compression.Num_components; ci++) - { - int vsamp = m_compression.Component_info[ci].V_samp_factor; - int row_width = m_compression.Component_info[ci].Width_in_blocks * JpegConstants.DCTSIZE * sizeof(byte); - for (int ypos = m_scancount * vsamp; ypos < JpegConstants.DCTSIZE * vsamp; ypos++) - Buffer.BlockCopy(m_ds_buffer[ci][ypos - 1], 0, m_ds_buffer[ci][ypos], 0, row_width); - } - - int n = m_compression.Max_v_samp_factor * JpegConstants.DCTSIZE; - if (TIFFjpeg_write_raw_data(m_ds_buffer, n) != n) - return false; - } - - return TIFFjpeg_finish_compress(); - } - - private void JPEGCleanup() - { - m_tif.m_tagmethods = m_parentTagMethods; - - if (m_cinfo_initialized) - { - // release LibJpeg.Net resources - TIFFjpeg_destroy(); - } - } - - /* - * JPEG Decoding. - */ - - /* - * Set up for decoding a strip or tile. - */ - private bool JPEGPreDecode(short s) - { - TiffDirectory td = m_tif.m_dir; - const string module = "JPEGPreDecode"; - int segment_width; - int segment_height; - int ci; - - Debug.Assert(m_common.IsDecompressor); - - /* - * Reset decoder state from any previous strip/tile, - * in case application didn't read the whole strip. - */ - if (!TIFFjpeg_abort()) - return false; - - /* - * Read the header for this strip/tile. - */ - if (TIFFjpeg_read_header(true) != ReadResult.JPEG_HEADER_OK) - return false; - - /* - * Check image parameters and set decompression parameters. - */ - segment_width = td.td_imagewidth; - segment_height = td.td_imagelength - m_tif.m_row; - if (m_tif.IsTiled()) - { - segment_width = td.td_tilewidth; - segment_height = td.td_tilelength; - m_bytesperline = m_tif.TileRowSize(); - } - else - { - if (segment_height > td.td_rowsperstrip) - segment_height = td.td_rowsperstrip; - m_bytesperline = m_tif.oldScanlineSize(); - } - - if (td.td_planarconfig == PlanarConfig.SEPARATE && s > 0) - { - /* - * For PC 2, scale down the expected strip/tile size - * to match a downsampled component - */ - segment_width = Tiff.howMany(segment_width, m_h_sampling); - segment_height = Tiff.howMany(segment_height, m_v_sampling); - } - - if (m_decompression.Image_width < segment_width || m_decompression.Image_height < segment_height) - { - Tiff.WarningExt(m_tif, m_tif.m_clientdata, module, - "Improper JPEG strip/tile size, expected {0}x{1}, got {2}x{3}", - segment_width, segment_height, m_decompression.Image_width, m_decompression.Image_height); - } - - if (m_decompression.Image_width > segment_width || m_decompression.Image_height > segment_height) - { - /* - * This case could be dangerous, if the strip or tile size has - * been reported as less than the amount of data jpeg will - * return, some potential security issues arise. Catch this - * case and error out. - */ - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "JPEG strip/tile size exceeds expected dimensions, expected {0}x{1}, got {2}x{3}", - segment_width, segment_height, m_decompression.Image_width, m_decompression.Image_height); - return false; - } - - if (m_decompression.Num_components != (td.td_planarconfig == PlanarConfig.CONTIG ? (int)td.td_samplesperpixel : 1)) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Improper JPEG component count"); - return false; - } - - if (m_decompression.Data_precision != td.td_bitspersample) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Improper JPEG data precision"); - return false; - } - - if (td.td_planarconfig == PlanarConfig.CONTIG) - { - /* Component 0 should have expected sampling factors */ - if (m_decompression.Comp_info[0].H_samp_factor != m_h_sampling || - m_decompression.Comp_info[0].V_samp_factor != m_v_sampling) - { - Tiff.WarningExt(m_tif, m_tif.m_clientdata, module, - "Improper JPEG sampling factors {0},{1}\nApparently should be {2},{3}.", - m_decompression.Comp_info[0].H_samp_factor, - m_decompression.Comp_info[0].V_samp_factor, m_h_sampling, m_v_sampling); - - /* - * There are potential security issues here - * for decoders that have already allocated - * buffers based on the expected sampling - * factors. Lets check the sampling factors - * dont exceed what we were expecting. - */ - if (m_decompression.Comp_info[0].H_samp_factor > m_h_sampling || - m_decompression.Comp_info[0].V_samp_factor > m_v_sampling) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "Cannot honour JPEG sampling factors that exceed those specified."); - return false; - } - - /* - * XXX: Files written by the Intergraph software - * has different sampling factors stored in the - * TIFF tags and in the JPEG structures. We will - * try to deduce Intergraph files by the presense - * of the tag 33918. - */ - if (m_tif.FindFieldInfo((TiffTag)33918, TiffType.ANY) == null) - { - Tiff.WarningExt(m_tif, m_tif.m_clientdata, module, - "Decompressor will try reading with sampling {0},{1}.", - m_decompression.Comp_info[0].H_samp_factor, - m_decompression.Comp_info[0].V_samp_factor); - - m_h_sampling = m_decompression.Comp_info[0].H_samp_factor; - m_v_sampling = m_decompression.Comp_info[0].V_samp_factor; - } - } - - /* Rest should have sampling factors 1,1 */ - for (ci = 1; ci < m_decompression.Num_components; ci++) - { - if (m_decompression.Comp_info[ci].H_samp_factor != 1 || - m_decompression.Comp_info[ci].V_samp_factor != 1) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Improper JPEG sampling factors"); - return false; - } - } - } - else - { - /* PC 2's single component should have sampling factors 1,1 */ - if (m_decompression.Comp_info[0].H_samp_factor != 1 || - m_decompression.Comp_info[0].V_samp_factor != 1) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Improper JPEG sampling factors"); - return false; - } - } - - bool downsampled_output = false; - if (td.td_planarconfig == PlanarConfig.CONTIG && - m_photometric == Photometric.YCBCR && - m_jpegcolormode == JpegColorMode.RGB) - { - /* Convert YCbCr to RGB */ - m_decompression.Jpeg_color_space = J_COLOR_SPACE.JCS_YCbCr; - m_decompression.Out_color_space = J_COLOR_SPACE.JCS_RGB; - } - else - { - /* Suppress colorspace handling */ - m_decompression.Jpeg_color_space = J_COLOR_SPACE.JCS_UNKNOWN; - m_decompression.Out_color_space = J_COLOR_SPACE.JCS_UNKNOWN; - if (td.td_planarconfig == PlanarConfig.CONTIG && - (m_h_sampling != 1 || m_v_sampling != 1)) - { - downsampled_output = true; - } - /* XXX what about up-sampling? */ - } - - if (downsampled_output) - { - // Need to use raw-data interface to LibJpeg.Net - m_decompression.Raw_data_out = true; - m_rawDecode = true; - } - else - { - // Use normal interface to LibJpeg.Net - m_decompression.Raw_data_out = false; - m_rawDecode = false; - } - - /* Start JPEG decompressor */ - if (!TIFFjpeg_start_decompress()) - return false; - - /* Allocate downsampled-data buffers if needed */ - if (downsampled_output) - { - if (!alloc_downsampled_buffers(m_decompression.Comp_info, m_decompression.Num_components)) - return false; - - m_scancount = JpegConstants.DCTSIZE; /* mark buffer empty */ - } - - return true; - } - - private bool prepare_JPEGTables() - { - InitializeLibJPEG(false, false); - - /* Initialize quant tables for current quality setting */ - if (!TIFFjpeg_set_quality(m_jpegquality, false)) - return false; - - /* Mark only the tables we want for output */ - /* NB: chrominance tables are currently used only with YCbCr */ - if (!TIFFjpeg_suppress_tables(true)) - return false; - - if ((m_jpegtablesmode & JpegTablesMode.QUANT) != 0) - { - unsuppress_quant_table(0); - if (m_photometric == Photometric.YCBCR) - unsuppress_quant_table(1); - } - - if ((m_jpegtablesmode & JpegTablesMode.HUFF) != 0) - { - unsuppress_huff_table(0); - if (m_photometric == Photometric.YCBCR) - unsuppress_huff_table(1); - } - - // Direct LibJpeg.Net output into jpegtables - if (!TIFFjpeg_tables_dest()) - return false; - - /* Emit tables-only datastream */ - if (!TIFFjpeg_write_tables()) - return false; - - return true; - } - - private bool JPEGSetupDecode() - { - TiffDirectory td = m_tif.m_dir; - - InitializeLibJPEG(false, true); - - Debug.Assert(m_common.IsDecompressor); - - /* Read JPEGTables if it is present */ - if (m_tif.fieldSet(FIELD_JPEGTABLES)) - { - m_decompression.Src = new JpegTablesSource(this); - if (TIFFjpeg_read_header(false) != ReadResult.JPEG_HEADER_TABLES_ONLY) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, "JPEGSetupDecode", "Bogus JPEGTables field"); - return false; - } - } - - /* Grab parameters that are same for all strips/tiles */ - m_photometric = td.td_photometric; - switch (m_photometric) - { - case Photometric.YCBCR: - m_h_sampling = td.td_ycbcrsubsampling[0]; - m_v_sampling = td.td_ycbcrsubsampling[1]; - break; - default: - /* TIFF 6.0 forbids subsampling of all other color spaces */ - m_h_sampling = 1; - m_v_sampling = 1; - break; - } - - /* Set up for reading normal data */ - m_decompression.Src = new JpegStdSource(this); - m_tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmNone; /* override byte swapping */ - return true; - } - - private int TIFFjpeg_read_scanlines(byte[][] scanlines, int max_lines) - { - int n = 0; - try - { - n = m_decompression.jpeg_read_scanlines(scanlines, max_lines); - } - catch (Exception) - { - return -1; - } - - return n; - } - - /// - /// Decode a chunk of pixels. - /// "Standard" case: returned data is not downsampled. - /// - private bool JPEGDecode(byte[] buffer, int offset, int count, short plane) - { - int nrows = count / m_bytesperline; - if ((count % m_bytesperline) != 0) - Tiff.WarningExt(m_tif, m_tif.m_clientdata, m_tif.m_name, "fractional scanline not read"); - - if (nrows > (int)m_decompression.Image_height) - nrows = m_decompression.Image_height; - - // data is expected to be read in multiples of a scanline - if (nrows != 0) - { - byte[][] bufptr = new byte[1][]; - bufptr[0] = new byte[m_bytesperline]; - do - { - // In the 8bit case. We read directly into the TIFF buffer. - Array.Clear(bufptr[0], 0, m_bytesperline); - if (TIFFjpeg_read_scanlines(bufptr, 1) != 1) - return false; - - ++m_tif.m_row; - Buffer.BlockCopy(bufptr[0], 0, buffer, offset, m_bytesperline); - offset += m_bytesperline; - count -= m_bytesperline; - } - while (--nrows > 0); - } - - // Close down the decompressor if we've finished the strip or tile. - return m_decompression.Output_scanline < m_decompression.Output_height || TIFFjpeg_finish_decompress(); - } - - /// - /// Decode a chunk of pixels. - /// Returned data is downsampled per sampling factors. - /// - private bool JPEGDecodeRaw(byte[] buffer, int offset, int count, short plane) - { - // data is expected to be read in multiples of a scanline - int nrows = m_decompression.Image_height; - if (nrows != 0) - { - // Cb,Cr both have sampling factors 1, so this is correct - int clumps_per_line = m_decompression.Comp_info[1].Downsampled_width; - - do - { - // Reload downsampled-data buffer if needed - if (m_scancount >= JpegConstants.DCTSIZE) - { - int n = m_decompression.Max_v_samp_factor * JpegConstants.DCTSIZE; - if (TIFFjpeg_read_raw_data(m_ds_buffer, n) != n) - return false; - - m_scancount = 0; - } - - // Fastest way to unseparate data is to make one pass over the scanline for - // each row of each component. - int clumpoffset = 0; // first sample in clump - for (int ci = 0; ci < m_decompression.Num_components; ci++) - { - int hsamp = m_decompression.Comp_info[ci].H_samp_factor; - int vsamp = m_decompression.Comp_info[ci].V_samp_factor; - - for (int ypos = 0; ypos < vsamp; ypos++) - { - byte[] inBuf = m_ds_buffer[ci][m_scancount * vsamp + ypos]; - int inptr = 0; - - int outptr = offset + clumpoffset; - if (outptr >= buffer.Length) - break; - - if (hsamp == 1) - { - // fast path for at least Cb and Cr - for (int nclump = clumps_per_line; nclump-- > 0; ) - { - buffer[outptr] = inBuf[inptr]; - inptr++; - outptr += m_samplesperclump; - } - } - else - { - // general case - for (int nclump = clumps_per_line; nclump-- > 0; ) - { - for (int xpos = 0; xpos < hsamp; xpos++) - { - buffer[outptr + xpos] = inBuf[inptr]; - inptr++; - } - - outptr += m_samplesperclump; - } - } - - clumpoffset += hsamp; - } - } - - ++m_scancount; - m_tif.m_row += m_v_sampling; - - // increment/decrement of buffer and count is still incorrect, but should not matter - // TODO: resolve this - offset += m_bytesperline; - count -= m_bytesperline; - nrows -= m_v_sampling; - } - while (nrows > 0); - } - - // Close down the decompressor if done. - return m_decompression.Output_scanline < m_decompression.Output_height || TIFFjpeg_finish_decompress(); - } - - /// - /// Encode a chunk of pixels. - /// "Standard" case: incoming data is not downsampled. - /// - private bool JPEGEncode(byte[] buffer, int offset, int count, short plane) - { - // data is expected to be supplied in multiples of a scanline - int nrows = count / m_bytesperline; - if ((count % m_bytesperline) != 0) - Tiff.WarningExt(m_tif, m_tif.m_clientdata, m_tif.m_name, "fractional scanline discarded"); - - // The last strip will be limited to image size - if (!m_tif.IsTiled() && m_tif.m_row + nrows > m_tif.m_dir.td_imagelength) - nrows = m_tif.m_dir.td_imagelength - m_tif.m_row; - - byte[][] bufptr = new byte[1][]; - bufptr[0] = new byte[m_bytesperline]; - while (nrows-- > 0) - { - Buffer.BlockCopy(buffer, offset, bufptr[0], 0, m_bytesperline); - if (TIFFjpeg_write_scanlines(bufptr, 1) != 1) - return false; - - if (nrows > 0) - m_tif.m_row++; - - offset += m_bytesperline; - } - - return true; - } - - /// - /// Encode a chunk of pixels. - /// Incoming data is expected to be downsampled per sampling factors. - /// - private bool JPEGEncodeRaw(byte[] buffer, int offset, int count, short plane) - { - // data is expected to be supplied in multiples of a clumpline - // a clumpline is equivalent to v_sampling desubsampled scanlines - - // TODO: the following calculation of bytesperclumpline, should substitute - // calculation of bytesperline, except that it is per v_sampling lines - int bytesperclumpline = (((m_compression.Image_width + m_h_sampling - 1) / m_h_sampling) * - (m_h_sampling * m_v_sampling + 2) * m_compression.Data_precision + 7) / 8; - - int nrows = (count / bytesperclumpline) * m_v_sampling; - if ((count % bytesperclumpline) != 0) - Tiff.WarningExt(m_tif, m_tif.m_clientdata, m_tif.m_name, "fractional scanline discarded"); - - // Cb,Cr both have sampling factors 1, so this is correct - int clumps_per_line = m_compression.Component_info[1].Downsampled_width; - - while (nrows > 0) - { - // Fastest way to separate the data is to make one pass over the scanline for - // each row of each component. - int clumpoffset = 0; // first sample in clump - for (int ci = 0; ci < m_compression.Num_components; ci++) - { - jpeg_component_info compptr = m_compression.Component_info[ci]; - int hsamp = compptr.H_samp_factor; - int vsamp = compptr.V_samp_factor; - int padding = compptr.Width_in_blocks * JpegConstants.DCTSIZE - clumps_per_line * hsamp; - for (int ypos = 0; ypos < vsamp; ypos++) - { - int inptr = offset + clumpoffset; - - byte[] outbuf = m_ds_buffer[ci][m_scancount * vsamp + ypos]; - int outptr = 0; - - if (hsamp == 1) - { - // fast path for at least Cb and Cr - for (int nclump = clumps_per_line; nclump-- > 0; ) - { - outbuf[outptr] = buffer[inptr]; - outptr++; - inptr += m_samplesperclump; - } - } - else - { - // general case - for (int nclump = clumps_per_line; nclump-- > 0; ) - { - for (int xpos = 0; xpos < hsamp; xpos++) - { - outbuf[outptr] = buffer[inptr + xpos]; - outptr++; - } - - inptr += m_samplesperclump; - } - } - - // pad each scanline as needed - for (int xpos = 0; xpos < padding; xpos++) - { - outbuf[outptr] = outbuf[outptr - 1]; - outptr++; - } - - clumpoffset += hsamp; - } - } - - m_scancount++; - if (m_scancount >= JpegConstants.DCTSIZE) - { - int n = m_compression.Max_v_samp_factor * JpegConstants.DCTSIZE; - if (TIFFjpeg_write_raw_data(m_ds_buffer, n) != n) - return false; - - m_scancount = 0; - } - - m_tif.m_row += m_v_sampling; - offset += m_bytesperline; - nrows -= m_v_sampling; - } - - return true; - } - - private int JPEGDefaultStripSize(int s) - { - s = base.DefStripSize(s); - if (s < m_tif.m_dir.td_imagelength) - s = Tiff.roundUp(s, m_tif.m_dir.td_ycbcrsubsampling[1] * JpegConstants.DCTSIZE); - - return s; - } - - private void JPEGDefaultTileSize(ref int tw, ref int th) - { - base.DefTileSize(ref tw, ref th); - tw = Tiff.roundUp(tw, m_tif.m_dir.td_ycbcrsubsampling[0] * JpegConstants.DCTSIZE); - th = Tiff.roundUp(th, m_tif.m_dir.td_ycbcrsubsampling[1] * JpegConstants.DCTSIZE); - } - - /* - * Interface routines. This layer of routines exists - * primarily to limit side-effects from LibJpeg.Net exceptions. - * Also, normal/error returns are converted into return - * values per LibTiff.Net practice. - */ - private bool TIFFjpeg_create_compress() - { - /* initialize JPEG error handling */ - try - { - m_compression = new jpeg_compress_struct(new JpegErrorManager(this)); - m_common = m_compression; - } - catch (Exception) - { - return false; - } - - return true; - } - - private bool TIFFjpeg_create_decompress() - { - /* initialize JPEG error handling */ - try - { - m_decompression = new jpeg_decompress_struct(new JpegErrorManager(this)); - m_common = m_decompression; - } - catch (Exception) - { - return false; - } - - return true; - } - - private bool TIFFjpeg_set_defaults() - { - try - { - m_compression.jpeg_set_defaults(); - } - catch (Exception) - { - return false; - } - - return true; - } - - private bool TIFFjpeg_set_colorspace(J_COLOR_SPACE colorspace) - { - try - { - m_compression.jpeg_set_colorspace(colorspace); - } - catch (Exception) - { - return false; - } - - return true; - } - - private bool TIFFjpeg_set_quality(int quality, bool force_baseline) - { - try - { - m_compression.jpeg_set_quality(quality, force_baseline); - } - catch (Exception) - { - return false; - } - - return true; - } - - private bool TIFFjpeg_suppress_tables(bool suppress) - { - try - { - m_compression.jpeg_suppress_tables(suppress); - } - catch (Exception) - { - return false; - } - - return true; - } - - private bool TIFFjpeg_start_compress(bool write_all_tables) - { - try - { - m_compression.jpeg_start_compress(write_all_tables); - } - catch (Exception) - { - return false; - } - - return true; - } - - private int TIFFjpeg_write_scanlines(byte[][] scanlines, int num_lines) - { - int n = 0; - try - { - n = m_compression.jpeg_write_scanlines(scanlines, num_lines); - } - catch (Exception) - { - return -1; - } - - return n; - } - - private int TIFFjpeg_write_raw_data(byte[][][] data, int num_lines) - { - int n = 0; - try - { - n = m_compression.jpeg_write_raw_data(data, num_lines); - } - catch (Exception) - { - return -1; - } - - return n; - } - - private bool TIFFjpeg_finish_compress() - { - try - { - m_compression.jpeg_finish_compress(); - } - catch (Exception) - { - return false; - } - - return true; - } - - private bool TIFFjpeg_write_tables() - { - try - { - m_compression.jpeg_write_tables(); - } - catch (Exception) - { - return false; - } - - return true; - } - - private ReadResult TIFFjpeg_read_header(bool require_image) - { - ReadResult res = ReadResult.JPEG_SUSPENDED; - try - { - res = m_decompression.jpeg_read_header(require_image); - } - catch (Exception) - { - return ReadResult.JPEG_SUSPENDED; - } - - return res; - } - - private bool TIFFjpeg_start_decompress() - { - try - { - m_decompression.jpeg_start_decompress(); - } - catch (Exception) - { - return false; - } - - return true; - } - - private int TIFFjpeg_read_raw_data(byte[][][] data, int max_lines) - { - int n = 0; - try - { - n = m_decompression.jpeg_read_raw_data(data, max_lines); - } - catch (Exception) - { - return -1; - } - - return n; - } - - private bool TIFFjpeg_finish_decompress() - { - bool res = true; - try - { - res = m_decompression.jpeg_finish_decompress(); - } - catch (Exception) - { - return false; - } - - return res; - } - - private bool TIFFjpeg_abort() - { - try - { - m_common.jpeg_abort(); - } - catch (Exception) - { - return false; - } - - return true; - } - - private bool TIFFjpeg_destroy() - { - try - { - m_common.jpeg_destroy(); - } - catch (Exception) - { - return false; - } - - return true; - } - - private static byte[][] TIFFjpeg_alloc_sarray(int samplesperrow, int numrows) - { - byte[][] result = new byte[numrows][]; - for (int i = 0; i < numrows; i++) - result[i] = new byte[samplesperrow]; - - return result; - } - - /* - * Allocate downsampled-data buffers needed for downsampled I/O. - * We use values computed in jpeg_start_compress or jpeg_start_decompress. - * We use LibJpeg.Net's allocator so that buffers will be released automatically - * when done with strip/tile. - * This is also a handy place to compute samplesperclump, bytesperline. - */ - private bool alloc_downsampled_buffers(jpeg_component_info[] comp_info, int num_components) - { - int samples_per_clump = 0; - for (int ci = 0; ci < num_components; ci++) - { - jpeg_component_info compptr = comp_info[ci]; - samples_per_clump += compptr.H_samp_factor * compptr.V_samp_factor; - - byte[][] buf = TIFFjpeg_alloc_sarray( - compptr.Width_in_blocks * JpegConstants.DCTSIZE, - compptr.V_samp_factor * JpegConstants.DCTSIZE); - m_ds_buffer[ci] = buf; - } - - m_samplesperclump = samples_per_clump; - return true; - } - - private void unsuppress_quant_table(int tblno) - { - JQUANT_TBL qtbl = m_compression.Quant_tbl_ptrs[tblno]; - if (qtbl != null) - qtbl.Sent_table = false; - } - - private void unsuppress_huff_table(int tblno) - { - JHUFF_TBL htbl = m_compression.Dc_huff_tbl_ptrs[tblno]; - - if (htbl != null) - htbl.Sent_table = false; - - htbl = m_compression.Ac_huff_tbl_ptrs[tblno]; - if (htbl != null) - htbl.Sent_table = false; - } - - private void TIFFjpeg_data_dest() - { - m_compression.Dest = new JpegStdDestination(m_tif); - } - - private bool TIFFjpeg_tables_dest() - { - /* - * Allocate a working buffer for building tables. - * Initial size is 1000 bytes, which is usually adequate. - */ - m_jpegtables_length = 1000; - m_jpegtables = new byte[m_jpegtables_length]; - m_compression.Dest = new JpegTablesDestination(this); - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegCodecTagMethods.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegCodecTagMethods.cs deleted file mode 100644 index f5f535e2..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegCodecTagMethods.cs +++ /dev/null @@ -1,252 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System; -using System.Diagnostics; -using System.IO; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - class JpegCodecTagMethods : TiffTagMethods - { - public override bool SetField(Tiff tif, TiffTag tag, FieldValue[] ap) - { - JpegCodec sp = tif.m_currentCodec as JpegCodec; - Debug.Assert(sp != null); - - switch (tag) - { - case TiffTag.JPEGTABLES: - int v32 = ap[0].ToInt(); - if (v32 == 0) - { - // XXX - return false; - } - - sp.m_jpegtables = new byte [v32]; - Buffer.BlockCopy(ap[1].ToByteArray(), 0, sp.m_jpegtables, 0, v32); - sp.m_jpegtables_length = v32; - tif.setFieldBit(JpegCodec.FIELD_JPEGTABLES); - break; - - case TiffTag.JPEGQUALITY: - sp.m_jpegquality = ap[0].ToInt(); - return true; // pseudo tag - - case TiffTag.JPEGCOLORMODE: - sp.m_jpegcolormode = (JpegColorMode)ap[0].ToShort(); - sp.JPEGResetUpsampled(); - return true; // pseudo tag - - case TiffTag.PHOTOMETRIC: - bool ret_value = base.SetField(tif, tag, ap); - sp.JPEGResetUpsampled(); - return ret_value; - - case TiffTag.JPEGTABLESMODE: - sp.m_jpegtablesmode = (JpegTablesMode)ap[0].ToShort(); - return true; // pseudo tag - - case TiffTag.YCBCRSUBSAMPLING: - // mark the fact that we have a real ycbcrsubsampling! - sp.m_ycbcrsampling_fetched = true; - // should we be recomputing upsampling info here? - return base.SetField(tif, tag, ap); - - case TiffTag.FAXRECVPARAMS: - sp.m_recvparams = ap[0].ToInt(); - break; - - case TiffTag.FAXSUBADDRESS: - Tiff.setString(out sp.m_subaddress, ap[0].ToString()); - break; - - case TiffTag.FAXRECVTIME: - sp.m_recvtime = ap[0].ToInt(); - break; - - case TiffTag.FAXDCS: - Tiff.setString(out sp.m_faxdcs, ap[0].ToString()); - break; - - default: - return base.SetField(tif, tag, ap); - } - - TiffFieldInfo fip = tif.FieldWithTag(tag); - if (fip != null) - tif.setFieldBit(fip.Bit); - else - return false; - - tif.m_flags |= TiffFlags.DIRTYDIRECT; - return true; - } - - public override FieldValue[] GetField(Tiff tif, TiffTag tag) - { - JpegCodec sp = tif.m_currentCodec as JpegCodec; - Debug.Assert(sp != null); - - FieldValue[] result = null; - - switch (tag) - { - case TiffTag.JPEGTABLES: - result = new FieldValue[2]; - result[0].Set(sp.m_jpegtables_length); - result[1].Set(sp.m_jpegtables); - break; - - case TiffTag.JPEGQUALITY: - result = new FieldValue[1]; - result[0].Set(sp.m_jpegquality); - break; - - case TiffTag.JPEGCOLORMODE: - result = new FieldValue[1]; - result[0].Set(sp.m_jpegcolormode); - break; - - case TiffTag.JPEGTABLESMODE: - result = new FieldValue[1]; - result[0].Set(sp.m_jpegtablesmode); - break; - - case TiffTag.YCBCRSUBSAMPLING: - JPEGFixupTestSubsampling(tif); - return base.GetField(tif, tag); - - case TiffTag.FAXRECVPARAMS: - result = new FieldValue[1]; - result[0].Set(sp.m_recvparams); - break; - - case TiffTag.FAXSUBADDRESS: - result = new FieldValue[1]; - result[0].Set(sp.m_subaddress); - break; - - case TiffTag.FAXRECVTIME: - result = new FieldValue[1]; - result[0].Set(sp.m_recvtime); - break; - - case TiffTag.FAXDCS: - result = new FieldValue[1]; - result[0].Set(sp.m_faxdcs); - break; - - default: - return base.GetField(tif, tag); - } - - return result; - } - - public override void PrintDir(Tiff tif, Stream fd, TiffPrintFlags flags) - { - JpegCodec sp = tif.m_currentCodec as JpegCodec; - Debug.Assert(sp != null); - - if (tif.fieldSet(JpegCodec.FIELD_JPEGTABLES)) - Tiff.fprintf(fd, " JPEG Tables: ({0} bytes)\n", sp.m_jpegtables_length); - - if (tif.fieldSet(JpegCodec.FIELD_RECVPARAMS)) - Tiff.fprintf(fd, " Fax Receive Parameters: {0,8:x}\n", sp.m_recvparams); - - if (tif.fieldSet(JpegCodec.FIELD_SUBADDRESS)) - Tiff.fprintf(fd, " Fax SubAddress: {0}\n", sp.m_subaddress); - - if (tif.fieldSet(JpegCodec.FIELD_RECVTIME)) - Tiff.fprintf(fd, " Fax Receive Time: {0} secs\n", sp.m_recvtime); - - if (tif.fieldSet(JpegCodec.FIELD_FAXDCS)) - Tiff.fprintf(fd, " Fax DCS: {0}\n", sp.m_faxdcs); - } - - /* - * Some JPEG-in-TIFF produces do not emit the YCBCRSUBSAMPLING values in - * the TIFF tags, but still use non-default (2,2) values within the jpeg - * data stream itself. In order for TIFF applications to work properly - * - for instance to get the strip buffer size right - it is imperative - * that the subsampling be available before we start reading the image - * data normally. This function will attempt to load the first strip in - * order to get the sampling values from the jpeg data stream. Various - * hacks are various places are done to ensure this function gets called - * before the td_ycbcrsubsampling values are used from the directory structure, - * including calling TIFFGetField() for the YCBCRSUBSAMPLING field from - * TIFFStripSize(), and the printing code in tif_print.c. - * - * Note that JPEGPreDeocode() will produce a fairly loud warning when the - * discovered sampling does not match the default sampling (2,2) or whatever - * was actually in the tiff tags. - * - * Problems: - * o This code will cause one whole strip/tile of compressed data to be - * loaded just to get the tags right, even if the imagery is never read. - * It would be more efficient to just load a bit of the header, and - * initialize things from that. - * - * See the bug in bugzilla for details: - * - * http://bugzilla.remotesensing.org/show_bug.cgi?id=168 - * - * Frank Warmerdam, July 2002 - */ - private static void JPEGFixupTestSubsampling(Tiff tif) - { - if (Tiff.CHECK_JPEG_YCBCR_SUBSAMPLING) - { - JpegCodec sp = tif.m_currentCodec as JpegCodec; - Debug.Assert(sp != null); - - sp.InitializeLibJPEG(false, false); - - /* - * Some JPEG-in-TIFF files don't provide the ycbcrsampling tags, - * and use a sampling schema other than the default 2,2. To handle - * this we actually have to scan the header of a strip or tile of - * jpeg data to get the sampling. - */ - if (!sp.m_common.IsDecompressor || sp.m_ycbcrsampling_fetched || - tif.m_dir.td_photometric != Photometric.YCBCR) - { - return; - } - - sp.m_ycbcrsampling_fetched = true; - if (tif.IsTiled()) - { - if (!tif.fillTile(0)) - return; - } - else - { - if (!tif.fillStrip(0)) - return; - } - - tif.SetField(TiffTag.YCBCRSUBSAMPLING, sp.m_h_sampling, sp.m_v_sampling); - - // We want to clear the loaded strip so the application has time - // to set JPEGCOLORMODE or other behavior modifiers. This essentially - // undoes the JPEGPreDecode triggers by FileStrip(). - tif.m_curstrip = -1; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegErrorManager.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegErrorManager.cs deleted file mode 100644 index a9069b49..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegErrorManager.cs +++ /dev/null @@ -1,66 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System; - -using BitMiracle.LibJpeg.Classic; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - /// - /// LibJpeg.Net interface layer. - /// - /// We handle fatal errors when they are encountered within the JPEG - /// library. We also direct LibJpeg.Net error and warning - /// messages through the appropriate LibTiff.Net handlers. - /// - class JpegErrorManager : jpeg_error_mgr - { - private JpegCodec m_sp; - - public JpegErrorManager(JpegCodec sp) - : base() - { - m_sp = sp; - } - - /* - * Error handling routines (these replace corresponding - * IJG routines). These are used for both - * compression and decompression. - */ - public override void error_exit() - { - string buffer = format_message(); - Tiff.ErrorExt(m_sp.GetTiff(), m_sp.GetTiff().m_clientdata, "JPEGLib", "{0}", buffer); /* display the error message */ - - // clean up LibJpeg.Net state - m_sp.m_common.jpeg_abort(); - - throw new Exception(buffer); - } - - /* - * This routine is invoked only for warning messages, - * since error_exit does its own thing and trace_level - * is never set > 0. - */ - public override void output_message() - { - string buffer = format_message(); - Tiff.WarningExt(m_sp.GetTiff(), m_sp.GetTiff().m_clientdata, "JPEGLib", "{0}", buffer); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegStdDestination.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegStdDestination.cs deleted file mode 100644 index 35785ebb..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegStdDestination.cs +++ /dev/null @@ -1,56 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using BitMiracle.LibJpeg.Classic; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - /// - /// JPEG library destination data manager. - /// These routines direct compressed data from LibJpeg.Net into the - /// LibTiff.Net output buffer. - /// - class JpegStdDestination : jpeg_destination_mgr - { - private Tiff m_tif; - - public JpegStdDestination(Tiff tif) - { - m_tif = tif; - } - - public override void init_destination() - { - initInternalBuffer(m_tif.m_rawdata, 0); - } - - public override bool empty_output_buffer() - { - /* the entire buffer has been filled */ - m_tif.m_rawcc = m_tif.m_rawdatasize; - m_tif.flushData1(); - - initInternalBuffer(m_tif.m_rawdata, 0); - return true; - } - - public override void term_destination() - { - m_tif.m_rawcp = m_tif.m_rawdatasize - freeInBuffer; - m_tif.m_rawcc = m_tif.m_rawdatasize - freeInBuffer; - /* NB: LibTiff.Net does the final buffer flush */ - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegStdSource.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegStdSource.cs deleted file mode 100644 index cf180814..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegStdSource.cs +++ /dev/null @@ -1,55 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using BitMiracle.LibJpeg.Classic; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - /// - /// JPEG library source data manager. - /// These routines supply compressed data to LibJpeg.Net - /// - class JpegStdSource : jpeg_source_mgr - { - private static readonly byte[] dummy_EOI = { 0xFF, (byte)JPEG_MARKER.EOI }; - protected JpegCodec m_sp; - - public JpegStdSource(JpegCodec sp) - { - initInternalBuffer(null, 0); - m_sp = sp; - } - - public override void init_source() - { - Tiff tif = m_sp.GetTiff(); - initInternalBuffer(tif.m_rawdata, tif.m_rawcc); - } - - public override bool fill_input_buffer() - { - /* - * Should never get here since entire strip/tile is - * read into memory before the decompressor is called, - * and thus was supplied by init_source. - */ - m_sp.m_decompression.WARNMS(J_MESSAGE_CODE.JWRN_JPEG_EOF); - - /* insert a fake EOI marker */ - initInternalBuffer(dummy_EOI, 2); - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegTablesDestination.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegTablesDestination.cs deleted file mode 100644 index d3da4829..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegTablesDestination.cs +++ /dev/null @@ -1,55 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using BitMiracle.LibJpeg.Classic; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - /// - /// Alternate destination manager for outputting to JPEGTables field. - /// - class JpegTablesDestination : jpeg_destination_mgr - { - private JpegCodec m_sp; - - public JpegTablesDestination(JpegCodec sp) - { - m_sp = sp; - } - - public override void init_destination() - { - /* while building, jpegtables_length is allocated buffer size */ - initInternalBuffer(m_sp.m_jpegtables, 0); - } - - public override bool empty_output_buffer() - { - /* the entire buffer has been filled; enlarge it by 1000 bytes */ - byte[] newbuf = Tiff.Realloc(m_sp.m_jpegtables, m_sp.m_jpegtables_length + 1000); - - initInternalBuffer(newbuf, m_sp.m_jpegtables_length); - m_sp.m_jpegtables = newbuf; - m_sp.m_jpegtables_length += 1000; - return true; - } - - public override void term_destination() - { - /* set tables length to number of bytes actually emitted */ - m_sp.m_jpegtables_length -= freeInBuffer; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegTablesSource.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegTablesSource.cs deleted file mode 100644 index 67f80ea6..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/JpegTablesSource.cs +++ /dev/null @@ -1,34 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic.Internal -{ - /// - /// Alternate source manager for reading from JPEGTables. - /// We can share all the code except for the init routine. - /// - class JpegTablesSource : JpegStdSource - { - public JpegTablesSource(JpegCodec sp) - : base(sp) - { - } - - public override void init_source() - { - initInternalBuffer(m_sp.m_jpegtables, m_sp.m_jpegtables_length); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/LZWCodec.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/LZWCodec.cs deleted file mode 100644 index c9881071..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/LZWCodec.cs +++ /dev/null @@ -1,1110 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Rev 5.0 Lempel-Ziv & Welch Compression Support - * - * This code is derived from the compress program whose code is - * derived from software contributed to Berkeley by James A. Woods, - * derived from original work by Spencer Thomas and Joseph Orost. - * - * The original Berkeley copyright notice appears below in its entirety. - */ - -/* - * NB: The 5.0 spec describes a different algorithm than Aldus - * implements. Specifically, Aldus does code length transitions - * one code earlier than should be done (for real LZW). - * Earlier versions of this library implemented the correct - * LZW algorithm, but emitted codes in a bit order opposite - * to the TIFF spec. Thus, to maintain compatibility w/ Aldus - * we interpret MSB-LSB ordered codes to be images written w/ - * old versions of this library, but otherwise adhere to the - * Aldus "off by one" algorithm. - * - * Future revisions to the TIFF spec are expected to "clarify this issue". - */ - -using System; -using System.Diagnostics; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - class LZWCodec : CodecWithPredictor - { - /* - * Each strip of data is supposed to be terminated by a CODE_EOI. - * If the following #define is included, the decoder will also - * check for end-of-strip w/o seeing this code. This makes the - * library more robust, but also slower. - */ - private bool LZW_CHECKEOS = true; /* include checks for strips w/o EOI code */ - - /* - * The TIFF spec specifies that encoded bit - * strings range from 9 to 12 bits. - */ - private const short BITS_MIN = 9; /* start with 9 bits */ - private const short BITS_MAX = 12; /* max of 12 bit strings */ - - /* predefined codes */ - private const short CODE_CLEAR = 256; /* code to clear string table */ - private const short CODE_EOI = 257; /* end-of-information code */ - private const short CODE_FIRST = 258; /* first free code entry */ - private const short CODE_MAX = ((1 << BITS_MAX) - 1); - private const short CODE_MIN = ((1 << BITS_MIN) - 1); - - private const int HSIZE = 9001; /* 91% occupancy */ - private const int HSHIFT = (13 - 8); - /* NB: +1024 is for compatibility with old files */ - private const int CSIZE = (((1 << BITS_MAX) - 1) + 1024); - - private const int CHECK_GAP = 10000; /* enc_ratio check interval */ - - /* - * Decoding-specific state. - */ - private struct code_t - { - public int next; - public short length; /* string len, including this token */ - public byte value; /* data value */ - public byte firstchar; /* first token of string */ - }; - - /* - * Encoding-specific state. - */ - private struct hash_t - { - public int hash; - public short code; - }; - - private bool m_compatDecode; - - private short m_nbits; /* # of bits/code */ - private short m_maxcode; /* maximum code for base.nbits */ - private short m_free_ent; /* next free entry in hash table */ - private int m_nextdata; /* next bits of i/o */ - private int m_nextbits; /* # of valid bits in base.nextdata */ - - private int m_rw_mode; /* preserve rw_mode from init */ - - /* Decoding specific data */ - private int m_dec_nbitsmask; /* lzw_nbits 1 bits, right adjusted */ - private int m_dec_restart; /* restart count */ - private int m_dec_bitsleft; /* available bits in raw data */ - private bool m_oldStyleCodeFound; /* if true, old style LZW code found*/ - private int m_dec_codep; /* current recognized code */ - private int m_dec_oldcodep; /* previously recognized code */ - private int m_dec_free_entp; /* next free entry */ - private int m_dec_maxcodep; /* max available entry */ - private code_t[] m_dec_codetab; /* kept separate for small machines */ - - /* Encoding specific data */ - private int m_enc_oldcode; /* last code encountered */ - private int m_enc_checkpoint; /* point at which to clear table */ - private int m_enc_ratio; /* current compression ratio */ - private int m_enc_incount; /* (input) data bytes encoded */ - private int m_enc_outcount; /* encoded (output) bytes */ - private int m_enc_rawlimit; /* bound on tif_rawdata buffer */ - private hash_t[] m_enc_hashtab; /* kept separate for small machines */ - - public LZWCodec(Tiff tif, Compression scheme, string name) - : base(tif, scheme, name) - { - } - - public override bool Init() - { - Debug.Assert(m_scheme == Compression.LZW); - - m_dec_codetab = null; - m_oldStyleCodeFound = false; - m_enc_hashtab = null; - m_rw_mode = m_tif.m_mode; - m_compatDecode = false; - - /* - * Setup predictor setup. - */ - TIFFPredictorInit(null); - return true; - } - - /// - /// Gets a value indicating whether this codec can encode data. - /// - /// - /// true if this codec can encode data; otherwise, false. - /// - public override bool CanEncode - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether this codec can decode data. - /// - /// - /// true if this codec can decode data; otherwise, false. - /// - public override bool CanDecode - { - get - { - return true; - } - } - - /// - /// Prepares the decoder part of the codec for a decoding. - /// - /// The zero-based sample plane index. - /// - /// true if this codec successfully prepared its decoder part and ready - /// to decode data; otherwise, false. - /// - /// - /// PreDecode is called after and before decoding. - /// - public override bool PreDecode(short plane) - { - return LZWPreDecode(plane); - } - - /// - /// Prepares the encoder part of the codec for a encoding. - /// - /// The zero-based sample plane index. - /// - /// true if this codec successfully prepared its encoder part and ready - /// to encode data; otherwise, false. - /// - /// - /// PreEncode is called after and before encoding. - /// - public override bool PreEncode(short plane) - { - return LZWPreEncode(plane); - } - - /// - /// Performs any actions after encoding required by the codec. - /// - /// - /// true if all post-encode actions succeeded; otherwise, false - /// - /// - /// PostEncode is called after encoding and can be used to release any external - /// resources needed during encoding. - /// - public override bool PostEncode() - { - return LZWPostEncode(); - } - - /// - /// Cleanups the state of the codec. - /// - /// - /// Cleanup is called when codec is no longer needed (won't be used) and can be - /// used for example to restore tag methods that were substituted. - public override void Cleanup() - { - LZWCleanup(); - m_tif.m_mode = m_rw_mode; - } - - // CodecWithPredictor overrides - - public override bool predictor_setupdecode() - { - return LZWSetupDecode(); - } - - public override bool predictor_decoderow(byte[] buffer, int offset, int count, short plane) - { - if (m_compatDecode) - return LZWDecodeCompat(buffer, offset, count, plane); - - return LZWDecode(buffer, offset, count, plane); - } - - public override bool predictor_decodestrip(byte[] buffer, int offset, int count, short plane) - { - if (m_compatDecode) - return LZWDecodeCompat(buffer, offset, count, plane); - - return LZWDecode(buffer, offset, count, plane); - } - - public override bool predictor_decodetile(byte[] buffer, int offset, int count, short plane) - { - if (m_compatDecode) - return LZWDecodeCompat(buffer, offset, count, plane); - - return LZWDecode(buffer, offset, count, plane); - } - - public override bool predictor_setupencode() - { - return LZWSetupEncode(); - } - - public override bool predictor_encoderow(byte[] buffer, int offset, int count, short plane) - { - return LZWEncode(buffer, offset, count, plane); - } - - public override bool predictor_encodestrip(byte[] buffer, int offset, int count, short plane) - { - return LZWEncode(buffer, offset, count, plane); - } - - public override bool predictor_encodetile(byte[] buffer, int offset, int count, short plane) - { - return LZWEncode(buffer, offset, count, plane); - } - - private bool LZWSetupDecode() - { - if (m_dec_codetab == null) - { - m_dec_codetab = new code_t [CSIZE]; - - /* - * Pre-load the table. - */ - int code = 255; - do - { - m_dec_codetab[code].value = (byte)code; - m_dec_codetab[code].firstchar = (byte)code; - m_dec_codetab[code].length = 1; - m_dec_codetab[code].next = -1; - } - while (code-- != 0); - - /* - * Zero-out the unused entries - */ - Array.Clear(m_dec_codetab, CODE_CLEAR, CODE_FIRST - CODE_CLEAR); - } - - return true; - } - - /* - * Setup state for decoding a strip. - */ - private bool LZWPreDecode(short s) - { - if (m_dec_codetab == null) - SetupDecode(); - - /* - * Check for old bit-reversed codes. - */ - if (m_tif.m_rawdata[0] == 0 && (m_tif.m_rawdata[1] & 0x1) != 0) - { - if (!m_oldStyleCodeFound) - { - Tiff.WarningExt(m_tif, m_tif.m_clientdata, m_tif.m_name, "Old-style LZW codes, convert file."); - m_compatDecode = true; - - /* - * If doing horizontal differencing, must - * re-setup the predictor logic since we - * switched the basic decoder methods... - */ - SetupDecode(); - m_oldStyleCodeFound = true; - } - - m_maxcode = CODE_MIN; - } - else - { - m_maxcode = CODE_MIN - 1; - m_oldStyleCodeFound = false; - } - - m_nbits = BITS_MIN; - m_nextbits = 0; - m_nextdata = 0; - - m_dec_restart = 0; - m_dec_nbitsmask = CODE_MIN; - m_dec_bitsleft = m_tif.m_rawcc << 3; - m_dec_free_entp = CODE_FIRST; - - /* - * Zero entries that are not yet filled in. We do - * this to guard against bogus input data that causes - * us to index into undefined entries. If you can - * come up with a way to safely bounds-check input codes - * while decoding then you can remove this operation. - */ - Array.Clear(m_dec_codetab, m_dec_free_entp, CSIZE - CODE_FIRST); - m_dec_oldcodep = -1; - m_dec_maxcodep = m_dec_nbitsmask - 1; - return true; - } - - private bool LZWDecode(byte[] buffer, int offset, int count, short plane) - { - Debug.Assert(m_dec_codetab != null); - - // Restart interrupted output operation. - if (m_dec_restart != 0) - { - int codep = m_dec_codep; - int residue = m_dec_codetab[codep].length - m_dec_restart; - if (residue > count) - { - // Residue from previous decode is sufficient to satisfy decode request. Skip - // to the start of the decoded string, place decoded values in the output - // buffer, and return. - m_dec_restart += count; - - do - { - codep = m_dec_codetab[codep].next; - } - while (--residue > count && codep != -1); - - if (codep != -1) - { - int tp = count; - do - { - tp--; - buffer[offset + tp] = m_dec_codetab[codep].value; - codep = m_dec_codetab[codep].next; - } - while (--count != 0 && codep != -1); - } - - return true; - } - - // Residue satisfies only part of the decode request. - offset += residue; - count -= residue; - int ttp = 0; - do - { - --ttp; - int t = m_dec_codetab[codep].value; - codep = m_dec_codetab[codep].next; - buffer[offset + ttp] = (byte)t; - } - while (--residue != 0 && codep != -1); - - m_dec_restart = 0; - } - - while (count > 0) - { - short code; - NextCode(out code, false); - if (code == CODE_EOI) - break; - - if (code == CODE_CLEAR) - { - m_dec_free_entp = CODE_FIRST; - Array.Clear(m_dec_codetab, m_dec_free_entp, CSIZE - CODE_FIRST); - - m_nbits = BITS_MIN; - m_dec_nbitsmask = CODE_MIN; - m_dec_maxcodep = m_dec_nbitsmask - 1; - NextCode(out code, false); - - if (code == CODE_EOI) - break; - - if (code == CODE_CLEAR) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "LZWDecode: Corrupted LZW table at scanline {0}", m_tif.m_row); - return false; - } - - buffer[offset] = (byte)code; - offset++; - count--; - m_dec_oldcodep = code; - continue; - } - - int codep = code; - - // Add the new entry to the code table. - if (m_dec_free_entp < 0 || m_dec_free_entp >= CSIZE) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "LZWDecode: Corrupted LZW table at scanline {0}", m_tif.m_row); - return false; - } - - m_dec_codetab[m_dec_free_entp].next = m_dec_oldcodep; - if (m_dec_codetab[m_dec_free_entp].next < 0 || m_dec_codetab[m_dec_free_entp].next >= CSIZE) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "LZWDecode: Corrupted LZW table at scanline {0}", m_tif.m_row); - return false; - } - - m_dec_codetab[m_dec_free_entp].firstchar = m_dec_codetab[m_dec_codetab[m_dec_free_entp].next].firstchar; - m_dec_codetab[m_dec_free_entp].length = (short)(m_dec_codetab[m_dec_codetab[m_dec_free_entp].next].length + 1); - m_dec_codetab[m_dec_free_entp].value = (codep < m_dec_free_entp) ? m_dec_codetab[codep].firstchar : m_dec_codetab[m_dec_free_entp].firstchar; - - if (++m_dec_free_entp > m_dec_maxcodep) - { - if (++m_nbits > BITS_MAX) - { - // should not happen - m_nbits = BITS_MAX; - } - - m_dec_nbitsmask = MAXCODE(m_nbits); - m_dec_maxcodep = m_dec_nbitsmask - 1; - } - - m_dec_oldcodep = code; - if (code >= 256) - { - // Code maps to a string, copy string value to output (written in reverse). - if (m_dec_codetab[codep].length == 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "LZWDecode: Wrong length of decoded string: data probably corrupted at scanline {0}", - m_tif.m_row); - return false; - } - - if (m_dec_codetab[codep].length > count) - { - // String is too long for decode buffer, locate portion that will fit, - // copy to the decode buffer, and setup restart logic for the next - // decoding call. - m_dec_codep = code; - do - { - codep = m_dec_codetab[codep].next; - } - while (codep != -1 && m_dec_codetab[codep].length > count); - - if (codep != -1) - { - m_dec_restart = count; - int tp = count; - do - { - tp--; - buffer[offset + tp] = m_dec_codetab[codep].value; - codep = m_dec_codetab[codep].next; - } - while (--count != 0 && codep != -1); - - if (codep != -1) - codeLoop(); - } - break; - } - - int len = m_dec_codetab[codep].length; - int ttp = len; - do - { - --ttp; - int t = m_dec_codetab[codep].value; - codep = m_dec_codetab[codep].next; - buffer[offset + ttp] = (byte)t; - } - while (codep != -1 && ttp > 0); - - if (codep != -1) - { - codeLoop(); - break; - } - - offset += len; - count -= len; - } - else - { - buffer[offset] = (byte)code; - offset++; - count--; - } - } - - if (count > 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "LZWDecode: Not enough data at scanline {0} (short {1} bytes)", - m_tif.m_row, count); - return false; - } - - return true; - } - - private bool LZWDecodeCompat(byte[] buffer, int offset, int count, short plane) - { - // Restart interrupted output operation. - if (m_dec_restart != 0) - { - int residue; - - int codep = m_dec_codep; - residue = m_dec_codetab[codep].length - m_dec_restart; - if (residue > count) - { - // Residue from previous decode is sufficient to satisfy decode request. - // Skip to the start of the decoded string, place decoded values in the output - // buffer, and return. - m_dec_restart += count; - do - { - codep = m_dec_codetab[codep].next; - } - while (--residue > count); - - int tp = count; - do - { - --tp; - buffer[offset + tp] = m_dec_codetab[codep].value; - codep = m_dec_codetab[codep].next; - } - while (--count != 0); - - return true; - } - - // Residue satisfies only part of the decode request. - offset += residue; - count -= residue; - int ttp = 0; - do - { - --ttp; - buffer[offset + ttp] = m_dec_codetab[codep].value; - codep = m_dec_codetab[codep].next; - } - while (--residue != 0); - - m_dec_restart = 0; - } - - while (count > 0) - { - short code; - NextCode(out code, true); - if (code == CODE_EOI) - break; - - if (code == CODE_CLEAR) - { - m_dec_free_entp = CODE_FIRST; - Array.Clear(m_dec_codetab, m_dec_free_entp, CSIZE - CODE_FIRST); - - m_nbits = BITS_MIN; - m_dec_nbitsmask = CODE_MIN; - m_dec_maxcodep = m_dec_nbitsmask; - NextCode(out code, true); - - if (code == CODE_EOI) - break; - - if (code == CODE_CLEAR) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "LZWDecode: Corrupted LZW table at scanline {0}", m_tif.m_row); - return false; - } - - buffer[offset] = (byte)code; - offset++; - count--; - m_dec_oldcodep = code; - continue; - } - - int codep = code; - - // Add the new entry to the code table. - if (m_dec_free_entp < 0 || m_dec_free_entp >= CSIZE) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "LZWDecodeCompat: Corrupted LZW table at scanline {0}", m_tif.m_row); - return false; - } - - m_dec_codetab[m_dec_free_entp].next = m_dec_oldcodep; - if (m_dec_codetab[m_dec_free_entp].next < 0 || m_dec_codetab[m_dec_free_entp].next >= CSIZE) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "LZWDecodeCompat: Corrupted LZW table at scanline {0}", m_tif.m_row); - return false; - } - - m_dec_codetab[m_dec_free_entp].firstchar = m_dec_codetab[m_dec_codetab[m_dec_free_entp].next].firstchar; - m_dec_codetab[m_dec_free_entp].length = (short)(m_dec_codetab[m_dec_codetab[m_dec_free_entp].next].length + 1); - m_dec_codetab[m_dec_free_entp].value = (codep < m_dec_free_entp) ? m_dec_codetab[codep].firstchar : m_dec_codetab[m_dec_free_entp].firstchar; - if (++m_dec_free_entp > m_dec_maxcodep) - { - if (++m_nbits > BITS_MAX) - { - // should not happen - m_nbits = BITS_MAX; - } - m_dec_nbitsmask = MAXCODE(m_nbits); - m_dec_maxcodep = m_dec_nbitsmask; - } - - m_dec_oldcodep = code; - if (code >= 256) - { - int op_orig = offset; - - // Code maps to a string, copy string value to output (written in reverse). - if (m_dec_codetab[codep].length == 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "LZWDecodeCompat: Wrong length of decoded string: data probably corrupted at scanline {0}", - m_tif.m_row); - return false; - } - - if (m_dec_codetab[codep].length > count) - { - // String is too long for decode buffer, locate portion that will fit, - // copy to the decode buffer, and setup restart logic for the next - // decoding call. - m_dec_codep = code; - do - { - codep = m_dec_codetab[codep].next; - } - while (m_dec_codetab[codep].length > count); - - m_dec_restart = count; - int tp = count; - do - { - --tp; - buffer[offset + tp] = m_dec_codetab[codep].value; - codep = m_dec_codetab[codep].next; - } - while (--count != 0); - - break; - } - - offset += m_dec_codetab[codep].length; - count -= m_dec_codetab[codep].length; - int ttp = offset; - do - { - --ttp; - buffer[ttp] = m_dec_codetab[codep].value; - codep = m_dec_codetab[codep].next; - } - while (codep != -1 && ttp > op_orig); - } - else - { - buffer[offset] = (byte)code; - offset++; - count--; - } - } - - if (count > 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "LZWDecodeCompat: Not enough data at scanline {0} (short {1} bytes)", - m_tif.m_row, count); - return false; - } - - return true; - } - - private bool LZWSetupEncode() - { - m_enc_hashtab = new hash_t [HSIZE]; - return true; - } - - /* - * Reset encoding state at the start of a strip. - */ - private bool LZWPreEncode(short s) - { - if (m_enc_hashtab == null) - SetupEncode(); - - m_nbits = BITS_MIN; - m_maxcode = CODE_MIN; - m_free_ent = CODE_FIRST; - m_nextbits = 0; - m_nextdata = 0; - m_enc_checkpoint = CHECK_GAP; - m_enc_ratio = 0; - m_enc_incount = 0; - m_enc_outcount = 0; - - /* - * The 4 here insures there is space for 2 max-sized - * codes in LZWEncode and LZWPostDecode. - */ - m_enc_rawlimit = m_tif.m_rawdatasize - 1 - 4; - cl_hash(); /* clear hash table */ - m_enc_oldcode = -1; /* generates CODE_CLEAR in LZWEncode */ - return true; - } - - /* - * Finish off an encoded strip by flushing the last - * string and tacking on an End Of Information code. - */ - private bool LZWPostEncode() - { - if (m_tif.m_rawcp > m_enc_rawlimit) - { - m_tif.m_rawcc = m_tif.m_rawcp; - m_tif.flushData1(); - m_tif.m_rawcp = 0; - } - - if (m_enc_oldcode != -1) - { - PutNextCode(m_enc_oldcode); - m_enc_oldcode = -1; - } - - PutNextCode(CODE_EOI); - - if (m_nextbits > 0) - { - m_tif.m_rawdata[m_tif.m_rawcp] = (byte)(m_nextdata << (8 - m_nextbits)); - m_tif.m_rawcp++; - } - - m_tif.m_rawcc = m_tif.m_rawcp; - return true; - } - - /// - /// Encode a chunk of pixels. - /// - /// - /// Uses an open addressing double hashing (no chaining) on the prefix code/next character - /// combination. We do a variant of Knuth's algorithm D (vol. 3, sec. 6.4) along with - /// G. Knott's relatively-prime secondary probe. Here, the modular division first probe is - /// gives way to a faster exclusive-or manipulation. Also do block compression with an - /// adaptive reset, whereby the code table is cleared when the compression ratio - /// decreases, but after the table fills. The variable-length output codes are re-sized at - /// this point, and a CODE_CLEAR is generated for the decoder. - /// - private bool LZWEncode(byte[] buffer, int offset, int count, short plane) - { - Debug.Assert(m_enc_hashtab != null); - if (m_enc_oldcode == -1 && count > 0) - { - // NB: This is safe because it can only happen at the start of a strip where we - // know there is space in the data buffer. - PutNextCode(CODE_CLEAR); - m_enc_oldcode = buffer[offset]; - offset++; - count--; - m_enc_incount++; - } - - while (count > 0) - { - int c = buffer[offset]; - offset++; - count--; - m_enc_incount++; - int fcode = (c << BITS_MAX) + m_enc_oldcode; - int h = (c << HSHIFT) ^ m_enc_oldcode; // xor hashing - - // Check hash index for an overflow. - if (h >= HSIZE) - h -= HSIZE; - - if (m_enc_hashtab[h].hash == fcode) - { - m_enc_oldcode = m_enc_hashtab[h].code; - continue; - } - - bool hit = false; - - if (m_enc_hashtab[h].hash >= 0) - { - // Primary hash failed, check secondary hash. - int disp = HSIZE - h; - if (h == 0) - disp = 1; - do - { - h -= disp; - if (h < 0) - h += HSIZE; - - if (m_enc_hashtab[h].hash == fcode) - { - m_enc_oldcode = m_enc_hashtab[h].code; - hit = true; - break; - } - } - while (m_enc_hashtab[h].hash >= 0); - } - - if (!hit) - { - // New entry, emit code and add to table. - // Verify there is space in the buffer for the code and any potential Clear - // code that might be emitted below. The value of limit is setup so that there - // are at least 4 bytes free - room for 2 codes. - if (m_tif.m_rawcp > m_enc_rawlimit) - { - m_tif.m_rawcc = m_tif.m_rawcp; - m_tif.flushData1(); - m_tif.m_rawcp = 0; - } - - PutNextCode(m_enc_oldcode); - m_enc_oldcode = c; - m_enc_hashtab[h].code = m_free_ent; - m_free_ent++; - m_enc_hashtab[h].hash = fcode; - if (m_free_ent == CODE_MAX - 1) - { - // table is full, emit clear code and reset - cl_hash(); - m_enc_ratio = 0; - m_enc_incount = 0; - m_enc_outcount = 0; - m_free_ent = CODE_FIRST; - PutNextCode(CODE_CLEAR); - m_nbits = BITS_MIN; - m_maxcode = CODE_MIN; - } - else - { - // If the next entry is going to be too big for the code size, then - // increase it, if possible. - if (m_free_ent > m_maxcode) - { - m_nbits++; - Debug.Assert(m_nbits <= BITS_MAX); - m_maxcode = (short)MAXCODE(m_nbits); - } - else if (m_enc_incount >= m_enc_checkpoint) - { - // Check compression ratio and, if things seem to be slipping, clear - // the hash table and reset state. The compression ratio is - // a 24 + 8-bit fractional number. - m_enc_checkpoint = m_enc_incount + CHECK_GAP; - - int rat; - if (m_enc_incount > 0x007fffff) - { - // NB: shift will overflow - rat = m_enc_outcount >> 8; - rat = (rat == 0 ? 0x7fffffff : m_enc_incount / rat); - } - else - rat = (m_enc_incount << 8) / m_enc_outcount; - - if (rat <= m_enc_ratio) - { - cl_hash(); - m_enc_ratio = 0; - m_enc_incount = 0; - m_enc_outcount = 0; - m_free_ent = CODE_FIRST; - PutNextCode(CODE_CLEAR); - m_nbits = BITS_MIN; - m_maxcode = CODE_MIN; - } - else - m_enc_ratio = rat; - } - } - } - } - - return true; - } - - private void LZWCleanup() - { - m_dec_codetab = null; - m_enc_hashtab = null; - } - - private static int MAXCODE(int n) - { - return ((1 << n) - 1); - } - - private void PutNextCode(int c) - { - m_nextdata = (m_nextdata << m_nbits) | c; - m_nextbits += m_nbits; - m_tif.m_rawdata[m_tif.m_rawcp] = (byte)(m_nextdata >> (m_nextbits - 8)); - m_tif.m_rawcp++; - m_nextbits -= 8; - if (m_nextbits >= 8) - { - m_tif.m_rawdata[m_tif.m_rawcp] = (byte)(m_nextdata >> (m_nextbits - 8)); - m_tif.m_rawcp++; - m_nextbits -= 8; - } - - m_enc_outcount += m_nbits; - } - - /* - * Reset encoding hash table. - */ - private void cl_hash() - { - int hp = HSIZE - 1; - int i = HSIZE - 8; - - do - { - i -= 8; - m_enc_hashtab[hp - 7].hash = -1; - m_enc_hashtab[hp - 6].hash = -1; - m_enc_hashtab[hp - 5].hash = -1; - m_enc_hashtab[hp - 4].hash = -1; - m_enc_hashtab[hp - 3].hash = -1; - m_enc_hashtab[hp - 2].hash = -1; - m_enc_hashtab[hp - 1].hash = -1; - m_enc_hashtab[hp].hash = -1; - hp -= 8; - } - while (i >= 0); - - for (i += 8; i > 0; i--, hp--) - m_enc_hashtab[hp].hash = -1; - } - - private void NextCode(out short _code, bool compat) - { - if (LZW_CHECKEOS) - { - if (m_dec_bitsleft < m_nbits) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "LZWDecode: Strip {0} not terminated with EOI code", m_tif.m_curstrip); - _code = CODE_EOI; - } - else - { - if (compat) - GetNextCodeCompat(out _code); - else - GetNextCode(out _code); - - m_dec_bitsleft -= m_nbits; - } - } - else - { - if (compat) - GetNextCodeCompat(out _code); - else - GetNextCode(out _code); - } - } - - private void GetNextCode(out short code) - { - m_nextdata = (m_nextdata << 8) | m_tif.m_rawdata[m_tif.m_rawcp]; - m_tif.m_rawcp++; - m_nextbits += 8; - if (m_nextbits < m_nbits) - { - m_nextdata = (m_nextdata << 8) | m_tif.m_rawdata[m_tif.m_rawcp]; - m_tif.m_rawcp++; - m_nextbits += 8; - } - code = (short)((m_nextdata >> (m_nextbits - m_nbits)) & m_dec_nbitsmask); - m_nextbits -= m_nbits; - } - - private void GetNextCodeCompat(out short code) - { - m_nextdata |= m_tif.m_rawdata[m_tif.m_rawcp] << m_nextbits; - m_tif.m_rawcp++; - m_nextbits += 8; - if (m_nextbits < m_nbits) - { - m_nextdata |= m_tif.m_rawdata[m_tif.m_rawcp] << m_nextbits; - m_tif.m_rawcp++; - m_nextbits += 8; - } - code = (short)(m_nextdata & m_dec_nbitsmask); - m_nextdata >>= m_nbits; - m_nextbits -= m_nbits; - } - - private void codeLoop() - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "LZWDecode: Bogus encoding, loop in the code table; scanline {0}", m_tif.m_row); - } - } -} - -/* - * Copyright (c) 1985, 1986 The Regents of the University of California. - * *. - * - * This code is derived from software contributed to Berkeley by - * James A. Woods, derived from original work by Spencer Thomas - * and Joseph Orost. - * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by the University of California, Berkeley. The name of the - * University may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/OJpegCodec.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/OJpegCodec.cs deleted file mode 100644 index e2244406..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/OJpegCodec.cs +++ /dev/null @@ -1,2392 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* WARNING: The type of JPEG encapsulation defined by the TIFF Version 6.0 - specification is now totally obsolete and deprecated for new applications and - images. This file was was created solely in order to read unconverted images - still present on some users' computer systems. It will never be extended - to write such files. Writing new-style JPEG compressed TIFFs is implemented - in tif_jpeg.c. - - The code is carefully crafted to robustly read all gathered JPEG-in-TIFF - testfiles, and anticipate as much as possible all other... But still, it may - fail on some. If you encounter problems, please report them on the TIFF - mailing list and/or to Joris Van Damme . - - Please read the file called "TIFF Technical Note #2" if you need to be - convinced this compression scheme is bad and breaks TIFF. That document - is linked to from the LibTiff site - and from AWare Systems' TIFF section - . It is also absorbed - in Adobe's specification supplements, marked "draft" up to this day, but - supported by the TIFF community. - - This file interfaces with Release 6B of the JPEG Library written by the - Independent JPEG Group. Previous versions of this file required a hack inside - the LibJpeg library. This version no longer requires that. Remember to - remove the hack if you update from the old version. - - Copyright (c) Joris Van Damme - Copyright (c) AWare Systems -*/ - -/* What is what, and what is not? - - This decoder starts with an input stream, that is essentially the JpegInterchangeFormat - stream, if any, followed by the strile data, if any. This stream is read in - OJPEGReadByte and related functions. - - It analyzes the start of this stream, until it encounters non-marker data, i.e. - compressed image data. Some of the header markers it sees have no actual content, - like the SOI marker, and APP/COM markers that really shouldn't even be there. Some - other markers do have content, and the valuable bits and pieces of information - in these markers are saved, checking all to verify that the stream is more or - less within expected bounds. This happens inside the OJPEGReadHeaderInfoSecStreamXxx - functions. - - Some OJPEG imagery contains no valid JPEG header markers. This situation is picked - up on if we've seen no SOF marker when we're at the start of the compressed image - data. In this case, the tables are read from JpegXxxTables tags, and the other - bits and pieces of information is initialized to its most basic value. This is - implemented in the OJPEGReadHeaderInfoSecTablesXxx functions. - - When this is complete, a good and valid JPEG header can be assembled, and this is - passed through to LibJpeg. When that's done, the remainder of the input stream, i.e. - the compressed image data, can be passed through unchanged. This is done in - OJPEGWriteStream functions. - - LibTiff rightly expects to know the subsampling values before decompression. Just like - in new-style JPEG-in-TIFF, though, or even more so, actually, the YCbCrsubsampling - tag is notoriously unreliable. To correct these tag values with the ones inside - the JPEG stream, the first part of the input stream is pre-scanned in - OJPEGSubsamplingCorrect, making no note of any other data, reporting no warnings - or errors, up to the point where either these values are read, or it's clear they - aren't there. This means that some of the data is read twice, but we feel speed - in correcting these values is important enough to warrant this sacrifice. Allthough - there is currently no define or other configuration mechanism to disable this behaviour, - the actual header scanning is build to robustly respond with error report if it - should encounter an uncorrected mismatch of subsampling values. See - OJPEGReadHeaderInfoSecStreamSof. - - The restart interval and restart markers are the most tricky part... The restart - interval can be specified in a tag. It can also be set inside the input JPEG stream. - It can be used inside the input JPEG stream. If reading from strile data, we've - consistenly discovered the need to insert restart markers in between the different - striles, as is also probably the most likely interpretation of the original TIFF 6.0 - specification. With all this setting of interval, and actual use of markers that is not - predictable at the time of valid JPEG header assembly, the restart thing may turn - out the Achilles heel of this implementation. Fortunately, most OJPEG writer vendors - succeed in reading back what they write, which may be the reason why we've been able - to discover ways that seem to work. - - Some special provision is made for planarconfig separate OJPEG files. These seem - to consistently contain header info, a SOS marker, a plane, SOS marker, plane, SOS, - and plane. This may or may not be a valid JPEG configuration, we don't know and don't - care. We want LibTiff to be able to access the planes individually, without huge - buffering inside LibJpeg, anyway. So we compose headers to feed to LibJpeg, in this - case, that allow us to pass a single plane such that LibJpeg sees a valid - single-channel JPEG stream. Locating subsequent SOS markers, and thus subsequent - planes, is done inside OJPEGReadSecondarySos. - - The benefit of the scheme is... that it works, basically. We know of no other that - does. It works without checking software tag, or otherwise going about things in an - OJPEG flavor specific manner. Instead, it is a single scheme, that covers the cases - with and without JpegInterchangeFormat, with and without striles, with part of - the header in JpegInterchangeFormat and remainder in first strile, etc. It is forgiving - and robust, may likely work with OJPEG flavors we've not seen yet, and makes most out - of the data. - - Another nice side-effect is that a complete JPEG single valid stream is build if - planarconfig is not separate (vast majority). We may one day use that to build - converters to JPEG, and/or to new-style JPEG compression inside TIFF. - - A dissadvantage is the lack of random access to the individual striles. This is the - reason for much of the complicated restart-and-position stuff inside OJPEGPreDecode. - Applications would do well accessing all striles in order, as this will result in - a single sequential scan of the input stream, and no restarting of LibJpeg decoding - session. -*/ - -/* Configuration defines here are: - * JPEG_ENCAP_EXTERNAL: The normal way to call libjpeg, uses longjump. In some environments, - * like eg LibTiffDelphi, this is not possible. For this reason, the actual calls to - * libjpeg, with longjump stuff, are encapsulated in dedicated functions. When - * JPEG_ENCAP_EXTERNAL is defined, these encapsulating functions are declared external - * to this unit, and can be defined elsewhere to use stuff other then longjump. - * The default mode, without JPEG_ENCAP_EXTERNAL, implements the call encapsulators - * here, internally, with normal longjump. - * SETJMP, LONGJMP, JMP_BUF: On some machines/environments a longjump equivalent is - * conviniently available, but still it may be worthwhile to use _setjmp or sigsetjmp - * in place of plain setjmp. These macros will make it easier. It is useless - * to fiddle with these if you define JPEG_ENCAP_EXTERNAL. - * OJPEG_BUFFER: Define the size of the desired buffer here. Should be small enough so as to guarantee - * instant processing, optimal streaming and optimal use of processor cache, but also big - * enough so as to not result in significant call overhead. It should be at least a few - * bytes to accomodate some structures (this is verified in asserts), but it would not be - * sensible to make it this small anyway, and it should be at most 64K since it is indexed - * with ushort. We recommend 2K. - * EGYPTIANWALK: You could also define EGYPTIANWALK here, but it is not used anywhere and has - * absolutely no effect. That is why most people insist the EGYPTIANWALK is a bit silly. - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.Diagnostics; -using System.IO; - -using BitMiracle.LibJpeg.Classic; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - class OJpegCodec : TiffCodec - { - internal const int FIELD_OJPEG_JPEGINTERCHANGEFORMAT = (FieldBit.Codec + 0); - internal const int FIELD_OJPEG_JPEGINTERCHANGEFORMATLENGTH = (FieldBit.Codec + 1); - internal const int FIELD_OJPEG_JPEGQTABLES = (FieldBit.Codec + 2); - internal const int FIELD_OJPEG_JPEGDCTABLES = (FieldBit.Codec + 3); - internal const int FIELD_OJPEG_JPEGACTABLES = (FieldBit.Codec + 4); - internal const int FIELD_OJPEG_JPEGPROC = (FieldBit.Codec + 5); - internal const int FIELD_OJPEG_JPEGRESTARTINTERVAL = (FieldBit.Codec + 6); - internal const int FIELD_OJPEG_COUNT = 7; - - private const int OJPEG_BUFFER = 2048; - - private enum OJPEGStateInBufferSource - { - osibsNotSetYet, - osibsJpegInterchangeFormat, - osibsStrile, - osibsEof - } - - private enum OJPEGStateOutState - { - ososSoi, - - ososQTable0, - ososQTable1, - ososQTable2, - ososQTable3, - - ososDcTable0, - ososDcTable1, - ososDcTable2, - ososDcTable3, - - ososAcTable0, - ososAcTable1, - ososAcTable2, - ososAcTable3, - - ososDri, - ososSof, - ososSos, - ososCompressed, - ososRst, - ososEoi - } - - private static readonly TiffFieldInfo[] ojpeg_field_info = - { - new TiffFieldInfo(TiffTag.JPEGIFOFFSET, 1, 1, TiffType.LONG, FIELD_OJPEG_JPEGINTERCHANGEFORMAT, true, false, "JpegInterchangeFormat"), - new TiffFieldInfo(TiffTag.JPEGIFBYTECOUNT, 1, 1, TiffType.LONG, FIELD_OJPEG_JPEGINTERCHANGEFORMATLENGTH, true, false, "JpegInterchangeFormatLength"), - new TiffFieldInfo(TiffTag.JPEGQTABLES, -1, -1, TiffType.LONG, FIELD_OJPEG_JPEGQTABLES, false, true, "JpegQTables"), - new TiffFieldInfo(TiffTag.JPEGDCTABLES, -1, -1, TiffType.LONG, FIELD_OJPEG_JPEGDCTABLES, false, true, "JpegDcTables"), - new TiffFieldInfo(TiffTag.JPEGACTABLES, -1, -1, TiffType.LONG, FIELD_OJPEG_JPEGACTABLES, false, true, "JpegAcTables"), - new TiffFieldInfo(TiffTag.JPEGPROC, 1, 1, TiffType.SHORT, FIELD_OJPEG_JPEGPROC, false, false, "JpegProc"), - new TiffFieldInfo(TiffTag.JPEGRESTARTINTERVAL, 1, 1, TiffType.SHORT, FIELD_OJPEG_JPEGRESTARTINTERVAL, false, false, "JpegRestartInterval"), - }; - - private struct SosEnd - { - public bool m_log; - public OJPEGStateInBufferSource m_in_buffer_source; - public uint m_in_buffer_next_strile; - public uint m_in_buffer_file_pos; - public uint m_in_buffer_file_togo; - } - - internal uint m_jpeg_interchange_format; - internal uint m_jpeg_interchange_format_length; - internal byte m_jpeg_proc; - - internal bool m_subsamplingcorrect_done; - internal bool m_subsampling_tag; - internal byte m_subsampling_hor; - internal byte m_subsampling_ver; - - internal byte m_qtable_offset_count; - internal byte m_dctable_offset_count; - internal byte m_actable_offset_count; - internal uint[] m_qtable_offset = new uint[3]; - internal uint[] m_dctable_offset = new uint[3]; - internal uint[] m_actable_offset = new uint[3]; - - internal ushort m_restart_interval; - - internal jpeg_decompress_struct m_libjpeg_jpeg_decompress_struct; - - private TiffTagMethods m_tagMethods; - private TiffTagMethods m_parentTagMethods; - - private uint m_file_size; - private uint m_image_width; - private uint m_image_length; - private uint m_strile_width; - private uint m_strile_length; - private uint m_strile_length_total; - private byte m_samples_per_pixel; - private byte m_plane_sample_offset; - private byte m_samples_per_pixel_per_plane; - private bool m_subsamplingcorrect; - private bool m_subsampling_force_desubsampling_inside_decompression; - private byte[][] m_qtable = new byte[4][]; - private byte[][] m_dctable = new byte[4][]; - private byte[][] m_actable = new byte[4][]; - private byte m_restart_index; - private bool m_sof_log; - private byte m_sof_marker_id; - private uint m_sof_x; - private uint m_sof_y; - private byte[] m_sof_c = new byte[3]; - private byte[] m_sof_hv = new byte[3]; - private byte[] m_sof_tq = new byte[3]; - private byte[] m_sos_cs = new byte[3]; - private byte[] m_sos_tda = new byte[3]; - private SosEnd[] m_sos_end = new SosEnd[3]; - private bool m_readheader_done; - private bool m_writeheader_done; - private short m_write_cursample; - private uint m_write_curstrile; - private bool m_libjpeg_session_active; - private byte m_libjpeg_jpeg_query_style; - private jpeg_error_mgr m_libjpeg_jpeg_error_mgr; - private jpeg_source_mgr m_libjpeg_jpeg_source_mgr; - private bool m_subsampling_convert_log; - private uint m_subsampling_convert_ylinelen; - private uint m_subsampling_convert_ylines; - private uint m_subsampling_convert_clinelen; - private uint m_subsampling_convert_clines; - private byte[][] m_subsampling_convert_ybuf; - private byte[][] m_subsampling_convert_cbbuf; - private byte[][] m_subsampling_convert_crbuf; - private byte[][][] m_subsampling_convert_ycbcrimage; - private uint m_subsampling_convert_clinelenout; - private uint m_subsampling_convert_state; - private uint m_bytes_per_line; /* if the codec outputs subsampled data, a 'line' in bytes_per_line */ - private uint m_lines_per_strile; /* and lines_per_strile means subsampling_ver desubsampled rows */ - private OJPEGStateInBufferSource m_in_buffer_source; - private uint m_in_buffer_next_strile; - private uint m_in_buffer_strile_count; - private uint m_in_buffer_file_pos; - private bool m_in_buffer_file_pos_log; - private uint m_in_buffer_file_togo; - private ushort m_in_buffer_togo; - private int m_in_buffer_cur; // index into m_in_buffer - private byte[] m_in_buffer = new byte[OJPEG_BUFFER]; - private OJPEGStateOutState m_out_state; - private byte[] m_out_buffer = new byte[OJPEG_BUFFER]; - private byte[] m_skip_buffer; - private bool m_forceProcessedRgbOutput; - - public OJpegCodec(Tiff tif, Compression scheme, string name) - : base(tif, scheme, name) - { - m_tagMethods = new OJpegCodecTagMethods(); - } - - private void cleanState() - { - m_jpeg_interchange_format = 0; - m_jpeg_interchange_format_length = 0; - m_jpeg_proc = 0; - - m_subsamplingcorrect_done = false; - m_subsampling_tag = false; - m_subsampling_hor = 0; - m_subsampling_ver = 0; - - m_qtable_offset_count = 0; - m_dctable_offset_count = 0; - m_actable_offset_count = 0; - m_qtable_offset = new uint[3]; - m_dctable_offset = new uint[3]; - m_actable_offset = new uint[3]; - - m_restart_interval = 0; - - m_libjpeg_jpeg_decompress_struct = null; - - m_file_size = 0; - m_image_width = 0; - m_image_length = 0; - m_strile_width = 0; - m_strile_length = 0; - m_strile_length_total = 0; - m_samples_per_pixel = 0; - m_plane_sample_offset = 0; - m_samples_per_pixel_per_plane = 0; - m_subsamplingcorrect = false; - m_subsampling_force_desubsampling_inside_decompression = false; - m_qtable = new byte[4][]; - m_dctable = new byte[4][]; - m_actable = new byte[4][]; - m_restart_index = 0; - m_sof_log = false; - m_sof_marker_id = 0; - m_sof_x = 0; - m_sof_y = 0; - m_sof_c = new byte[3]; - m_sof_hv = new byte[3]; - m_sof_tq = new byte[3]; - m_sos_cs = new byte[3]; - m_sos_tda = new byte[3]; - m_sos_end = new SosEnd[3]; - m_readheader_done = false; - m_writeheader_done = false; - m_write_cursample = 0; - m_write_curstrile = 0; - m_libjpeg_session_active = false; - m_libjpeg_jpeg_query_style = 0; - m_libjpeg_jpeg_error_mgr = null; - m_libjpeg_jpeg_source_mgr = null; - m_subsampling_convert_log = false; - m_subsampling_convert_ylinelen = 0; - m_subsampling_convert_ylines = 0; - m_subsampling_convert_clinelen = 0; - m_subsampling_convert_clines = 0; - m_subsampling_convert_ybuf = null; - m_subsampling_convert_cbbuf = null; - m_subsampling_convert_crbuf = null; - m_subsampling_convert_ycbcrimage = null; - m_subsampling_convert_clinelenout = 0; - m_subsampling_convert_state = 0; - m_bytes_per_line = 0; - m_lines_per_strile = 0; - m_in_buffer_source = OJPEGStateInBufferSource.osibsNotSetYet; - m_in_buffer_next_strile = 0; - m_in_buffer_strile_count = 0; - m_in_buffer_file_pos = 0; - m_in_buffer_file_pos_log = false; - m_in_buffer_file_togo = 0; - m_in_buffer_togo = 0; - m_in_buffer_cur = 0; // index into m_in_buffer - m_in_buffer = new byte[OJPEG_BUFFER]; - m_out_state = 0; - m_out_buffer = new byte[OJPEG_BUFFER]; - m_skip_buffer = null; - m_forceProcessedRgbOutput = false; - } - - public override bool Init() - { - Debug.Assert(m_scheme == Compression.OJPEG); - - /* - * Merge codec-specific tag information. - */ - m_tif.MergeFieldInfo(ojpeg_field_info, ojpeg_field_info.Length); - - cleanState(); - m_jpeg_proc = 1; - m_subsampling_hor = 2; - m_subsampling_ver = 2; - - m_tif.SetField(TiffTag.YCBCRSUBSAMPLING, 2, 2); - - /* tif tag methods */ - m_parentTagMethods = m_tif.m_tagmethods; - m_tif.m_tagmethods = m_tagMethods; - - /* Some OJPEG files don't have strip or tile offsets or bytecounts - * tags. Some others do, but have totally meaningless or corrupt - * values in these tags. In these cases, the JpegInterchangeFormat - * stream is reliable. In any case, this decoder reads the - * compressed data itself, from the most reliable locations, and - * we need to notify encapsulating LibTiff not to read raw strips - * or tiles for us. - */ - m_tif.m_flags |= TiffFlags.NOREADRAW; - return true; - } - - /// - /// Gets a value indicating whether this codec can encode data. - /// - /// - /// true if this codec can encode data; otherwise, false. - /// - public override bool CanEncode - { - get - { - return false; - } - } - - /// - /// Gets a value indicating whether this codec can decode data. - /// - /// - /// true if this codec can decode data; otherwise, false. - /// - public override bool CanDecode - { - get - { - return true; - } - } - - public Tiff GetTiff() - { - return m_tif; - } - - /// - /// Setups the decoder part of the codec. - /// - /// - /// true if this codec successfully setup its decoder part and can decode data; - /// otherwise, false. - /// - /// - /// SetupDecode is called once before - /// . - public override bool SetupDecode() - { - return OJPEGSetupDecode(); - } - - /// - /// Prepares the decoder part of the codec for a decoding. - /// - /// The zero-based sample plane index. - /// - /// true if this codec successfully prepared its decoder part and ready - /// to decode data; otherwise, false. - /// - /// - /// PreDecode is called after and before decoding. - /// - public override bool PreDecode(short plane) - { - return OJPEGPreDecode(plane); - } - - /// - /// Decodes one row of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeRow(byte[] buffer, int offset, int count, short plane) - { - return OJPEGDecode(buffer, offset, count, plane); - } - - /// - /// Decodes one strip of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeStrip(byte[] buffer, int offset, int count, short plane) - { - return OJPEGDecode(buffer, offset, count, plane); - } - - /// - /// Decodes one tile of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeTile(byte[] buffer, int offset, int count, short plane) - { - return OJPEGDecode(buffer, offset, count, plane); - } - - /// - /// Setups the encoder part of the codec. - /// - /// - /// true if this codec successfully setup its encoder part and can encode data; - /// otherwise, false. - /// - /// - /// SetupEncode is called once before - /// . - public override bool SetupEncode() - { - return OJpegEncodeIsUnsupported(); - } - - /// - /// Prepares the encoder part of the codec for a encoding. - /// - /// The zero-based sample plane index. - /// - /// true if this codec successfully prepared its encoder part and ready - /// to encode data; otherwise, false. - /// - /// - /// PreEncode is called after and before encoding. - /// - public override bool PreEncode(short plane) - { - return OJpegEncodeIsUnsupported(); - } - - /// - /// Performs any actions after encoding required by the codec. - /// - /// - /// true if all post-encode actions succeeded; otherwise, false - /// - /// - /// PostEncode is called after encoding and can be used to release any external - /// resources needed during encoding. - /// - public override bool PostEncode() - { - return OJpegEncodeIsUnsupported(); - } - - /// - /// Encodes one row of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeRow(byte[] buffer, int offset, int count, short plane) - { - return OJpegEncodeIsUnsupported(); - } - - /// - /// Encodes one strip of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeStrip(byte[] buffer, int offset, int count, short plane) - { - return OJpegEncodeIsUnsupported(); - } - - /// - /// Encodes one tile of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeTile(byte[] buffer, int offset, int count, short plane) - { - return OJpegEncodeIsUnsupported(); - } - - /// - /// Cleanups the state of the codec. - /// - /// - /// Cleanup is called when codec is no longer needed (won't be used) and can be - /// used for example to restore tag methods that were substituted. - public override void Cleanup() - { - OJPEGCleanup(); - } - - internal void ForceProcessedRgbOutput(bool force) - { - // forces codec to output de-subsampled RGB image data - m_forceProcessedRgbOutput = force; - m_subsamplingcorrect_done = false; - } - - private bool OJPEGSetupDecode() - { - Tiff.WarningExt(m_tif.m_clientdata, "OJPEGSetupDecode", - "Depreciated and troublesome old-style JPEG compression mode, please convert to new-style JPEG compression and notify vendor of writing software"); - - return true; - } - - private bool OJPEGPreDecode(short s) - { - uint m; - if (!m_subsamplingcorrect_done) - OJPEGSubsamplingCorrect(); - - if (!m_readheader_done) - { - if (!OJPEGReadHeaderInfo()) - return false; - } - - if (!m_sos_end[s].m_log) - { - if (!OJPEGReadSecondarySos(s)) - return false; - } - - if (m_tif.IsTiled()) - m = (uint)m_tif.m_curtile; - else - m = (uint)m_tif.m_curstrip; - - if (m_writeheader_done && ((m_write_cursample != s) || (m_write_curstrile > m))) - { - if (m_libjpeg_session_active) - OJPEGLibjpegSessionAbort(); - m_writeheader_done = false; - } - - if (!m_writeheader_done) - { - m_plane_sample_offset = (byte)s; - m_write_cursample = s; - m_write_curstrile = (uint)(s * m_tif.m_dir.td_stripsperimage); - if (!m_in_buffer_file_pos_log || - (m_in_buffer_file_pos - m_in_buffer_togo != m_sos_end[s].m_in_buffer_file_pos)) - { - m_in_buffer_source = m_sos_end[s].m_in_buffer_source; - m_in_buffer_next_strile = m_sos_end[s].m_in_buffer_next_strile; - m_in_buffer_file_pos = m_sos_end[s].m_in_buffer_file_pos; - m_in_buffer_file_pos_log = false; - m_in_buffer_file_togo = m_sos_end[s].m_in_buffer_file_togo; - m_in_buffer_togo = 0; - m_in_buffer_cur = 0; - } - if (!OJPEGWriteHeaderInfo()) - return false; - } - - while (m_write_curstrile < m) - { - if (m_libjpeg_jpeg_query_style == 0) - { - if (!OJPEGPreDecodeSkipRaw()) - return false; - } - else - { - if (!OJPEGPreDecodeSkipScanlines()) - return false; - } - m_write_curstrile++; - } - - return true; - } - - private bool OJPEGDecode(byte[] buf, int offset, int cc, short s) - { - if (m_libjpeg_jpeg_query_style == 0) - { - if (!OJPEGDecodeRaw(buf, offset, cc)) - return false; - } - else - { - if (!OJPEGDecodeScanlines(buf, offset, cc)) - return false; - } - return true; - } - - private bool OJpegEncodeIsUnsupported() - { - Tiff.ErrorExt(m_tif.m_clientdata, "OJPEGSetupEncode", - "OJPEG encoding not supported; use new-style JPEG compression instead"); - - return false; - } - - private void OJPEGCleanup() - { - m_tif.m_tagmethods = m_parentTagMethods; - if (m_libjpeg_session_active) - OJPEGLibjpegSessionAbort(); - } - - private bool OJPEGPreDecodeSkipRaw() - { - uint m; - m = m_lines_per_strile; - if (m_subsampling_convert_state != 0) - { - if (m_subsampling_convert_clines - m_subsampling_convert_state >= m) - { - m_subsampling_convert_state += m; - if (m_subsampling_convert_state == m_subsampling_convert_clines) - m_subsampling_convert_state = 0; - return true; - } - m -= m_subsampling_convert_clines - m_subsampling_convert_state; - m_subsampling_convert_state = 0; - } - while (m >= m_subsampling_convert_clines) - { - if (jpeg_read_raw_data_encap(m_subsampling_ver * 8) == 0) - return false; - m -= m_subsampling_convert_clines; - } - if (m > 0) - { - if (jpeg_read_raw_data_encap(m_subsampling_ver * 8) == 0) - return false; - m_subsampling_convert_state = m; - } - return true; - } - - private bool OJPEGPreDecodeSkipScanlines() - { - uint m; - if (m_skip_buffer == null) - m_skip_buffer = new byte[m_bytes_per_line]; - - for (m = 0; m < m_lines_per_strile; m++) - { - if (jpeg_read_scanlines_encap(m_skip_buffer, 1) == 0) - return false; - } - return true; - } - - private bool OJPEGDecodeRaw(byte[] buf, int offset, int cc) - { - const string module = "OJPEGDecodeRaw"; - - if (cc % m_bytes_per_line != 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Fractional scanline not read"); - return false; - } - - Debug.Assert(cc > 0); - int m = offset; - int n = cc; - do - { - if (m_subsampling_convert_state == 0) - { - if (jpeg_read_raw_data_encap(m_subsampling_ver * 8) == 0) - return false; - } - - uint oy = m_subsampling_convert_state * m_subsampling_ver * m_subsampling_convert_ylinelen; - uint ocb = m_subsampling_convert_state * m_subsampling_convert_clinelen; - uint ocr = m_subsampling_convert_state * m_subsampling_convert_clinelen; - - int i = 0; - int ii = 0; - int p = m; - for (uint q = 0; q < m_subsampling_convert_clinelenout; q++) - { - uint r = oy; - for (byte sy = 0; sy < m_subsampling_ver; sy++) - { - for (byte sx = 0; sx < m_subsampling_hor; sx++) - { - i = (int)(r / m_subsampling_convert_ylinelen); - ii = (int)(r % m_subsampling_convert_ylinelen); - r++; - buf[p++] = m_subsampling_convert_ybuf[i][ii]; - } - - r += m_subsampling_convert_ylinelen - m_subsampling_hor; - } - oy += m_subsampling_hor; - - i = (int)(ocb / m_subsampling_convert_clinelen); - ii = (int)(ocb % m_subsampling_convert_clinelen); - ocb++; - buf[p++] = m_subsampling_convert_cbbuf[i][ii]; - - i = (int)(ocr / m_subsampling_convert_clinelen); - ii = (int)(ocr % m_subsampling_convert_clinelen); - ocr++; - buf[p++] = m_subsampling_convert_crbuf[i][ii]; - } - m_subsampling_convert_state++; - if (m_subsampling_convert_state == m_subsampling_convert_clines) - m_subsampling_convert_state = 0; - m += (int)m_bytes_per_line; - n -= (int)m_bytes_per_line; - } while (n > 0); - return true; - } - - private bool OJPEGDecodeScanlines(byte[] buf, int offset, int cc) - { - const string module = "OJPEGDecodeScanlines"; - - if (cc % m_bytes_per_line != 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Fractional scanline not read"); - return false; - } - - Debug.Assert(cc > 0); - - int m = offset; - byte[] temp = new byte[m_bytes_per_line]; - int n = cc; - do - { - if (jpeg_read_scanlines_encap(temp, 1) == 0) - return false; - - Buffer.BlockCopy(temp, 0, buf, m, temp.Length); - m += (int)m_bytes_per_line; - n -= (int)m_bytes_per_line; - } while (n > 0); - - return true; - } - - public void OJPEGSubsamplingCorrect() - { - const string module = "OJPEGSubsamplingCorrect"; - byte mh; - byte mv; - Debug.Assert(!m_subsamplingcorrect_done); - - if ((m_tif.m_dir.td_samplesperpixel != 3) || ((m_tif.m_dir.td_photometric != Photometric.YCBCR) && - (m_tif.m_dir.td_photometric != Photometric.ITULAB))) - { - if (m_subsampling_tag) - { - Tiff.WarningExt(m_tif, m_tif.m_clientdata, module, - "Subsampling tag not appropriate for this Photometric and/or SamplesPerPixel"); - } - - m_subsampling_hor = 1; - m_subsampling_ver = 1; - m_subsampling_force_desubsampling_inside_decompression = false; - } - else - { - m_subsamplingcorrect_done = true; - mh = m_subsampling_hor; - mv = m_subsampling_ver; - m_subsamplingcorrect = true; - OJPEGReadHeaderInfoSec(); - if (m_subsampling_force_desubsampling_inside_decompression) - { - m_subsampling_hor = 1; - m_subsampling_ver = 1; - } - m_subsamplingcorrect = false; - - if (((m_subsampling_hor != mh) || (m_subsampling_ver != mv)) && !m_subsampling_force_desubsampling_inside_decompression) - { - if (!m_subsampling_tag) - { - Tiff.WarningExt(m_tif, m_tif.m_clientdata, module, - "Subsampling tag is not set, yet subsampling inside JPEG data [{0},{1}] does not match default values [2,2]; assuming subsampling inside JPEG data is correct", - m_subsampling_hor, m_subsampling_ver); - } - else - { - Tiff.WarningExt(m_tif, m_tif.m_clientdata, module, - "Subsampling inside JPEG data [{0},{1}] does not match subsampling tag values [{2},{3}]; assuming subsampling inside JPEG data is correct", - m_subsampling_hor, m_subsampling_ver, mh, mv); - } - } - - if (m_subsampling_force_desubsampling_inside_decompression) - { - if (!m_subsampling_tag) - { - Tiff.WarningExt(m_tif, m_tif.m_clientdata, module, - "Subsampling tag is not set, yet subsampling inside JPEG data does not match default values [2,2] (nor any other values allowed in TIFF); assuming subsampling inside JPEG data is correct and desubsampling inside JPEG decompression"); - } - else - { - Tiff.WarningExt(m_tif, m_tif.m_clientdata, module, - "Subsampling inside JPEG data does not match subsampling tag values [{0},{1}] (nor any other values allowed in TIFF); assuming subsampling inside JPEG data is correct and desubsampling inside JPEG decompression", - mh, mv); - } - } - - if (!m_subsampling_force_desubsampling_inside_decompression) - { - if (m_subsampling_hor < m_subsampling_ver) - { - Tiff.WarningExt(m_tif, m_tif.m_clientdata, module, - "Subsampling values [{0},{1}] are not allowed in TIFF", - m_subsampling_hor, m_subsampling_ver); - } - } - } - - m_subsamplingcorrect_done = true; - } - - private bool OJPEGReadHeaderInfo() - { - const string module = "OJPEGReadHeaderInfo"; - Debug.Assert(!m_readheader_done); - m_image_width = (uint)m_tif.m_dir.td_imagewidth; - m_image_length = (uint)m_tif.m_dir.td_imagelength; - if (m_tif.IsTiled()) - { - m_strile_width = (uint)m_tif.m_dir.td_tilewidth; - m_strile_length = (uint)m_tif.m_dir.td_tilelength; - m_strile_length_total = ((m_image_length + m_strile_length - 1) / m_strile_length) * m_strile_length; - } - else - { - m_strile_width = m_image_width; - m_strile_length = (uint)m_tif.m_dir.td_rowsperstrip; - m_strile_length_total = m_image_length; - } - m_samples_per_pixel = (byte)m_tif.m_dir.td_samplesperpixel; - if (m_samples_per_pixel == 1) - { - m_plane_sample_offset = 0; - m_samples_per_pixel_per_plane = m_samples_per_pixel; - m_subsampling_hor = 1; - m_subsampling_ver = 1; - } - else - { - if (m_samples_per_pixel != 3) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "SamplesPerPixel {0} not supported for this compression scheme", - m_samples_per_pixel); - return false; - } - - m_plane_sample_offset = 0; - if (m_tif.m_dir.td_planarconfig == PlanarConfig.CONTIG) - m_samples_per_pixel_per_plane = 3; - else - m_samples_per_pixel_per_plane = 1; - } - if (m_strile_length < m_image_length) - { - if (m_strile_length % (m_subsampling_ver * 8) != 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, - "Incompatible vertical subsampling and image strip/tile length"); - return false; - } - m_restart_interval = (ushort)(((m_strile_width + m_subsampling_hor * 8 - 1) / (m_subsampling_hor * 8)) * (m_strile_length / (m_subsampling_ver * 8))); - } - - if (!OJPEGReadHeaderInfoSec()) - return false; - - m_sos_end[0].m_log = true; - m_sos_end[0].m_in_buffer_source = m_in_buffer_source; - m_sos_end[0].m_in_buffer_next_strile = m_in_buffer_next_strile; - m_sos_end[0].m_in_buffer_file_pos = m_in_buffer_file_pos - m_in_buffer_togo; - m_sos_end[0].m_in_buffer_file_togo = m_in_buffer_file_togo + m_in_buffer_togo; - m_readheader_done = true; - return true; - } - - private bool OJPEGReadSecondarySos(short s) - { - Debug.Assert(s > 0); - Debug.Assert(s < 3); - Debug.Assert(m_sos_end[0].m_log); - Debug.Assert(!m_sos_end[s].m_log); - - m_plane_sample_offset = (byte)(s - 1); - while (!m_sos_end[m_plane_sample_offset].m_log) - m_plane_sample_offset--; - - m_in_buffer_source = m_sos_end[m_plane_sample_offset].m_in_buffer_source; - m_in_buffer_next_strile = m_sos_end[m_plane_sample_offset].m_in_buffer_next_strile; - m_in_buffer_file_pos = m_sos_end[m_plane_sample_offset].m_in_buffer_file_pos; - m_in_buffer_file_pos_log = false; - m_in_buffer_file_togo = m_sos_end[m_plane_sample_offset].m_in_buffer_file_togo; - m_in_buffer_togo = 0; - m_in_buffer_cur = 0; - - while (m_plane_sample_offset < s) - { - do - { - byte m; - if (!OJPEGReadByte(out m)) - return false; - - if (m == 255) - { - do - { - if (!OJPEGReadByte(out m)) - return false; - - if (m != 255) - break; - } while (true); - - if (m == (byte)JPEG_MARKER.SOS) - break; - } - } while (true); - - m_plane_sample_offset++; - if (!OJPEGReadHeaderInfoSecStreamSos()) - return false; - - m_sos_end[m_plane_sample_offset].m_log = true; - m_sos_end[m_plane_sample_offset].m_in_buffer_source = m_in_buffer_source; - m_sos_end[m_plane_sample_offset].m_in_buffer_next_strile = m_in_buffer_next_strile; - m_sos_end[m_plane_sample_offset].m_in_buffer_file_pos = m_in_buffer_file_pos - m_in_buffer_togo; - m_sos_end[m_plane_sample_offset].m_in_buffer_file_togo = m_in_buffer_file_togo + m_in_buffer_togo; - } - - return true; - } - - private bool OJPEGWriteHeaderInfo() - { - Debug.Assert(!m_libjpeg_session_active); - - m_out_state = OJPEGStateOutState.ososSoi; - m_restart_index = 0; - - m_libjpeg_jpeg_error_mgr = new OJpegErrorManager(this); - if (!jpeg_create_decompress_encap()) - return false; - - m_libjpeg_session_active = true; - m_libjpeg_jpeg_source_mgr = new OJpegSrcManager(this); - m_libjpeg_jpeg_decompress_struct.Src = m_libjpeg_jpeg_source_mgr; - - if (jpeg_read_header_encap(true) == ReadResult.JPEG_SUSPENDED) - return false; - - if (!m_subsampling_force_desubsampling_inside_decompression && (m_samples_per_pixel_per_plane > 1)) - { - m_libjpeg_jpeg_decompress_struct.Raw_data_out = true; - m_libjpeg_jpeg_decompress_struct.Do_fancy_upsampling = false; - - //#if JPEG_LIB_VERSION >= 70 - // libjpeg_jpeg_decompress_struct.do_fancy_upsampling=FALSE; - //#endif - m_libjpeg_jpeg_query_style = 0; - if (!m_subsampling_convert_log) - { - Debug.Assert(m_subsampling_convert_ybuf == null); - Debug.Assert(m_subsampling_convert_cbbuf == null); - Debug.Assert(m_subsampling_convert_crbuf == null); - Debug.Assert(m_subsampling_convert_ycbcrimage == null); - - m_subsampling_convert_ylinelen = (uint)((m_strile_width + m_subsampling_hor * 8 - 1) / (m_subsampling_hor * 8) * m_subsampling_hor * 8); - m_subsampling_convert_ylines = (uint)(m_subsampling_ver * 8); - m_subsampling_convert_clinelen = m_subsampling_convert_ylinelen / m_subsampling_hor; - m_subsampling_convert_clines = 8; - - m_subsampling_convert_ybuf = new byte[m_subsampling_convert_ylines][]; - for (int i = 0; i < m_subsampling_convert_ylines; i++) - m_subsampling_convert_ybuf[i] = new byte[m_subsampling_convert_ylinelen]; - - m_subsampling_convert_cbbuf = new byte[m_subsampling_convert_clines][]; - m_subsampling_convert_crbuf = new byte[m_subsampling_convert_clines][]; - for (int i = 0; i < m_subsampling_convert_clines; i++) - { - m_subsampling_convert_cbbuf[i] = new byte[m_subsampling_convert_clinelen]; - m_subsampling_convert_crbuf[i] = new byte[m_subsampling_convert_clinelen]; - } - - m_subsampling_convert_ycbcrimage = new byte[3][][]; - m_subsampling_convert_ycbcrimage[0] = new byte[m_subsampling_convert_ylines][]; - for (uint n = 0; n < m_subsampling_convert_ylines; n++) - m_subsampling_convert_ycbcrimage[0][n] = m_subsampling_convert_ybuf[n]; - - m_subsampling_convert_ycbcrimage[1] = new byte[m_subsampling_convert_clines][]; - for (uint n = 0; n < m_subsampling_convert_clines; n++) - m_subsampling_convert_ycbcrimage[1][n] = m_subsampling_convert_cbbuf[n]; - - m_subsampling_convert_ycbcrimage[2] = new byte[m_subsampling_convert_clines][]; - for (uint n = 0; n < m_subsampling_convert_clines; n++) - m_subsampling_convert_ycbcrimage[2][n] = m_subsampling_convert_crbuf[n]; - - m_subsampling_convert_clinelenout = ((m_strile_width + m_subsampling_hor - 1) / m_subsampling_hor); - m_subsampling_convert_state = 0; - m_bytes_per_line = (uint)(m_subsampling_convert_clinelenout * (m_subsampling_ver * m_subsampling_hor + 2)); - m_lines_per_strile = ((m_strile_length + m_subsampling_ver - 1) / m_subsampling_ver); - m_subsampling_convert_log = true; - } - } - else - { - if (m_forceProcessedRgbOutput) - { - m_libjpeg_jpeg_decompress_struct.Do_fancy_upsampling = false; - m_libjpeg_jpeg_decompress_struct.Jpeg_color_space = J_COLOR_SPACE.JCS_YCbCr; - m_libjpeg_jpeg_decompress_struct.Out_color_space = J_COLOR_SPACE.JCS_RGB; - } - else - { - m_libjpeg_jpeg_decompress_struct.Jpeg_color_space = J_COLOR_SPACE.JCS_UNKNOWN; - m_libjpeg_jpeg_decompress_struct.Out_color_space = J_COLOR_SPACE.JCS_UNKNOWN; - } - - m_libjpeg_jpeg_query_style = 1; - m_bytes_per_line = m_samples_per_pixel_per_plane * m_strile_width; - m_lines_per_strile = m_strile_length; - } - - if (!jpeg_start_decompress_encap()) - return false; - - m_writeheader_done = true; - return true; - } - - private void OJPEGLibjpegSessionAbort() - { - Debug.Assert(m_libjpeg_session_active); - m_libjpeg_jpeg_decompress_struct.jpeg_destroy(); - m_libjpeg_session_active = false; - } - - private bool OJPEGReadHeaderInfoSec() - { - const string module = "OJPEGReadHeaderInfoSec"; - byte m; - ushort n; - byte o; - if (m_file_size == 0) - m_file_size = (uint)m_tif.GetStream().Size(m_tif.m_clientdata); - - if (m_jpeg_interchange_format != 0) - { - if (m_jpeg_interchange_format >= m_file_size) - { - m_jpeg_interchange_format = 0; - m_jpeg_interchange_format_length = 0; - } - else - { - if ((m_jpeg_interchange_format_length == 0) || (m_jpeg_interchange_format + m_jpeg_interchange_format_length > m_file_size)) - m_jpeg_interchange_format_length = m_file_size - m_jpeg_interchange_format; - } - } - - m_in_buffer_source = OJPEGStateInBufferSource.osibsNotSetYet; - m_in_buffer_next_strile = 0; - m_in_buffer_strile_count = (uint)m_tif.m_dir.td_nstrips; - m_in_buffer_file_togo = 0; - m_in_buffer_togo = 0; - - do - { - if (!OJPEGReadBytePeek(out m)) - return false; - - if (m != 255) - break; - - OJPEGReadByteAdvance(); - do - { - if (!OJPEGReadByte(out m)) - return false; - } while (m == 255); - - switch ((JPEG_MARKER)m) - { - case JPEG_MARKER.SOI: - /* this type of marker has no data, and should be skipped */ - break; - case JPEG_MARKER.COM: - case JPEG_MARKER.APP0: - case JPEG_MARKER.APP1: - case JPEG_MARKER.APP2: - case JPEG_MARKER.APP3: - case JPEG_MARKER.APP4: - case JPEG_MARKER.APP5: - case JPEG_MARKER.APP6: - case JPEG_MARKER.APP7: - case JPEG_MARKER.APP8: - case JPEG_MARKER.APP9: - case JPEG_MARKER.APP10: - case JPEG_MARKER.APP11: - case JPEG_MARKER.APP12: - case JPEG_MARKER.APP13: - case JPEG_MARKER.APP14: - case JPEG_MARKER.APP15: - /* this type of marker has data, but it has no use to us (and no place here) and should be skipped */ - if (!OJPEGReadWord(out n)) - return false; - if (n < 2) - { - if (!m_subsamplingcorrect) - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt JPEG data"); - return false; - } - if (n > 2) - OJPEGReadSkip((ushort)(n - 2)); - break; - case JPEG_MARKER.DRI: - if (!OJPEGReadHeaderInfoSecStreamDri()) - return false; - break; - case JPEG_MARKER.DQT: - if (!OJPEGReadHeaderInfoSecStreamDqt()) - return false; - break; - case JPEG_MARKER.DHT: - if (!OJPEGReadHeaderInfoSecStreamDht()) - return false; - break; - case JPEG_MARKER.SOF0: - case JPEG_MARKER.SOF1: - case JPEG_MARKER.SOF3: - if (!OJPEGReadHeaderInfoSecStreamSof(m)) - return false; - if (m_subsamplingcorrect) - return true; - break; - case JPEG_MARKER.SOS: - if (m_subsamplingcorrect) - return true; - Debug.Assert(m_plane_sample_offset == 0); - if (!OJPEGReadHeaderInfoSecStreamSos()) - return false; - break; - default: - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Unknown marker type {0} in JPEG data", m); - return false; - } - } while (m != (byte)JPEG_MARKER.SOS); - - if (m_subsamplingcorrect) - return true; - - if (!m_sof_log) - { - if (!OJPEGReadHeaderInfoSecTablesQTable()) - return false; - - m_sof_marker_id = (byte)JPEG_MARKER.SOF0; - for (o = 0; o < m_samples_per_pixel; o++) - m_sof_c[o] = o; - - m_sof_hv[0] = (byte)((m_subsampling_hor << 4) | m_subsampling_ver); - for (o = 1; o < m_samples_per_pixel; o++) - m_sof_hv[o] = 17; - - m_sof_x = m_strile_width; - m_sof_y = m_strile_length_total; - m_sof_log = true; - - if (!OJPEGReadHeaderInfoSecTablesDcTable()) - return false; - - if (!OJPEGReadHeaderInfoSecTablesAcTable()) - return false; - - for (o = 1; o < m_samples_per_pixel; o++) - m_sos_cs[o] = o; - } - - return true; - } - - private bool OJPEGReadHeaderInfoSecStreamDri() - { - // this could easilly cause trouble in some cases... - // but no such cases have occured so far - const string module = "OJPEGReadHeaderInfoSecStreamDri"; - ushort m; - if (!OJPEGReadWord(out m)) - return false; - - if (m != 4) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt DRI marker in JPEG data"); - return false; - } - - if (!OJPEGReadWord(out m)) - return false; - - m_restart_interval = m; - return true; - } - - private bool OJPEGReadHeaderInfoSecStreamDqt() - { - // this is a table marker, and it is to be saved as a whole for - // exact pushing on the jpeg stream later on - const string module = "OJPEGReadHeaderInfoSecStreamDqt"; - ushort m; - uint na; - byte[] nb; - byte o; - if (!OJPEGReadWord(out m)) - return false; - - if (m <= 2) - { - if (!m_subsamplingcorrect) - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt DQT marker in JPEG data"); - return false; - } - - if (m_subsamplingcorrect) - { - OJPEGReadSkip((ushort)(m - 2)); - } - else - { - m -= 2; - do - { - if (m < 65) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt DQT marker in JPEG data"); - return false; - } - - na = 69; - nb = new byte[na]; - nb[0] = 255; - nb[1] = (byte)JPEG_MARKER.DQT; - nb[2] = 0; - nb[3] = 67; - if (!OJPEGReadBlock(65, nb, 4)) - return false; - - o = (byte)(nb[4] & 15); - if (3 < o) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt DQT marker in JPEG data"); - return false; - } - - m_qtable[o] = nb; - m -= 65; - } while (m > 0); - } - return true; - } - - private bool OJPEGReadHeaderInfoSecStreamDht() - { - // this is a table marker, and it is to be saved as a whole for - // exact pushing on the jpeg stream later on - // TODO: the following assumes there is only one table in - // this marker... but i'm not quite sure that assumption is - // guaranteed correct - const string module = "OJPEGReadHeaderInfoSecStreamDht"; - ushort m; - uint na; - byte[] nb; - byte o; - if (!OJPEGReadWord(out m)) - return false; - if (m <= 2) - { - if (!m_subsamplingcorrect) - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt DHT marker in JPEG data"); - return false; - } - if (m_subsamplingcorrect) - { - OJPEGReadSkip((ushort)(m - 2)); - } - else - { - na = (uint)(2 + m); - nb = new byte[na]; - nb[0] = 255; - nb[1] = (byte)JPEG_MARKER.DHT; - nb[2] = (byte)(m >> 8); - nb[3] = (byte)(m & 255); - if (!OJPEGReadBlock((ushort)(m - 2), nb, 4)) - return false; - o = nb[4]; - if ((o & 240) == 0) - { - if (3 < o) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt DHT marker in JPEG data"); - return false; - } - m_dctable[o] = nb; - } - else - { - if ((o & 240) != 16) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt DHT marker in JPEG data"); - return false; - } - o &= 15; - if (3 < o) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt DHT marker in JPEG data"); - return false; - } - m_actable[o] = nb; - } - } - return true; - } - - private bool OJPEGReadHeaderInfoSecStreamSof(byte marker_id) - { - /* this marker needs to be checked, and part of its data needs to be saved for regeneration later on */ - const string module = "OJPEGReadHeaderInfoSecStreamSof"; - ushort m; - ushort n; - byte o; - ushort p; - ushort q; - if (m_sof_log) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt JPEG data"); - return false; - } - if (!m_subsamplingcorrect) - m_sof_marker_id = marker_id; - /* Lf: data length */ - if (!OJPEGReadWord(out m)) - return false; - if (m < 11) - { - if (!m_subsamplingcorrect) - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt SOF marker in JPEG data"); - return false; - } - m -= 8; - if (m % 3 != 0) - { - if (!m_subsamplingcorrect) - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt SOF marker in JPEG data"); - return false; - } - n = (ushort)(m / 3); - if (!m_subsamplingcorrect) - { - if (n != m_samples_per_pixel) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "JPEG compressed data indicates unexpected number of samples"); - return false; - } - } - /* P: Sample precision */ - if (!OJPEGReadByte(out o)) - return false; - if (o != 8) - { - if (!m_subsamplingcorrect) - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "JPEG compressed data indicates unexpected number of bits per sample"); - return false; - } - /* Y: Number of lines, X: Number of samples per line */ - if (m_subsamplingcorrect) - OJPEGReadSkip(4); - else - { - /* TODO: probably best to also add check on allowed upper bound, especially x, may cause buffer overflow otherwise i think */ - /* Y: Number of lines */ - if (!OJPEGReadWord(out p)) - return false; - if ((p < m_image_length) && (p < m_strile_length_total)) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "JPEG compressed data indicates unexpected height"); - return false; - } - m_sof_y = p; - /* X: Number of samples per line */ - if (!OJPEGReadWord(out p)) - return false; - if ((p < m_image_width) && (p < m_strile_width)) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "JPEG compressed data indicates unexpected width"); - return false; - } - m_sof_x = p; - } - /* Nf: Number of image components in frame */ - if (!OJPEGReadByte(out o)) - return false; - if (o != n) - { - if (!m_subsamplingcorrect) - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt SOF marker in JPEG data"); - return false; - } - /* per component stuff */ - /* TODO: double-check that flow implies that n cannot be as big as to make us overflow sof_c, sof_hv and sof_tq arrays */ - for (q = 0; q < n; q++) - { - /* C: Component identifier */ - if (!OJPEGReadByte(out o)) - return false; - if (!m_subsamplingcorrect) - m_sof_c[q] = o; - /* H: Horizontal sampling factor, and V: Vertical sampling factor */ - if (!OJPEGReadByte(out o)) - return false; - if (m_subsamplingcorrect) - { - if (q == 0) - { - m_subsampling_hor = (byte)(o >> 4); - m_subsampling_ver = (byte)(o & 15); - if (((m_subsampling_hor != 1) && (m_subsampling_hor != 2) && (m_subsampling_hor != 4)) || - ((m_subsampling_ver != 1) && (m_subsampling_ver != 2) && (m_subsampling_ver != 4)) || - m_forceProcessedRgbOutput) - { - m_subsampling_force_desubsampling_inside_decompression = true; - } - } - else - { - if (o != 17) - m_subsampling_force_desubsampling_inside_decompression = true; - } - } - else - { - m_sof_hv[q] = o; - if (!m_subsampling_force_desubsampling_inside_decompression) - { - if (q == 0) - { - if (o != ((m_subsampling_hor << 4) | m_subsampling_ver)) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "JPEG compressed data indicates unexpected subsampling values"); - return false; - } - } - else - { - if (o != 17) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "JPEG compressed data indicates unexpected subsampling values"); - return false; - } - } - } - } - /* Tq: Quantization table destination selector */ - if (!OJPEGReadByte(out o)) - return false; - if (!m_subsamplingcorrect) - m_sof_tq[q] = o; - } - if (!m_subsamplingcorrect) - m_sof_log = true; - return true; - } - - private bool OJPEGReadHeaderInfoSecStreamSos() - { - /* this marker needs to be checked, and part of its data needs to be saved for regeneration later on */ - const string module = "OJPEGReadHeaderInfoSecStreamSos"; - ushort m; - byte n; - byte o; - Debug.Assert(!m_subsamplingcorrect); - if (!m_sof_log) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt SOS marker in JPEG data"); - return false; - } - /* Ls */ - if (!OJPEGReadWord(out m)) - return false; - if (m != 6 + m_samples_per_pixel_per_plane * 2) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt SOS marker in JPEG data"); - return false; - } - /* Ns */ - if (!OJPEGReadByte(out n)) - return false; - if (n != m_samples_per_pixel_per_plane) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt SOS marker in JPEG data"); - return false; - } - /* Cs, Td, and Ta */ - for (o = 0; o < m_samples_per_pixel_per_plane; o++) - { - /* Cs */ - if (!OJPEGReadByte(out n)) - return false; - m_sos_cs[m_plane_sample_offset + o] = n; - /* Td and Ta */ - if (!OJPEGReadByte(out n)) - return false; - m_sos_tda[m_plane_sample_offset + o] = n; - } - /* skip Ss, Se, Ah, en Al -> no check, as per Tom Lane recommendation, as per LibJpeg source */ - OJPEGReadSkip(3); - return true; - } - - private bool OJPEGReadHeaderInfoSecTablesQTable() - { - const string module = "OJPEGReadHeaderInfoSecTablesQTable"; - byte m; - byte n; - uint oa; - byte[] ob; - uint p; - if (m_qtable_offset[0] == 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Missing JPEG tables"); - return false; - } - m_in_buffer_file_pos_log = false; - for (m = 0; m < m_samples_per_pixel; m++) - { - if ((m_qtable_offset[m] != 0) && ((m == 0) || (m_qtable_offset[m] != m_qtable_offset[m - 1]))) - { - for (n = 0; n < m - 1; n++) - { - if (m_qtable_offset[m] == m_qtable_offset[n]) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt JpegQTables tag value"); - return false; - } - } - oa = 69; - ob = new byte[oa]; - ob[0] = 255; - ob[1] = (byte)JPEG_MARKER.DQT; - ob[2] = 0; - ob[3] = 67; - ob[4] = m; - TiffStream stream = m_tif.GetStream(); - stream.Seek(m_tif.m_clientdata, m_qtable_offset[m], SeekOrigin.Begin); - p = (uint)stream.Read(m_tif.m_clientdata, ob, 5, 64); - if (p != 64) - return false; - m_qtable[m] = ob; - m_sof_tq[m] = m; - } - else - m_sof_tq[m] = m_sof_tq[m - 1]; - } - return true; - } - - private bool OJPEGReadHeaderInfoSecTablesDcTable() - { - const string module = "OJPEGReadHeaderInfoSecTablesDcTable"; - byte m; - byte n; - byte[] o = new byte[16]; - uint p; - uint q; - uint ra; - byte[] rb; - if (m_dctable_offset[0] == 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Missing JPEG tables"); - return false; - } - m_in_buffer_file_pos_log = false; - for (m = 0; m < m_samples_per_pixel; m++) - { - if ((m_dctable_offset[m] != 0) && ((m == 0) || (m_dctable_offset[m] != m_dctable_offset[m - 1]))) - { - for (n = 0; n < m - 1; n++) - { - if (m_dctable_offset[m] == m_dctable_offset[n]) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt JpegDcTables tag value"); - return false; - } - } - - TiffStream stream = m_tif.GetStream(); - stream.Seek(m_tif.m_clientdata, m_dctable_offset[m], SeekOrigin.Begin); - p = (uint)stream.Read(m_tif.m_clientdata, o, 0, 16); - if (p != 16) - return false; - q = 0; - for (n = 0; n < 16; n++) - q += o[n]; - ra = 21 + q; - rb = new byte[ra]; - rb[0] = 255; - rb[1] = (byte)JPEG_MARKER.DHT; - rb[2] = (byte)((19 + q) >> 8); - rb[3] = (byte)((19 + q) & 255); - rb[4] = m; - for (n = 0; n < 16; n++) - rb[5 + n] = o[n]; - - p = (uint)stream.Read(m_tif.m_clientdata, rb, 21, (int)q); - if (p != q) - return false; - m_dctable[m] = rb; - m_sos_tda[m] = (byte)(m << 4); - } - else - m_sos_tda[m] = m_sos_tda[m - 1]; - } - return true; - } - - private bool OJPEGReadHeaderInfoSecTablesAcTable() - { - const string module = "OJPEGReadHeaderInfoSecTablesAcTable"; - byte m; - byte n; - byte[] o = new byte[16]; - uint p; - uint q; - uint ra; - byte[] rb; - if (m_actable_offset[0] == 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Missing JPEG tables"); - return false; - } - m_in_buffer_file_pos_log = false; - for (m = 0; m < m_samples_per_pixel; m++) - { - if ((m_actable_offset[m] != 0) && ((m == 0) || (m_actable_offset[m] != m_actable_offset[m - 1]))) - { - for (n = 0; n < m - 1; n++) - { - if (m_actable_offset[m] == m_actable_offset[n]) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, module, "Corrupt JpegAcTables tag value"); - return false; - } - } - TiffStream stream = m_tif.GetStream(); - stream.Seek(m_tif.m_clientdata, m_actable_offset[m], SeekOrigin.Begin); - p = (uint)stream.Read(m_tif.m_clientdata, o, 0, 16); - if (p != 16) - return false; - q = 0; - for (n = 0; n < 16; n++) - q += o[n]; - ra = 21 + q; - rb = new byte[ra]; - rb[0] = 255; - rb[1] = (byte)JPEG_MARKER.DHT; - rb[2] = (byte)((19 + q) >> 8); - rb[3] = (byte)((19 + q) & 255); - rb[4] = (byte)(16 | m); - for (n = 0; n < 16; n++) - rb[5 + n] = o[n]; - - p = (uint)stream.Read(m_tif.m_clientdata, rb, 21, (int)q); - if (p != q) - return false; - m_actable[m] = rb; - m_sos_tda[m] = (byte)(m_sos_tda[m] | m); - } - else - m_sos_tda[m] = (byte)(m_sos_tda[m] | (m_sos_tda[m - 1] & 15)); - } - return true; - } - - private bool OJPEGReadBufferFill() - { - ushort m; - int n; - /* TODO: double-check: when subsamplingcorrect is set, no call to TIFFErrorExt or TIFFWarningExt should be made - * in any other case, seek or read errors should be passed through */ - do - { - if (m_in_buffer_file_togo != 0) - { - TiffStream stream = m_tif.GetStream(); - if (!m_in_buffer_file_pos_log) - { - stream.Seek(m_tif.m_clientdata, m_in_buffer_file_pos, SeekOrigin.Begin); - m_in_buffer_file_pos_log = true; - } - m = OJPEG_BUFFER; - if (m > m_in_buffer_file_togo) - m = (ushort)m_in_buffer_file_togo; - - n = stream.Read(m_tif.m_clientdata, m_in_buffer, 0, (int)m); - if (n == 0) - return false; - Debug.Assert(n > 0); - Debug.Assert(n <= OJPEG_BUFFER); - Debug.Assert(n < 65536); - Debug.Assert((ushort)n <= m_in_buffer_file_togo); - m = (ushort)n; - m_in_buffer_togo = m; - m_in_buffer_cur = 0; - m_in_buffer_file_togo -= m; - m_in_buffer_file_pos += m; - break; - } - m_in_buffer_file_pos_log = false; - switch (m_in_buffer_source) - { - case OJPEGStateInBufferSource.osibsNotSetYet: - if (m_jpeg_interchange_format != 0) - { - m_in_buffer_file_pos = m_jpeg_interchange_format; - m_in_buffer_file_togo = m_jpeg_interchange_format_length; - } - m_in_buffer_source = OJPEGStateInBufferSource.osibsJpegInterchangeFormat; - break; - case OJPEGStateInBufferSource.osibsJpegInterchangeFormat: - m_in_buffer_source = OJPEGStateInBufferSource.osibsStrile; - goto case OJPEGStateInBufferSource.osibsStrile; - case OJPEGStateInBufferSource.osibsStrile: - if (m_in_buffer_next_strile == m_in_buffer_strile_count) - m_in_buffer_source = OJPEGStateInBufferSource.osibsEof; - else - { - if (m_tif.m_dir.td_stripoffset == null) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, "Strip offsets are missing"); - return false; - } - m_in_buffer_file_pos = m_tif.m_dir.td_stripoffset[m_in_buffer_next_strile]; - if (m_in_buffer_file_pos != 0) - { - if (m_in_buffer_file_pos >= m_file_size) - m_in_buffer_file_pos = 0; - else - { - m_in_buffer_file_togo = m_tif.m_dir.td_stripbytecount[m_in_buffer_next_strile]; - if (m_in_buffer_file_togo == 0) - m_in_buffer_file_pos = 0; - else if (m_in_buffer_file_pos + m_in_buffer_file_togo > m_file_size) - m_in_buffer_file_togo = m_file_size - m_in_buffer_file_pos; - } - } - m_in_buffer_next_strile++; - } - break; - default: - return false; - } - } while (true); - return true; - } - - private bool OJPEGReadByte(out byte b) - { - if (m_in_buffer_togo == 0) - { - if (!OJPEGReadBufferFill()) - { - b = 0; - return false; - } - - Debug.Assert(m_in_buffer_togo > 0); - } - - b = m_in_buffer[m_in_buffer_cur]; - m_in_buffer_cur++; - m_in_buffer_togo--; - return true; - } - - public bool OJPEGReadBytePeek(out byte b) - { - if (m_in_buffer_togo == 0) - { - if (!OJPEGReadBufferFill()) - { - b = 0; - return false; - } - - Debug.Assert(m_in_buffer_togo > 0); - } - - b = m_in_buffer[m_in_buffer_cur]; - return true; - } - - private void OJPEGReadByteAdvance() - { - Debug.Assert(m_in_buffer_togo > 0); - m_in_buffer_cur++; - m_in_buffer_togo--; - } - - private bool OJPEGReadWord(out ushort word) - { - word = 0; - byte m; - if (!OJPEGReadByte(out m)) - return false; - - word = (ushort)(m << 8); - if (!OJPEGReadByte(out m)) - return false; - - word |= m; - return true; - } - - public bool OJPEGReadBlock(ushort len, byte[] mem, int offset) - { - ushort mlen; - ushort n; - Debug.Assert(len > 0); - mlen = len; - int mmem = offset; - do - { - if (m_in_buffer_togo == 0) - { - if (!OJPEGReadBufferFill()) - return false; - Debug.Assert(m_in_buffer_togo > 0); - } - n = mlen; - if (n > m_in_buffer_togo) - n = m_in_buffer_togo; - - Buffer.BlockCopy(m_in_buffer, m_in_buffer_cur, mem, mmem, n); - m_in_buffer_cur += n; - m_in_buffer_togo -= n; - mlen -= n; - mmem += n; - } while (mlen > 0); - return true; - } - - private void OJPEGReadSkip(ushort len) - { - ushort m; - ushort n; - m = len; - n = m; - if (n > m_in_buffer_togo) - n = m_in_buffer_togo; - m_in_buffer_cur += n; - m_in_buffer_togo -= n; - m -= n; - if (m > 0) - { - Debug.Assert(m_in_buffer_togo == 0); - n = m; - if (n > m_in_buffer_file_togo) - n = (ushort)m_in_buffer_file_togo; - m_in_buffer_file_pos += n; - m_in_buffer_file_togo -= n; - m_in_buffer_file_pos_log = false; - /* we don't skip past jpeginterchangeformat/strile block... - * if that is asked from us, we're dealing with totally bazurk - * data anyway, and we've not seen this happening on any - * testfile, so we might as well likely cause some other - * meaningless error to be passed at some later time - */ - } - } - - internal bool OJPEGWriteStream(out byte[] mem, out uint len) - { - mem = null; - len = 0; - do - { - Debug.Assert(m_out_state <= OJPEGStateOutState.ososEoi); - switch (m_out_state) - { - case OJPEGStateOutState.ososSoi: - OJPEGWriteStreamSoi(out mem, out len); - break; - case OJPEGStateOutState.ososQTable0: - OJPEGWriteStreamQTable(0, out mem, out len); - break; - case OJPEGStateOutState.ososQTable1: - OJPEGWriteStreamQTable(1, out mem, out len); - break; - case OJPEGStateOutState.ososQTable2: - OJPEGWriteStreamQTable(2, out mem, out len); - break; - case OJPEGStateOutState.ososQTable3: - OJPEGWriteStreamQTable(3, out mem, out len); - break; - case OJPEGStateOutState.ososDcTable0: - OJPEGWriteStreamDcTable(0, out mem, out len); - break; - case OJPEGStateOutState.ososDcTable1: - OJPEGWriteStreamDcTable(1, out mem, out len); - break; - case OJPEGStateOutState.ososDcTable2: - OJPEGWriteStreamDcTable(2, out mem, out len); - break; - case OJPEGStateOutState.ososDcTable3: - OJPEGWriteStreamDcTable(3, out mem, out len); - break; - case OJPEGStateOutState.ososAcTable0: - OJPEGWriteStreamAcTable(0, out mem, out len); - break; - case OJPEGStateOutState.ososAcTable1: - OJPEGWriteStreamAcTable(1, out mem, out len); - break; - case OJPEGStateOutState.ososAcTable2: - OJPEGWriteStreamAcTable(2, out mem, out len); - break; - case OJPEGStateOutState.ososAcTable3: - OJPEGWriteStreamAcTable(3, out mem, out len); - break; - case OJPEGStateOutState.ososDri: - OJPEGWriteStreamDri(out mem, out len); - break; - case OJPEGStateOutState.ososSof: - OJPEGWriteStreamSof(out mem, out len); - break; - case OJPEGStateOutState.ososSos: - OJPEGWriteStreamSos(out mem, out len); - break; - case OJPEGStateOutState.ososCompressed: - if (!OJPEGWriteStreamCompressed(out mem, out len)) - return false; - break; - case OJPEGStateOutState.ososRst: - OJPEGWriteStreamRst(out mem, out len); - break; - case OJPEGStateOutState.ososEoi: - OJPEGWriteStreamEoi(out mem, out len); - break; - } - } while (len == 0); - return true; - } - - private void OJPEGWriteStreamSoi(out byte[] mem, out uint len) - { - Debug.Assert(OJPEG_BUFFER >= 2); - m_out_buffer[0] = 255; - m_out_buffer[1] = (byte)JPEG_MARKER.SOI; - len = 2; - mem = m_out_buffer; - m_out_state++; - } - - private void OJPEGWriteStreamQTable(byte table_index, out byte[] mem, out uint len) - { - mem = null; - len = 0; - - if (m_qtable[table_index] != null) - { - mem = m_qtable[table_index]; - len = (uint)m_qtable[table_index].Length; - } - m_out_state++; - } - - private void OJPEGWriteStreamDcTable(byte table_index, out byte[] mem, out uint len) - { - mem = null; - len = 0; - - if (m_dctable[table_index] != null) - { - mem = m_dctable[table_index]; - len = (uint)m_dctable[table_index].Length; - } - m_out_state++; - } - - private void OJPEGWriteStreamAcTable(byte table_index, out byte[] mem, out uint len) - { - mem = null; - len = 0; - - if (m_actable[table_index] != null) - { - mem = m_actable[table_index]; - len = (uint)m_actable[table_index].Length; - } - m_out_state++; - } - - private void OJPEGWriteStreamDri(out byte[] mem, out uint len) - { - Debug.Assert(OJPEG_BUFFER >= 6); - mem = null; - len = 0; - - if (m_restart_interval != 0) - { - m_out_buffer[0] = 255; - m_out_buffer[1] = (byte)JPEG_MARKER.DRI; - m_out_buffer[2] = 0; - m_out_buffer[3] = 4; - m_out_buffer[4] = (byte)(m_restart_interval >> 8); - m_out_buffer[5] = (byte)(m_restart_interval & 255); - len = 6; - mem = m_out_buffer; - } - m_out_state++; - } - - private void OJPEGWriteStreamSof(out byte[] mem, out uint len) - { - byte m; - Debug.Assert(OJPEG_BUFFER >= 2 + 8 + m_samples_per_pixel_per_plane * 3); - Debug.Assert(255 >= 8 + m_samples_per_pixel_per_plane * 3); - m_out_buffer[0] = 255; - m_out_buffer[1] = m_sof_marker_id; - /* Lf */ - m_out_buffer[2] = 0; - m_out_buffer[3] = (byte)(8 + m_samples_per_pixel_per_plane * 3); - /* P */ - m_out_buffer[4] = 8; - /* Y */ - m_out_buffer[5] = (byte)(m_sof_y >> 8); - m_out_buffer[6] = (byte)(m_sof_y & 255); - /* X */ - m_out_buffer[7] = (byte)(m_sof_x >> 8); - m_out_buffer[8] = (byte)(m_sof_x & 255); - /* Nf */ - m_out_buffer[9] = m_samples_per_pixel_per_plane; - for (m = 0; m < m_samples_per_pixel_per_plane; m++) - { - /* C */ - m_out_buffer[10 + m * 3] = m_sof_c[m_plane_sample_offset + m]; - /* H and V */ - m_out_buffer[10 + m * 3 + 1] = m_sof_hv[m_plane_sample_offset + m]; - /* Tq */ - m_out_buffer[10 + m * 3 + 2] = m_sof_tq[m_plane_sample_offset + m]; - } - len = (uint)(10 + m_samples_per_pixel_per_plane * 3); - mem = m_out_buffer; - m_out_state++; - } - - private void OJPEGWriteStreamSos(out byte[] mem, out uint len) - { - byte m; - Debug.Assert(OJPEG_BUFFER >= 2 + 6 + m_samples_per_pixel_per_plane * 2); - Debug.Assert(255 >= 6 + m_samples_per_pixel_per_plane * 2); - m_out_buffer[0] = 255; - m_out_buffer[1] = (byte)JPEG_MARKER.SOS; - /* Ls */ - m_out_buffer[2] = 0; - m_out_buffer[3] = (byte)(6 + m_samples_per_pixel_per_plane * 2); - /* Ns */ - m_out_buffer[4] = m_samples_per_pixel_per_plane; - for (m = 0; m < m_samples_per_pixel_per_plane; m++) - { - /* Cs */ - m_out_buffer[5 + m * 2] = m_sos_cs[m_plane_sample_offset + m]; - /* Td and Ta */ - m_out_buffer[5 + m * 2 + 1] = m_sos_tda[m_plane_sample_offset + m]; - } - /* Ss */ - m_out_buffer[5 + m_samples_per_pixel_per_plane * 2] = 0; - /* Se */ - m_out_buffer[5 + m_samples_per_pixel_per_plane * 2 + 1] = 63; - /* Ah and Al */ - m_out_buffer[5 + m_samples_per_pixel_per_plane * 2 + 2] = 0; - len = (uint)(8 + m_samples_per_pixel_per_plane * 2); - mem = m_out_buffer; - m_out_state++; - } - - private bool OJPEGWriteStreamCompressed(out byte[] mem, out uint len) - { - mem = null; - len = 0; - - if (m_in_buffer_togo == 0) - { - if (!OJPEGReadBufferFill()) - return false; - Debug.Assert(m_in_buffer_togo > 0); - } - len = m_in_buffer_togo; - - if (m_in_buffer_cur == 0) - { - mem = m_in_buffer; - } - else - { - mem = new byte[len]; - Buffer.BlockCopy(m_in_buffer, m_in_buffer_cur, mem, 0, (int)len); - } - - m_in_buffer_togo = 0; - if (m_in_buffer_file_togo == 0) - { - switch (m_in_buffer_source) - { - case OJPEGStateInBufferSource.osibsStrile: - if (m_in_buffer_next_strile < m_in_buffer_strile_count) - m_out_state = OJPEGStateOutState.ososRst; - else - m_out_state = OJPEGStateOutState.ososEoi; - break; - case OJPEGStateInBufferSource.osibsEof: - m_out_state = OJPEGStateOutState.ososEoi; - break; - default: - break; - } - } - return true; - } - - private void OJPEGWriteStreamRst(out byte[] mem, out uint len) - { - Debug.Assert(OJPEG_BUFFER >= 2); - m_out_buffer[0] = 255; - m_out_buffer[1] = (byte)((byte)JPEG_MARKER.RST0 + m_restart_index); - m_restart_index++; - if (m_restart_index == 8) - m_restart_index = 0; - len = 2; - mem = m_out_buffer; - m_out_state = OJPEGStateOutState.ososCompressed; - } - - private void OJPEGWriteStreamEoi(out byte[] mem, out uint len) - { - Debug.Assert(OJPEG_BUFFER >= 2); - m_out_buffer[0] = 255; - m_out_buffer[1] = (byte)JPEG_MARKER.EOI; - len = 2; - mem = m_out_buffer; - } - - private bool jpeg_create_decompress_encap() - { - try - { - m_libjpeg_jpeg_decompress_struct = new jpeg_decompress_struct(m_libjpeg_jpeg_error_mgr); - } - catch (Exception) - { - return false; - } - - return true; - } - - private ReadResult jpeg_read_header_encap(bool require_image) - { - ReadResult res = ReadResult.JPEG_SUSPENDED; - try - { - res = m_libjpeg_jpeg_decompress_struct.jpeg_read_header(require_image); - } - catch (Exception) - { - return ReadResult.JPEG_SUSPENDED; - } - - return res; - } - - private bool jpeg_start_decompress_encap() - { - try - { - m_libjpeg_jpeg_decompress_struct.jpeg_start_decompress(); - } - catch (Exception) - { - return false; - } - - return true; - } - - private int jpeg_read_scanlines_encap(byte[] scanlines, int max_lines) - { - int n = 0; - try - { - byte[][] temp = new byte[1][]; - temp[0] = scanlines; - n = m_libjpeg_jpeg_decompress_struct.jpeg_read_scanlines(temp, max_lines); - } - catch (Exception) - { - return 0; - } - - return n; - } - - private int jpeg_read_raw_data_encap(int max_lines) - { - int n = 0; - try - { - n = m_libjpeg_jpeg_decompress_struct.jpeg_read_raw_data(m_subsampling_convert_ycbcrimage, max_lines); - } - catch (Exception) - { - return 0; - } - - return n; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/OJpegCodecTagMethods.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/OJpegCodecTagMethods.cs deleted file mode 100644 index 2daec8a0..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/OJpegCodecTagMethods.cs +++ /dev/null @@ -1,212 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; -using System.Diagnostics; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - class OJpegCodecTagMethods : TiffTagMethods - { - public override bool SetField(Tiff tif, TiffTag tag, FieldValue[] ap) - { - const string module = "OJPEGVSetField"; - OJpegCodec sp = tif.m_currentCodec as OJpegCodec; - Debug.Assert(sp != null); - - uint ma; - uint[] mb; - uint n; - switch (tag) - { - case TiffTag.JPEGIFOFFSET: - sp.m_jpeg_interchange_format = ap[0].ToUInt(); - break; - case TiffTag.JPEGIFBYTECOUNT: - sp.m_jpeg_interchange_format_length = ap[0].ToUInt(); - break; - case TiffTag.YCBCRSUBSAMPLING: - sp.m_subsampling_tag = true; - sp.m_subsampling_hor = ap[0].ToByte(); - sp.m_subsampling_ver = ap[1].ToByte(); - tif.m_dir.td_ycbcrsubsampling[0] = sp.m_subsampling_hor; - tif.m_dir.td_ycbcrsubsampling[1] = sp.m_subsampling_ver; - break; - case TiffTag.JPEGQTABLES: - ma = ap[0].ToUInt(); - if (ma != 0) - { - if (ma > 3) - { - Tiff.ErrorExt(tif, tif.m_clientdata, module, "JpegQTables tag has incorrect count"); - return false; - } - sp.m_qtable_offset_count = (byte)ma; - mb = ap[1].ToUIntArray(); - for (n = 0; n < ma; n++) - sp.m_qtable_offset[n] = mb[n]; - } - break; - case TiffTag.JPEGDCTABLES: - ma = ap[0].ToUInt(); - if (ma != 0) - { - if (ma > 3) - { - Tiff.ErrorExt(tif, tif.m_clientdata, module, "JpegDcTables tag has incorrect count"); - return false; - } - sp.m_dctable_offset_count = (byte)ma; - mb = ap[1].ToUIntArray(); - for (n = 0; n < ma; n++) - sp.m_dctable_offset[n] = mb[n]; - } - break; - case TiffTag.JPEGACTABLES: - ma = ap[0].ToUInt(); - if (ma != 0) - { - if (ma > 3) - { - Tiff.ErrorExt(tif, tif.m_clientdata, module, "JpegAcTables tag has incorrect count"); - return false; - } - sp.m_actable_offset_count = (byte)ma; - mb = ap[1].ToUIntArray(); - for (n = 0; n < ma; n++) - sp.m_actable_offset[n] = mb[n]; - } - break; - case TiffTag.JPEGPROC: - sp.m_jpeg_proc = ap[0].ToByte(); - break; - case TiffTag.JPEGRESTARTINTERVAL: - sp.m_restart_interval = ap[0].ToUShort(); - break; - default: - return base.SetField(tif, tag, ap); - } - - TiffFieldInfo fip = tif.FieldWithTag(tag); - if (fip != null) - tif.setFieldBit(fip.Bit); - else - return false; - - tif.m_flags |= TiffFlags.DIRTYDIRECT; - return true; - } - - public override FieldValue[] GetField(Tiff tif, TiffTag tag) - { - OJpegCodec sp = tif.m_currentCodec as OJpegCodec; - Debug.Assert(sp != null); - - FieldValue[] result = null; - - switch (tag) - { - case TiffTag.JPEGIFOFFSET: - result = new FieldValue[1]; - result[0].Set(sp.m_jpeg_interchange_format); - break; - case TiffTag.JPEGIFBYTECOUNT: - result = new FieldValue[1]; - result[0].Set(sp.m_jpeg_interchange_format_length); - break; - case TiffTag.YCBCRSUBSAMPLING: - if (!sp.m_subsamplingcorrect_done) - sp.OJPEGSubsamplingCorrect(); - - result = new FieldValue[2]; - result[0].Set(sp.m_subsampling_hor); - result[1].Set(sp.m_subsampling_ver); - break; - case TiffTag.JPEGQTABLES: - result = new FieldValue[2]; - result[0].Set(sp.m_qtable_offset_count); - result[1].Set(sp.m_qtable_offset); - break; - case TiffTag.JPEGDCTABLES: - result = new FieldValue[2]; - result[0].Set(sp.m_dctable_offset_count); - result[1].Set(sp.m_dctable_offset); - break; - case TiffTag.JPEGACTABLES: - result = new FieldValue[2]; - result[0].Set(sp.m_actable_offset_count); - result[1].Set(sp.m_actable_offset); - break; - case TiffTag.JPEGPROC: - result = new FieldValue[1]; - result[0].Set(sp.m_jpeg_proc); - break; - case TiffTag.JPEGRESTARTINTERVAL: - result = new FieldValue[1]; - result[0].Set(sp.m_restart_interval); - break; - default: - return base.GetField(tif, tag); - } - - return result; - } - - public override void PrintDir(Tiff tif, Stream fd, TiffPrintFlags flags) - { - OJpegCodec sp = tif.m_currentCodec as OJpegCodec; - Debug.Assert(sp != null); - - if (tif.fieldSet(OJpegCodec.FIELD_OJPEG_JPEGINTERCHANGEFORMAT)) - Tiff.fprintf(fd, " JpegInterchangeFormat: {0}\n", sp.m_jpeg_interchange_format); - - if (tif.fieldSet(OJpegCodec.FIELD_OJPEG_JPEGINTERCHANGEFORMATLENGTH)) - Tiff.fprintf(fd, " JpegInterchangeFormatLength: {0}\n", sp.m_jpeg_interchange_format_length); - - if (tif.fieldSet(OJpegCodec.FIELD_OJPEG_JPEGQTABLES)) - { - Tiff.fprintf(fd, " JpegQTables:"); - for (byte m = 0; m < sp.m_qtable_offset_count; m++) - Tiff.fprintf(fd, " {0}", sp.m_qtable_offset[m]); - Tiff.fprintf(fd, "\n"); - } - - if (tif.fieldSet(OJpegCodec.FIELD_OJPEG_JPEGDCTABLES)) - { - Tiff.fprintf(fd, " JpegDcTables:"); - for (byte m = 0; m < sp.m_dctable_offset_count; m++) - Tiff.fprintf(fd, " {0}", sp.m_dctable_offset[m]); - Tiff.fprintf(fd, "\n"); - } - - if (tif.fieldSet(OJpegCodec.FIELD_OJPEG_JPEGACTABLES)) - { - Tiff.fprintf(fd, " JpegAcTables:"); - for (byte m = 0; m < sp.m_actable_offset_count; m++) - Tiff.fprintf(fd, " {0}", sp.m_actable_offset[m]); - Tiff.fprintf(fd, "\n"); - } - - if (tif.fieldSet(OJpegCodec.FIELD_OJPEG_JPEGPROC)) - Tiff.fprintf(fd, " JpegProc: {0}\n", sp.m_jpeg_proc); - - if (tif.fieldSet(OJpegCodec.FIELD_OJPEG_JPEGRESTARTINTERVAL)) - Tiff.fprintf(fd, " JpegRestartInterval: {0}\n", sp.m_restart_interval); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/OJpegErrorManager.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/OJpegErrorManager.cs deleted file mode 100644 index dac26e0a..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/OJpegErrorManager.cs +++ /dev/null @@ -1,46 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -using System; -using System.Collections.Generic; -using System.Text; - -using BitMiracle.LibJpeg.Classic; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - class OJpegErrorManager : jpeg_error_mgr - { - private OJpegCodec m_sp; - - public OJpegErrorManager(OJpegCodec sp) - : base() - { - m_sp = sp; - } - - /* Error handling routines (these replace corresponding IJG routines). - * These are used for both compression and decompression. - */ - public override void error_exit() - { - string buffer = format_message(); - Tiff.ErrorExt(m_sp.GetTiff(), m_sp.GetTiff().m_clientdata, "LibJpeg", "{0}", buffer); /* display the error message */ - - // clean up LibJpeg.Net state - m_sp.m_libjpeg_jpeg_decompress_struct.jpeg_abort(); - - throw new Exception(buffer); - } - - /* This routine is invoked only for warning messages, since error_exit - * does its own thing and trace_level is never set > 0. - */ - public override void output_message() - { - string buffer = format_message(); - Tiff.WarningExt(m_sp.GetTiff(), m_sp.GetTiff().m_clientdata, "LibJpeg", "{0}", buffer); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/OJpegSrcManager.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/OJpegSrcManager.cs deleted file mode 100644 index d440bafc..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/OJpegSrcManager.cs +++ /dev/null @@ -1,131 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -using System; -using System.Collections.Generic; -using System.Text; -using BitMiracle.LibJpeg.Classic; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - class OJpegSrcManager : jpeg_source_mgr - { - protected OJpegCodec m_sp; - - public OJpegSrcManager(OJpegCodec sp) - { - initInternalBuffer(null, 0); - m_sp = sp; - } - - /// - /// Initializes this instance. - /// - public override void init_source() - { - } - - /// - /// Fills input buffer - /// - /// - /// true if operation succeed; otherwise, false - /// - public override bool fill_input_buffer() - { - Tiff tif = m_sp.GetTiff(); - byte[] mem = null; - uint len = 0; - if (!m_sp.OJPEGWriteStream(out mem, out len)) - Tiff.ErrorExt(tif, tif.m_clientdata, "LibJpeg", "Premature end of JPEG data"); - - initInternalBuffer(mem, (int)len); - return true; - } - - /// - /// Skip data - used to skip over a potentially large amount of - /// uninteresting data (such as an APPn marker). - /// - /// The number of bytes to skip. - /// Writers of suspendable-input applications must note that skip_input_data - /// is not granted the right to give a suspension return. If the skip extends - /// beyond the data currently in the buffer, the buffer can be marked empty so - /// that the next read will cause a fill_input_buffer call that can suspend. - /// Arranging for additional bytes to be discarded before reloading the input - /// buffer is the application writer's problem. - public override void skip_input_data(int num_bytes) - { - Tiff tif = m_sp.GetTiff(); - Tiff.ErrorExt(tif, tif.m_clientdata, "LibJpeg", "Unexpected error"); - } - - /// - /// This is the default resync_to_restart method for data source - /// managers to use if they don't have any better approach. - /// - /// An instance of - /// The desired - /// false if suspension is required. - /// That method assumes that no backtracking is possible. - /// Some data source managers may be able to back up, or may have - /// additional knowledge about the data which permits a more - /// intelligent recovery strategy; such managers would - /// presumably supply their own resync method.

- /// read_restart_marker calls resync_to_restart if it finds a marker other than - /// the restart marker it was expecting. (This code is *not* used unless - /// a nonzero restart interval has been declared.) cinfo.unread_marker is - /// the marker code actually found (might be anything, except 0 or FF). - /// The desired restart marker number (0..7) is passed as a parameter.

- /// This routine is supposed to apply whatever error recovery strategy seems - /// appropriate in order to position the input stream to the next data segment. - /// Note that cinfo.unread_marker is treated as a marker appearing before - /// the current data-source input point; usually it should be reset to zero - /// before returning.

- /// This implementation is substantially constrained by wanting to treat the - /// input as a data stream; this means we can't back up. Therefore, we have - /// only the following actions to work with:
- /// 1. Simply discard the marker and let the entropy decoder resume at next - /// byte of file.
- /// 2. Read forward until we find another marker, discarding intervening - /// data. (In theory we could look ahead within the current bufferload, - /// without having to discard data if we don't find the desired marker. - /// This idea is not implemented here, in part because it makes behavior - /// dependent on buffer size and chance buffer-boundary positions.)
- /// 3. Leave the marker unread (by failing to zero cinfo.unread_marker). - /// This will cause the entropy decoder to process an empty data segment, - /// inserting dummy zeroes, and then we will reprocess the marker.
- /// #2 is appropriate if we think the desired marker lies ahead, while #3 is - /// appropriate if the found marker is a future restart marker (indicating - /// that we have missed the desired restart marker, probably because it got - /// corrupted).
- /// We apply #2 or #3 if the found marker is a restart marker no more than - /// two counts behind or ahead of the expected one. We also apply #2 if the - /// found marker is not a legal JPEG marker code (it's certainly bogus data). - /// If the found marker is a restart marker more than 2 counts away, we do #1 - /// (too much risk that the marker is erroneous; with luck we will be able to - /// resync at some future point).
- /// For any valid non-restart JPEG marker, we apply #3. This keeps us from - /// overrunning the end of a scan. An implementation limited to single-scan - /// files might find it better to apply #2 for markers other than EOI, since - /// any other marker would have to be bogus data in that case.
- public override bool resync_to_restart(jpeg_decompress_struct cinfo, int desired) - { - Tiff tif = m_sp.GetTiff(); - Tiff.ErrorExt(tif, tif.m_clientdata, "LibJpeg", "Unexpected error"); - return false; - } - - /// - /// Terminate source - called by jpeg_finish_decompress - /// after all data has been read. Often a no-op. - /// - /// NB: not called by jpeg_abort or jpeg_destroy; surrounding - /// application must deal with any cleanup that should happen even - /// for error exit. - public override void term_source() - { - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/PackBitsCodec.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/PackBitsCodec.cs deleted file mode 100644 index 0bd0c503..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/PackBitsCodec.cs +++ /dev/null @@ -1,487 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * PackBits Compression Algorithm Support - */ - -using System; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - class PackBitsCodec : TiffCodec - { - private enum EncodingState - { - BASE, - LITERAL, - RUN, - LITERAL_RUN - }; - - private int m_rowsize; - - public PackBitsCodec(Tiff tif, Compression scheme, string name) - : base(tif, scheme, name) - { - } - - public override bool Init() - { - return true; - } - - /// - /// Gets a value indicating whether this codec can encode data. - /// - /// - /// true if this codec can encode data; otherwise, false. - /// - public override bool CanEncode - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether this codec can decode data. - /// - /// - /// true if this codec can decode data; otherwise, false. - /// - public override bool CanDecode - { - get - { - return true; - } - } - - /// - /// Decodes one row of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeRow(byte[] buffer, int offset, int count, short plane) - { - return PackBitsDecode(buffer, offset, count, plane); - } - - /// - /// Decodes one strip of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeStrip(byte[] buffer, int offset, int count, short plane) - { - return PackBitsDecode(buffer, offset, count, plane); - } - - /// - /// Decodes one tile of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public override bool DecodeTile(byte[] buffer, int offset, int count, short plane) - { - return PackBitsDecode(buffer, offset, count, plane); - } - - /// - /// Prepares the encoder part of the codec for a encoding. - /// - /// The zero-based sample plane index. - /// - /// true if this codec successfully prepared its encoder part and ready - /// to encode data; otherwise, false. - /// - /// - /// PreEncode is called after and before encoding. - /// - public override bool PreEncode(short plane) - { - return PackBitsPreEncode(plane); - } - - /// - /// Encodes one row of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeRow(byte[] buffer, int offset, int count, short plane) - { - return PackBitsEncode(buffer, offset, count, plane); - } - - /// - /// Encodes one strip of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeStrip(byte[] buffer, int offset, int count, short plane) - { - return PackBitsEncodeChunk(buffer, offset, count, plane); - } - - /// - /// Encodes one tile of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public override bool EncodeTile(byte[] buffer, int offset, int count, short plane) - { - return PackBitsEncodeChunk(buffer, offset, count, plane); - } - - private bool PackBitsPreEncode(short s) - { - /* - * Calculate the scanline/tile-width size in bytes. - */ - if (m_tif.IsTiled()) - m_rowsize = m_tif.TileRowSize(); - else - m_rowsize = m_tif.ScanlineSize(); - return true; - } - - /* - * Encode a run of pixels. - */ - private bool PackBitsEncode(byte[] buf, int offset, int cc, short s) - { - int op = m_tif.m_rawcp; - EncodingState state = EncodingState.BASE; - int lastliteral = 0; - int bp = offset; - while (cc > 0) - { - /* - * Find the longest string of identical bytes. - */ - int b = buf[bp]; - bp++; - cc--; - int n = 1; - for (; cc > 0 && b == buf[bp]; cc--, bp++) - n++; - - bool stop = false; - while (!stop) - { - if (op + 2 >= m_tif.m_rawdatasize) - { - /* insure space for new data */ - /* - * Be careful about writing the last - * literal. Must write up to that point - * and then copy the remainder to the - * front of the buffer. - */ - if (state == EncodingState.LITERAL || state == EncodingState.LITERAL_RUN) - { - int slop = op - lastliteral; - m_tif.m_rawcc += lastliteral - m_tif.m_rawcp; - if (!m_tif.flushData1()) - return false; - op = m_tif.m_rawcp; - while (slop-- > 0) - { - m_tif.m_rawdata[op] = m_tif.m_rawdata[lastliteral]; - lastliteral++; - op++; - } - - lastliteral = m_tif.m_rawcp; - } - else - { - m_tif.m_rawcc += op - m_tif.m_rawcp; - if (!m_tif.flushData1()) - return false; - op = m_tif.m_rawcp; - } - } - - switch (state) - { - case EncodingState.BASE: - /* initial state, set run/literal */ - if (n > 1) - { - state = EncodingState.RUN; - if (n > 128) - { - int temp = -127; - m_tif.m_rawdata[op] = (byte)temp; - op++; - m_tif.m_rawdata[op] = (byte)b; - op++; - n -= 128; - continue; - } - - m_tif.m_rawdata[op] = (byte)(-n + 1); - op++; - m_tif.m_rawdata[op] = (byte)b; - op++; - } - else - { - lastliteral = op; - m_tif.m_rawdata[op] = 0; - op++; - m_tif.m_rawdata[op] = (byte)b; - op++; - state = EncodingState.LITERAL; - } - stop = true; - break; - - case EncodingState.LITERAL: - /* last object was literal string */ - if (n > 1) - { - state = EncodingState.LITERAL_RUN; - if (n > 128) - { - int temp = -127; - m_tif.m_rawdata[op] = (byte)temp; - op++; - m_tif.m_rawdata[op] = (byte)b; - op++; - n -= 128; - continue; - } - - m_tif.m_rawdata[op] = (byte)(-n + 1); /* encode run */ - op++; - m_tif.m_rawdata[op] = (byte)b; - op++; - } - else - { - /* extend literal */ - m_tif.m_rawdata[lastliteral]++; - if (m_tif.m_rawdata[lastliteral] == 127) - state = EncodingState.BASE; - - m_tif.m_rawdata[op] = (byte)b; - op++; - } - stop = true; - break; - - case EncodingState.RUN: - /* last object was run */ - if (n > 1) - { - if (n > 128) - { - int temp = -127; - m_tif.m_rawdata[op] = (byte)temp; - op++; - m_tif.m_rawdata[op] = (byte)b; - op++; - n -= 128; - continue; - } - - m_tif.m_rawdata[op] = (byte)(-n + 1); - op++; - m_tif.m_rawdata[op] = (byte)b; - op++; - } - else - { - lastliteral = op; - m_tif.m_rawdata[op] = 0; - op++; - m_tif.m_rawdata[op] = (byte)b; - op++; - state = EncodingState.LITERAL; - } - stop = true; - break; - - case EncodingState.LITERAL_RUN: - /* literal followed by a run */ - /* - * Check to see if previous run should - * be converted to a literal, in which - * case we convert literal-run-literal - * to a single literal. - */ - int atemp = -1; - if (n == 1 && m_tif.m_rawdata[op - 2] == (byte)atemp && m_tif.m_rawdata[lastliteral] < 126) - { - m_tif.m_rawdata[lastliteral] += 2; - state = (m_tif.m_rawdata[lastliteral] == 127 ? EncodingState.BASE : EncodingState.LITERAL); - m_tif.m_rawdata[op - 2] = m_tif.m_rawdata[op - 1]; /* replicate */ - } - else - state = EncodingState.RUN; - continue; - } - } - } - - m_tif.m_rawcc += op - m_tif.m_rawcp; - m_tif.m_rawcp = op; - return true; - } - - /// - /// Encode a rectangular chunk of pixels. We break it up into row-sized pieces to insure - /// that encoded runs do not span rows. Otherwise, there can be problems with the decoder - /// if data is read, for example, by scanlines when it was encoded by strips. - /// - private bool PackBitsEncodeChunk(byte[] buffer, int offset, int count, short plane) - { - while (count > 0) - { - int chunk = m_rowsize; - if (count < chunk) - chunk = count; - - if (!PackBitsEncode(buffer, offset, chunk, plane)) - return false; - - offset += chunk; - count -= chunk; - } - - return true; - } - - private bool PackBitsDecode(byte[] buffer, int offset, int count, short plane) - { - int bp = m_tif.m_rawcp; - int cc = m_tif.m_rawcc; - while (cc > 0 && count > 0) - { - int n = m_tif.m_rawdata[bp]; - bp++; - cc--; - - // Watch out for compilers that don't sign extend chars... - if (n >= 128) - n -= 256; - - if (n < 0) - { - // replicate next byte (-n + 1) times - if (n == -128) - { - // nop - continue; - } - - n = -n + 1; - if (count < n) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "PackBitsDecode: discarding {0} bytes to avoid buffer overrun", - n - count); - - n = count; - } - count -= n; - int b = m_tif.m_rawdata[bp]; - bp++; - cc--; - while (n-- > 0) - { - buffer[offset] = (byte)b; - offset++; - } - } - else - { - // copy next (n + 1) bytes literally - if (count < n + 1) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "PackBitsDecode: discarding {0} bytes to avoid buffer overrun", - n - count + 1); - - n = count - 1; - } - - Buffer.BlockCopy(m_tif.m_rawdata, bp, buffer, offset, ++n); - offset += n; - count -= n; - bp += n; - cc -= n; - } - } - - m_tif.m_rawcp = bp; - m_tif.m_rawcc = cc; - if (count > 0) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "PackBitsDecode: Not enough data for scanline {0}", m_tif.m_row); - return false; - } - - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TagCompare.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TagCompare.cs deleted file mode 100644 index 720d1501..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TagCompare.cs +++ /dev/null @@ -1,37 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System.Collections; -using System.Diagnostics; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - internal class TagCompare : IComparer - { - int IComparer.Compare(object x, object y) - { - TiffFieldInfo ta = x as TiffFieldInfo; - TiffFieldInfo tb = y as TiffFieldInfo; - - Debug.Assert(ta != null); - Debug.Assert(tb != null); - - if (ta.Tag != tb.Tag) - return ((int)ta.Tag - (int)tb.Tag); - - return (ta.Type == TiffType.ANY) ? 0 : ((int)tb.Type - (int)ta.Type); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffCIELabToRGB.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffCIELabToRGB.cs deleted file mode 100644 index b563bdd0..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffCIELabToRGB.cs +++ /dev/null @@ -1,180 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* -* CIE L*a*b* to CIE XYZ and CIE XYZ to RGB conversion routines are taken -* from the VIPS library (http://www.vips.ecs.soton.ac.uk) with -* the permission of John Cupitt, the VIPS author. -*/ - -using System; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - /// - /// CIE Lab 1976->RGB support - /// - class TiffCIELabToRGB - { - public const int CIELABTORGB_TABLE_RANGE = 1500; - - /// - /// Size of conversion table - /// - private int range; - - private float rstep; - private float gstep; - private float bstep; - - // Reference white point - private float X0; - private float Y0; - private float Z0; - - private TiffDisplay display; - - /// - /// Conversion of Yr to r - /// - private float[] Yr2r = new float[CIELABTORGB_TABLE_RANGE + 1]; - - /// - /// Conversion of Yg to g - /// - private float[] Yg2g = new float[CIELABTORGB_TABLE_RANGE + 1]; - - /// - /// Conversion of Yb to b - /// - private float[] Yb2b = new float[CIELABTORGB_TABLE_RANGE + 1]; - - /* - * Allocate conversion state structures and make look_up tables for - * the Yr,Yb,Yg <=> r,g,b conversions. - */ - public void Init(TiffDisplay refDisplay, float[] refWhite) - { - range = CIELABTORGB_TABLE_RANGE; - - display = refDisplay; - - /* Red */ - double gamma = 1.0 / display.d_gammaR; - rstep = (display.d_YCR - display.d_Y0R) / range; - for (int i = 0; i <= range; i++) - { - Yr2r[i] = display.d_Vrwr * ((float)Math.Pow((double)i / range, gamma)); - } - - /* Green */ - gamma = 1.0 / display.d_gammaG; - gstep = (display.d_YCR - display.d_Y0R) / range; - for (int i = 0; i <= range; i++) - { - Yg2g[i] = display.d_Vrwg * ((float)Math.Pow((double)i / range, gamma)); - } - - /* Blue */ - gamma = 1.0 / display.d_gammaB; - bstep = (display.d_YCR - display.d_Y0R) / range; - for (int i = 0; i <= range; i++) - { - Yb2b[i] = display.d_Vrwb * ((float)Math.Pow((double)i / range, gamma)); - } - - /* Init reference white point */ - X0 = refWhite[0]; - Y0 = refWhite[1]; - Z0 = refWhite[2]; - } - - /* - * Convert color value from the CIE L*a*b* 1976 space to CIE XYZ. - */ - public void CIELabToXYZ(int l, int a, int b, out float X, out float Y, out float Z) - { - float L = (float)l * 100.0F / 255.0F; - float cby; - - if (L < 8.856F) - { - Y = (L * Y0) / 903.292F; - cby = 7.787F * (Y / Y0) + 16.0F / 116.0F; - } - else - { - cby = (L + 16.0F) / 116.0F; - Y = Y0 * cby * cby * cby; - } - - float tmp = (float)a / 500.0F + cby; - if (tmp < 0.2069F) - X = X0 * (tmp - 0.13793F) / 7.787F; - else - X = X0 * tmp * tmp * tmp; - - tmp = cby - (float)b / 200.0F; - if (tmp < 0.2069F) - Z = Z0 * (tmp - 0.13793F) / 7.787F; - else - Z = Z0 * tmp * tmp * tmp; - } - - /* - * Convert color value from the XYZ space to RGB. - */ - public void XYZToRGB(float X, float Y, float Z, out int r, out int g, out int b) - { - /* Multiply through the matrix to get luminosity values. */ - float Yr = display.d_mat[0][0] * X + display.d_mat[0][1] * Y + display.d_mat[0][2] * Z; - float Yg = display.d_mat[1][0] * X + display.d_mat[1][1] * Y + display.d_mat[1][2] * Z; - float Yb = display.d_mat[2][0] * X + display.d_mat[2][1] * Y + display.d_mat[2][2] * Z; - - /* Clip input */ - Yr = Math.Max(Yr, display.d_Y0R); - Yg = Math.Max(Yg, display.d_Y0G); - Yb = Math.Max(Yb, display.d_Y0B); - - /* Avoid overflow in case of wrong input values */ - Yr = Math.Min(Yr, display.d_YCR); - Yg = Math.Min(Yg, display.d_YCG); - Yb = Math.Min(Yb, display.d_YCB); - - /* Turn luminosity to color value. */ - int i = (int)((Yr - display.d_Y0R) / rstep); - i = Math.Min(range, i); - r = rInt(Yr2r[i]); - - i = (int)((Yg - display.d_Y0G) / gstep); - i = Math.Min(range, i); - g = rInt(Yg2g[i]); - - i = (int)((Yb - display.d_Y0B) / bstep); - i = Math.Min(range, i); - b = rInt(Yb2b[i]); - - /* Clip output. */ - r = Math.Min(r, display.d_Vrwr); - g = Math.Min(g, display.d_Vrwg); - b = Math.Min(b, display.d_Vrwb); - } - - private static int rInt(float R) - { - return (int)(R > 0 ? (R + 0.5) : (R - 0.5)); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffDirEntry.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffDirEntry.cs deleted file mode 100644 index 9cb7385d..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffDirEntry.cs +++ /dev/null @@ -1,54 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System.Globalization; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - /// - /// TIFF Image File Directories are comprised of a table of field - /// descriptors of the form shown below. The table is sorted in - /// ascending order by tag. The values associated with each entry are - /// disjoint and may appear anywhere in the file (so long as they are - /// placed on a word boundary). - /// - /// If the value is 4 bytes or less, then it is placed in the offset - /// field to save space. If the value is less than 4 bytes, it is - /// left-justified in the offset field. - /// - class TiffDirEntry - { - public const int SizeInBytes = 12; - - public TiffTag tdir_tag; - public TiffType tdir_type; - - /// - /// number of items; length in spec - /// - public int tdir_count; - - /// - /// byte offset to field data - /// - public uint tdir_offset; - - public new string ToString() - { - return tdir_tag.ToString() + ", " + tdir_type.ToString() + " " + - tdir_offset.ToString(CultureInfo.InvariantCulture); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffDirectory.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffDirectory.cs deleted file mode 100644 index b407c041..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffDirectory.cs +++ /dev/null @@ -1,114 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic.Internal -{ - /// - /// Internal format of a TIFF directory entry. - /// - class TiffDirectory - { - /// - /// bit vector of fields that are set - /// - public int[] td_fieldsset = new int[FieldBit.SetLongs]; - - public int td_imagewidth; - public int td_imagelength; - public int td_imagedepth; - public int td_tilewidth; - public int td_tilelength; - public int td_tiledepth; - public FileType td_subfiletype; - public short td_bitspersample; - public SampleFormat td_sampleformat; - public Compression td_compression; - public Photometric td_photometric; - public Threshold td_threshholding; - public FillOrder td_fillorder; - public Orientation td_orientation; - public short td_samplesperpixel; - public int td_rowsperstrip; - public short td_minsamplevalue; - public short td_maxsamplevalue; - public double td_sminsamplevalue; - public double td_smaxsamplevalue; - public float td_xresolution; - public float td_yresolution; - public ResUnit td_resolutionunit; - public PlanarConfig td_planarconfig; - public float td_xposition; - public float td_yposition; - public short[] td_pagenumber = new short[2]; - public short[][] td_colormap = { null, null, null }; - public short[] td_halftonehints = new short[2]; - public short td_extrasamples; - public ExtraSample[] td_sampleinfo; - public int td_stripsperimage; - - /// - /// size of offset and bytecount arrays - /// - public int td_nstrips; - public uint[] td_stripoffset; - public uint[] td_stripbytecount; - - /// - /// is the bytecount array sorted ascending? - /// - public bool td_stripbytecountsorted; - - public short td_nsubifd; - public int[] td_subifd; - - // YCbCr parameters - public short[] td_ycbcrsubsampling = new short[2]; - public YCbCrPosition td_ycbcrpositioning; - - // Colorimetry parameters - public float[] td_refblackwhite; - public short[][] td_transferfunction = { null, null, null }; - - // CMYK parameters - public int td_inknameslen; - public string td_inknames; - - public int td_customValueCount; - public TiffTagValue[] td_customValues; - - public TiffDirectory() - { - td_subfiletype = 0; - td_compression = 0; - td_photometric = 0; - td_planarconfig = 0; - - td_fillorder = FillOrder.MSB2LSB; - td_bitspersample = 1; - td_threshholding = Threshold.BILEVEL; - td_orientation = Orientation.TOPLEFT; - td_samplesperpixel = 1; - td_rowsperstrip = -1; - td_tiledepth = 1; - td_stripbytecountsorted = true; // Our own arrays always sorted. - td_resolutionunit = ResUnit.INCH; - td_sampleformat = SampleFormat.UINT; - td_imagedepth = 1; - td_ycbcrsubsampling[0] = 2; - td_ycbcrsubsampling[1] = 2; - td_ycbcrpositioning = YCbCrPosition.CENTERED; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffDisplay.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffDisplay.cs deleted file mode 100644 index aa362c8a..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffDisplay.cs +++ /dev/null @@ -1,72 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic.Internal -{ - /// - /// Structure for holding information about a display device. - /// - class TiffDisplay - { - /// - /// XYZ -> luminance matrix - /// - internal float[][] d_mat; - - // Light o/p for reference white - internal float d_YCR; - internal float d_YCG; - internal float d_YCB; - - // Pixel values for ref. white - internal int d_Vrwr; - internal int d_Vrwg; - internal int d_Vrwb; - - // Residual light for black pixel - internal float d_Y0R; - internal float d_Y0G; - internal float d_Y0B; - - // Gamma values for the three guns - internal float d_gammaR; - internal float d_gammaG; - internal float d_gammaB; - - public TiffDisplay() - { - } - - public TiffDisplay(float[] mat0, float[] mat1, float[] mat2, - float YCR, float YCG, float YCB, int Vrwr, int Vrwg, - int Vrwb, float Y0R, float Y0G, float Y0B, - float gammaR, float gammaG, float gammaB) - { - d_mat = new float[3][] { mat0, mat1, mat2 }; - d_YCR = YCR; - d_YCG = YCG; - d_YCB = YCB; - d_Vrwr = Vrwr; - d_Vrwg = Vrwg; - d_Vrwb = Vrwb; - d_Y0R = Y0R; - d_Y0G = Y0G; - d_Y0B = Y0B; - d_gammaR = gammaR; - d_gammaG = gammaG; - d_gammaB = gammaB; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffFlags.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffFlags.cs deleted file mode 100644 index dbf9e944..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffFlags.cs +++ /dev/null @@ -1,97 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -using System; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - [Flags] - enum TiffFlags - { - /// - /// Use MSB2LSB (most significant -> least) fill order - /// - MSB2LSB = 1, - - /// - /// Use LSB2MSB (least significant -> most) fill order - /// - LSB2MSB = 2, - - /// - /// natural bit fill order for machine - /// - FILLORDER = 0x0003, - - /// - /// current directory must be written - /// - DIRTYDIRECT = 0x0008, - - /// - /// data buffers setup - /// - BUFFERSETUP = 0x0010, - - /// - /// encoder/decoder setup done - /// - CODERSETUP = 0x0020, - - /// - /// written 1+ scanlines to file - /// - BEENWRITING = 0x0040, - - /// - /// byte swap file information - /// - SWAB = 0x0080, - - /// - /// inhibit bit reversal logic - /// - NOBITREV = 0x0100, - - /// - /// my raw data buffer; free on close - /// - MYBUFFER = 0x0200, - - /// - /// file is tile, not strip- based - /// - ISTILED = 0x0400, - - /// - /// need call to postencode routine - /// - POSTENCODE = 0x1000, - - /// - /// currently writing a subifd - /// - INSUBIFD = 0x2000, - - /// - /// library is doing data up-sampling - /// - UPSAMPLED = 0x4000, - - /// - /// enable strip chopping support - /// - STRIPCHOP = 0x8000, - - /// - /// read header only, do not process the first directory - /// - HEADERONLY = 0x10000, - - /// - /// skip reading of raw uncompressed image data - /// - NOREADRAW = 0x20000, - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffHeader.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffHeader.cs deleted file mode 100644 index 7509ba09..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffHeader.cs +++ /dev/null @@ -1,41 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic.Internal -{ - struct TiffHeader - { - public const int TIFF_MAGIC_SIZE = 2; - public const int TIFF_VERSION_SIZE = 2; - public const int TIFF_DIROFFSET_SIZE = 4; - - public const int SizeInBytes = TIFF_MAGIC_SIZE + TIFF_VERSION_SIZE + TIFF_DIROFFSET_SIZE; - - /// - /// magic number (defines byte order) - /// - public short tiff_magic; - - /// - /// TIFF version number - /// - public short tiff_version; - - /// - /// byte offset to first directory - /// - public uint tiff_diroff; - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffTagValue.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffTagValue.cs deleted file mode 100644 index 7f5c59d5..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffTagValue.cs +++ /dev/null @@ -1,24 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic.Internal -{ - struct TiffTagValue - { - public TiffFieldInfo info; - public int count; - public byte[] value; - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffYCbCrToRGB.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffYCbCrToRGB.cs deleted file mode 100644 index 825e5e47..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/TiffYCbCrToRGB.cs +++ /dev/null @@ -1,149 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System; - -namespace BitMiracle.LibTiff.Classic.Internal -{ - /// - /// Convert color value from the YCbCr space to CIE XYZ. - /// The colorspace conversion algorithm comes from the IJG v5a code; - /// see below for more information on how it works. - /// - class TiffYCbCrToRGB - { - private const int clamptabOffset = 256; - private const int SHIFT = 16; - private const int ONE_HALF = 1 << (SHIFT - 1); - - /// - /// range clamping table - /// - private byte[] clamptab; - - private int[] Cr_r_tab; - private int[] Cb_b_tab; - private int[] Cr_g_tab; - private int[] Cb_g_tab; - private int[] Y_tab; - - public TiffYCbCrToRGB() - { - clamptab = new byte[4 * 256]; - Cr_r_tab = new int[256]; - Cb_b_tab = new int[256]; - Cr_g_tab = new int[256]; - Cb_g_tab = new int[256]; - Y_tab = new int[256]; - } - - /* - * Initialize the YCbCr->RGB conversion tables. The conversion - * is done according to the 6.0 spec: - * - * R = Y + Cr * (2 - 2 * LumaRed) - * B = Y + Cb * (2 - 2 * LumaBlue) - * G = Y - * - LumaBlue * Cb * (2 - 2 * LumaBlue) / LumaGreen - * - LumaRed * Cr * (2 - 2 * LumaRed) / LumaGreen - * - * To avoid floating point arithmetic the fractional constants that - * come out of the equations are represented as fixed point values - * in the range 0...2^16. We also eliminate multiplications by - * pre-calculating possible values indexed by Cb and Cr (this code - * assumes conversion is being done for 8-bit samples). - */ - public void Init(float[] luma, float[] refBlackWhite) - { - Array.Clear(clamptab, 0, 256); /* v < 0 => 0 */ - - for (int i = 0; i < 256; i++) - clamptab[clamptabOffset + i] = (byte)i; - - int start = clamptabOffset + 256; - int stop = start + 2 * 256; - - for (int i = start; i < stop; i++) - clamptab[i] = 255; /* v > 255 => 255 */ - - float LumaRed = luma[0]; - float LumaGreen = luma[1]; - float LumaBlue = luma[2]; - - float f1 = 2 - 2 * LumaRed; - int D1 = fix(f1); - - float f2 = LumaRed * f1 / LumaGreen; - int D2 = -fix(f2); - - float f3 = 2 - 2 * LumaBlue; - int D3 = fix(f3); - - float f4 = LumaBlue * f3 / LumaGreen; - int D4 = -fix(f4); - - /* - * i is the actual input pixel value in the range 0..255 - * Cb and Cr values are in the range -128..127 (actually - * they are in a range defined by the ReferenceBlackWhite - * tag) so there is some range shifting to do here when - * constructing tables indexed by the raw pixel data. - */ - for (int i = 0, x = -128; i < 256; i++, x++) - { - int Cr = code2V(x, refBlackWhite[4] - 128.0F, refBlackWhite[5] - 128.0F, 127); - int Cb = code2V(x, refBlackWhite[2] - 128.0F, refBlackWhite[3] - 128.0F, 127); - - Cr_r_tab[i] = (D1 * Cr + ONE_HALF) >> SHIFT; - Cb_b_tab[i] = (D3 * Cb + ONE_HALF) >> SHIFT; - Cr_g_tab[i] = D2 * Cr; - Cb_g_tab[i] = D4 * Cb + ONE_HALF; - Y_tab[i] = code2V(x + 128, refBlackWhite[0], refBlackWhite[1], 255); - } - } - - public void YCbCrtoRGB(int Y, int Cb, int Cr, out int r, out int g, out int b) - { - /* XXX: Only 8-bit YCbCr input supported for now */ - Y = hiClamp(Y, 255); - Cb = clamp(Cb, 0, 255); - Cr = clamp(Cr, 0, 255); - - r = clamptab[clamptabOffset + Y_tab[Y] + Cr_r_tab[Cr]]; - g = clamptab[clamptabOffset + Y_tab[Y] + ((Cb_g_tab[Cb] + Cr_g_tab[Cr]) >> SHIFT)]; - b = clamptab[clamptabOffset + Y_tab[Y] + Cb_b_tab[Cb]]; - } - - private static int fix(float x) - { - return (int)(x * (1L << SHIFT) + 0.5); - } - - private static int code2V(int c, float RB, float RW, float CR) - { - return (int)(((c - (int)RB) * CR) / ((int)(RW - RB) != 0 ? (RW - RB) : 1.0f)); - } - - private static int clamp(int f, int min, int max) - { - return (f < min ? min : f > max ? max : f); - } - - private static int hiClamp(int f, int max) - { - return (f > max ? max : f); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Aux.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Aux.cs deleted file mode 100644 index 36701652..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Aux.cs +++ /dev/null @@ -1,126 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Auxiliary Support Routines. - */ - -using System; -using System.Globalization; -using System.IO; - -using BitMiracle.LibTiff.Classic.Internal; - -namespace BitMiracle.LibTiff.Classic -{ -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff - { - private static bool defaultTransferFunction(TiffDirectory td) - { - short[][] tf = td.td_transferfunction; - tf[0] = null; - tf[1] = null; - tf[2] = null; - - if (td.td_bitspersample >= sizeof(int) * 8 - 2) - return false; - - int n = 1 << td.td_bitspersample; - tf[0] = new short [n]; - tf[0][0] = 0; - for (int i = 1; i < n; i++) - { - double t = (double)i / ((double)n - 1.0); - tf[0][i] = (short)Math.Floor(65535.0 * Math.Pow(t, 2.2) + 0.5); - } - - if (td.td_samplesperpixel - td.td_extrasamples > 1) - { - tf[1] = new short [n]; - Buffer.BlockCopy(tf[0], 0, tf[1], 0, tf[0].Length * sizeof(short)); - - tf[2] = new short [n]; - Buffer.BlockCopy(tf[0], 0, tf[2], 0, tf[0].Length * sizeof(short)); - } - - return true; - } - - private static void defaultRefBlackWhite(TiffDirectory td) - { - td.td_refblackwhite = new float[6]; - if (td.td_photometric == Photometric.YCBCR) - { - // YCbCr (Class Y) images must have the ReferenceBlackWhite tag set. Fix the - // broken images, which lacks that tag. - td.td_refblackwhite[0] = 0.0F; - td.td_refblackwhite[1] = td.td_refblackwhite[3] = td.td_refblackwhite[5] = 255.0F; - td.td_refblackwhite[2] = td.td_refblackwhite[4] = 128.0F; - } - else - { - // Assume RGB (Class R) - for (int i = 0; i < 3; i++) - { - td.td_refblackwhite[2 * i + 0] = 0; - td.td_refblackwhite[2 * i + 1] = (float)((1L << td.td_bitspersample) - 1L); - } - } - } - - internal static int readInt(byte[] buffer, int offset) - { - int value = buffer[offset++] & 0xFF; - value += (buffer[offset++] & 0xFF) << 8; - value += (buffer[offset++] & 0xFF) << 16; - value += buffer[offset++] << 24; - return value; - } - - internal static void writeInt(int value, byte[] buffer, int offset) - { - buffer[offset++] = (byte)value; - buffer[offset++] = (byte)(value >> 8); - buffer[offset++] = (byte)(value >> 16); - buffer[offset++] = (byte)(value >> 24); - } - - internal static short readShort(byte[] buffer, int offset) - { - short value = (short)(buffer[offset] & 0xFF); - value += (short)((buffer[offset + 1] & 0xFF) << 8); - return value; - } - - internal static void fprintf(Stream fd, string format, params object[] list) - { - string s = string.Format(CultureInfo.InvariantCulture, format, list); - byte[] bytes = Latin1Encoding.GetBytes(s); - fd.Write(bytes, 0, bytes.Length); - } - - private static string encodeOctalString(byte value) - { - //convert to int, for cleaner syntax below. - int x = value; - - //return octal encoding \ddd of the character value. - return string.Format(CultureInfo.InvariantCulture, @"\{0}{1}{2}", (x >> 6) & 7, (x >> 3) & 7, x & 7); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Codec.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Codec.cs deleted file mode 100644 index 0230f951..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Codec.cs +++ /dev/null @@ -1,61 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Builtin Compression Scheme Configuration Support. - */ - -using BitMiracle.LibTiff.Classic.Internal; - -namespace BitMiracle.LibTiff.Classic -{ -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff - { - /// - /// Compression schemes statically built into the library. - /// - private void setupBuiltInCodecs() - { - // change initial syntax of m_builtInCodecs, maintains easier. - // San Chen - - m_builtInCodecs = new TiffCodec[] - { - new TiffCodec(this, (Compression)(-1), "Not configured"), - new DumpModeCodec(this, Compression.NONE, "None"), - new LZWCodec(this, Compression.LZW, "LZW"), - new PackBitsCodec(this, Compression.PACKBITS, "PackBits"), - new TiffCodec(this, Compression.THUNDERSCAN, "ThunderScan"), - new TiffCodec(this, Compression.NEXT, "NeXT"), - new JpegCodec(this, Compression.JPEG, "JPEG"), - new OJpegCodec(this, Compression.OJPEG, "Old-style JPEG"), - new CCITTCodec(this, Compression.CCITTRLE, "CCITT RLE"), - new CCITTCodec(this, Compression.CCITTRLEW, "CCITT RLE/W"), - new CCITTCodec(this, Compression.CCITTFAX3, "CCITT Group 3"), - new CCITTCodec(this, Compression.CCITTFAX4, "CCITT Group 4"), - new TiffCodec(this, Compression.JBIG, "ISO JBIG"), - new DeflateCodec(this, Compression.DEFLATE, "Deflate"), - new DeflateCodec(this, Compression.ADOBE_DEFLATE, "AdobeDeflate"), - new TiffCodec(this, Compression.PIXARLOG, "PixarLog"), - new TiffCodec(this, Compression.SGILOG, "SGILog"), - new TiffCodec(this, Compression.SGILOG24, "SGILog24"), - null, - }; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Dir.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Dir.cs deleted file mode 100644 index 5f34bcdb..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Dir.cs +++ /dev/null @@ -1,203 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Directory Tag Get & Set Routines. - * (and also some miscellaneous stuff) - */ - -using System.IO; - -using BitMiracle.LibTiff.Classic.Internal; - -namespace BitMiracle.LibTiff.Classic -{ -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff - { - /* is tag value normal or pseudo */ - internal static bool isPseudoTag(TiffTag t) - { - return ((int)t > 0xffff); - } - - private bool isFillOrder(FillOrder o) - { - TiffFlags order = (TiffFlags)o; - return ((m_flags & order) == order); - } - - private static int BITn(int n) - { - return (1 << (n & 0x1f)); - } - - /* - * Return true / false according to whether or not - * it is permissible to set the tag's value. - * Note that we allow ImageLength to be changed - * so that we can append and extend to images. - * Any other tag may not be altered once writing - * has commenced, unless its value has no effect - * on the format of the data that is written. - */ - private bool okToChangeTag(TiffTag tag) - { - TiffFieldInfo fip = FindFieldInfo(tag, TiffType.ANY); - if (fip == null) - { - // unknown tag - ErrorExt(this, m_clientdata, "SetField", "{0}: Unknown {1}tag {2}", - m_name, isPseudoTag(tag) ? "pseudo-" : "", tag); - return false; - } - - if (tag != TiffTag.IMAGELENGTH && - (m_flags & TiffFlags.BEENWRITING) == TiffFlags.BEENWRITING && - !fip.OkToChange) - { - // Consult info table to see if tag can be changed after we've - // started writing. We only allow changes to those tags that - // don't / shouldn't affect the compression and / or format of - // the data. - ErrorExt(this, m_clientdata, "SetField", "{0}: Cannot modify tag \"{1}\" while writing", - m_name, fip.Name); - return false; - } - - return true; - } - - /* - * Setup a default directory structure. - */ - private void setupDefaultDirectory() - { - int tiffFieldInfoCount; - TiffFieldInfo[] tiffFieldInfo = getFieldInfo(out tiffFieldInfoCount); - setupFieldInfo(tiffFieldInfo, tiffFieldInfoCount); - - m_dir = new TiffDirectory(); - m_postDecodeMethod = PostDecodeMethodType.pdmNone; - m_foundfield = null; - - m_tagmethods = m_defaultTagMethods; - - /* - * Give client code a chance to install their own - * tag extensions & methods, prior to compression overloads. - */ - if (m_extender != null) - m_extender(this); - - SetField(TiffTag.COMPRESSION, Compression.NONE); - - /* - * NB: The directory is marked dirty as a result of setting - * up the default compression scheme. However, this really - * isn't correct -- we want DIRTYDIRECT to be set only - * if the user does something. We could just do the setup - * by hand, but it seems better to use the normal mechanism - * (i.e. SetField). - */ - m_flags &= ~TiffFlags.DIRTYDIRECT; - - /* - * we clear the ISTILED flag when setting up a new directory. - * Should we also be clearing stuff like INSUBIFD? - */ - m_flags &= ~TiffFlags.ISTILED; - - /* - * Clear other directory-specific fields. - */ - m_tilesize = -1; - m_scanlinesize = -1; - } - - private bool advanceDirectory(ref uint nextdir, out long off) - { - off = 0; - - const string module = "advanceDirectory"; - short dircount; - - if (!seekOK(nextdir) || !readShortOK(out dircount)) - { - ErrorExt(this, m_clientdata, module, "{0}: Error fetching directory count", m_name); - return false; - } - - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabShort(ref dircount); - - off = seekFile(dircount * TiffDirEntry.SizeInBytes, SeekOrigin.Current); - - if (!readUIntOK(out nextdir)) - { - ErrorExt(this, m_clientdata, module, "{0}: Error fetching directory link", m_name); - return false; - } - - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabUInt(ref nextdir); - - return true; - } - - internal static void setString(out string cpp, string cp) - { - cpp = cp; - } - - internal static void setShortArray(out short[] wpp, short[] wp, int n) - { - wpp = new short[n]; - for (int i = 0; i < n; i++) - wpp[i] = wp[i]; - } - - internal static void setLongArray(out int[] lpp, int[] lp, int n) - { - lpp = new int[n]; - for (int i = 0; i < n; i++) - lpp[i] = lp[i]; - } - - internal static void setFloatArray(out float[] fpp, float[] fp, int n) - { - fpp = new float[n]; - for (int i = 0; i < n; i++) - fpp[i] = fp[i]; - } - - internal bool fieldSet(int field) - { - return ((m_dir.td_fieldsset[field / 32] & BITn(field)) != 0); - } - - internal void setFieldBit(int field) - { - m_dir.td_fieldsset[field / 32] |= BITn(field); - } - - internal void clearFieldBit(int field) - { - m_dir.td_fieldsset[field / 32] &= ~BITn(field); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_DirInfo.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_DirInfo.cs deleted file mode 100644 index 765545ec..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_DirInfo.cs +++ /dev/null @@ -1,373 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Core Directory Tag Support. - */ - -using System.Globalization; - -namespace BitMiracle.LibTiff.Classic -{ -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff - { - /// - /// NB: THIS ARRAY IS ASSUMED TO BE SORTED BY TAG. - /// If a tag can have both LONG and SHORT types then the LONG must - /// be placed before the SHORT for writing to work properly. - /// - /// NOTE: The second field (field_readcount) and third field - /// (field_writecount) sometimes use the values - /// TiffFieldInfo.Variable (-1), TiffFieldInfo.Variable2 (-3) - /// and TiffFieldInfo.Spp (-2). These values should be used but - /// would throw off the formatting of the code, so please - /// interpret the -1, -2 and -3 values accordingly. - /// - private static readonly TiffFieldInfo[] tiffFieldInfo = - { - new TiffFieldInfo(TiffTag.SUBFILETYPE, 1, 1, TiffType.LONG, FieldBit.SubFileType, true, false, "SubfileType"), - /* XXX SHORT for compatibility w/ old versions of the library */ - new TiffFieldInfo(TiffTag.SUBFILETYPE, 1, 1, TiffType.SHORT, FieldBit.SubFileType, true, false, "SubfileType"), - new TiffFieldInfo(TiffTag.OSUBFILETYPE, 1, 1, TiffType.SHORT, FieldBit.SubFileType, true, false, "OldSubfileType"), - new TiffFieldInfo(TiffTag.IMAGEWIDTH, 1, 1, TiffType.LONG, FieldBit.ImageDimensions, false, false, "ImageWidth"), - new TiffFieldInfo(TiffTag.IMAGEWIDTH, 1, 1, TiffType.SHORT, FieldBit.ImageDimensions, false, false, "ImageWidth"), - new TiffFieldInfo(TiffTag.IMAGELENGTH, 1, 1, TiffType.LONG, FieldBit.ImageDimensions, true, false, "ImageLength"), - new TiffFieldInfo(TiffTag.IMAGELENGTH, 1, 1, TiffType.SHORT, FieldBit.ImageDimensions, true, false, "ImageLength"), - new TiffFieldInfo(TiffTag.BITSPERSAMPLE, -1, -1, TiffType.SHORT, FieldBit.BitsPerSample, false, false, "BitsPerSample"), - /* XXX LONG for compatibility with some broken TIFF writers */ - new TiffFieldInfo(TiffTag.BITSPERSAMPLE, -1, -1, TiffType.LONG, FieldBit.BitsPerSample, false, false, "BitsPerSample"), - new TiffFieldInfo(TiffTag.COMPRESSION, -1, 1, TiffType.SHORT, FieldBit.Compression, false, false, "Compression"), - /* XXX LONG for compatibility with some broken TIFF writers */ - new TiffFieldInfo(TiffTag.COMPRESSION, -1, 1, TiffType.LONG, FieldBit.Compression, false, false, "Compression"), - new TiffFieldInfo(TiffTag.PHOTOMETRIC, 1, 1, TiffType.SHORT, FieldBit.Photometric, false, false, "PhotometricInterpretation"), - /* XXX LONG for compatibility with some broken TIFF writers */ - new TiffFieldInfo(TiffTag.PHOTOMETRIC, 1, 1, TiffType.LONG, FieldBit.Photometric, false, false, "PhotometricInterpretation"), - new TiffFieldInfo(TiffTag.THRESHHOLDING, 1, 1, TiffType.SHORT, FieldBit.Thresholding, true, false, "Threshholding"), - new TiffFieldInfo(TiffTag.CELLWIDTH, 1, 1, TiffType.SHORT, FieldBit.Ignore, true, false, "CellWidth"), - new TiffFieldInfo(TiffTag.CELLLENGTH, 1, 1, TiffType.SHORT, FieldBit.Ignore, true, false, "CellLength"), - new TiffFieldInfo(TiffTag.FILLORDER, 1, 1, TiffType.SHORT, FieldBit.FillOrder, false, false, "FillOrder"), - new TiffFieldInfo(TiffTag.DOCUMENTNAME, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "DocumentName"), - new TiffFieldInfo(TiffTag.IMAGEDESCRIPTION, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "ImageDescription"), - new TiffFieldInfo(TiffTag.MAKE, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "Make"), - new TiffFieldInfo(TiffTag.MODEL, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "Model"), - new TiffFieldInfo(TiffTag.STRIPOFFSETS, -1, -1, TiffType.LONG, FieldBit.StripOffsets, false, false, "StripOffsets"), - new TiffFieldInfo(TiffTag.STRIPOFFSETS, -1, -1, TiffType.SHORT, FieldBit.StripOffsets, false, false, "StripOffsets"), - new TiffFieldInfo(TiffTag.ORIENTATION, 1, 1, TiffType.SHORT, FieldBit.Orientation, false, false, "Orientation"), - new TiffFieldInfo(TiffTag.SAMPLESPERPIXEL, 1, 1, TiffType.SHORT, FieldBit.SamplesPerPixel, false, false, "SamplesPerPixel"), - new TiffFieldInfo(TiffTag.ROWSPERSTRIP, 1, 1, TiffType.LONG, FieldBit.RowsPerStrip, false, false, "RowsPerStrip"), - new TiffFieldInfo(TiffTag.ROWSPERSTRIP, 1, 1, TiffType.SHORT, FieldBit.RowsPerStrip, false, false, "RowsPerStrip"), - new TiffFieldInfo(TiffTag.STRIPBYTECOUNTS, -1, -1, TiffType.LONG, FieldBit.StripByteCounts, false, false, "StripByteCounts"), - new TiffFieldInfo(TiffTag.STRIPBYTECOUNTS, -1, -1, TiffType.SHORT, FieldBit.StripByteCounts, false, false, "StripByteCounts"), - new TiffFieldInfo(TiffTag.MINSAMPLEVALUE, -2, -1, TiffType.SHORT, FieldBit.MinSampleValue, true, false, "MinSampleValue"), - new TiffFieldInfo(TiffTag.MAXSAMPLEVALUE, -2, -1, TiffType.SHORT, FieldBit.MaxSampleValue, true, false, "MaxSampleValue"), - new TiffFieldInfo(TiffTag.XRESOLUTION, 1, 1, TiffType.RATIONAL, FieldBit.Resolution, true, false, "XResolution"), - new TiffFieldInfo(TiffTag.YRESOLUTION, 1, 1, TiffType.RATIONAL, FieldBit.Resolution, true, false, "YResolution"), - new TiffFieldInfo(TiffTag.PLANARCONFIG, 1, 1, TiffType.SHORT, FieldBit.PlanarConfig, false, false, "PlanarConfiguration"), - new TiffFieldInfo(TiffTag.PAGENAME, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "PageName"), - new TiffFieldInfo(TiffTag.XPOSITION, 1, 1, TiffType.RATIONAL, FieldBit.Position, true, false, "XPosition"), - new TiffFieldInfo(TiffTag.YPOSITION, 1, 1, TiffType.RATIONAL, FieldBit.Position, true, false, "YPosition"), - new TiffFieldInfo(TiffTag.FREEOFFSETS, -1, -1, TiffType.LONG, FieldBit.Ignore, false, false, "FreeOffsets"), - new TiffFieldInfo(TiffTag.FREEBYTECOUNTS, -1, -1, TiffType.LONG, FieldBit.Ignore, false, false, "FreeByteCounts"), - new TiffFieldInfo(TiffTag.GRAYRESPONSEUNIT, 1, 1, TiffType.SHORT, FieldBit.Ignore, true, false, "GrayResponseUnit"), - new TiffFieldInfo(TiffTag.GRAYRESPONSECURVE, -1, -1, TiffType.SHORT, FieldBit.Ignore, true, false, "GrayResponseCurve"), - new TiffFieldInfo(TiffTag.RESOLUTIONUNIT, 1, 1, TiffType.SHORT, FieldBit.ResolutionUnit, true, false, "ResolutionUnit"), - new TiffFieldInfo(TiffTag.PAGENUMBER, 2, 2, TiffType.SHORT, FieldBit.PageNumber, true, false, "PageNumber"), - new TiffFieldInfo(TiffTag.COLORRESPONSEUNIT, 1, 1, TiffType.SHORT, FieldBit.Ignore, true, false, "ColorResponseUnit"), - new TiffFieldInfo(TiffTag.TRANSFERFUNCTION, -1, -1, TiffType.SHORT, FieldBit.TransferFunction, true, false, "TransferFunction"), - new TiffFieldInfo(TiffTag.SOFTWARE, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "Software"), - new TiffFieldInfo(TiffTag.DATETIME, 20, 20, TiffType.ASCII, FieldBit.Custom, true, false, "DateTime"), - new TiffFieldInfo(TiffTag.ARTIST, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "Artist"), - new TiffFieldInfo(TiffTag.HOSTCOMPUTER, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "HostComputer"), - new TiffFieldInfo(TiffTag.WHITEPOINT, 2, 2, TiffType.RATIONAL, FieldBit.Custom, true, false, "WhitePoint"), - new TiffFieldInfo(TiffTag.PRIMARYCHROMATICITIES, 6, 6, TiffType.RATIONAL, FieldBit.Custom, true, false, "PrimaryChromaticities"), - new TiffFieldInfo(TiffTag.COLORMAP, -1, -1, TiffType.SHORT, FieldBit.ColorMap, true, false, "ColorMap"), - new TiffFieldInfo(TiffTag.HALFTONEHINTS, 2, 2, TiffType.SHORT, FieldBit.HalftoneHints, true, false, "HalftoneHints"), - new TiffFieldInfo(TiffTag.TILEWIDTH, 1, 1, TiffType.LONG, FieldBit.TileDimensions, false, false, "TileWidth"), - new TiffFieldInfo(TiffTag.TILEWIDTH, 1, 1, TiffType.SHORT, FieldBit.TileDimensions, false, false, "TileWidth"), - new TiffFieldInfo(TiffTag.TILELENGTH, 1, 1, TiffType.LONG, FieldBit.TileDimensions, false, false, "TileLength"), - new TiffFieldInfo(TiffTag.TILELENGTH, 1, 1, TiffType.SHORT, FieldBit.TileDimensions, false, false, "TileLength"), - new TiffFieldInfo(TiffTag.TILEOFFSETS, -1, 1, TiffType.LONG, FieldBit.StripOffsets, false, false, "TileOffsets"), - new TiffFieldInfo(TiffTag.TILEBYTECOUNTS, -1, 1, TiffType.LONG, FieldBit.StripByteCounts, false, false, "TileByteCounts"), - new TiffFieldInfo(TiffTag.TILEBYTECOUNTS, -1, 1, TiffType.SHORT, FieldBit.StripByteCounts, false, false, "TileByteCounts"), - new TiffFieldInfo(TiffTag.SUBIFD, -1, -1, TiffType.IFD, FieldBit.SubIFD, true, true, "SubIFD"), - new TiffFieldInfo(TiffTag.SUBIFD, -1, -1, TiffType.LONG, FieldBit.SubIFD, true, true, "SubIFD"), - new TiffFieldInfo(TiffTag.INKSET, 1, 1, TiffType.SHORT, FieldBit.Custom, false, false, "InkSet"), - new TiffFieldInfo(TiffTag.INKNAMES, -1, -1, TiffType.ASCII, FieldBit.InkNames, true, true, "InkNames"), - new TiffFieldInfo(TiffTag.NUMBEROFINKS, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "NumberOfInks"), - new TiffFieldInfo(TiffTag.DOTRANGE, 2, 2, TiffType.SHORT, FieldBit.Custom, false, false, "DotRange"), - new TiffFieldInfo(TiffTag.DOTRANGE, 2, 2, TiffType.BYTE, FieldBit.Custom, false, false, "DotRange"), - new TiffFieldInfo(TiffTag.TARGETPRINTER, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "TargetPrinter"), - new TiffFieldInfo(TiffTag.EXTRASAMPLES, -1, -1, TiffType.SHORT, FieldBit.ExtraSamples, false, true, "ExtraSamples"), - /* XXX for bogus Adobe Photoshop v2.5 files */ - new TiffFieldInfo(TiffTag.EXTRASAMPLES, -1, -1, TiffType.BYTE, FieldBit.ExtraSamples, false, true, "ExtraSamples"), - new TiffFieldInfo(TiffTag.SAMPLEFORMAT, -1, -1, TiffType.SHORT, FieldBit.SampleFormat, false, false, "SampleFormat"), - new TiffFieldInfo(TiffTag.SMINSAMPLEVALUE, -2, -1, TiffType.ANY, FieldBit.SMinSampleValue, true, false, "SMinSampleValue"), - new TiffFieldInfo(TiffTag.SMAXSAMPLEVALUE, -2, -1, TiffType.ANY, FieldBit.SMaxSampleValue, true, false, "SMaxSampleValue"), - new TiffFieldInfo(TiffTag.CLIPPATH, -1, -3, TiffType.BYTE, FieldBit.Custom, false, true, "ClipPath"), - new TiffFieldInfo(TiffTag.XCLIPPATHUNITS, 1, 1, TiffType.SLONG, FieldBit.Custom, false, false, "XClipPathUnits"), - new TiffFieldInfo(TiffTag.XCLIPPATHUNITS, 1, 1, TiffType.SSHORT, FieldBit.Custom, false, false, "XClipPathUnits"), - new TiffFieldInfo(TiffTag.XCLIPPATHUNITS, 1, 1, TiffType.SBYTE, FieldBit.Custom, false, false, "XClipPathUnits"), - new TiffFieldInfo(TiffTag.YCLIPPATHUNITS, 1, 1, TiffType.SLONG, FieldBit.Custom, false, false, "YClipPathUnits"), - new TiffFieldInfo(TiffTag.YCLIPPATHUNITS, 1, 1, TiffType.SSHORT, FieldBit.Custom, false, false, "YClipPathUnits"), - new TiffFieldInfo(TiffTag.YCLIPPATHUNITS, 1, 1, TiffType.SBYTE, FieldBit.Custom, false, false, "YClipPathUnits"), - new TiffFieldInfo(TiffTag.YCBCRCOEFFICIENTS, 3, 3, TiffType.RATIONAL, FieldBit.Custom, false, false, "YCbCrCoefficients"), - new TiffFieldInfo(TiffTag.YCBCRSUBSAMPLING, 2, 2, TiffType.SHORT, FieldBit.YCbCrSubsampling, false, false, "YCbCrSubsampling"), - new TiffFieldInfo(TiffTag.YCBCRPOSITIONING, 1, 1, TiffType.SHORT, FieldBit.YCbCrPositioning, false, false, "YCbCrPositioning"), - new TiffFieldInfo(TiffTag.REFERENCEBLACKWHITE, 6, 6, TiffType.RATIONAL, FieldBit.RefBlackWhite, true, false, "ReferenceBlackWhite"), - /* XXX temporarily accept LONG for backwards compatibility */ - new TiffFieldInfo(TiffTag.REFERENCEBLACKWHITE, 6, 6, TiffType.LONG, FieldBit.RefBlackWhite, true, false, "ReferenceBlackWhite"), - new TiffFieldInfo(TiffTag.XMLPACKET, -3, -3, TiffType.BYTE, FieldBit.Custom, false, true, "XMLPacket"), - /* begin SGI tags */ - new TiffFieldInfo(TiffTag.MATTEING, 1, 1, TiffType.SHORT, FieldBit.ExtraSamples, false, false, "Matteing"), - new TiffFieldInfo(TiffTag.DATATYPE, -2, -1, TiffType.SHORT, FieldBit.SampleFormat, false, false, "DataType"), - new TiffFieldInfo(TiffTag.IMAGEDEPTH, 1, 1, TiffType.LONG, FieldBit.ImageDepth, false, false, "ImageDepth"), - new TiffFieldInfo(TiffTag.IMAGEDEPTH, 1, 1, TiffType.SHORT, FieldBit.ImageDepth, false, false, "ImageDepth"), - new TiffFieldInfo(TiffTag.TILEDEPTH, 1, 1, TiffType.LONG, FieldBit.TileDepth, false, false, "TileDepth"), - new TiffFieldInfo(TiffTag.TILEDEPTH, 1, 1, TiffType.SHORT, FieldBit.TileDepth, false, false, "TileDepth"), - /* end SGI tags */ - /* begin Pixar tags */ - new TiffFieldInfo(TiffTag.PIXAR_IMAGEFULLWIDTH, 1, 1, TiffType.LONG, FieldBit.Custom, true, false, "ImageFullWidth"), - new TiffFieldInfo(TiffTag.PIXAR_IMAGEFULLLENGTH, 1, 1, TiffType.LONG, FieldBit.Custom, true, false, "ImageFullLength"), - new TiffFieldInfo(TiffTag.PIXAR_TEXTUREFORMAT, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "TextureFormat"), - new TiffFieldInfo(TiffTag.PIXAR_WRAPMODES, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "TextureWrapModes"), - new TiffFieldInfo(TiffTag.PIXAR_FOVCOT, 1, 1, TiffType.FLOAT, FieldBit.Custom, true, false, "FieldOfViewCotangent"), - new TiffFieldInfo(TiffTag.PIXAR_MATRIX_WORLDTOSCREEN, 16, 16, TiffType.FLOAT, FieldBit.Custom, true, false, "MatrixWorldToScreen"), - new TiffFieldInfo(TiffTag.PIXAR_MATRIX_WORLDTOCAMERA, 16, 16, TiffType.FLOAT, FieldBit.Custom, true, false, "MatrixWorldToCamera"), - new TiffFieldInfo(TiffTag.COPYRIGHT, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "Copyright"), - /* end Pixar tags */ - new TiffFieldInfo(TiffTag.RICHTIFFIPTC, -3, -3, TiffType.LONG, FieldBit.Custom, false, true, "RichTIFFIPTC"), - new TiffFieldInfo(TiffTag.PHOTOSHOP, -3, -3, TiffType.BYTE, FieldBit.Custom, false, true, "Photoshop"), - new TiffFieldInfo(TiffTag.EXIFIFD, 1, 1, TiffType.LONG, FieldBit.Custom, false, false, "EXIFIFDOffset"), - new TiffFieldInfo(TiffTag.ICCPROFILE, -3, -3, TiffType.UNDEFINED, FieldBit.Custom, false, true, "ICC Profile"), - new TiffFieldInfo(TiffTag.GPSIFD, 1, 1, TiffType.LONG, FieldBit.Custom, false, false, "GPSIFDOffset"), - new TiffFieldInfo(TiffTag.STONITS, 1, 1, TiffType.DOUBLE, FieldBit.Custom, false, false, "StoNits"), - new TiffFieldInfo(TiffTag.INTEROPERABILITYIFD, 1, 1, TiffType.LONG, FieldBit.Custom, false, false, "InteroperabilityIFDOffset"), - /* begin DNG tags */ - new TiffFieldInfo(TiffTag.DNGVERSION, 4, 4, TiffType.BYTE, FieldBit.Custom, false, false, "DNGVersion"), - new TiffFieldInfo(TiffTag.DNGBACKWARDVERSION, 4, 4, TiffType.BYTE, FieldBit.Custom, false, false, "DNGBackwardVersion"), - new TiffFieldInfo(TiffTag.UNIQUECAMERAMODEL, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "UniqueCameraModel"), - new TiffFieldInfo(TiffTag.LOCALIZEDCAMERAMODEL, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "LocalizedCameraModel"), - new TiffFieldInfo(TiffTag.LOCALIZEDCAMERAMODEL, -1, -1, TiffType.BYTE, FieldBit.Custom, true, true, "LocalizedCameraModel"), - new TiffFieldInfo(TiffTag.CFAPLANECOLOR, -1, -1, TiffType.BYTE, FieldBit.Custom, false, true, "CFAPlaneColor"), - new TiffFieldInfo(TiffTag.CFALAYOUT, 1, 1, TiffType.SHORT, FieldBit.Custom, false, false, "CFALayout"), - new TiffFieldInfo(TiffTag.LINEARIZATIONTABLE, -1, -1, TiffType.SHORT, FieldBit.Custom, false, true, "LinearizationTable"), - new TiffFieldInfo(TiffTag.BLACKLEVELREPEATDIM, 2, 2, TiffType.SHORT, FieldBit.Custom, false, false, "BlackLevelRepeatDim"), - new TiffFieldInfo(TiffTag.BLACKLEVEL, -1, -1, TiffType.LONG, FieldBit.Custom, false, true, "BlackLevel"), - new TiffFieldInfo(TiffTag.BLACKLEVEL, -1, -1, TiffType.SHORT, FieldBit.Custom, false, true, "BlackLevel"), - new TiffFieldInfo(TiffTag.BLACKLEVEL, -1, -1, TiffType.RATIONAL, FieldBit.Custom, false, true, "BlackLevel"), - new TiffFieldInfo(TiffTag.BLACKLEVELDELTAH, -1, -1, TiffType.SRATIONAL, FieldBit.Custom, false, true, "BlackLevelDeltaH"), - new TiffFieldInfo(TiffTag.BLACKLEVELDELTAV, -1, -1, TiffType.SRATIONAL, FieldBit.Custom, false, true, "BlackLevelDeltaV"), - new TiffFieldInfo(TiffTag.WHITELEVEL, -2, -2, TiffType.LONG, FieldBit.Custom, false, false, "WhiteLevel"), - new TiffFieldInfo(TiffTag.WHITELEVEL, -2, -2, TiffType.SHORT, FieldBit.Custom, false, false, "WhiteLevel"), - new TiffFieldInfo(TiffTag.DEFAULTSCALE, 2, 2, TiffType.RATIONAL, FieldBit.Custom, false, false, "DefaultScale"), - new TiffFieldInfo(TiffTag.BESTQUALITYSCALE, 1, 1, TiffType.RATIONAL, FieldBit.Custom, false, false, "BestQualityScale"), - new TiffFieldInfo(TiffTag.DEFAULTCROPORIGIN, 2, 2, TiffType.LONG, FieldBit.Custom, false, false, "DefaultCropOrigin"), - new TiffFieldInfo(TiffTag.DEFAULTCROPORIGIN, 2, 2, TiffType.SHORT, FieldBit.Custom, false, false, "DefaultCropOrigin"), - new TiffFieldInfo(TiffTag.DEFAULTCROPORIGIN, 2, 2, TiffType.RATIONAL, FieldBit.Custom, false, false, "DefaultCropOrigin"), - new TiffFieldInfo(TiffTag.DEFAULTCROPSIZE, 2, 2, TiffType.LONG, FieldBit.Custom, false, false, "DefaultCropSize"), - new TiffFieldInfo(TiffTag.DEFAULTCROPSIZE, 2, 2, TiffType.SHORT, FieldBit.Custom, false, false, "DefaultCropSize"), - new TiffFieldInfo(TiffTag.DEFAULTCROPSIZE, 2, 2, TiffType.RATIONAL, FieldBit.Custom, false, false, "DefaultCropSize"), - new TiffFieldInfo(TiffTag.COLORMATRIX1, -1, -1, TiffType.SRATIONAL, FieldBit.Custom, false, true, "ColorMatrix1"), - new TiffFieldInfo(TiffTag.COLORMATRIX2, -1, -1, TiffType.SRATIONAL, FieldBit.Custom, false, true, "ColorMatrix2"), - new TiffFieldInfo(TiffTag.CAMERACALIBRATION1, -1, -1, TiffType.SRATIONAL, FieldBit.Custom, false, true, "CameraCalibration1"), - new TiffFieldInfo(TiffTag.CAMERACALIBRATION2, -1, -1, TiffType.SRATIONAL, FieldBit.Custom, false, true, "CameraCalibration2"), - new TiffFieldInfo(TiffTag.REDUCTIONMATRIX1, -1, -1, TiffType.SRATIONAL, FieldBit.Custom, false, true, "ReductionMatrix1"), - new TiffFieldInfo(TiffTag.REDUCTIONMATRIX2, -1, -1, TiffType.SRATIONAL, FieldBit.Custom, false, true, "ReductionMatrix2"), - new TiffFieldInfo(TiffTag.ANALOGBALANCE, -1, -1, TiffType.RATIONAL, FieldBit.Custom, false, true, "AnalogBalance"), - new TiffFieldInfo(TiffTag.ASSHOTNEUTRAL, -1, -1, TiffType.SHORT, FieldBit.Custom, false, true, "AsShotNeutral"), - new TiffFieldInfo(TiffTag.ASSHOTNEUTRAL, -1, -1, TiffType.RATIONAL, FieldBit.Custom, false, true, "AsShotNeutral"), - new TiffFieldInfo(TiffTag.ASSHOTWHITEXY, 2, 2, TiffType.RATIONAL, FieldBit.Custom, false, false, "AsShotWhiteXY"), - new TiffFieldInfo(TiffTag.BASELINEEXPOSURE, 1, 1, TiffType.SRATIONAL, FieldBit.Custom, false, false, "BaselineExposure"), - new TiffFieldInfo(TiffTag.BASELINENOISE, 1, 1, TiffType.RATIONAL, FieldBit.Custom, false, false, "BaselineNoise"), - new TiffFieldInfo(TiffTag.BASELINESHARPNESS, 1, 1, TiffType.RATIONAL, FieldBit.Custom, false, false, "BaselineSharpness"), - new TiffFieldInfo(TiffTag.BAYERGREENSPLIT, 1, 1, TiffType.LONG, FieldBit.Custom, false, false, "BayerGreenSplit"), - new TiffFieldInfo(TiffTag.LINEARRESPONSELIMIT, 1, 1, TiffType.RATIONAL, FieldBit.Custom, false, false, "LinearResponseLimit"), - new TiffFieldInfo(TiffTag.CAMERASERIALNUMBER, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "CameraSerialNumber"), - new TiffFieldInfo(TiffTag.LENSINFO, 4, 4, TiffType.RATIONAL, FieldBit.Custom, false, false, "LensInfo"), - new TiffFieldInfo(TiffTag.CHROMABLURRADIUS, 1, 1, TiffType.RATIONAL, FieldBit.Custom, false, false, "ChromaBlurRadius"), - new TiffFieldInfo(TiffTag.ANTIALIASSTRENGTH, 1, 1, TiffType.RATIONAL, FieldBit.Custom, false, false, "AntiAliasStrength"), - new TiffFieldInfo(TiffTag.SHADOWSCALE, 1, 1, TiffType.RATIONAL, FieldBit.Custom, false, false, "ShadowScale"), - new TiffFieldInfo(TiffTag.DNGPRIVATEDATA, -1, -1, TiffType.BYTE, FieldBit.Custom, false, true, "DNGPrivateData"), - new TiffFieldInfo(TiffTag.MAKERNOTESAFETY, 1, 1, TiffType.SHORT, FieldBit.Custom, false, false, "MakerNoteSafety"), - new TiffFieldInfo(TiffTag.CALIBRATIONILLUMINANT1, 1, 1, TiffType.SHORT, FieldBit.Custom, false, false, "CalibrationIlluminant1"), - new TiffFieldInfo(TiffTag.CALIBRATIONILLUMINANT2, 1, 1, TiffType.SHORT, FieldBit.Custom, false, false, "CalibrationIlluminant2"), - new TiffFieldInfo(TiffTag.RAWDATAUNIQUEID, 16, 16, TiffType.BYTE, FieldBit.Custom, false, false, "RawDataUniqueID"), - new TiffFieldInfo(TiffTag.ORIGINALRAWFILENAME, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "OriginalRawFileName"), - new TiffFieldInfo(TiffTag.ORIGINALRAWFILENAME, -1, -1, TiffType.BYTE, FieldBit.Custom, true, true, "OriginalRawFileName"), - new TiffFieldInfo(TiffTag.ORIGINALRAWFILEDATA, -1, -1, TiffType.UNDEFINED, FieldBit.Custom, false, true, "OriginalRawFileData"), - new TiffFieldInfo(TiffTag.ACTIVEAREA, 4, 4, TiffType.LONG, FieldBit.Custom, false, false, "ActiveArea"), - new TiffFieldInfo(TiffTag.ACTIVEAREA, 4, 4, TiffType.SHORT, FieldBit.Custom, false, false, "ActiveArea"), - new TiffFieldInfo(TiffTag.MASKEDAREAS, -1, -1, TiffType.LONG, FieldBit.Custom, false, true, "MaskedAreas"), - new TiffFieldInfo(TiffTag.ASSHOTICCPROFILE, -1, -1, TiffType.UNDEFINED, FieldBit.Custom, false, true, "AsShotICCProfile"), - new TiffFieldInfo(TiffTag.ASSHOTPREPROFILEMATRIX, -1, -1, TiffType.SRATIONAL, FieldBit.Custom, false, true, "AsShotPreProfileMatrix"), - new TiffFieldInfo(TiffTag.CURRENTICCPROFILE, -1, -1, TiffType.UNDEFINED, FieldBit.Custom, false, true, "CurrentICCProfile"), - new TiffFieldInfo(TiffTag.CURRENTPREPROFILEMATRIX, -1, -1, TiffType.SRATIONAL, FieldBit.Custom, false, true, "CurrentPreProfileMatrix"), - /* end DNG tags */ - }; - - private static readonly TiffFieldInfo[] exifFieldInfo = - { - new TiffFieldInfo(TiffTag.EXIF_EXPOSURETIME, 1, 1, TiffType.RATIONAL, FieldBit.Custom, true, false, "ExposureTime"), - new TiffFieldInfo(TiffTag.EXIF_FNUMBER, 1, 1, TiffType.RATIONAL, FieldBit.Custom, true, false, "FNumber"), - new TiffFieldInfo(TiffTag.EXIF_EXPOSUREPROGRAM, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "ExposureProgram"), - new TiffFieldInfo(TiffTag.EXIF_SPECTRALSENSITIVITY, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "SpectralSensitivity"), - new TiffFieldInfo(TiffTag.EXIF_ISOSPEEDRATINGS, -1, -1, TiffType.SHORT, FieldBit.Custom, true, true, "ISOSpeedRatings"), - new TiffFieldInfo(TiffTag.EXIF_OECF, -1, -1, TiffType.UNDEFINED, FieldBit.Custom, true, true, "OptoelectricConversionFactor"), - new TiffFieldInfo(TiffTag.EXIF_EXIFVERSION, 4, 4, TiffType.UNDEFINED, FieldBit.Custom, true, false, "ExifVersion"), - new TiffFieldInfo(TiffTag.EXIF_DATETIMEORIGINAL, 20, 20, TiffType.ASCII, FieldBit.Custom, true, false, "DateTimeOriginal"), - new TiffFieldInfo(TiffTag.EXIF_DATETIMEDIGITIZED, 20, 20, TiffType.ASCII, FieldBit.Custom, true, false, "DateTimeDigitized"), - new TiffFieldInfo(TiffTag.EXIF_COMPONENTSCONFIGURATION, 4, 4, TiffType.UNDEFINED, FieldBit.Custom, true, false, "ComponentsConfiguration"), - new TiffFieldInfo(TiffTag.EXIF_COMPRESSEDBITSPERPIXEL, 1, 1, TiffType.RATIONAL, FieldBit.Custom, true, false, "CompressedBitsPerPixel"), - new TiffFieldInfo(TiffTag.EXIF_SHUTTERSPEEDVALUE, 1, 1, TiffType.SRATIONAL, FieldBit.Custom, true, false, "ShutterSpeedValue"), - new TiffFieldInfo(TiffTag.EXIF_APERTUREVALUE, 1, 1, TiffType.RATIONAL, FieldBit.Custom, true, false, "ApertureValue"), - new TiffFieldInfo(TiffTag.EXIF_BRIGHTNESSVALUE, 1, 1, TiffType.SRATIONAL, FieldBit.Custom, true, false, "BrightnessValue"), - new TiffFieldInfo(TiffTag.EXIF_EXPOSUREBIASVALUE, 1, 1, TiffType.SRATIONAL, FieldBit.Custom, true, false, "ExposureBiasValue"), - new TiffFieldInfo(TiffTag.EXIF_MAXAPERTUREVALUE, 1, 1, TiffType.RATIONAL, FieldBit.Custom, true, false, "MaxApertureValue"), - new TiffFieldInfo(TiffTag.EXIF_SUBJECTDISTANCE, 1, 1, TiffType.RATIONAL, FieldBit.Custom, true, false, "SubjectDistance"), - new TiffFieldInfo(TiffTag.EXIF_METERINGMODE, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "MeteringMode"), - new TiffFieldInfo(TiffTag.EXIF_LIGHTSOURCE, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "LightSource"), - new TiffFieldInfo(TiffTag.EXIF_FLASH, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "Flash"), - new TiffFieldInfo(TiffTag.EXIF_FOCALLENGTH, 1, 1, TiffType.RATIONAL, FieldBit.Custom, true, false, "FocalLength"), - new TiffFieldInfo(TiffTag.EXIF_SUBJECTAREA, -1, -1, TiffType.SHORT, FieldBit.Custom, true, true, "SubjectArea"), - new TiffFieldInfo(TiffTag.EXIF_MAKERNOTE, -1, -1, TiffType.UNDEFINED, FieldBit.Custom, true, true, "MakerNote"), - new TiffFieldInfo(TiffTag.EXIF_USERCOMMENT, -1, -1, TiffType.UNDEFINED, FieldBit.Custom, true, true, "UserComment"), - new TiffFieldInfo(TiffTag.EXIF_SUBSECTIME, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "SubSecTime"), - new TiffFieldInfo(TiffTag.EXIF_SUBSECTIMEORIGINAL, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "SubSecTimeOriginal"), - new TiffFieldInfo(TiffTag.EXIF_SUBSECTIMEDIGITIZED, -1, -1, TiffType.ASCII, FieldBit.Custom, true, false, "SubSecTimeDigitized"), - new TiffFieldInfo(TiffTag.EXIF_FLASHPIXVERSION, 4, 4, TiffType.UNDEFINED, FieldBit.Custom, true, false, "FlashpixVersion"), - new TiffFieldInfo(TiffTag.EXIF_COLORSPACE, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "ColorSpace"), - new TiffFieldInfo(TiffTag.EXIF_PIXELXDIMENSION, 1, 1, TiffType.LONG, FieldBit.Custom, true, false, "PixelXDimension"), - new TiffFieldInfo(TiffTag.EXIF_PIXELXDIMENSION, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "PixelXDimension"), - new TiffFieldInfo(TiffTag.EXIF_PIXELYDIMENSION, 1, 1, TiffType.LONG, FieldBit.Custom, true, false, "PixelYDimension"), - new TiffFieldInfo(TiffTag.EXIF_PIXELYDIMENSION, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "PixelYDimension"), - new TiffFieldInfo(TiffTag.EXIF_RELATEDSOUNDFILE, 13, 13, TiffType.ASCII, FieldBit.Custom, true, false, "RelatedSoundFile"), - new TiffFieldInfo(TiffTag.EXIF_FLASHENERGY, 1, 1, TiffType.RATIONAL, FieldBit.Custom, true, false, "FlashEnergy"), - new TiffFieldInfo(TiffTag.EXIF_SPATIALFREQUENCYRESPONSE, -1, -1, TiffType.UNDEFINED, FieldBit.Custom, true, true, "SpatialFrequencyResponse"), - new TiffFieldInfo(TiffTag.EXIF_FOCALPLANEXRESOLUTION, 1, 1, TiffType.RATIONAL, FieldBit.Custom, true, false, "FocalPlaneXResolution"), - new TiffFieldInfo(TiffTag.EXIF_FOCALPLANEYRESOLUTION, 1, 1, TiffType.RATIONAL, FieldBit.Custom, true, false, "FocalPlaneYResolution"), - new TiffFieldInfo(TiffTag.EXIF_FOCALPLANERESOLUTIONUNIT, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "FocalPlaneResolutionUnit"), - new TiffFieldInfo(TiffTag.EXIF_SUBJECTLOCATION, 2, 2, TiffType.SHORT, FieldBit.Custom, true, false, "SubjectLocation"), - new TiffFieldInfo(TiffTag.EXIF_EXPOSUREINDEX, 1, 1, TiffType.RATIONAL, FieldBit.Custom, true, false, "ExposureIndex"), - new TiffFieldInfo(TiffTag.EXIF_SENSINGMETHOD, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "SensingMethod"), - new TiffFieldInfo(TiffTag.EXIF_FILESOURCE, 1, 1, TiffType.UNDEFINED, FieldBit.Custom, true, false, "FileSource"), - new TiffFieldInfo(TiffTag.EXIF_SCENETYPE, 1, 1, TiffType.UNDEFINED, FieldBit.Custom, true, false, "SceneType"), - new TiffFieldInfo(TiffTag.EXIF_CFAPATTERN, -1, -1, TiffType.UNDEFINED, FieldBit.Custom, true, true, "CFAPattern"), - new TiffFieldInfo(TiffTag.EXIF_CUSTOMRENDERED, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "CustomRendered"), - new TiffFieldInfo(TiffTag.EXIF_EXPOSUREMODE, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "ExposureMode"), - new TiffFieldInfo(TiffTag.EXIF_WHITEBALANCE, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "WhiteBalance"), - new TiffFieldInfo(TiffTag.EXIF_DIGITALZOOMRATIO, 1, 1, TiffType.RATIONAL, FieldBit.Custom, true, false, "DigitalZoomRatio"), - new TiffFieldInfo(TiffTag.EXIF_FOCALLENGTHIN35MMFILM, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "FocalLengthIn35mmFilm"), - new TiffFieldInfo(TiffTag.EXIF_SCENECAPTURETYPE, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "SceneCaptureType"), - new TiffFieldInfo(TiffTag.EXIF_GAINCONTROL, 1, 1, TiffType.RATIONAL, FieldBit.Custom, true, false, "GainControl"), - new TiffFieldInfo(TiffTag.EXIF_CONTRAST, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "Contrast"), - new TiffFieldInfo(TiffTag.EXIF_SATURATION, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "Saturation"), - new TiffFieldInfo(TiffTag.EXIF_SHARPNESS, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "Sharpness"), - new TiffFieldInfo(TiffTag.EXIF_DEVICESETTINGDESCRIPTION, -1, -1, TiffType.UNDEFINED, FieldBit.Custom, true, true, "DeviceSettingDescription"), - new TiffFieldInfo(TiffTag.EXIF_SUBJECTDISTANCERANGE, 1, 1, TiffType.SHORT, FieldBit.Custom, true, false, "SubjectDistanceRange"), - new TiffFieldInfo(TiffTag.EXIF_IMAGEUNIQUEID, 33, 33, TiffType.ASCII, FieldBit.Custom, true, false, "ImageUniqueID") - }; - - private static TiffFieldInfo[] getFieldInfo(out int size) - { - size = tiffFieldInfo.Length; - return tiffFieldInfo; - } - - private static TiffFieldInfo[] getExifFieldInfo(out int size) - { - size = exifFieldInfo.Length; - return exifFieldInfo; - } - - private void setupFieldInfo(TiffFieldInfo[] info, int n) - { - m_nfields = 0; - MergeFieldInfo(info, n); - } - - /* - * Return nearest TiffDataType to the sample type of an image. - */ - private TiffType sampleToTagType() - { - int bps = howMany8(m_dir.td_bitspersample); - - switch (m_dir.td_sampleformat) - { - case SampleFormat.IEEEFP: - return (bps == 4 ? TiffType.FLOAT : TiffType.DOUBLE); - case SampleFormat.INT: - return (bps <= 1 ? TiffType.SBYTE : bps <= 2 ? TiffType.SSHORT : TiffType.SLONG); - case SampleFormat.UINT: - return (bps <= 1 ? TiffType.BYTE : bps <= 2 ? TiffType.SHORT : TiffType.LONG); - case SampleFormat.VOID: - return TiffType.UNDEFINED; - } - - return TiffType.UNDEFINED; - } - - private static TiffFieldInfo createAnonFieldInfo(TiffTag tag, TiffType field_type) - { - TiffFieldInfo fld = new TiffFieldInfo(tag, TiffFieldInfo.Variable2, - TiffFieldInfo.Variable2, field_type, FieldBit.Custom, true, true, null); - - // note that this name is a special sign to Close() and - // setupFieldInfo() to free the field - fld.Name = string.Format(CultureInfo.InvariantCulture, "Tag {0}", tag); - return fld; - } - - /* - * Return size of TiffDataType in bytes. - * - * XXX: We need a separate function to determine the space needed - * to store the value. For TiffType.RATIONAL values DataWidth() - * returns 8, but we use 4-byte float to represent rationals. - */ - internal static int dataSize(TiffType type) - { - switch (type) - { - case TiffType.BYTE: - case TiffType.SBYTE: - case TiffType.ASCII: - case TiffType.UNDEFINED: - return 1; - - case TiffType.SHORT: - case TiffType.SSHORT: - return 2; - - case TiffType.LONG: - case TiffType.SLONG: - case TiffType.FLOAT: - case TiffType.IFD: - case TiffType.RATIONAL: - case TiffType.SRATIONAL: - return 4; - - case TiffType.DOUBLE: - return 8; - - default: - return 0; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_DirRead.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_DirRead.cs deleted file mode 100644 index 126081b9..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_DirRead.cs +++ /dev/null @@ -1,1301 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Directory Read Support Routines. - */ - -using System; -using System.Diagnostics; - -using BitMiracle.LibTiff.Classic.Internal; - -namespace BitMiracle.LibTiff.Classic -{ -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff - { - private int extractData(TiffDirEntry dir) - { - int type = (int)dir.tdir_type; - if (m_header.tiff_magic == TIFF_BIGENDIAN) - return (int)((dir.tdir_offset >> m_typeshift[type]) & m_typemask[type]); - - return (int)(dir.tdir_offset & m_typemask[type]); - } - - private bool byteCountLooksBad(TiffDirectory td) - { - /* - * Assume we have wrong StripByteCount value (in case of single strip) in - * following cases: - * - it is equal to zero along with StripOffset; - * - it is larger than file itself (in case of uncompressed image); - * - it is smaller than the size of the bytes per row multiplied on the - * number of rows. The last case should not be checked in the case of - * writing new image, because we may do not know the exact strip size - * until the whole image will be written and directory dumped out. - */ - return - ( - (td.td_stripbytecount[0] == 0 && td.td_stripoffset[0] != 0) || - (td.td_compression == Compression.NONE && td.td_stripbytecount[0] > getFileSize() - td.td_stripoffset[0]) || - (m_mode == O_RDONLY && td.td_compression == Compression.NONE && td.td_stripbytecount[0] < ScanlineSize() * td.td_imagelength) - ); - } - - private static int howMany8(int x) - { - return ((x & 0x07) != 0 ? (x >> 3) + 1 : x >> 3); - } - - private bool estimateStripByteCounts(TiffDirEntry[] dir, short dircount) - { - const string module = "estimateStripByteCounts"; - - m_dir.td_stripbytecount = new uint [m_dir.td_nstrips]; - - if (m_dir.td_compression != Compression.NONE) - { - long space = TiffHeader.SizeInBytes + sizeof(short) + (dircount * TiffDirEntry.SizeInBytes) + sizeof(int); - long filesize = getFileSize(); - - // calculate amount of space used by indirect values - for (short n = 0; n < dircount; n++) - { - int cc = DataWidth((TiffType)dir[n].tdir_type); - if (cc == 0) - { - ErrorExt(this, m_clientdata, module, - "{0}: Cannot determine size of unknown tag type {1}", - m_name, dir[n].tdir_type); - return false; - } - - cc = cc * dir[n].tdir_count; - if (cc > sizeof(int)) - space += cc; - } - - space = filesize - space; - if (m_dir.td_planarconfig == PlanarConfig.SEPARATE) - space /= m_dir.td_samplesperpixel; - - int strip = 0; - for ( ; strip < m_dir.td_nstrips; strip++) - m_dir.td_stripbytecount[strip] = (uint)space; - - // This gross hack handles the case were the offset to the last - // strip is past the place where we think the strip should begin. - // Since a strip of data must be contiguous, it's safe to assume - // that we've overestimated the amount of data in the strip and - // trim this number back accordingly. - strip--; - if ((m_dir.td_stripoffset[strip] + m_dir.td_stripbytecount[strip]) > filesize) - m_dir.td_stripbytecount[strip] = (uint)(filesize - m_dir.td_stripoffset[strip]); - } - else if (IsTiled()) - { - int bytespertile = TileSize(); - for (int strip = 0; strip < m_dir.td_nstrips; strip++) - m_dir.td_stripbytecount[strip] = (uint)bytespertile; - } - else - { - int rowbytes = ScanlineSize(); - int rowsperstrip = m_dir.td_imagelength / m_dir.td_stripsperimage; - for (int strip = 0; strip < m_dir.td_nstrips; strip++) - m_dir.td_stripbytecount[strip] = (uint)(rowbytes * rowsperstrip); - } - - setFieldBit(FieldBit.StripByteCounts); - if (!fieldSet(FieldBit.RowsPerStrip)) - m_dir.td_rowsperstrip = m_dir.td_imagelength; - - return true; - } - - private void missingRequired(string tagname) - { - const string module = "missingRequired"; - ErrorExt(this, m_clientdata, module, - "{0}: TIFF directory is missing required \"{1}\" field", - m_name, tagname); - } - - private int fetchFailed(TiffDirEntry dir) - { - ErrorExt(this, m_clientdata, m_name, - "Error fetching data for field \"{0}\"", FieldWithTag(dir.tdir_tag).Name); - return 0; - } - - private static int readDirectoryFind(TiffDirEntry[] dir, short dircount, TiffTag tagid) - { - for (short n = 0; n < dircount; n++) - { - if (dir[n].tdir_tag == tagid) - return n; - } - - return -1; - } - - /// - /// Checks the directory offset against the list of already seen directory - /// offsets. - /// - /// This is a trick to prevent IFD looping. The one can - /// create TIFF file with looped directory pointers. We will maintain a - /// list of already seen directories and check every IFD offset against - /// that list. - private bool checkDirOffset(uint diroff) - { - if (diroff == 0) - { - // no more directories - return false; - } - - for (short n = 0; n < m_dirnumber && m_dirlist != null; n++) - { - if (m_dirlist[n] == diroff) - return false; - } - - m_dirnumber++; - - if (m_dirnumber > m_dirlistsize) - { - // XXX: Reduce memory allocation granularity of the dirlist array. - uint[] new_dirlist = Realloc(m_dirlist, m_dirnumber - 1, 2 * m_dirnumber); - m_dirlistsize = 2 * m_dirnumber; - m_dirlist = new_dirlist; - } - - m_dirlist[m_dirnumber - 1] = diroff; - return true; - } - - /// - /// Reads IFD structure from the specified offset. - /// - /// The number of fields in the directory or 0 if failed. - private short fetchDirectory(uint diroff, out TiffDirEntry[] pdir, out uint nextdiroff) - { - const string module = "fetchDirectory"; - - m_diroff = diroff; - nextdiroff = 0; - - short dircount; - TiffDirEntry[] dir = null; - pdir = null; - - if (!seekOK(m_diroff)) - { - ErrorExt(this, m_clientdata, module, - "{0}: Seek error accessing TIFF directory", m_name); - return 0; - } - - if (!readShortOK(out dircount)) - { - ErrorExt(this, m_clientdata, module, - "{0}: Can not read TIFF directory count", m_name); - return 0; - } - - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabShort(ref dircount); - - dir = new TiffDirEntry [dircount]; - if (!readDirEntryOk(dir, dircount)) - { - ErrorExt(this, m_clientdata, module, "{0}: Can not read TIFF directory", m_name); - return 0; - } - - // Read offset to next directory for sequential scans. - int temp; - readIntOK(out temp); - nextdiroff = (uint)temp; - - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - { - temp = (int)nextdiroff; - SwabLong(ref temp); - nextdiroff = (uint)temp; - } - - pdir = dir; - return dircount; - } - - /* - * Fetch and set the SubjectDistance EXIF tag. - */ - private bool fetchSubjectDistance(TiffDirEntry dir) - { - if (dir.tdir_count != 1 || dir.tdir_type != TiffType.RATIONAL) - { - Tiff.WarningExt(this, m_clientdata, m_name, - "incorrect count or type for SubjectDistance, tag ignored"); - - return false; - } - - bool ok = false; - - byte[] b = new byte[2 * sizeof(int)]; - int read = fetchData(dir, b); - if (read != 0) - { - int[] l = new int[2]; - l[0] = readInt(b, 0); - l[1] = readInt(b, sizeof(int)); - - float v; - if (cvtRational(dir, l[0], l[1], out v)) - { - /* - * XXX: Numerator -1 means that we have infinite - * distance. Indicate that with a negative floating point - * SubjectDistance value. - */ - ok = SetField(dir.tdir_tag, (l[0] != -1) ? v : -v); - } - } - - return ok; - } - - /* - * Check the count field of a directory - * entry against a known value. The caller - * is expected to skip/ignore the tag if - * there is a mismatch. - */ - private bool checkDirCount(TiffDirEntry dir, int count) - { - if (count > dir.tdir_count) - { - WarningExt(this, m_clientdata, m_name, - "incorrect count for field \"{0}\" ({1}, expecting {2}); tag ignored", - FieldWithTag(dir.tdir_tag).Name, dir.tdir_count, count); - return false; - } - else if (count < dir.tdir_count) - { - WarningExt(this, m_clientdata, m_name, - "incorrect count for field \"{0}\" ({1}, expecting {2}); tag trimmed", - FieldWithTag(dir.tdir_tag).Name, dir.tdir_count, count); - return true; - } - - return true; - } - - /// - /// Fetches a contiguous directory item. - /// - private int fetchData(TiffDirEntry dir, byte[] buffer) - { - int width = DataWidth(dir.tdir_type); - int count = (int)dir.tdir_count * width; - - // Check for overflow. - if (dir.tdir_count == 0 || width == 0 || (count / width) != dir.tdir_count) - fetchFailed(dir); - - if (!seekOK(dir.tdir_offset)) - fetchFailed(dir); - - if (!readOK(buffer, count)) - fetchFailed(dir); - - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - { - switch (dir.tdir_type) - { - case TiffType.SHORT: - case TiffType.SSHORT: - short[] s = ByteArrayToShorts(buffer, 0, count); - SwabArrayOfShort(s, dir.tdir_count); - ShortsToByteArray(s, 0, dir.tdir_count, buffer, 0); - break; - - case TiffType.LONG: - case TiffType.SLONG: - case TiffType.FLOAT: - int[] l = ByteArrayToInts(buffer, 0, count); - SwabArrayOfLong(l, dir.tdir_count); - IntsToByteArray(l, 0, dir.tdir_count, buffer, 0); - break; - - case TiffType.RATIONAL: - case TiffType.SRATIONAL: - int[] r = ByteArrayToInts(buffer, 0, count); - SwabArrayOfLong(r, 2 * dir.tdir_count); - IntsToByteArray(r, 0, 2 * dir.tdir_count, buffer, 0); - break; - - case TiffType.DOUBLE: - swab64BitData(buffer, 0, count); - break; - } - } - - return count; - } - - /// - /// Fetches an ASCII item from the file. - /// - private int fetchString(TiffDirEntry dir, out string cp) - { - byte[] bytes = null; - - if (dir.tdir_count <= 4) - { - int l = (int)dir.tdir_offset; - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabLong(ref l); - - bytes = new byte[sizeof(int)]; - writeInt(l, bytes, 0); - cp = Latin1Encoding.GetString(bytes, 0, dir.tdir_count); - return 1; - } - - bytes = new byte[dir.tdir_count]; - int res = fetchData(dir, bytes); - cp = Latin1Encoding.GetString(bytes, 0, dir.tdir_count); - return res; - } - - /* - * Convert numerator+denominator to float. - */ - private bool cvtRational(TiffDirEntry dir, int num, int denom, out float rv) - { - if (denom == 0) - { - ErrorExt(this, m_clientdata, m_name, - "{0}: Rational with zero denominator (num = {1})", - FieldWithTag(dir.tdir_tag).Name, num); - rv = float.NaN; - return false; - } - else - { - rv = ((float)num / (float)denom); - return true; - } - } - - /* - * Fetch a rational item from the file - * at offset off and return the value - * as a floating point number. - */ - private float fetchRational(TiffDirEntry dir) - { - byte[] bytes = new byte[sizeof(int) * 2]; - int read = fetchData(dir, bytes); - if (read != 0) - { - int[] l = new int[2]; - l[0] = readInt(bytes, 0); - l[1] = readInt(bytes, sizeof(int)); - - float v; - bool res = cvtRational(dir, l[0], l[1], out v); - if (res) - return v; - } - - return 1.0f; - } - - /// - /// Fetch a single floating point value from the offset field and - /// return it as a native float. - /// - private float fetchFloat(TiffDirEntry dir) - { - int l = extractData(dir); - return BitConverter.ToSingle(BitConverter.GetBytes(l), 0); - } - - /// - /// Fetches an array of BYTE or SBYTE values. - /// - private bool fetchByteArray(TiffDirEntry dir, byte[] v) - { - if (dir.tdir_count <= 4) - { - // Extract data from offset field. - int count = dir.tdir_count; - - if (m_header.tiff_magic == TIFF_BIGENDIAN) - { - if (count == 4) - v[3] = (byte)(dir.tdir_offset & 0xff); - - if (count >= 3) - v[2] = (byte)((dir.tdir_offset >> 8) & 0xff); - - if (count >= 2) - v[1] = (byte)((dir.tdir_offset >> 16) & 0xff); - - if (count >= 1) - v[0] = (byte)(dir.tdir_offset >> 24); - } - else - { - if (count == 4) - v[3] = (byte)(dir.tdir_offset >> 24); - - if (count >= 3) - v[2] = (byte)((dir.tdir_offset >> 16) & 0xff); - - if (count >= 2) - v[1] = (byte)((dir.tdir_offset >> 8) & 0xff); - - if (count >= 1) - v[0] = (byte)(dir.tdir_offset & 0xff); - } - - return true; - } - - return (fetchData(dir, v) != 0); - } - - /// - /// Fetch an array of SHORT or SSHORT values. - /// - private bool fetchShortArray(TiffDirEntry dir, short[] v) - { - if (dir.tdir_count <= 2) - { - int count = dir.tdir_count; - - if (m_header.tiff_magic == TIFF_BIGENDIAN) - { - if (count == 2) - v[1] = (short)(dir.tdir_offset & 0xffff); - - if (count >= 1) - v[0] = (short)(dir.tdir_offset >> 16); - } - else - { - if (count == 2) - v[1] = (short)(dir.tdir_offset >> 16); - - if (count >= 1) - v[0] = (short)(dir.tdir_offset & 0xffff); - } - - return true; - } - - int cc = dir.tdir_count * sizeof(short); - byte[] b = new byte[cc]; - int read = fetchData(dir, b); - if (read != 0) - Buffer.BlockCopy(b, 0, v, 0, b.Length); - - return (read != 0); - } - - /* - * Fetch a pair of SHORT or BYTE values. Some tags may have either BYTE - * or SHORT type and this function works with both ones. - */ - private bool fetchShortPair(TiffDirEntry dir) - { - /* - * Prevent overflowing arrays below by performing a sanity - * check on tdir_count, this should never be greater than two. - */ - if (dir.tdir_count > 2) - { - WarningExt(this, m_clientdata, m_name, - "unexpected count for field \"{0}\", {1}, expected 2; ignored", - FieldWithTag(dir.tdir_tag).Name, dir.tdir_count); - return false; - } - - switch (dir.tdir_type) - { - case TiffType.BYTE: - case TiffType.SBYTE: - byte[] bytes = new byte[4]; - return fetchByteArray(dir, bytes) && SetField(dir.tdir_tag, bytes[0], bytes[1]); - - case TiffType.SHORT: - case TiffType.SSHORT: - short[] shorts = new short[2]; - return fetchShortArray(dir, shorts) && SetField(dir.tdir_tag, shorts[0], shorts[1]); - } - - return false; - } - - /// - /// Fetches an array of LONG or SLONG values. - /// - private bool fetchLongArray(TiffDirEntry dir, int[] v) - { - if (dir.tdir_count == 1) - { - v[0] = (int)dir.tdir_offset; - return true; - } - - int cc = dir.tdir_count * sizeof(int); - byte[] b = new byte[cc]; - int read = fetchData(dir, b); - if (read != 0) - Buffer.BlockCopy(b, 0, v, 0, b.Length); - - return (read != 0); - } - - /// - /// Fetch an array of RATIONAL or SRATIONAL values. - /// - private bool fetchRationalArray(TiffDirEntry dir, float[] v) - { - Debug.Assert(sizeof(float) == sizeof(int)); - - bool ok = false; - byte[] l = new byte [dir.tdir_count * DataWidth(dir.tdir_type)]; - if (fetchData(dir, l) != 0) - { - int offset = 0; - int[] pair = new int[2]; - for (int i = 0; i < dir.tdir_count; i++) - { - pair[0] = readInt(l, offset); - offset += sizeof(int); - pair[1] = readInt(l, offset); - offset += sizeof(int); - - ok = cvtRational(dir, pair[0], pair[1], out v[i]); - if (!ok) - break; - } - } - - return ok; - } - - /// - /// Fetches an array of FLOAT values. - /// - private bool fetchFloatArray(TiffDirEntry dir, float[] v) - { - if (dir.tdir_count == 1) - { - v[0] = BitConverter.ToSingle(BitConverter.GetBytes(dir.tdir_offset), 0); - return true; - } - - int w = DataWidth(dir.tdir_type); - int cc = dir.tdir_count * w; - byte[] b = new byte [cc]; - int read = fetchData(dir, b); - if (read != 0) - { - int byteOffset = 0; - for (int i = 0; i < read / 4; i++) - { - v[i] = BitConverter.ToSingle(b, byteOffset); - byteOffset += 4; - } - } - - return (read != 0); - } - - /// - /// Fetches an array of DOUBLE values. - /// - private bool fetchDoubleArray(TiffDirEntry dir, double[] v) - { - int w = DataWidth(dir.tdir_type); - int cc = dir.tdir_count * w; - byte[] b = new byte [cc]; - int read = fetchData(dir, b); - if (read != 0) - { - int byteOffset = 0; - for (int i = 0; i < read / 8; i++) - { - v[i] = BitConverter.ToDouble(b, byteOffset); - byteOffset += 8; - } - } - - return (read != 0); - } - - /// - /// Fetches an array of ANY values. - /// - /// The actual values are returned as doubles which should be - /// able hold all the types. Note in particular that we assume that the - /// double return value vector is large enough to read in any - /// fundamental type. We use that vector as a buffer to read in the base - /// type vector and then convert it in place to double (from end to - /// front of course). - private bool fetchAnyArray(TiffDirEntry dir, double[] v) - { - int i = 0; - bool res = false; - switch (dir.tdir_type) - { - case TiffType.BYTE: - case TiffType.SBYTE: - byte[] b = new byte[dir.tdir_count]; - res = fetchByteArray(dir, b); - if (res) - { - for (i = dir.tdir_count - 1; i >= 0; i--) - v[i] = b[i]; - } - - if (!res) - return false; - - break; - case TiffType.SHORT: - case TiffType.SSHORT: - short[] u = new short[dir.tdir_count]; - res = fetchShortArray(dir, u); - if (res) - { - for (i = dir.tdir_count - 1; i >= 0; i--) - v[i] = u[i]; - } - - if (!res) - return false; - - break; - case TiffType.LONG: - case TiffType.SLONG: - int[] l = new int[dir.tdir_count]; - res = fetchLongArray(dir, l); - if (res) - { - for (i = dir.tdir_count - 1; i >= 0; i--) - v[i] = l[i]; - } - - if (!res) - return false; - - break; - case TiffType.RATIONAL: - case TiffType.SRATIONAL: - float[] r = new float[dir.tdir_count]; - res = fetchRationalArray(dir, r); - if (res) - { - for (i = dir.tdir_count - 1; i >= 0; i--) - v[i] = r[i]; - } - - if (!res) - return false; - - break; - case TiffType.FLOAT: - float[] f = new float[dir.tdir_count]; - res = fetchFloatArray(dir, f); - if (res) - { - for (i = dir.tdir_count - 1; i >= 0; i--) - v[i] = f[i]; - } - - if (!res) - return false; - - break; - case TiffType.DOUBLE: - return fetchDoubleArray(dir, v); - default: - // NOTYPE - // ASCII - // UNDEFINED - ErrorExt(this, m_clientdata, m_name, - "cannot read TIFF_ANY type {0} for field \"{1}\"", - dir.tdir_type, FieldWithTag(dir.tdir_tag).Name); - return false; - } - - return true; - } - - /// - /// Fetches a tag that is not handled by special case code. - /// - private bool fetchNormalTag(TiffDirEntry dir) - { - bool ok = false; - TiffFieldInfo fip = FieldWithTag(dir.tdir_tag); - - if (dir.tdir_count > 1) - { - switch (dir.tdir_type) - { - case TiffType.BYTE: - case TiffType.SBYTE: - byte[] bytes = new byte [dir.tdir_count]; - ok = fetchByteArray(dir, bytes); - if (ok) - { - if (fip.PassCount) - ok = SetField(dir.tdir_tag, dir.tdir_count, bytes); - else - ok = SetField(dir.tdir_tag, bytes); - } - break; - - case TiffType.SHORT: - case TiffType.SSHORT: - short[] shorts = new short [dir.tdir_count]; - ok = fetchShortArray(dir, shorts); - if (ok) - { - if (fip.PassCount) - ok = SetField(dir.tdir_tag, dir.tdir_count, shorts); - else - ok = SetField(dir.tdir_tag, shorts); - } - break; - - case TiffType.LONG: - case TiffType.SLONG: - int[] ints = new int [dir.tdir_count]; - ok = fetchLongArray(dir, ints); - if (ok) - { - if (fip.PassCount) - ok = SetField(dir.tdir_tag, dir.tdir_count, ints); - else - ok = SetField(dir.tdir_tag, ints); - } - break; - - case TiffType.RATIONAL: - case TiffType.SRATIONAL: - float[] rs = new float [dir.tdir_count]; - ok = fetchRationalArray(dir, rs); - if (ok) - { - if (fip.PassCount) - ok = SetField(dir.tdir_tag, dir.tdir_count, rs); - else - ok = SetField(dir.tdir_tag, rs); - } - break; - - case TiffType.FLOAT: - float[] fs = new float [dir.tdir_count]; - ok = fetchFloatArray(dir, fs); - if (ok) - { - if (fip.PassCount) - ok = SetField(dir.tdir_tag, dir.tdir_count, fs); - else - ok = SetField(dir.tdir_tag, fs); - } - break; - - case TiffType.DOUBLE: - double[] ds = new double [dir.tdir_count]; - ok = fetchDoubleArray(dir, ds); - if (ok) - { - if (fip.PassCount) - ok = SetField(dir.tdir_tag, dir.tdir_count, ds); - else - ok = SetField(dir.tdir_tag, ds); - } - break; - - case TiffType.ASCII: - case TiffType.UNDEFINED: - // bit of a cheat... - - // Some vendors write strings w/o the trailing null - // byte, so always append one just in case. - string cp; - ok = fetchString(dir, out cp) != 0; - if (ok) - { - if (fip.PassCount) - ok = SetField(dir.tdir_tag, dir.tdir_count, cp); - else - ok = SetField(dir.tdir_tag, cp); - } - break; - } - } - else if (checkDirCount(dir, 1)) - { - int v32 = 0; - // singleton value - switch (dir.tdir_type) - { - case TiffType.BYTE: - case TiffType.SBYTE: - case TiffType.SHORT: - case TiffType.SSHORT: - // If the tag is also acceptable as a LONG or SLONG - // then SetField will expect an int parameter - // passed to it. - // - // NB: We use FieldWithTag here knowing that - // it returns us the first entry in the table - // for the tag and that that entry is for the - // widest potential data type the tag may have. - TiffType type = fip.Type; - if (type != TiffType.LONG && type != TiffType.SLONG) - { - short v = (short)extractData(dir); - if (fip.PassCount) - { - short[] a = new short[1]; - a[0] = v; - ok = SetField(dir.tdir_tag, 1, a); - } - else - ok = SetField(dir.tdir_tag, v); - - break; - } - - v32 = extractData(dir); - if (fip.PassCount) - { - int[] a = new int[1]; - a[0] = (int)v32; - ok = SetField(dir.tdir_tag, 1, a); - } - else - ok = SetField(dir.tdir_tag, v32); - - break; - - case TiffType.LONG: - case TiffType.SLONG: - v32 = extractData(dir); - if (fip.PassCount) - { - int[] a = new int[1]; - a[0] = (int)v32; - ok = SetField(dir.tdir_tag, 1, a); - } - else - ok = SetField(dir.tdir_tag, v32); - - break; - - case TiffType.RATIONAL: - case TiffType.SRATIONAL: - case TiffType.FLOAT: - float f = (dir.tdir_type == TiffType.FLOAT ? fetchFloat(dir): fetchRational(dir)); - if (fip.PassCount) - { - float[] a = new float[1]; - a[0] = f; - ok = SetField(dir.tdir_tag, 1, a); - } - else - ok = SetField(dir.tdir_tag, f); - - break; - - case TiffType.DOUBLE: - double[] ds = new double[1]; - ok = fetchDoubleArray(dir, ds); - if (ok) - { - if (fip.PassCount) - ok = SetField(dir.tdir_tag, 1, ds); - else - ok = SetField(dir.tdir_tag, ds[0]); - } - break; - - case TiffType.ASCII: - case TiffType.UNDEFINED: - // bit of a cheat... - string c; - ok = fetchString(dir, out c) != 0; - if (ok) - { - if (fip.PassCount) - ok = SetField(dir.tdir_tag, 1, c); - else - ok = SetField(dir.tdir_tag, c); - } - break; - } - } - - return ok; - } - - /// - /// Fetches samples/pixel short values for the specified tag and verify - /// that all values are the same. - /// - private bool fetchPerSampleShorts(TiffDirEntry dir, out short pl) - { - pl = 0; - short samples = m_dir.td_samplesperpixel; - bool status = false; - - if (checkDirCount(dir, samples)) - { - short[] v = new short [dir.tdir_count]; - if (fetchShortArray(dir, v)) - { - int check_count = dir.tdir_count; - if (samples < check_count) - check_count = samples; - - bool failed = false; - for (ushort i = 1; i < check_count; i++) - { - if (v[i] != v[0]) - { - ErrorExt(this, m_clientdata, m_name, - "Cannot handle different per-sample values for field \"{0}\"", - FieldWithTag(dir.tdir_tag).Name); - failed = true; - break; - } - } - - if (!failed) - { - pl = v[0]; - status = true; - } - } - } - - return status; - } - - /// - /// Fetches samples/pixel long values for the specified tag and verify - /// that all values are the same. - /// - private bool fetchPerSampleLongs(TiffDirEntry dir, out int pl) - { - pl = 0; - short samples = m_dir.td_samplesperpixel; - bool status = false; - - if (checkDirCount(dir, samples)) - { - int[] v = new int [dir.tdir_count]; - if (fetchLongArray(dir, v)) - { - int check_count = dir.tdir_count; - if (samples < check_count) - check_count = samples; - - bool failed = false; - for (ushort i = 1; i < check_count; i++) - { - if (v[i] != v[0]) - { - ErrorExt(this, m_clientdata, m_name, - "Cannot handle different per-sample values for field \"{0}\"", - FieldWithTag(dir.tdir_tag).Name); - failed = true; - break; - } - } - - if (!failed) - { - pl = (int)v[0]; - status = true; - } - } - } - - return status; - } - - /// - /// Fetches samples/pixel ANY values for the specified tag and verify - /// that all values are the same. - /// - private bool fetchPerSampleAnys(TiffDirEntry dir, out double pl) - { - pl = 0; - short samples = m_dir.td_samplesperpixel; - bool status = false; - - if (checkDirCount(dir, samples)) - { - double[] v = new double [dir.tdir_count]; - if (fetchAnyArray(dir, v)) - { - int check_count = dir.tdir_count; - if (samples < check_count) - check_count = samples; - - bool failed = false; - for (ushort i = 1; i < check_count; i++) - { - if (v[i] != v[0]) - { - ErrorExt(this, m_clientdata, m_name, - "Cannot handle different per-sample values for field \"{0}\"", - FieldWithTag(dir.tdir_tag).Name); - failed = true; - break; - } - } - - if (!failed) - { - pl = v[0]; - status = true; - } - } - } - - return status; - } - - /// - /// Fetches a set of offsets or lengths. - /// - /// While this routine says "strips", in fact it's also used - /// for tiles. - private bool fetchStripThing(TiffDirEntry dir, int nstrips, ref int[] lpp) - { - checkDirCount(dir, nstrips); - - // Allocate space for strip information. - if (lpp == null) - lpp = new int [nstrips]; - else - Array.Clear(lpp, 0, lpp.Length); - - bool status = false; - if (dir.tdir_type == TiffType.SHORT) - { - // Handle short -> int expansion. - short[] dp = new short[dir.tdir_count]; - status = fetchShortArray(dir, dp); - if (status) - { - for (int i = 0; i < nstrips && i < dir.tdir_count; i++) - lpp[i] = dp[i]; - } - } - else if (nstrips != dir.tdir_count) - { - // Special case to correct length - int[] dp = new int[dir.tdir_count]; - status = fetchLongArray(dir, dp); - if (status) - { - for (int i = 0; i < nstrips && i < dir.tdir_count; i++) - lpp[i] = dp[i]; - } - } - else - { - status = fetchLongArray(dir, lpp); - } - - return status; - } - - private bool fetchStripThing(TiffDirEntry dir, int nstrips, ref uint[] lpp) - { - int[] temp = null; - if (lpp != null) - temp = new int[lpp.Length]; - - bool res = fetchStripThing(dir, nstrips, ref temp); - if (res) - { - if (lpp == null) - lpp = new uint[temp.Length]; - - Buffer.BlockCopy(temp, 0, lpp, 0, temp.Length * sizeof(uint)); - } - - return res; - } - - /// - /// Fetches and sets the RefBlackWhite tag. - /// - private bool fetchRefBlackWhite(TiffDirEntry dir) - { - // some OJPEG images specify Reference BlackWhite as array of longs - // - // so, we'll try to read value as float array and check them - // if read was successfull and there is at least one value greater - // then 1.0 then ok, return value just read. - // - // if read failed or all values are less then or equal to 1.0 then - // try once again but read as long array this time. - - if (dir.tdir_type == TiffType.RATIONAL) - { - bool res = fetchNormalTag(dir); - if (res) - { - for (int i = 0; i < m_dir.td_refblackwhite.Length; i++) - { - if (m_dir.td_refblackwhite[i] > 1) - return true; - } - } - } - - // Handle LONG's for backward compatibility. - dir.tdir_type = TiffType.LONG; - int[] cp = new int [dir.tdir_count]; - bool ok = fetchLongArray(dir, cp); - dir.tdir_type = TiffType.RATIONAL; - - if (ok) - { - float[] fp = new float [dir.tdir_count]; - for (int i = 0; i < dir.tdir_count; i++) - fp[i] = (float)cp[i]; - - ok = SetField(dir.tdir_tag, fp); - } - - return ok; - } - - /// - /// Replace a single strip (tile) of uncompressed data with multiple - /// strips (tiles), each approximately 8Kbytes. - /// - /// This is useful for dealing with large images or for - /// dealing with machines with a limited amount of memory. - private void chopUpSingleUncompressedStrip() - { - uint bytecount = m_dir.td_stripbytecount[0]; - uint offset = m_dir.td_stripoffset[0]; - - // Make the rows hold at least one scanline, but fill specified - // amount of data if possible. - int rowbytes = VTileSize(1); - uint stripbytes; - int rowsperstrip; - if (rowbytes > STRIP_SIZE_DEFAULT) - { - stripbytes = (uint)rowbytes; - rowsperstrip = 1; - } - else if (rowbytes > 0) - { - rowsperstrip = STRIP_SIZE_DEFAULT / rowbytes; - stripbytes = (uint)(rowbytes * rowsperstrip); - } - else - { - return; - } - - // never increase the number of strips in an image - if (rowsperstrip >= m_dir.td_rowsperstrip) - return; - - uint nstrips = howMany(bytecount, stripbytes); - if (nstrips == 0) - { - // something is wonky, do nothing. - return; - } - - uint[] newcounts = new uint [nstrips]; - uint[] newoffsets = new uint [nstrips]; - - // Fill the strip information arrays with new bytecounts and offsets - // that reflect the broken-up format. - for (int strip = 0; strip < nstrips; strip++) - { - if (stripbytes > bytecount) - stripbytes = bytecount; - - newcounts[strip] = stripbytes; - newoffsets[strip] = offset; - offset += stripbytes; - bytecount -= stripbytes; - } - - // Replace old single strip info with multi-strip info. - m_dir.td_nstrips = (int)nstrips; - m_dir.td_stripsperimage = (int)nstrips; - SetField(TiffTag.ROWSPERSTRIP, rowsperstrip); - - m_dir.td_stripbytecount = newcounts; - m_dir.td_stripoffset = newoffsets; - m_dir.td_stripbytecountsorted = true; - } - - internal static int roundUp(int x, int y) - { - return (howMany(x, y) * y); - } - - internal static int howMany(int x, int y) - { - long res = (((long)x + ((long)y - 1)) / (long)y); - if (res > int.MaxValue) - return 0; - - return (int)res; - } - - internal static uint howMany(uint x, uint y) - { - long res = (((long)x + ((long)y - 1)) / (long)y); - if (res > uint.MaxValue) - return 0; - - return (uint)res; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_DirWrite.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_DirWrite.cs deleted file mode 100644 index 57d6f228..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_DirWrite.cs +++ /dev/null @@ -1,1260 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Directory Write Support Routines. - */ - -using System; -using System.IO; - -using BitMiracle.LibTiff.Classic.Internal; - -namespace BitMiracle.LibTiff.Classic -{ -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff - { - private uint insertData(TiffType type, int v) - { - int t = (int)type; - if (m_header.tiff_magic == TIFF_BIGENDIAN) - return (((uint)v & m_typemask[t]) << m_typeshift[t]); - - return ((uint)v & m_typemask[t]); - } - - private static void resetFieldBit(int[] fields, short f) - { - fields[f / 32] &= ~BITn(f); - } - - private static bool fieldSet(int[] fields, short f) - { - return ((fields[f / 32] & BITn(f)) != 0); - } - - private bool writeRational(TiffType type, TiffTag tag, ref TiffDirEntry dir, float v) - { - dir.tdir_tag = tag; - dir.tdir_type = type; - dir.tdir_count = 1; - - float[] a = new float[1]; - a[0] = v; - if (!writeRationalArray(ref dir, a)) - return false; - - return true; - } - - private bool writeRationalPair(TiffDirEntry[] entries, int dirOffset, TiffType type, TiffTag tag1, float v1, TiffTag tag2, float v2) - { - if (!writeRational(type, tag1, ref entries[dirOffset], v1)) - return false; - - if (!writeRational(type, tag2, ref entries[dirOffset + 1], v2)) - return false; - - return true; - } - - /// - /// Writes the contents of the current directory to the specified file. - /// - /// This routine doesn't handle overwriting a directory with - /// auxiliary storage that's been changed. - private bool writeDirectory(bool done) - { - if (m_mode == O_RDONLY) - return true; - - // Clear write state so that subsequent images with different - // characteristics get the right buffers setup for them. - if (done) - { - if ((m_flags & TiffFlags.POSTENCODE) == TiffFlags.POSTENCODE) - { - m_flags &= ~TiffFlags.POSTENCODE; - if (!m_currentCodec.PostEncode()) - { - ErrorExt(this, m_clientdata, m_name, "Error post-encoding before directory write"); - return false; - } - } - - // shutdown encoder - m_currentCodec.Close(); - - // Flush any data that might have been written by the - // compression close+cleanup routines. - if (m_rawcc > 0 && (m_flags & TiffFlags.BEENWRITING) == TiffFlags.BEENWRITING && !flushData1()) - { - ErrorExt(this, m_clientdata, m_name, "Error flushing data before directory write"); - return false; - } - - if ((m_flags & TiffFlags.MYBUFFER) == TiffFlags.MYBUFFER && m_rawdata != null) - { - m_rawdata = null; - m_rawcc = 0; - m_rawdatasize = 0; - } - - m_flags &= ~(TiffFlags.BEENWRITING | TiffFlags.BUFFERSETUP); - } - - // Size the directory so that we can calculate offsets for the data - // items that aren't kept in-place in each field. - int nfields = 0; - for (int b = 0; b <= FieldBit.Last; b++) - { - if (fieldSet(b) && b != FieldBit.Custom) - nfields += (b < FieldBit.SubFileType ? 2 : 1); - } - - nfields += m_dir.td_customValueCount; - int dirsize = nfields * TiffDirEntry.SizeInBytes; - TiffDirEntry[] data = new TiffDirEntry [nfields]; - for (int i = 0; i < nfields; i++) - data[i] = new TiffDirEntry(); - - // Directory hasn't been placed yet, put it at the end of the file - // and link it into the existing directory structure. - if (m_diroff == 0 && !linkDirectory()) - return false; - - m_dataoff = m_diroff + sizeof(short) + (uint)dirsize + sizeof(int); - if ((m_dataoff & 1) != 0) - m_dataoff++; - - seekFile(m_dataoff, SeekOrigin.Begin); - m_curdir++; - int dir = 0; - - // Setup external form of directory entries and write data items. - int[] fields = new int[FieldBit.SetLongs]; - Buffer.BlockCopy(m_dir.td_fieldsset, 0, fields, 0, FieldBit.SetLongs * sizeof(int)); - - // Write out ExtraSamples tag only if extra samples are present in the data. - if (fieldSet(fields, FieldBit.ExtraSamples) && m_dir.td_extrasamples == 0) - { - resetFieldBit(fields, FieldBit.ExtraSamples); - nfields--; - dirsize -= TiffDirEntry.SizeInBytes; - } // XXX - - for (int fi = 0, nfi = m_nfields; nfi > 0; nfi--, fi++) - { - TiffFieldInfo fip = m_fieldinfo[fi]; - - // For custom fields, we test to see if the custom field is set - // or not. For normal fields, we just use the fieldSet test. - if (fip.Bit == FieldBit.Custom) - { - bool is_set = false; - for (int ci = 0; ci < m_dir.td_customValueCount; ci++) - is_set |= (m_dir.td_customValues[ci].info == fip); - - if (!is_set) - continue; - } - else if (!fieldSet(fields, fip.Bit)) - { - continue; - } - - // Handle other fields. - - TiffTag tag = (TiffTag)FieldBit.Ignore; - switch (fip.Bit) - { - case FieldBit.StripOffsets: - // We use one field bit for both strip and tile - // offsets, and so must be careful in selecting - // the appropriate field descriptor (so that tags - // are written in sorted order). - tag = IsTiled() ? TiffTag.TILEOFFSETS : TiffTag.STRIPOFFSETS; - if (tag != fip.Tag) - continue; - - data[dir].tdir_tag = tag; - data[dir].tdir_type = TiffType.LONG; - data[dir].tdir_count = m_dir.td_nstrips; - if (!writeLongArray(ref data[dir], m_dir.td_stripoffset)) - return false; - - break; - case FieldBit.StripByteCounts: - // We use one field bit for both strip and tile byte - // counts, and so must be careful in selecting the - // appropriate field descriptor (so that tags are - // written in sorted order). - tag = IsTiled() ? TiffTag.TILEBYTECOUNTS: TiffTag.STRIPBYTECOUNTS; - if (tag != fip.Tag) - continue; - - data[dir].tdir_tag = tag; - data[dir].tdir_type = TiffType.LONG; - data[dir].tdir_count = m_dir.td_nstrips; - if (!writeLongArray(ref data[dir], m_dir.td_stripbytecount)) - return false; - - break; - case FieldBit.RowsPerStrip: - setupShortLong(TiffTag.ROWSPERSTRIP, ref data[dir], m_dir.td_rowsperstrip); - break; - case FieldBit.ColorMap: - if (!writeShortTable(TiffTag.COLORMAP, ref data[dir], 3, m_dir.td_colormap)) - return false; - - break; - case FieldBit.ImageDimensions: - setupShortLong(TiffTag.IMAGEWIDTH, ref data[dir++], m_dir.td_imagewidth); - setupShortLong(TiffTag.IMAGELENGTH, ref data[dir], m_dir.td_imagelength); - break; - case FieldBit.TileDimensions: - setupShortLong(TiffTag.TILEWIDTH, ref data[dir++], m_dir.td_tilewidth); - setupShortLong(TiffTag.TILELENGTH, ref data[dir], m_dir.td_tilelength); - break; - case FieldBit.Compression: - setupShort(TiffTag.COMPRESSION, ref data[dir], (short)m_dir.td_compression); - break; - case FieldBit.Photometric: - setupShort(TiffTag.PHOTOMETRIC, ref data[dir], (short)m_dir.td_photometric); - break; - case FieldBit.Position: - if (!writeRationalPair(data, dir, TiffType.RATIONAL, TiffTag.XPOSITION, m_dir.td_xposition, TiffTag.YPOSITION, m_dir.td_yposition)) - return false; - - dir++; - break; - case FieldBit.Resolution: - if (!writeRationalPair(data, dir, TiffType.RATIONAL, TiffTag.XRESOLUTION, m_dir.td_xresolution, TiffTag.YRESOLUTION, m_dir.td_yresolution)) - return false; - - dir++; - break; - case FieldBit.BitsPerSample: - case FieldBit.MinSampleValue: - case FieldBit.MaxSampleValue: - case FieldBit.SampleFormat: - if (!writePerSampleShorts(fip.Tag, ref data[dir])) - return false; - - break; - case FieldBit.SMinSampleValue: - case FieldBit.SMaxSampleValue: - if (!writePerSampleAnys(sampleToTagType(), fip.Tag, ref data[dir])) - return false; - - break; - case FieldBit.PageNumber: - case FieldBit.HalftoneHints: - case FieldBit.YCbCrSubsampling: - if (!setupShortPair(fip.Tag, ref data[dir])) - return false; - - break; - case FieldBit.InkNames: - if (!writeInkNames(ref data[dir])) - return false; - - break; - case FieldBit.TransferFunction: - if (!writeTransferFunction(ref data[dir])) - return false; - - break; - case FieldBit.SubIFD: - // XXX: Always write this field using LONG type - // for backward compatibility. - data[dir].tdir_tag = fip.Tag; - data[dir].tdir_type = TiffType.LONG; - data[dir].tdir_count = m_dir.td_nsubifd; - if (!writeLongArray(ref data[dir], m_dir.td_subifd)) - return false; - - // Total hack: if this directory includes a SubIFD - // tag then force the next directories to be - // written as "sub directories" of this one. This - // is used to write things like thumbnails and - // image masks that one wants to keep out of the - // normal directory linkage access mechanism. - if (data[dir].tdir_count > 0) - { - m_flags |= TiffFlags.INSUBIFD; - m_nsubifd = (short)data[dir].tdir_count; - if (data[dir].tdir_count > 1) - { - m_subifdoff = data[dir].tdir_offset; - } - else - { - m_subifdoff = m_diroff + sizeof(short) + - (uint)dir * TiffDirEntry.SizeInBytes + - sizeof(short) * 2 + sizeof(int); - } - } - break; - default: - // XXX: Should be fixed and removed. - if (fip.Tag == TiffTag.DOTRANGE) - { - if (!setupShortPair(fip.Tag, ref data[dir])) - return false; - } - else if (!writeNormalTag(ref data[dir], fip)) - return false; - - break; - } - - dir++; - - if (fip.Bit != FieldBit.Custom) - resetFieldBit(fields, fip.Bit); - } - - // Write directory. - - short dircount = (short)nfields; - uint diroff = m_nextdiroff; - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - { - // The file's byte order is opposite to the native machine - // architecture. We overwrite the directory information with - // impunity because it'll be released below after we write it to - // the file. Note that all the other tag construction routines - // assume that we do this byte-swapping; i.e. they only - // byte-swap indirect data. - for (dir = 0; dircount != 0; dir++, dircount--) - { - short temp = (short)data[dir].tdir_tag; - SwabShort(ref temp); - data[dir].tdir_tag = (TiffTag)(ushort)temp; - - temp = (short)data[dir].tdir_type; - SwabShort(ref temp); - data[dir].tdir_type = (TiffType)temp; - - SwabLong(ref data[dir].tdir_count); - SwabUInt(ref data[dir].tdir_offset); - } - - dircount = (short)nfields; - SwabShort(ref dircount); - SwabUInt(ref diroff); - } - - seekFile(m_diroff, SeekOrigin.Begin); - if (!writeShortOK(dircount)) - { - ErrorExt(this, m_clientdata, m_name, "Error writing directory count"); - return false; - } - - if (!writeDirEntryOK(data, dirsize / TiffDirEntry.SizeInBytes)) - { - ErrorExt(this, m_clientdata, m_name, "Error writing directory contents"); - return false; - } - - if (!writeIntOK((int)diroff)) - { - ErrorExt(this, m_clientdata, m_name, "Error writing directory link"); - return false; - } - - if (done) - { - FreeDirectory(); - m_flags &= ~TiffFlags.DIRTYDIRECT; - m_currentCodec.Cleanup(); - - // Reset directory-related state for subsequent directories. - CreateDirectory(); - } - - return true; - } - - /// - /// Writes tags that are not special cased. - /// - private bool writeNormalTag(ref TiffDirEntry dir, TiffFieldInfo fip) - { - short wc = fip.WriteCount; - dir.tdir_tag = fip.Tag; - dir.tdir_type = fip.Type; - dir.tdir_count = wc; - - switch (fip.Type) - { - case TiffType.SHORT: - case TiffType.SSHORT: - if (fip.PassCount) - { - short[] wp; - int wc2; - if (wc == TiffFieldInfo.Variable2) - { - FieldValue[] result = GetField(fip.Tag); - wc2 = result[0].ToInt(); - wp = result[1].ToShortArray(); - - dir.tdir_count = wc2; - } - else - { - // Assume TiffFieldInfo.Variable - FieldValue[] result = GetField(fip.Tag); - wc = result[0].ToShort(); - wp = result[1].ToShortArray(); - dir.tdir_count = wc; - } - - if (!writeShortArray(ref dir, wp)) - return false; - } - else - { - if (wc == 1) - { - FieldValue[] result = GetField(fip.Tag); - short sv = result[0].ToShort(); - dir.tdir_offset = insertData(dir.tdir_type, sv); - } - else - { - FieldValue[] result = GetField(fip.Tag); - short[] wp = result[0].ToShortArray(); - if (!writeShortArray(ref dir, wp)) - return false; - } - } - break; - case TiffType.LONG: - case TiffType.SLONG: - case TiffType.IFD: - if (fip.PassCount) - { - int[] lp; - int wc2; - if (wc == TiffFieldInfo.Variable2) - { - FieldValue[] result = GetField(fip.Tag); - wc2 = result[0].ToInt(); - lp = result[1].ToIntArray(); - dir.tdir_count = wc2; - } - else - { - // Assume TiffFieldInfo.Variable - FieldValue[] result = GetField(fip.Tag); - wc = result[0].ToShort(); - lp = result[1].ToIntArray(); - dir.tdir_count = wc; - } - - if (!writeLongArray(ref dir, lp)) - return false; - } - else - { - if (wc == 1) - { - // XXX handle LONG->SHORT conversion - FieldValue[] result = GetField(fip.Tag); - dir.tdir_offset = result[0].ToUInt(); - } - else - { - int[] lp; - FieldValue[] result = GetField(fip.Tag); - lp = result[0].ToIntArray(); - if (!writeLongArray(ref dir, lp)) - return false; - } - } - break; - case TiffType.RATIONAL: - case TiffType.SRATIONAL: - if (fip.PassCount) - { - float[] fp; - int wc2; - if (wc == TiffFieldInfo.Variable2) - { - FieldValue[] result = GetField(fip.Tag); - wc2 = result[0].ToInt(); - fp = result[1].ToFloatArray(); - dir.tdir_count = wc2; - } - else - { - // Assume TiffFieldInfo.Variable - FieldValue[] result = GetField(fip.Tag); - wc = result[0].ToShort(); - fp = result[1].ToFloatArray(); - dir.tdir_count = wc; - } - - if (!writeRationalArray(ref dir, fp)) - return false; - } - else - { - if (wc == 1) - { - float[] fv = new float[1]; - FieldValue[] result = GetField(fip.Tag); - fv[0] = result[0].ToFloat(); - if (!writeRationalArray(ref dir, fv)) - return false; - } - else - { - FieldValue[] result = GetField(fip.Tag); - float[] fp = result[0].ToFloatArray(); - if (!writeRationalArray(ref dir, fp)) - return false; - } - } - break; - case TiffType.FLOAT: - if (fip.PassCount) - { - float[] fp; - int wc2; - if (wc == TiffFieldInfo.Variable2) - { - FieldValue[] result = GetField(fip.Tag); - wc2 = result[0].ToInt(); - fp = result[1].ToFloatArray(); - dir.tdir_count = wc2; - } - else - { - // Assume TiffFieldInfo.Variable - FieldValue[] result = GetField(fip.Tag); - wc = result[0].ToShort(); - fp = result[1].ToFloatArray(); - dir.tdir_count = wc; - } - - if (!writeFloatArray(ref dir, fp)) - return false; - } - else - { - if (wc == 1) - { - float[] fv = new float[1]; - FieldValue[] result = GetField(fip.Tag); - fv[0] = result[0].ToFloat(); - if (!writeFloatArray(ref dir, fv)) - return false; - } - else - { - FieldValue[] result = GetField(fip.Tag); - float[] fp = result[0].ToFloatArray(); - if (!writeFloatArray(ref dir, fp)) - return false; - } - } - break; - case TiffType.DOUBLE: - if (fip.PassCount) - { - double[] dp; - int wc2; - if (wc == TiffFieldInfo.Variable2) - { - FieldValue[] result = GetField(fip.Tag); - wc2 = result[0].ToInt(); - dp = result[1].ToDoubleArray(); - dir.tdir_count = wc2; - } - else - { - // Assume TiffFieldInfo.Variable - FieldValue[] result = GetField(fip.Tag); - wc = result[0].ToShort(); - dp = result[1].ToDoubleArray(); - dir.tdir_count = wc; - } - - if (!writeDoubleArray(ref dir, dp)) - return false; - } - else - { - if (wc == 1) - { - double[] dv = new double[1]; - FieldValue[] result = GetField(fip.Tag); - dv[0] = result[0].ToDouble(); - if (!writeDoubleArray(ref dir, dv)) - return false; - } - else - { - FieldValue[] result = GetField(fip.Tag); - double[] dp = result[0].ToDoubleArray(); - if (!writeDoubleArray(ref dir, dp)) - return false; - } - } - break; - case TiffType.ASCII: - { - FieldValue[] result = GetField(fip.Tag); - - string cp; - if (fip.PassCount) - cp = result[1].ToString(); - else - cp = result[0].ToString(); - - // add zero ('\0') at the end of the byte array - byte[] stringBytes = Latin1Encoding.GetBytes(cp); - byte[] totalBytes = new byte[stringBytes.Length + 1]; - Buffer.BlockCopy(stringBytes, 0, totalBytes, 0, stringBytes.Length); - - dir.tdir_count = totalBytes.Length; - if (!writeByteArray(ref dir, totalBytes)) - return false; - } - break; - - case TiffType.BYTE: - case TiffType.SBYTE: - if (fip.PassCount) - { - byte[] cp; - int wc2; - if (wc == TiffFieldInfo.Variable2) - { - FieldValue[] result = GetField(fip.Tag); - wc2 = result[0].ToInt(); - cp = result[1].ToByteArray(); - dir.tdir_count = wc2; - } - else - { - // Assume TiffFieldInfo.Variable - FieldValue[] result = GetField(fip.Tag); - wc = result[0].ToShort(); - cp = result[1].ToByteArray(); - dir.tdir_count = wc; - } - - if (!writeByteArray(ref dir, cp)) - return false; - } - else - { - if (wc == 1) - { - byte[] cv = new byte[1]; - FieldValue[] result = GetField(fip.Tag); - cv[0] = result[0].ToByte(); - if (!writeByteArray(ref dir, cv)) - return false; - } - else - { - FieldValue[] result = GetField(fip.Tag); - byte[] cp = result[0].ToByteArray(); - if (!writeByteArray(ref dir, cp)) - return false; - } - } - break; - - case TiffType.UNDEFINED: - { - byte[] cp; - int wc2; - if (wc == TiffFieldInfo.Variable) - { - FieldValue[] result = GetField(fip.Tag); - wc = result[0].ToShort(); - cp = result[1].ToByteArray(); - dir.tdir_count = wc; - } - else if (wc == TiffFieldInfo.Variable2) - { - FieldValue[] result = GetField(fip.Tag); - wc2 = result[0].ToInt(); - cp = result[1].ToByteArray(); - dir.tdir_count = wc2; - } - else - { - FieldValue[] result = GetField(fip.Tag); - cp = result[0].ToByteArray(); - } - - if (!writeByteArray(ref dir, cp)) - return false; - } - break; - - case TiffType.NOTYPE: - break; - } - - return true; - } - - /// - /// Setups a directory entry with either a SHORT or LONG type - /// according to the value. - /// - private void setupShortLong(TiffTag tag, ref TiffDirEntry dir, int v) - { - dir.tdir_tag = tag; - dir.tdir_count = 1; - if (v > 0xffffL) - { - dir.tdir_type = TiffType.LONG; - dir.tdir_offset = (uint)v; - } - else - { - dir.tdir_type = TiffType.SHORT; - dir.tdir_offset = insertData(TiffType.SHORT, v); - } - } - - /// - /// Setups a SHORT directory entry - /// - private void setupShort(TiffTag tag, ref TiffDirEntry dir, short v) - { - dir.tdir_tag = tag; - dir.tdir_count = 1; - dir.tdir_type = TiffType.SHORT; - dir.tdir_offset = insertData(TiffType.SHORT, v); - } - - /* - * Setup a directory entry that references a - * samples/pixel array of SHORT values and - * (potentially) write the associated indirect - * values. - */ - private bool writePerSampleShorts(TiffTag tag, ref TiffDirEntry dir) - { - short[] w = new short [m_dir.td_samplesperpixel]; - - FieldValue[] result = GetField(tag); - short v = result[0].ToShort(); - - for (short i = 0; i < m_dir.td_samplesperpixel; i++) - w[i] = v; - - dir.tdir_tag = tag; - dir.tdir_type = TiffType.SHORT; - dir.tdir_count = m_dir.td_samplesperpixel; - bool status = writeShortArray(ref dir, w); - return status; - } - - /* - * Setup a directory entry that references a samples/pixel array of ``type'' - * values and (potentially) write the associated indirect values. The source - * data from GetField() for the specified tag must be returned as double. - */ - private bool writePerSampleAnys(TiffType type, TiffTag tag, ref TiffDirEntry dir) - { - double[] w = new double [m_dir.td_samplesperpixel]; - - FieldValue[] result = GetField(tag); - double v = result[0].ToDouble(); - - for (short i = 0; i < m_dir.td_samplesperpixel; i++) - w[i] = v; - - bool status = writeAnyArray(type, tag, ref dir, m_dir.td_samplesperpixel, w); - return status; - } - - /* - * Setup a pair of shorts that are returned by - * value, rather than as a reference to an array. - */ - private bool setupShortPair(TiffTag tag, ref TiffDirEntry dir) - { - short[] v = new short[2]; - FieldValue[] result = GetField(tag); - v[0] = result[0].ToShort(); - v[1] = result[1].ToShort(); - - dir.tdir_tag = tag; - dir.tdir_type = TiffType.SHORT; - dir.tdir_count = 2; - return writeShortArray(ref dir, v); - } - - /// - /// Setup a directory entry for an NxM table of shorts, where M is - /// known to be 2**bitspersample, and write the associated indirect data. - /// - private bool writeShortTable(TiffTag tag, ref TiffDirEntry dir, int n, short[][] table) - { - dir.tdir_tag = tag; - dir.tdir_type = TiffType.SHORT; - - // XXX -- yech, fool writeData - dir.tdir_count = 1 << m_dir.td_bitspersample; - uint off = m_dataoff; - for (int i = 0; i < n; i++) - { - if (!writeData(ref dir, table[i], dir.tdir_count)) - return false; - } - - dir.tdir_count *= n; - dir.tdir_offset = off; - return true; - } - - /// - /// Write/copy data associated with an ASCII or opaque tag value. - /// - private bool writeByteArray(ref TiffDirEntry dir, byte[] cp) - { - if (dir.tdir_count <= 4) - { - if (m_header.tiff_magic == TIFF_BIGENDIAN) - { - dir.tdir_offset = (uint)(cp[0] << 24); - if (dir.tdir_count >= 2) - dir.tdir_offset |= (uint)(cp[1] << 16); - - if (dir.tdir_count >= 3) - dir.tdir_offset |= (uint)(cp[2] << 8); - - if (dir.tdir_count == 4) - dir.tdir_offset |= cp[3]; - } - else - { - dir.tdir_offset = cp[0]; - if (dir.tdir_count >= 2) - dir.tdir_offset |= (uint)(cp[1] << 8); - - if (dir.tdir_count >= 3) - dir.tdir_offset |= (uint)(cp[2] << 16); - - if (dir.tdir_count == 4) - dir.tdir_offset |= (uint)(cp[3] << 24); - } - - return true; - } - - return writeData(ref dir, cp, dir.tdir_count); - } - - /// - /// Setup a directory entry of an array of SHORT or SSHORT and write - /// the associated indirect values. - /// - private bool writeShortArray(ref TiffDirEntry dir, short[] v) - { - if (dir.tdir_count <= 2) - { - if (m_header.tiff_magic == TIFF_BIGENDIAN) - { - dir.tdir_offset = (uint)(v[0] << 16); - if (dir.tdir_count == 2) - dir.tdir_offset |= (uint)(v[1] & 0xffff); - } - else - { - dir.tdir_offset = (uint)(v[0] & 0xffff); - if (dir.tdir_count == 2) - dir.tdir_offset |= (uint)(v[1] << 16); - } - - return true; - } - - return writeData(ref dir, v, dir.tdir_count); - } - - /// - /// Setup a directory entry of an array of LONG or SLONG and write the - /// associated indirect values. - /// - private bool writeLongArray(ref TiffDirEntry dir, int[] v) - { - if (dir.tdir_count == 1) - { - dir.tdir_offset = (uint)v[0]; - return true; - } - - return writeData(ref dir, v, dir.tdir_count); - } - - private bool writeLongArray(ref TiffDirEntry dir, uint[] v) - { - int[] temp = new int[v.Length]; - Buffer.BlockCopy(v, 0, temp, 0, v.Length * sizeof(uint)); - return writeLongArray(ref dir, temp); - } - - /// - /// Setup a directory entry of an array of RATIONAL or SRATIONAL and - /// write the associated indirect values. - /// - private bool writeRationalArray(ref TiffDirEntry dir, float[] v) - { - int[] t = new int [2 * dir.tdir_count]; - for (int i = 0; i < dir.tdir_count; i++) - { - int sign = 1; - float fv = v[i]; - if (fv < 0) - { - if (dir.tdir_type == TiffType.RATIONAL) - { - WarningExt(this, m_clientdata, m_name, - "\"{0}\": Information lost writing value ({1:G}) as (unsigned) RATIONAL", - FieldWithTag(dir.tdir_tag).Name, fv); - fv = 0; - } - else - { - fv = -fv; - sign = -1; - } - } - - int den = 1; - if (fv > 0) - { - while (fv < (1L << (31 - 3)) && den < (1L << (31 - 3))) - { - fv *= 1 << 3; - den *= 1 << 3; - } - } - - t[2 * i + 0] = (int)(sign * (fv + 0.5)); - t[2 * i + 1] = den; - } - - return writeData(ref dir, t, 2 * dir.tdir_count); - } - - private bool writeFloatArray(ref TiffDirEntry dir, float[] v) - { - if (dir.tdir_count == 1) - { - dir.tdir_offset = BitConverter.ToUInt32(BitConverter.GetBytes(v[0]), 0); - return true; - } - - return writeData(ref dir, v, dir.tdir_count); - } - - private bool writeDoubleArray(ref TiffDirEntry dir, double[] v) - { - return writeData(ref dir, v, dir.tdir_count); - } - - /// - /// Writes an array of "type" values for a specified tag (i.e. this is - /// a tag which is allowed to have different types, e.g. SMaxSampleType). - /// Internally the data values are represented as double since a double - /// can hold any of the TIFF tag types (yes, this should really be an abstract - /// type tany_t for portability). The data is converted into the specified - /// type in a temporary buffer and then handed off to the appropriate array - /// writer. - /// - private bool writeAnyArray(TiffType type, TiffTag tag, ref TiffDirEntry dir, int n, double[] v) - { - dir.tdir_tag = tag; - dir.tdir_type = type; - dir.tdir_count = n; - - bool failed = false; - switch (type) - { - case TiffType.BYTE: - case TiffType.SBYTE: - { - byte[] bp = new byte [n]; - for (int i = 0; i < n; i++) - bp[i] = (byte)v[i]; - - if (!writeByteArray(ref dir, bp)) - failed = true; - } - break; - case TiffType.SHORT: - case TiffType.SSHORT: - { - short[] bp = new short [n]; - for (int i = 0; i < n; i++) - bp[i] = (short)v[i]; - - if (!writeShortArray(ref dir, bp)) - failed = true; - } - break; - case TiffType.LONG: - case TiffType.SLONG: - { - int[] bp = new int [n]; - for (int i = 0; i < n; i++) - bp[i] = (int)v[i]; - - if (!writeLongArray(ref dir, bp)) - failed = true; - } - break; - case TiffType.FLOAT: - { - float[] bp = new float [n]; - for (int i = 0; i < n; i++) - bp[i] = (float)v[i]; - - if (!writeFloatArray(ref dir, bp)) - failed = true; - } - break; - case TiffType.DOUBLE: - if (!writeDoubleArray(ref dir, v)) - failed = true; - - break; - - default: - // NOTYPE - // ASCII - // UNDEFINED - // RATIONAL - // SRATIONAL - failed = true; - break; - } - - return !failed; - } - - private bool writeTransferFunction(ref TiffDirEntry dir) - { - // Check if the table can be written as a single column, or if it - // must be written as 3 columns. Note that we write a 3-column tag - // if there are 2 samples/pixel and a single column of data - // won't suffice--hmm. - int u = m_dir.td_samplesperpixel - m_dir.td_extrasamples; - int ncols = 1; - bool reCheck = false; - int n = 1 << m_dir.td_bitspersample; - - if (u < 0 || u > 2) - { - if (Compare(m_dir.td_transferfunction[0], m_dir.td_transferfunction[2], n) != 0) - ncols = 3; - else - reCheck = true; - } - - if (u == 2 || reCheck) - { - if (Compare(m_dir.td_transferfunction[0], m_dir.td_transferfunction[1], n) != 0) - ncols = 3; - } - - return writeShortTable(TiffTag.TRANSFERFUNCTION, ref dir, ncols, m_dir.td_transferfunction); - } - - private bool writeInkNames(ref TiffDirEntry dir) - { - dir.tdir_tag = TiffTag.INKNAMES; - dir.tdir_type = TiffType.ASCII; - byte[] bytes = Latin1Encoding.GetBytes(m_dir.td_inknames); - dir.tdir_count = bytes.Length; - return writeByteArray(ref dir, bytes); - } - - /// - /// Writes a contiguous directory item. - /// - private bool writeData(ref TiffDirEntry dir, byte[] buffer, int count) - { - dir.tdir_offset = m_dataoff; - count = (int)dir.tdir_count * DataWidth(dir.tdir_type); - if (seekOK(dir.tdir_offset) && writeOK(buffer, 0, count)) - { - m_dataoff += (uint)((count + 1) & ~1); - return true; - } - - ErrorExt(this, m_clientdata, m_name, - "Error writing data for field \"{0}\"", - FieldWithTag(dir.tdir_tag).Name); - return false; - } - - private bool writeData(ref TiffDirEntry dir, short[] buffer, int count) - { - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabArrayOfShort(buffer, count); - - int byteCount = count * sizeof(short); - byte[] bytes = new byte [byteCount]; - ShortsToByteArray(buffer, 0, count, bytes, 0); - return writeData(ref dir, bytes, byteCount); - } - - private bool writeData(ref TiffDirEntry dir, int[] cp, int cc) - { - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabArrayOfLong(cp, cc); - - int byteCount = cc * sizeof(int); - byte[] bytes = new byte [byteCount]; - IntsToByteArray(cp, 0, cc, bytes, 0); - bool res = writeData(ref dir, bytes, byteCount); - return res; - } - - private bool writeData(ref TiffDirEntry dir, float[] cp, int cc) - { - int[] ints = new int[cc]; - for (int i = 0; i < cc; i++) - { - byte[] result = BitConverter.GetBytes(cp[i]); - ints[i] = BitConverter.ToInt32(result, 0); - } - - return writeData(ref dir, ints, cc); - } - - private bool writeData(ref TiffDirEntry dir, double[] buffer, int count) - { - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabArrayOfDouble(buffer, count); - - byte[] bytes = new byte[count * sizeof(double)]; - Buffer.BlockCopy(buffer, 0, bytes, 0, bytes.Length); - - return writeData(ref dir, bytes, count * sizeof(double)); - } - - /// - /// Link the current directory into the directory chain for the file. - /// - private bool linkDirectory() - { - const string module = "linkDirectory"; - - m_diroff = (uint)((seekFile(0, SeekOrigin.End) + 1) & ~1); - uint diroff = m_diroff; - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabUInt(ref diroff); - - // Handle SubIFDs - - if ((m_flags & TiffFlags.INSUBIFD) == TiffFlags.INSUBIFD) - { - seekFile(m_subifdoff, SeekOrigin.Begin); - if (!writeIntOK((int)diroff)) - { - ErrorExt(this, m_clientdata, module, - "{0}: Error writing SubIFD directory link", m_name); - return false; - } - - // Advance to the next SubIFD or, if this is the last one - // configured, revert back to the normal directory linkage. - --m_nsubifd; - - if (m_nsubifd != 0) - m_subifdoff += sizeof(int); - else - m_flags &= ~TiffFlags.INSUBIFD; - - return true; - } - - if (m_header.tiff_diroff == 0) - { - // First directory, overwrite offset in header. - - m_header.tiff_diroff = m_diroff; - seekFile(TiffHeader.TIFF_MAGIC_SIZE + TiffHeader.TIFF_VERSION_SIZE, SeekOrigin.Begin); - if (!writeIntOK((int)diroff)) - { - ErrorExt(this, m_clientdata, m_name, "Error writing TIFF header"); - return false; - } - - return true; - } - - // Not the first directory, search to the last and append. - - uint nextdir = m_header.tiff_diroff; - do - { - short dircount; - if (!seekOK(nextdir) || !readShortOK(out dircount)) - { - ErrorExt(this, m_clientdata, module, "Error fetching directory count"); - return false; - } - - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabShort(ref dircount); - - seekFile(dircount * TiffDirEntry.SizeInBytes, SeekOrigin.Current); - if (!readUIntOK(out nextdir)) - { - ErrorExt(this, m_clientdata, module, "Error fetching directory link"); - return false; - } - - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabUInt(ref nextdir); - } - while (nextdir != 0); - - // get current offset - long off = seekFile(0, SeekOrigin.Current); - seekFile(off - sizeof(int), SeekOrigin.Begin); - - if (!writeIntOK((int)diroff)) - { - ErrorExt(this, m_clientdata, module, "Error writing directory link"); - return false; - } - - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Internal.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Internal.cs deleted file mode 100644 index e2b797ff..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Internal.cs +++ /dev/null @@ -1,562 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System; -using System.IO; -using System.Text; - -using BitMiracle.LibTiff.Classic.Internal; - -namespace BitMiracle.LibTiff.Classic -{ -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff - { - private const int TIFF_VERSION = 42; - private const int TIFF_BIGTIFF_VERSION = 43; - - private const short TIFF_BIGENDIAN = 0x4d4d; - private const short TIFF_LITTLEENDIAN = 0x4949; - private const short MDI_LITTLEENDIAN = 0x5045; - - // reference white - private const float D50_X0 = 96.4250F; - private const float D50_Y0 = 100.0F; - private const float D50_Z0 = 82.4680F; - - internal const int STRIP_SIZE_DEFAULT = 8192; - - /// - /// Support strip chopping (whether or not to convert single-strip - /// uncompressed images to mutiple strips of ~8Kb to reduce memory usage) - /// - internal const TiffFlags STRIPCHOP_DEFAULT = TiffFlags.STRIPCHOP; - - /// - /// Treat extra sample as alpha (default enabled). The RGBA interface - /// will treat a fourth sample with no EXTRASAMPLE_ value as being - /// ASSOCALPHA. Many packages produce RGBA files but don't mark the - /// alpha properly. - /// - internal const bool DEFAULT_EXTRASAMPLE_AS_ALPHA = true; - - /// - /// Pick up YCbCr subsampling info from the JPEG data stream to support - /// files lacking the tag (default enabled). - /// - internal const bool CHECK_JPEG_YCBCR_SUBSAMPLING = true; - -#if !SILVERLIGHT - internal static readonly Encoding Latin1Encoding = Encoding.GetEncoding("Latin1"); -#else - // Encoding.GetEncoding("Latin1") is not supported in Silverlight. Will throw exceptions at runtime. - internal static readonly Enc28591 Latin1Encoding = new Enc28591(); -#endif - - internal enum PostDecodeMethodType - { - pdmNone, - pdmSwab16Bit, - pdmSwab24Bit, - pdmSwab32Bit, - pdmSwab64Bit - }; - - /// - /// name of open file - /// - internal string m_name; - - /// - /// open mode (O_*) - /// - internal int m_mode; - internal TiffFlags m_flags; - - // - // the first directory - // - - /// - /// file offset of current directory - /// - internal uint m_diroff; - - // directories to prevent IFD looping - - /// - /// internal rep of current directory - /// - internal TiffDirectory m_dir; - - /// - /// current scanline - /// - internal int m_row; - - /// - /// current strip for read/write - /// - internal int m_curstrip; - - // tiling support - - /// - /// current tile for read/write - /// - internal int m_curtile; - - /// - /// # of bytes in a tile - /// - internal int m_tilesize; - - // compression scheme hooks - internal TiffCodec m_currentCodec; - - // input/output buffering - - /// - /// # of bytes in a scanline - /// - internal int m_scanlinesize; - - /// - /// raw data buffer - /// - internal byte[] m_rawdata; - - /// - /// # of bytes in raw data buffer - /// - internal int m_rawdatasize; - - /// - /// current spot in raw buffer - /// - internal int m_rawcp; - - /// - /// bytes unread from raw buffer - /// - internal int m_rawcc; - - /// - /// callback parameter - /// - internal object m_clientdata; - - // post-decoding support - - /// - /// post decoding method type - /// - internal PostDecodeMethodType m_postDecodeMethod; - - // tag support - - /// - /// tag get/set/print routines - /// - internal TiffTagMethods m_tagmethods; - - private class codecList - { - public codecList next; - public TiffCodec codec; - }; - - private class clientInfoLink - { - public clientInfoLink next; - public object data; - public string name; - }; - - // the first directory - - /// - /// file offset of following directory - /// - private uint m_nextdiroff; - - /// - /// list of offsets to already seen directories to prevent IFD looping - /// - private uint[] m_dirlist; - - /// - /// number of entires in offset list - /// - private int m_dirlistsize; - - /// - /// number of already seen directories - /// - private short m_dirnumber; - - /// - /// file's header block - /// - private TiffHeader m_header; - - /// - /// data type shift counts - /// - private int[] m_typeshift; - - /// - /// data type masks - /// - private uint[] m_typemask; - - /// - /// current directory (index) - /// - private short m_curdir; - - /// - /// current offset for read/write - /// - private uint m_curoff; - - /// - /// current offset for writing dir - /// - private uint m_dataoff; - - // - // SubIFD support - // - - /// - /// remaining subifds to write - /// - private short m_nsubifd; - - /// - /// offset for patching SubIFD link - /// - private uint m_subifdoff; - - // tiling support - - /// - /// current column (offset by row too) - /// - private int m_col; - - // compression scheme hooks - - private bool m_decodestatus; - - // tag support - - /// - /// sorted table of registered tags - /// - private TiffFieldInfo[] m_fieldinfo; - - /// - /// # entries in registered tag table - /// - private int m_nfields; - - /// - /// cached pointer to already found tag - /// - private TiffFieldInfo m_foundfield; - - /// - /// extra client information. - /// - private clientInfoLink m_clientinfo; - - private TiffCodec[] m_builtInCodecs; - private codecList m_registeredCodecs; - - private TiffTagMethods m_defaultTagMethods; - - private bool m_disposed; - private Stream m_fileStream; - - /// - /// stream used for read|write|etc. - /// - private TiffStream m_stream; - - private Tiff() - { - m_clientdata = 0; - m_postDecodeMethod = PostDecodeMethodType.pdmNone; - - setupBuiltInCodecs(); - - m_defaultTagMethods = new TiffTagMethods(); - - if (m_errorHandler == null) - { - // user did not setup custom handler. - // install default - m_errorHandler = new TiffErrorHandler(); - } - } - - private void Dispose(bool disposing) - { - if (!this.m_disposed) - { - // If disposing equals true, dispose all managed - // and unmanaged resources. - if (disposing) - { - // Dispose managed resources. - Close(); - - if (m_fileStream != null) - m_fileStream.Dispose(); - } - - // Call the appropriate methods to clean up - // unmanaged resources here. - - // Note disposing has been done. - m_disposed = true; - } - } - - /// - /// Writes custom directory. See ticket #51. - /// - /// Output directory offset. - /// true if succeeded; otherwise, false - private bool WriteCustomDirectory(out long pdiroff) - { - pdiroff = -1; - - if (m_mode == O_RDONLY) - return true; - - // Size the directory so that we can calculate offsets for the data - // items that aren't kept in-place in each field. - int nfields = 0; - for (int b = 0; b <= FieldBit.Last; b++) - { - if (fieldSet(b) && b != FieldBit.Custom) - nfields += (b < FieldBit.SubFileType ? 2 : 1); - } - - nfields += m_dir.td_customValueCount; - int dirsize = nfields * TiffDirEntry.SizeInBytes; - TiffDirEntry[] data = new TiffDirEntry[nfields]; - - // Put the directory at the end of the file. - m_diroff = (uint)((seekFile(0, SeekOrigin.End) + 1) & ~1); - m_dataoff = m_diroff + sizeof(short) + (uint)dirsize + sizeof(int); - if ((m_dataoff & 1) != 0) - m_dataoff++; - - seekFile(m_dataoff, SeekOrigin.Begin); - - // Setup external form of directory entries and write data items. - int[] fields = new int[FieldBit.SetLongs]; - Buffer.BlockCopy(m_dir.td_fieldsset, 0, fields, 0, FieldBit.SetLongs * sizeof(int)); - - for (int fi = 0, nfi = m_nfields; nfi > 0; nfi--, fi++) - { - TiffFieldInfo fip = m_fieldinfo[fi]; - - // For custom fields, we test to see if the custom field - // is set or not. For normal fields, we just use the FieldSet test. - if (fip.Bit == FieldBit.Custom) - { - bool is_set = false; - for (int ci = 0; ci < m_dir.td_customValueCount; ci++) - is_set |= (m_dir.td_customValues[ci].info == fip); - - if (!is_set) - continue; - } - else if (!fieldSet(fields, fip.Bit)) - continue; - - if (fip.Bit != FieldBit.Custom) - resetFieldBit(fields, fip.Bit); - } - - // Write directory. - - short dircount = (short)nfields; - pdiroff = m_nextdiroff; - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - { - // The file's byte order is opposite to the native machine - // architecture. We overwrite the directory information with - // impunity because it'll be released below after we write it - // to the file. Note that all the other tag construction - // routines assume that we do this byte-swapping; i.e. they only - // byte-swap indirect data. - for (int i = 0; i < dircount; i++) - { - TiffDirEntry dirEntry = data[i]; - - short temp = (short)dirEntry.tdir_tag; - SwabShort(ref temp); - dirEntry.tdir_tag = (TiffTag)(ushort)temp; - - temp = (short)dirEntry.tdir_type; - SwabShort(ref temp); - dirEntry.tdir_type = (TiffType)temp; - - SwabLong(ref dirEntry.tdir_count); - SwabUInt(ref dirEntry.tdir_offset); - } - - dircount = (short)nfields; - SwabShort(ref dircount); - - int tempOff = (int)pdiroff; - SwabLong(ref tempOff); - pdiroff = tempOff; - } - - seekFile(m_diroff, SeekOrigin.Begin); - if (!writeShortOK(dircount)) - { - ErrorExt(this, m_clientdata, m_name, "Error writing directory count"); - return false; - } - - if (!writeDirEntryOK(data, dirsize / TiffDirEntry.SizeInBytes)) - { - ErrorExt(this, m_clientdata, m_name, "Error writing directory contents"); - return false; - } - - if (!writeIntOK((int)pdiroff)) - { - ErrorExt(this, m_clientdata, m_name, "Error writing directory link"); - return false; - } - - return true; - } - - internal static void SwabUInt(ref uint lp) - { - byte[] cp = new byte[4]; - cp[0] = (byte)lp; - cp[1] = (byte)(lp >> 8); - cp[2] = (byte)(lp >> 16); - cp[3] = (byte)(lp >> 24); - - byte t = cp[3]; - cp[3] = cp[0]; - cp[0] = t; - - t = cp[2]; - cp[2] = cp[1]; - cp[1] = t; - - lp = (uint)(cp[0] & 0xFF); - lp += (uint)((cp[1] & 0xFF) << 8); - lp += (uint)((cp[2] & 0xFF) << 16); - lp += (uint)(cp[3] << 24); - } - - internal static uint[] Realloc(uint[] buffer, int elementCount, int newElementCount) - { - uint[] newBuffer = new uint[newElementCount]; - if (buffer != null) - { - int copyLength = Math.Min(elementCount, newElementCount); - Buffer.BlockCopy(buffer, 0, newBuffer, 0, copyLength * sizeof(uint)); - } - - return newBuffer; - } - - internal static TiffFieldInfo[] Realloc(TiffFieldInfo[] buffer, int elementCount, int newElementCount) - { - TiffFieldInfo[] newBuffer = new TiffFieldInfo [newElementCount]; - - if (buffer != null) - { - int copyLength = Math.Min(elementCount, newElementCount); - Array.Copy(buffer, newBuffer, copyLength); - } - - return newBuffer; - } - - internal static TiffTagValue[] Realloc(TiffTagValue[] buffer, int elementCount, int newElementCount) - { - TiffTagValue[] newBuffer = new TiffTagValue[newElementCount]; - - if (buffer != null) - { - int copyLength = Math.Min(elementCount, newElementCount); - Array.Copy(buffer, newBuffer, copyLength); - } - - return newBuffer; - } - - internal bool setCompressionScheme(Compression scheme) - { - TiffCodec c = FindCodec(scheme); - if (c == null) - { - /* - * Don't treat an unknown compression scheme as an error. - * This permits applications to open files with data that - * the library does not have builtin support for, but which - * may still be meaningful. - */ - c = m_builtInCodecs[0]; - } - - m_decodestatus = c.CanDecode; - m_flags &= ~(TiffFlags.NOBITREV | TiffFlags.NOREADRAW); - - m_currentCodec = c; - return c.Init(); - } - - /// - /// post decoding routine - /// - private void postDecode(byte[] buffer, int offset, int count) - { - switch (m_postDecodeMethod) - { - case PostDecodeMethodType.pdmSwab16Bit: - swab16BitData(buffer, offset, count); - break; - case PostDecodeMethodType.pdmSwab24Bit: - swab24BitData(buffer, offset, count); - break; - case PostDecodeMethodType.pdmSwab32Bit: - swab32BitData(buffer, offset, count); - break; - case PostDecodeMethodType.pdmSwab64Bit: - swab64BitData(buffer, offset, count); - break; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Open.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Open.cs deleted file mode 100644 index 6b289135..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Open.cs +++ /dev/null @@ -1,139 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System.IO; - -using BitMiracle.LibTiff.Classic.Internal; - -namespace BitMiracle.LibTiff.Classic -{ -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff - { - private static readonly uint[] typemask = - { - 0, // TIFF_NOTYPE - 0x000000ff, // TIFF_BYTE - 0xffffffff, // TIFF_ASCII - 0x0000ffff, // TIFF_SHORT - 0xffffffff, // TIFF_LONG - 0xffffffff, // TIFF_RATIONAL - 0x000000ff, // TIFF_SBYTE - 0x000000ff, // TIFF_UNDEFINED - 0x0000ffff, // TIFF_SSHORT - 0xffffffff, // TIFF_SLONG - 0xffffffff, // TIFF_SRATIONAL - 0xffffffff, // TIFF_FLOAT - 0xffffffff, // TIFF_DOUBLE - }; - - private static readonly int[] bigTypeshift = - { - 0, // TIFF_NOTYPE - 24, // TIFF_BYTE - 0, // TIFF_ASCII - 16, // TIFF_SHORT - 0, // TIFF_LONG - 0, // TIFF_RATIONAL - 24, // TIFF_SBYTE - 24, // TIFF_UNDEFINED - 16, // TIFF_SSHORT - 0, // TIFF_SLONG - 0, // TIFF_SRATIONAL - 0, // TIFF_FLOAT - 0, // TIFF_DOUBLE - }; - - private static readonly int[] litTypeshift = - { - 0, // TIFF_NOTYPE - 0, // TIFF_BYTE - 0, // TIFF_ASCII - 0, // TIFF_SHORT - 0, // TIFF_LONG - 0, // TIFF_RATIONAL - 0, // TIFF_SBYTE - 0, // TIFF_UNDEFINED - 0, // TIFF_SSHORT - 0, // TIFF_SLONG - 0, // TIFF_SRATIONAL - 0, // TIFF_FLOAT - 0, // TIFF_DOUBLE - }; - - /* - * Initialize the shift & mask tables, and the - * byte swapping state according to the file - * contents and the machine architecture. - */ - private void initOrder(int magic) - { - m_typemask = typemask; - if (magic == TIFF_BIGENDIAN) - { - m_typeshift = bigTypeshift; - m_flags |= TiffFlags.SWAB; - } - else - { - m_typeshift = litTypeshift; - } - } - - private static int getMode(string mode, string module, out FileMode m, out FileAccess a) - { - m = 0; - a = 0; - int tiffMode = -1; - - if (mode.Length == 0) - return tiffMode; - - switch (mode[0]) - { - case 'r': - m = FileMode.Open; - a = FileAccess.Read; - tiffMode = O_RDONLY; - if (mode.Length > 1 && mode[1] == '+') - { - a = FileAccess.ReadWrite; - tiffMode = O_RDWR; - } - break; - - case 'w': - m = FileMode.Create; - a = FileAccess.ReadWrite; - tiffMode = O_RDWR | O_CREAT | O_TRUNC; - break; - - case 'a': - m = FileMode.Open; - a = FileAccess.ReadWrite; - tiffMode = O_RDWR | O_CREAT; - break; - - default: - ErrorExt(null, 0, module, "\"{0}\": Bad mode", mode); - break; - } - - return tiffMode; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Print.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Print.cs deleted file mode 100644 index 0a152963..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Print.cs +++ /dev/null @@ -1,253 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Directory Printing Support - */ - -using System.IO; - -namespace BitMiracle.LibTiff.Classic -{ -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff - { - private static readonly string[] photoNames = - { - "min-is-white", // Photometric.MINISWHITE - "min-is-black", // Photometric.MINISBLACK - "RGB color", // Photometric.RGB - "palette color (RGB from colormap)", // Photometric.PALETTE - "transparency mask", // Photometric.MASK - "separated", // Photometric.SEPARATED - "YCbCr", // Photometric.YCBCR - "7 (0x7)", - "CIE L*a*b*", // Photometric.CIELAB - }; - - private static readonly string[] orientNames = - { - "0 (0x0)", - "row 0 top, col 0 lhs", // Orientation.TOPLEFT - "row 0 top, col 0 rhs", // Orientation.TOPRIGHT - "row 0 bottom, col 0 rhs", // Orientation.BOTRIGHT - "row 0 bottom, col 0 lhs", // Orientation.BOTLEFT - "row 0 lhs, col 0 top", // Orientation.LEFTTOP - "row 0 rhs, col 0 top", // Orientation.RIGHTTOP - "row 0 rhs, col 0 bottom", // Orientation.RIGHTBOT - "row 0 lhs, col 0 bottom", // Orientation.LEFTBOT - }; - - private static void printField(Stream fd, TiffFieldInfo fip, int value_count, object raw_data) - { - fprintf(fd, " {0}: ", fip.Name); - - byte[] bytes = raw_data as byte[]; - sbyte[] sbytes = raw_data as sbyte[]; - short[] shorts = raw_data as short[]; - ushort[] ushorts = raw_data as ushort[]; - int[] ints = raw_data as int[]; - uint[] uints = raw_data as uint[]; - float[] floats = raw_data as float[]; - double[] doubles = raw_data as double[]; - string s = raw_data as string; - - for (int j = 0; j < value_count; j++) - { - if (fip.Type == TiffType.BYTE || fip.Type == TiffType.SBYTE) - { - if (bytes != null) - fprintf(fd, "{0}", bytes[j]); - else if (sbytes != null) - fprintf(fd, "{0}", sbytes[j]); - } - else if (fip.Type == TiffType.UNDEFINED) - { - if (bytes != null) - fprintf(fd, "0x{0:x}", bytes[j]); - } - else if (fip.Type == TiffType.SHORT || fip.Type == TiffType.SSHORT) - { - if (shorts != null) - fprintf(fd, "{0}", shorts[j]); - else if (ushorts != null) - fprintf(fd, "{0}", ushorts[j]); - } - else if (fip.Type == TiffType.LONG || fip.Type == TiffType.SLONG) - { - if (ints != null) - fprintf(fd, "{0}", ints[j]); - else if (uints != null) - fprintf(fd, "{0}", uints[j]); - } - else if (fip.Type == TiffType.RATIONAL || - fip.Type == TiffType.SRATIONAL || - fip.Type == TiffType.FLOAT) - { - if (floats != null) - fprintf(fd, "{0}", floats[j]); - } - else if (fip.Type == TiffType.IFD) - { - if (ints != null) - fprintf(fd, "0x{0:x}", ints[j]); - else if (uints != null) - fprintf(fd, "0x{0:x}", uints[j]); - } - else if (fip.Type == TiffType.ASCII) - { - if (s != null) - fprintf(fd, "{0}", s); - - break; - } - else if (fip.Type == TiffType.DOUBLE || fip.Type == TiffType.FLOAT) - { - if (floats != null) - fprintf(fd, "{0}", floats[j]); - else if (doubles != null) - fprintf(fd, "{0}", doubles[j]); - } - else - { - fprintf(fd, ""); - break; - } - - if (j < value_count - 1) - fprintf(fd, ","); - } - - fprintf(fd, "\r\n"); - } - - private bool prettyPrintField(Stream fd, TiffTag tag, int value_count, object raw_data) - { - FieldValue value = new FieldValue(raw_data); - short[] sdata = value.ToShortArray(); - float[] fdata = value.ToFloatArray(); - double[] ddata = value.ToDoubleArray(); - - switch (tag) - { - case TiffTag.INKSET: - if (sdata != null) - { - fprintf(fd, " Ink Set: "); - switch ((InkSet)sdata[0]) - { - case InkSet.CMYK: - fprintf(fd, "CMYK\n"); - break; - - default: - fprintf(fd, "{0} (0x{1:x})\n", sdata[0], sdata[0]); - break; - } - return true; - } - return false; - - case TiffTag.DOTRANGE: - if (sdata != null) - { - fprintf(fd, " Dot Range: {0}-{1}\n", sdata[0], sdata[1]); - return true; - } - return false; - - case TiffTag.WHITEPOINT: - if (fdata != null) - { - fprintf(fd, " White Point: {0:G}-{1:G}\n", fdata[0], fdata[1]); - return true; - } - return false; - - case TiffTag.REFERENCEBLACKWHITE: - if (fdata != null) - { - fprintf(fd, " Reference Black/White:\n"); - for (short i = 0; i < 3; i++) - fprintf(fd, " {0,2:D}: {1,5:G} {2,5:G}\n", i, fdata[2 * i + 0], fdata[2 * i + 1]); - return true; - } - return false; - - case TiffTag.XMLPACKET: - string s = raw_data as string; - if (s != null) - { - fprintf(fd, " XMLPacket (XMP Metadata):\n"); - fprintf(fd, s.Substring(0, value_count)); - fprintf(fd, "\n"); - return true; - } - return false; - - case TiffTag.RICHTIFFIPTC: - // XXX: for some weird reason RichTIFFIPTC tag defined - // as array of LONG values. - fprintf(fd, " RichTIFFIPTC Data: , {0} bytes\n", value_count * 4); - return true; - - case TiffTag.PHOTOSHOP: - fprintf(fd, " Photoshop Data: , {0} bytes\n", value_count); - return true; - - case TiffTag.ICCPROFILE: - fprintf(fd, " ICC Profile: , {0} bytes\n", value_count); - return true; - - case TiffTag.STONITS: - if (ddata != null) - { - fprintf(fd, " Sample to Nits conversion factor: {0:e4}\n", ddata[0]); - return true; - } - return false; - } - - return false; - } - - private static void printAscii(Stream fd, string cp) - { - for (int cpPos = 0; cp[cpPos] != '\0'; cpPos++) - { - if (!char.IsControl(cp[cpPos])) - { - fprintf(fd, "{0}", cp[cpPos]); - continue; - } - - string tp = "\tt\bb\rr\nn\vv"; - int tpPos = 0; - for (; tp[tpPos] != 0; tpPos++) - { - if (tp[tpPos++] == cp[cpPos]) - break; - } - - if (tp[tpPos] != 0) - fprintf(fd, "\\{0}", tp[tpPos]); - else - fprintf(fd, "\\{0}", encodeOctalString((byte)(cp[cpPos] & 0xff))); - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Read.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Read.cs deleted file mode 100644 index 5ba1600a..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Read.cs +++ /dev/null @@ -1,490 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Scanline-oriented Read Support - */ - -using System; -using System.Diagnostics; -using System.IO; - -using BitMiracle.LibTiff.Classic.Internal; - -namespace BitMiracle.LibTiff.Classic -{ -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff - { - /// - /// undefined state - /// - private const int NOSTRIP = -1; - - /// - /// undefined state - /// - private const int NOTILE = -1; - - internal const int O_RDONLY = 0; - internal const int O_WRONLY = 0x0001; - internal const int O_CREAT = 0x0100; - internal const int O_TRUNC = 0x0200; - internal const int O_RDWR = 0x0002; - - // - // Default Read/Seek/Write definitions. - // - - private int readFile(byte[] buf, int offset, int size) - { - return m_stream.Read(m_clientdata, buf, offset, size); - } - - private long seekFile(long off, SeekOrigin whence) - { - return m_stream.Seek(m_clientdata, off, whence); - } - - private long getFileSize() - { - return m_stream.Size(m_clientdata); - } - - private bool readOK(byte[] buf, int size) - { - return (readFile(buf, 0, size) == size); - } - - private bool readShortOK(out short value) - { - byte[] bytes = new byte[2]; - bool res = readOK(bytes, 2); - value = 0; - if (res) - { - value = (short)(bytes[0] & 0xFF); - value += (short)((bytes[1] & 0xFF) << 8); - } - - return res; - } - - private bool readUIntOK(out uint value) - { - int temp; - bool res = readIntOK(out temp); - if (res) - value = (uint)temp; - else - value = 0; - - return res; - } - - private bool readIntOK(out int value) - { - byte[] cp = new byte[4]; - bool res = readOK(cp, 4); - value = 0; - if (res) - { - value = cp[0] & 0xFF; - value += (cp[1] & 0xFF) << 8; - value += (cp[2] & 0xFF) << 16; - value += cp[3] << 24; - } - - return res; - } - - private bool readDirEntryOk(TiffDirEntry[] dir, short dircount) - { - int entrySize = sizeof(short) * 2 + sizeof(int) * 2; - int totalSize = entrySize * dircount; - byte[] bytes = new byte[totalSize]; - bool res = readOK(bytes, totalSize); - if (res) - readDirEntry(dir, dircount, bytes, 0); - - return res; - } - - private static void readDirEntry(TiffDirEntry[] dir, short dircount, byte[] bytes, int offset) - { - int pos = offset; - for (int i = 0; i < dircount; i++) - { - TiffDirEntry entry = new TiffDirEntry(); - entry.tdir_tag = (TiffTag)(ushort)readShort(bytes, pos); - pos += sizeof(short); - entry.tdir_type = (TiffType)readShort(bytes, pos); - pos += sizeof(short); - entry.tdir_count = readInt(bytes, pos); - pos += sizeof(int); - entry.tdir_offset = (uint)readInt(bytes, pos); - pos += sizeof(int); - dir[i] = entry; - } - } - - private bool readHeaderOk(ref TiffHeader header) - { - bool res = readShortOK(out header.tiff_magic); - - if (res) - res = readShortOK(out header.tiff_version); - - if (res) - res = readUIntOK(out header.tiff_diroff); - - return res; - } - - private bool seekOK(long off) - { - return (seekFile(off, SeekOrigin.Begin) == off); - } - - /* - * Seek to a random row+sample in a file. - */ - private bool seek(int row, short sample) - { - if (row >= m_dir.td_imagelength) - { - /* out of range */ - ErrorExt(this, m_clientdata, m_name, - "{0}: Row out of range, max {1}", row, m_dir.td_imagelength); - return false; - } - - int strip; - if (m_dir.td_planarconfig == PlanarConfig.SEPARATE) - { - if (sample >= m_dir.td_samplesperpixel) - { - ErrorExt(this, m_clientdata, m_name, - "{0}: Sample out of range, max {1}", sample, m_dir.td_samplesperpixel); - return false; - } - - if (m_dir.td_rowsperstrip != -1) - strip = sample * m_dir.td_stripsperimage + row / m_dir.td_rowsperstrip; - else - strip = 0; - } - else - { - if (m_dir.td_rowsperstrip != -1) - strip = row / m_dir.td_rowsperstrip; - else - strip = 0; - } - - if (strip != m_curstrip) - { - /* different strip, refill */ - if (!fillStrip(strip)) - return false; - } - else if (row < m_row) - { - /* - * Moving backwards within the same strip: backup - * to the start and then decode forward (below). - * - * NB: If you're planning on lots of random access within a - * strip, it's better to just read and decode the entire - * strip, and then access the decoded data in a random fashion. - */ - if (!startStrip(strip)) - return false; - } - - if (row != m_row) - { - /* - * Seek forward to the desired row. - */ - if (!m_currentCodec.Seek(row - m_row)) - return false; - - m_row = row; - } - - return true; - } - - private int readRawStrip1(int strip, byte[] buf, int offset, int size, string module) - { - Debug.Assert((m_flags & TiffFlags.NOREADRAW) != TiffFlags.NOREADRAW); - - if (!seekOK(m_dir.td_stripoffset[strip])) - { - ErrorExt(this, m_clientdata, module, - "{0}: Seek error at scanline {1}, strip {2}", m_name, m_row, strip); - return -1; - } - - int cc = readFile(buf, offset, size); - if (cc != size) - { - ErrorExt(this, m_clientdata, module, - "{0}: Read error at scanline {1}; got {2} bytes, expected {3}", - m_name, m_row, cc, size); - return -1; - } - - return size; - } - - private int readRawTile1(int tile, byte[] buf, int offset, int size, string module) - { - Debug.Assert((m_flags & TiffFlags.NOREADRAW) != TiffFlags.NOREADRAW); - - if (!seekOK(m_dir.td_stripoffset[tile])) - { - ErrorExt(this, m_clientdata, module, - "{0}: Seek error at row {1}, col {2}, tile {3}", m_name, m_row, m_col, tile); - return -1; - } - - int cc = readFile(buf, offset, size); - if (cc != size) - { - ErrorExt(this, m_clientdata, module, - "{0}: Read error at row {1}, col {2}; got {3} bytes, expected {4}", - m_name, m_row, m_col, cc, size); - return -1; - } - - return size; - } - - /// - /// Set state to appear as if a strip has just been read in. - /// - private bool startStrip(int strip) - { - if ((m_flags & TiffFlags.CODERSETUP) != TiffFlags.CODERSETUP) - { - if (!m_currentCodec.SetupDecode()) - return false; - - m_flags |= TiffFlags.CODERSETUP; - } - - m_curstrip = strip; - m_row = (strip % m_dir.td_stripsperimage) * m_dir.td_rowsperstrip; - m_rawcp = 0; - - if ((m_flags & TiffFlags.NOREADRAW) == TiffFlags.NOREADRAW) - m_rawcc = 0; - else - m_rawcc = (int)m_dir.td_stripbytecount[strip]; - - return m_currentCodec.PreDecode((short)(strip / m_dir.td_stripsperimage)); - } - - /* - * Set state to appear as if a - * tile has just been read in. - */ - private bool startTile(int tile) - { - if ((m_flags & TiffFlags.CODERSETUP) != TiffFlags.CODERSETUP) - { - if (!m_currentCodec.SetupDecode()) - return false; - - m_flags |= TiffFlags.CODERSETUP; - } - - m_curtile = tile; - m_row = (tile % howMany(m_dir.td_imagewidth, m_dir.td_tilewidth)) * m_dir.td_tilelength; - m_col = (tile % howMany(m_dir.td_imagelength, m_dir.td_tilelength)) * m_dir.td_tilewidth; - m_rawcp = 0; - - if ((m_flags & TiffFlags.NOREADRAW) == TiffFlags.NOREADRAW) - m_rawcc = 0; - else - m_rawcc = (int)m_dir.td_stripbytecount[tile]; - - return m_currentCodec.PreDecode((short)(tile / m_dir.td_stripsperimage)); - } - - private bool checkRead(bool tiles) - { - if (m_mode == O_WRONLY) - { - ErrorExt(this, m_clientdata, m_name, "File not open for reading"); - return false; - } - - if (tiles ^ IsTiled()) - { - ErrorExt(this, m_clientdata, m_name, tiles ? - "Can not read tiles from a stripped image" : - "Can not read scanlines from a tiled image"); - return false; - } - - return true; - } - - private static void swab16BitData(byte[] buffer, int offset, int count) - { - Debug.Assert((count & 1) == 0); - short[] swabee = ByteArrayToShorts(buffer, offset, count); - SwabArrayOfShort(swabee, count / 2); - ShortsToByteArray(swabee, 0, count / 2, buffer, offset); - } - - private static void swab24BitData(byte[] buffer, int offset, int count) - { - Debug.Assert((count % 3) == 0); - SwabArrayOfTriples(buffer, offset, count / 3); - } - - private static void swab32BitData(byte[] buffer, int offset, int count) - { - Debug.Assert((count & 3) == 0); - int[] swabee = ByteArrayToInts(buffer, offset, count); - SwabArrayOfLong(swabee, count / 4); - IntsToByteArray(swabee, 0, count / 4, buffer, offset); - } - - private static void swab64BitData(byte[] buffer, int offset, int count) - { - Debug.Assert((count & 7) == 0); - - int doubleCount = count / 8; - double[] doubles = new double[doubleCount]; - int byteOffset = offset; - for (int i = 0; i < doubleCount; i++) - { - doubles[i] = BitConverter.ToDouble(buffer, byteOffset); - byteOffset += 8; - } - - SwabArrayOfDouble(doubles, doubleCount); - - byteOffset = offset; - for (int i = 0; i < doubleCount; i++) - { - byte[] bytes = BitConverter.GetBytes(doubles[i]); - Buffer.BlockCopy(bytes, 0, buffer, byteOffset, bytes.Length); - byteOffset += bytes.Length; - } - } - - /// - /// Read the specified strip and setup for decoding. - /// The data buffer is expanded, as necessary, to hold the strip's data. - /// - internal bool fillStrip(int strip) - { - const string module = "fillStrip"; - - if ((m_flags & TiffFlags.NOREADRAW) != TiffFlags.NOREADRAW) - { - int bytecount = (int)m_dir.td_stripbytecount[strip]; - if (bytecount <= 0) - { - ErrorExt(this, m_clientdata, m_name, - "{0}: Invalid strip byte count, strip {1}", bytecount, strip); - return false; - } - - /* - * Expand raw data buffer, if needed, to - * hold data strip coming from file - * (perhaps should set upper bound on - * the size of a buffer we'll use?). - */ - if (bytecount > m_rawdatasize) - { - m_curstrip = NOSTRIP; - if ((m_flags & TiffFlags.MYBUFFER) != TiffFlags.MYBUFFER) - { - ErrorExt(this, m_clientdata, module, - "{0}: Data buffer too small to hold strip {1}", m_name, strip); - return false; - } - - ReadBufferSetup(null, roundUp(bytecount, 1024)); - } - - if (readRawStrip1(strip, m_rawdata, 0, bytecount, module) != bytecount) - return false; - - if (!isFillOrder(m_dir.td_fillorder) && (m_flags & TiffFlags.NOBITREV) != TiffFlags.NOBITREV) - ReverseBits(m_rawdata, bytecount); - } - - return startStrip(strip); - } - - /// - /// Read the specified tile and setup for decoding. - /// The data buffer is expanded, as necessary, to hold the tile's data. - /// - internal bool fillTile(int tile) - { - const string module = "fillTile"; - - if ((m_flags & TiffFlags.NOREADRAW) != TiffFlags.NOREADRAW) - { - int bytecount = (int)m_dir.td_stripbytecount[tile]; - if (bytecount <= 0) - { - ErrorExt(this, m_clientdata, m_name, - "{0}: Invalid tile byte count, tile {1}", bytecount, tile); - return false; - } - - /* - * Expand raw data buffer, if needed, to - * hold data tile coming from file - * (perhaps should set upper bound on - * the size of a buffer we'll use?). - */ - if (bytecount > m_rawdatasize) - { - m_curtile = NOTILE; - if ((m_flags & TiffFlags.MYBUFFER) != TiffFlags.MYBUFFER) - { - ErrorExt(this, m_clientdata, module, - "{0}: Data buffer too small to hold tile {1}", m_name, tile); - return false; - } - - ReadBufferSetup(null, roundUp(bytecount, 1024)); - } - - if (readRawTile1(tile, m_rawdata, 0, bytecount, module) != bytecount) - return false; - - if (!isFillOrder(m_dir.td_fillorder) && (m_flags & TiffFlags.NOBITREV) != TiffFlags.NOBITREV) - ReverseBits(m_rawdata, bytecount); - } - - return startTile(tile); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Strip.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Strip.cs deleted file mode 100644 index 737155b8..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Strip.cs +++ /dev/null @@ -1,108 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Strip-organized Image Support Routines. - */ - -namespace BitMiracle.LibTiff.Classic -{ -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff - { - private int summarize(int summand1, int summand2, string where) - { - int bytes = summand1 + summand2; - if (bytes - summand1 != summand2) - { - ErrorExt(this, m_clientdata, m_name, "Integer overflow in {0}", where); - bytes = 0; - } - - return bytes; - } - - private int multiply(int nmemb, int elem_size, string where) - { - int bytes = nmemb * elem_size; - if (elem_size != 0 && bytes / elem_size != nmemb) - { - ErrorExt(this, m_clientdata, m_name, "Integer overflow in {0}", where); - bytes = 0; - } - - return bytes; - } - - /* - * Return the number of bytes to read/write in a call to - * one of the scanline-oriented i/o routines. Note that - * this number may be 1/samples-per-pixel if data is - * stored as separate planes. - * The ScanlineSize in case of YCbCrSubsampling is defined as the - * strip size divided by the strip height, i.e. the size of a pack of vertical - * subsampling lines divided by vertical subsampling. It should thus make - * sense when multiplied by a multiple of vertical subsampling. - * Some stuff depends on this newer version of TIFFScanlineSize - * TODO: resolve this - */ - internal int newScanlineSize() - { - int scanline; - if (m_dir.td_planarconfig == PlanarConfig.CONTIG) - { - if (m_dir.td_photometric == Photometric.YCBCR && !IsUpSampled()) - { - FieldValue[] result = GetField(TiffTag.YCBCRSUBSAMPLING); - ushort ycbcrsubsampling0 = result[0].ToUShort(); - ushort ycbcrsubsampling1 = result[1].ToUShort(); - - if (ycbcrsubsampling0 * ycbcrsubsampling1 == 0) - { - ErrorExt(this, m_clientdata, m_name, "Invalid YCbCr subsampling"); - return 0; - } - - return ((((m_dir.td_imagewidth + ycbcrsubsampling0 - 1) / ycbcrsubsampling0) * (ycbcrsubsampling0 * ycbcrsubsampling1 + 2) * m_dir.td_bitspersample + 7) / 8) / ycbcrsubsampling1; - } - else - { - scanline = multiply(m_dir.td_imagewidth, m_dir.td_samplesperpixel, "TIFFScanlineSize"); - } - } - else - { - scanline = m_dir.td_imagewidth; - } - - return howMany8(multiply(scanline, m_dir.td_bitspersample, "TIFFScanlineSize")); - } - - /* - * Some stuff depends on this older version of TIFFScanlineSize - * TODO: resolve this - */ - internal int oldScanlineSize() - { - int scanline = multiply(m_dir.td_bitspersample, m_dir.td_imagewidth, "TIFFScanlineSize"); - if (m_dir.td_planarconfig == PlanarConfig.CONTIG) - scanline = multiply(scanline, m_dir.td_samplesperpixel, "TIFFScanlineSize"); - - return howMany8(scanline); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Swab.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Swab.cs deleted file mode 100644 index 7a4768ff..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Swab.cs +++ /dev/null @@ -1,90 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Bit & Byte Swapping Support. - * - * XXX We assume short = 16-bits and long = 32-bits XXX - */ - -namespace BitMiracle.LibTiff.Classic -{ -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff - { - /* - * Bit reversal tables. TIFFBitRevTable[] gives - * the bit reversed value of . Used in various - * places in the library when the FillOrder requires - * bit reversal of byte values (e.g. CCITT Fax 3 - * encoding/decoding). TIFFNoBitRevTable is provided - * for algorithms that want an equivalent table that - * do not reverse bit values. - */ - private static readonly byte[] TIFFBitRevTable = - { - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, - 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, - 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, - 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, - 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, - 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, - 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, - 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, - 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, - 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, - 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, - 0x3f, 0xbf, 0x7f, 0xff - }; - - private static readonly byte[] TIFFNoBitRevTable = - { - 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, 0x80, 0x81, 0x82, 0x83, - 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 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, - }; - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Write.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Write.cs deleted file mode 100644 index 63ba80a9..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Internal/Tiff_Write.cs +++ /dev/null @@ -1,213 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Scanline-oriented Write Support - */ - -using System; -using System.Diagnostics; -using System.IO; - -using BitMiracle.LibTiff.Classic.Internal; - -namespace BitMiracle.LibTiff.Classic -{ -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff - { - private bool writeCheckStrips(string module) - { - return ((m_flags & TiffFlags.BEENWRITING) == TiffFlags.BEENWRITING || WriteCheck(false, module)); - } - - private bool writeCheckTiles(string module) - { - return ((m_flags & TiffFlags.BEENWRITING) == TiffFlags.BEENWRITING || WriteCheck(true, module)); - } - - private void bufferCheck() - { - if (!((m_flags & TiffFlags.BUFFERSETUP) == TiffFlags.BUFFERSETUP && m_rawdata != null)) - WriteBufferSetup(null, -1); - } - - private bool writeOK(byte[] buffer, int offset, int count) - { - try - { - m_stream.Write(m_clientdata, buffer, offset, count); - } - catch (Exception) - { - Tiff.Warning(this, "writeOK", "Failed to write {0} bytes", count); - return false; - } - - return true; - } - - private bool writeHeaderOK(TiffHeader header) - { - bool res = writeShortOK(header.tiff_magic); - - if (res) - res = writeShortOK(header.tiff_version); - - if (res) - res = writeIntOK((int)header.tiff_diroff); - - return res; - } - - private bool writeDirEntryOK(TiffDirEntry[] entries, int count) - { - bool res = true; - for (int i = 0; i < count; i++) - { - res = writeShortOK((short)entries[i].tdir_tag); - - if (res) - res = writeShortOK((short)entries[i].tdir_type); - - if (res) - res = writeIntOK(entries[i].tdir_count); - - if (res) - res = writeIntOK((int)entries[i].tdir_offset); - - if (!res) - break; - } - - return res; - } - - private bool writeShortOK(short value) - { - byte[] cp = new byte[2]; - cp[0] = (byte)value; - cp[1] = (byte)(value >> 8); - - return writeOK(cp, 0, 2); - } - - private bool writeIntOK(int value) - { - byte[] cp = new byte[4]; - cp[0] = (byte)value; - cp[1] = (byte)(value >> 8); - cp[2] = (byte)(value >> 16); - cp[3] = (byte)(value >> 24); - - return writeOK(cp, 0, 4); - } - - private bool isUnspecified(int f) - { - return (fieldSet(f) && m_dir.td_imagelength == 0); - } - - /* - * Grow the strip data structures by delta strips. - */ - private bool growStrips(int delta) - { - Debug.Assert(m_dir.td_planarconfig == PlanarConfig.CONTIG); - uint[] new_stripoffset = Realloc(m_dir.td_stripoffset, m_dir.td_nstrips, m_dir.td_nstrips + delta); - uint[] new_stripbytecount = Realloc(m_dir.td_stripbytecount, m_dir.td_nstrips, m_dir.td_nstrips + delta); - m_dir.td_stripoffset = new_stripoffset; - m_dir.td_stripbytecount = new_stripbytecount; - Array.Clear(m_dir.td_stripoffset, m_dir.td_nstrips, delta); - Array.Clear(m_dir.td_stripbytecount, m_dir.td_nstrips, delta); - m_dir.td_nstrips += delta; - return true; - } - - /// - /// Appends the data to the specified strip. - /// - private bool appendToStrip(int strip, byte[] buffer, int offset, int count) - { - const string module = "appendToStrip"; - - if (m_dir.td_stripoffset[strip] == 0 || m_curoff == 0) - { - Debug.Assert(m_dir.td_nstrips > 0); - - if (m_dir.td_stripbytecount[strip] != 0 && - m_dir.td_stripoffset[strip] != 0 && - m_dir.td_stripbytecount[strip] >= count) - { - // There is already tile data on disk, and the new tile - // data we have to will fit in the same space. The only - // aspect of this that is risky is that there could be - // more data to append to this strip before we are done - // depending on how we are getting called. - if (!seekOK(m_dir.td_stripoffset[strip])) - { - ErrorExt(this, m_clientdata, module, "Seek error at scanline {0}", m_row); - return false; - } - } - else - { - // Seek to end of file, and set that as our location - // to write this strip. - m_dir.td_stripoffset[strip] = (uint)seekFile(0, SeekOrigin.End); - } - - m_curoff = m_dir.td_stripoffset[strip]; - - // We are starting a fresh strip/tile, so set the size to zero. - m_dir.td_stripbytecount[strip] = 0; - } - - if (!writeOK(buffer, offset, count)) - { - ErrorExt(this, m_clientdata, module, "Write error at scanline {0}", m_row); - return false; - } - - m_curoff += (uint)count; - m_dir.td_stripbytecount[strip] += (uint)count; - return true; - } - - /* - * Internal version of FlushData that can be - * called by ``encodestrip routines'' w/o concern - * for infinite recursion. - */ - internal bool flushData1() - { - if (m_rawcc > 0) - { - if (!isFillOrder(m_dir.td_fillorder) && (m_flags & TiffFlags.NOBITREV) != TiffFlags.NOBITREV) - ReverseBits(m_rawdata, m_rawcc); - - if (!appendToStrip(IsTiled() ? m_curtile : m_curstrip, m_rawdata, 0, m_rawcc)) - return false; - - m_rawcc = 0; - m_rawcp = 0; - } - - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/DensityUnit.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/DensityUnit.cs deleted file mode 100644 index 16692293..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/DensityUnit.cs +++ /dev/null @@ -1,34 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// The unit of density. - /// - /// - /// -#if EXPOSE_LIBJPEG - public -#endif - enum DensityUnit - { - /// - /// Unknown density - /// - Unknown = 0, - /// - /// Dots/inch - /// - DotsInch = 1, - /// - /// Dots/cm - /// - DotsCm = 2 - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/ComponentBuffer.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/ComponentBuffer.cs deleted file mode 100644 index 43ba18d4..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/ComponentBuffer.cs +++ /dev/null @@ -1,59 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Encapsulates buffer of image samples for one color component - /// When provided with funny indices (see jpeg_d_main_controller for - /// explanation of what it is) uses them for non-linear row access. - /// - class ComponentBuffer - { - private byte[][] m_buffer; - - // array of funny indices - private int[] m_funnyIndices; - - // index of "first funny index" (used because some code uses negative - // indices when retrieve rows) - // see for example my_upsampler.h2v2_fancy_upsample - private int m_funnyOffset; - - public ComponentBuffer() - { - } - - public ComponentBuffer(byte[][] buf, int[] funnyIndices, int funnyOffset) - { - SetBuffer(buf, funnyIndices, funnyOffset); - } - - public void SetBuffer(byte[][] buf, int[] funnyIndices, int funnyOffset) - { - m_buffer = buf; - m_funnyIndices = funnyIndices; - m_funnyOffset = funnyOffset; - } - - public byte[] this[int i] - { - get - { - if (m_funnyIndices == null) - return m_buffer[i]; - - return m_buffer[m_funnyIndices[i + m_funnyOffset]]; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/J_BUF_MODE.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/J_BUF_MODE.cs deleted file mode 100644 index 0eab3efa..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/J_BUF_MODE.cs +++ /dev/null @@ -1,33 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Operating modes for buffer controllers - /// - enum J_BUF_MODE - { - JBUF_PASS_THRU, /* Plain stripwise operation */ - - /* Remaining modes require a full-image buffer to have been created */ - - JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ - JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ - JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/JpegUtils.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/JpegUtils.cs deleted file mode 100644 index 02c05778..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/JpegUtils.cs +++ /dev/null @@ -1,120 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains tables and miscellaneous utility routines needed - * for both compression and decompression. - * Note we prefix all global names with "j" to minimize conflicts with - * a surrounding application. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - class JpegUtils - { - /* - * jpeg_natural_order[i] is the natural-order position of the i'th element - * of zigzag order. - * - * When reading corrupted data, the Huffman decoders could attempt - * to reference an entry beyond the end of this array (if the decoded - * zero run length reaches past the end of the block). To prevent - * wild stores without adding an inner-loop test, we put some extra - * "63"s after the real entries. This will cause the extra coefficient - * to be stored in location 63 of the block, not somewhere random. - * The worst case would be a run-length of 15, which means we need 16 - * fake entries. - */ - public static int[] jpeg_natural_order = - { - 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, - 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, - 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, - 63, 63, 63, 63, 63, 63, 63, 63, 63, - /* extra entries for safety in decoder */ - 63, 63, 63, 63, 63, 63, 63, 63 - }; - - /* We assume that right shift corresponds to signed division by 2 with - * rounding towards minus infinity. This is correct for typical "arithmetic - * shift" instructions that shift in copies of the sign bit. - * RIGHT_SHIFT provides a proper signed right shift of an int quantity. - * It is only applied with constant shift counts. SHIFT_TEMPS must be - * included in the variables of any routine using RIGHT_SHIFT. - */ - public static int RIGHT_SHIFT(int x, int shft) - { - return (x >> shft); - } - - /* Descale and correctly round an int value that's scaled by N bits. - * We assume RIGHT_SHIFT rounds towards minus infinity, so adding - * the fudge factor is correct for either sign of X. - */ - public static int DESCALE(int x, int n) - { - return RIGHT_SHIFT(x + (1 << (n - 1)), n); - } - - ////////////////////////////////////////////////////////////////////////// - // Arithmetic utilities - - /// - /// Compute a/b rounded up to next integer, ie, ceil(a/b) - /// Assumes a >= 0, b > 0 - /// - public static int jdiv_round_up(int a, int b) - { - return (a + b - 1) / b; - } - - /// - /// Compute a rounded up to next multiple of b, ie, ceil(a/b)*b - /// Assumes a >= 0, b > 0 - /// - public static int jround_up(int a, int b) - { - a += b - 1; - return a - (a % b); - } - - /// - /// Copy some rows of samples from one place to another. - /// num_rows rows are copied from input_array[source_row++] - /// to output_array[dest_row++]; these areas may overlap for duplication. - /// The source and destination arrays must be at least as wide as num_cols. - /// - public static void jcopy_sample_rows(ComponentBuffer input_array, int source_row, byte[][] output_array, int dest_row, int num_rows, int num_cols) - { - for (int row = 0; row < num_rows; row++) - Buffer.BlockCopy(input_array[source_row + row], 0, output_array[dest_row + row], 0, num_cols); - } - - public static void jcopy_sample_rows(ComponentBuffer input_array, int source_row, ComponentBuffer output_array, int dest_row, int num_rows, int num_cols) - { - for (int row = 0; row < num_rows; row++) - Buffer.BlockCopy(input_array[source_row + row], 0, output_array[dest_row + row], 0, num_cols); - } - - public static void jcopy_sample_rows(byte[][] input_array, int source_row, byte[][] output_array, int dest_row, int num_rows, int num_cols) - { - for (int row = 0; row < num_rows; row++) - Buffer.BlockCopy(input_array[source_row++], 0, output_array[dest_row++], 0, num_cols); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/bitread_perm_state.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/bitread_perm_state.cs deleted file mode 100644 index 5104adfa..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/bitread_perm_state.cs +++ /dev/null @@ -1,28 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Bitreading state saved across MCUs - /// - struct bitread_perm_state - { - public int get_buffer; /* current bit-extraction buffer */ - public int bits_left; /* # of unused bits in it */ - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/bitread_working_state.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/bitread_working_state.cs deleted file mode 100644 index 89ac7ba3..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/bitread_working_state.cs +++ /dev/null @@ -1,31 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Bitreading working state within an MCU - /// - struct bitread_working_state - { - public int get_buffer; /* current bit-extraction buffer */ - public int bits_left; /* # of unused bits in it */ - - /* Pointer needed by jpeg_fill_bit_buffer. */ - public jpeg_decompress_struct cinfo; /* back link to decompress master record */ - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/d_derived_tbl.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/d_derived_tbl.cs deleted file mode 100644 index 842068fd..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/d_derived_tbl.cs +++ /dev/null @@ -1,45 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Derived data constructed for each Huffman table - /// - class d_derived_tbl - { - /* Basic tables: (element [0] of each array is unused) */ - public int[] maxcode = new int[18]; /* largest code of length k (-1 if none) */ - /* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */ - public int[] valoffset = new int[17]; /* huffval[] offset for codes of length k */ - /* valoffset[k] = huffval[] index of 1st symbol of code length k, less - * the smallest code of length k; so given a code of length k, the - * corresponding symbol is huffval[code + valoffset[k]] - */ - - /* Link to public Huffman table (needed only in jpeg_huff_decode) */ - public JHUFF_TBL pub; - - /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of - * the input data stream. If the next Huffman code is no more - * than HUFF_LOOKAHEAD bits long, we can obtain its length and - * the corresponding symbol directly from these tables. - */ - public int[] look_nbits = new int[1 << JpegConstants.HUFF_LOOKAHEAD]; /* # bits, or 0 if too long */ - public byte[] look_sym = new byte[1 << JpegConstants.HUFF_LOOKAHEAD]; /* symbol, or unused */ - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/huff_entropy_decoder.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/huff_entropy_decoder.cs deleted file mode 100644 index d3c8cee7..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/huff_entropy_decoder.cs +++ /dev/null @@ -1,320 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains Huffman entropy decoding routines. - * - * Much of the complexity here has to do with supporting input suspension. - * If the data source module demands suspension, we want to be able to back - * up to the start of the current MCU. To do this, we copy state variables - * into local working storage, and update them back to the permanent - * storage only upon successful completion of an MCU. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Expanded entropy decoder object for Huffman decoding. - /// - /// The savable_state subrecord contains fields that change within an MCU, - /// but must not be updated permanently until we complete the MCU. - /// - class huff_entropy_decoder : jpeg_entropy_decoder - { - private class savable_state - { - public int[] last_dc_val = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ - - public void Assign(savable_state ss) - { - Buffer.BlockCopy(ss.last_dc_val, 0, last_dc_val, 0, last_dc_val.Length * sizeof(int)); - } - } - - /* These fields are loaded into local variables at start of each MCU. - * In case of suspension, we exit WITHOUT updating them. - */ - private bitread_perm_state m_bitstate; /* Bit buffer at start of MCU */ - private savable_state m_saved = new savable_state(); /* Other state at start of MCU */ - - /* These fields are NOT loaded into local working state. */ - private int m_restarts_to_go; /* MCUs left in this restart interval */ - - /* Pointers to derived tables (these workspaces have image lifespan) */ - private d_derived_tbl[] m_dc_derived_tbls = new d_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; - private d_derived_tbl[] m_ac_derived_tbls = new d_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; - - /* Precalculated info set up by start_pass for use in decode_mcu: */ - - /* Pointers to derived tables to be used for each block within an MCU */ - private d_derived_tbl[] m_dc_cur_tbls = new d_derived_tbl[JpegConstants.D_MAX_BLOCKS_IN_MCU]; - private d_derived_tbl[] m_ac_cur_tbls = new d_derived_tbl[JpegConstants.D_MAX_BLOCKS_IN_MCU]; - - /* Whether we care about the DC and AC coefficient values for each block */ - private bool[] m_dc_needed = new bool[JpegConstants.D_MAX_BLOCKS_IN_MCU]; - private bool[] m_ac_needed = new bool[JpegConstants.D_MAX_BLOCKS_IN_MCU]; - - public huff_entropy_decoder(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Mark tables unallocated */ - for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) - m_dc_derived_tbls[i] = m_ac_derived_tbls[i] = null; - } - - /// - /// Initialize for a Huffman-compressed scan. - /// - public override void start_pass() - { - /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. - * This ought to be an error condition, but we make it a warning because - * there are some baseline files out there with all zeroes in these bytes. - */ - if (m_cinfo.m_Ss != 0 || m_cinfo.m_Se != JpegConstants.DCTSIZE2 - 1 || m_cinfo.m_Ah != 0 || m_cinfo.m_Al != 0) - m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_NOT_SEQUENTIAL); - - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - int dctbl = componentInfo.Dc_tbl_no; - int actbl = componentInfo.Ac_tbl_no; - - /* Compute derived values for Huffman tables */ - /* We may do this more than once for a table, but it's not expensive */ - jpeg_make_d_derived_tbl(true, dctbl, ref m_dc_derived_tbls[dctbl]); - jpeg_make_d_derived_tbl(false, actbl, ref m_ac_derived_tbls[actbl]); - - /* Initialize DC predictions to 0 */ - m_saved.last_dc_val[ci] = 0; - } - - /* Precalculate decoding info for each block in an MCU of this scan */ - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - int ci = m_cinfo.m_MCU_membership[blkn]; - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - - /* Precalculate which table to use for each block */ - m_dc_cur_tbls[blkn] = m_dc_derived_tbls[componentInfo.Dc_tbl_no]; - m_ac_cur_tbls[blkn] = m_ac_derived_tbls[componentInfo.Ac_tbl_no]; - - /* Decide whether we really care about the coefficient values */ - if (componentInfo.component_needed) - { - m_dc_needed[blkn] = true; - /* we don't need the ACs if producing a 1/8th-size image */ - m_ac_needed[blkn] = (componentInfo.DCT_scaled_size > 1); - } - else - { - m_dc_needed[blkn] = m_ac_needed[blkn] = false; - } - } - - /* Initialize bitread state variables */ - m_bitstate.bits_left = 0; - m_bitstate.get_buffer = 0; - m_insufficient_data = false; - - /* Initialize restart counter */ - m_restarts_to_go = m_cinfo.m_restart_interval; - } - - /// - /// Decode and return one MCU's worth of Huffman-compressed coefficients. - /// The coefficients are reordered from zigzag order into natural array order, - /// but are not dequantized. - /// - /// The i'th block of the MCU is stored into the block pointed to by - /// MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER. - /// (Wholesale zeroing is usually a little faster than retail...) - /// - /// Returns false if data source requested suspension. In that case no - /// changes have been made to permanent state. (Exception: some output - /// coefficients may already have been assigned. This is harmless for - /// this module, since we'll just re-assign them on the next call.) - /// - public override bool decode_mcu(JBLOCK[] MCU_data) - { - /* Process restart marker if needed; may have to suspend */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - if (!process_restart()) - return false; - } - } - - /* If we've run out of data, just leave the MCU set to zeroes. - * This way, we return uniform gray for the remainder of the segment. - */ - if (!m_insufficient_data) - { - /* Load up working state */ - int get_buffer; - int bits_left; - bitread_working_state br_state = new bitread_working_state(); - BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); - savable_state state = new savable_state(); - state.Assign(m_saved); - - /* Outer loop handles each block in the MCU */ - - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - /* Decode a single block's worth of coefficients */ - - /* Section F.2.2.1: decode the DC coefficient difference */ - int s; - if (!HUFF_DECODE(out s, ref br_state, m_dc_cur_tbls[blkn], ref get_buffer, ref bits_left)) - return false; - - if (s != 0) - { - if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) - return false; - - int r = GET_BITS(s, get_buffer, ref bits_left); - s = HUFF_EXTEND(r, s); - } - - if (m_dc_needed[blkn]) - { - /* Convert DC difference to actual value, update last_dc_val */ - int ci = m_cinfo.m_MCU_membership[blkn]; - s += state.last_dc_val[ci]; - state.last_dc_val[ci] = s; - - /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ - MCU_data[blkn][0] = (short) s; - } - - if (m_ac_needed[blkn]) - { - /* Section F.2.2.2: decode the AC coefficients */ - /* Since zeroes are skipped, output area must be cleared beforehand */ - for (int k = 1; k < JpegConstants.DCTSIZE2; k++) - { - if (!HUFF_DECODE(out s, ref br_state, m_ac_cur_tbls[blkn], ref get_buffer, ref bits_left)) - return false; - - int r = s >> 4; - s &= 15; - - if (s != 0) - { - k += r; - if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) - return false; - r = GET_BITS(s, get_buffer, ref bits_left); - s = HUFF_EXTEND(r, s); - - /* Output coefficient in natural (dezigzagged) order. - * Note: the extra entries in jpeg_natural_order[] will save us - * if k >= DCTSIZE2, which could happen if the data is corrupted. - */ - MCU_data[blkn][JpegUtils.jpeg_natural_order[k]] = (short) s; - } - else - { - if (r != 15) - break; - - k += 15; - } - } - } - else - { - /* Section F.2.2.2: decode the AC coefficients */ - /* In this path we just discard the values */ - for (int k = 1; k < JpegConstants.DCTSIZE2; k++) - { - if (!HUFF_DECODE(out s, ref br_state, m_ac_cur_tbls[blkn], ref get_buffer, ref bits_left)) - return false; - - int r = s >> 4; - s &= 15; - - if (s != 0) - { - k += r; - if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) - return false; - - DROP_BITS(s, ref bits_left); - } - else - { - if (r != 15) - break; - - k += 15; - } - } - } - } - - /* Completed MCU, so update state */ - BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); - m_saved.Assign(state); - } - - /* Account for restart interval (no-op if not using restarts) */ - m_restarts_to_go--; - - return true; - - } - - /// - /// Check for a restart marker and resynchronize decoder. - /// Returns false if must suspend. - /// - private bool process_restart() - { - /* Throw away any unused bits remaining in bit buffer; */ - /* include any full bytes in next_marker's count of discarded bytes */ - m_cinfo.m_marker.SkipBytes(m_bitstate.bits_left / 8); - m_bitstate.bits_left = 0; - - /* Advance past the RSTn marker */ - if (!m_cinfo.m_marker.read_restart_marker()) - return false; - - /* Re-initialize DC predictions to 0 */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - m_saved.last_dc_val[ci] = 0; - - /* Reset restart counter */ - m_restarts_to_go = m_cinfo.m_restart_interval; - - /* Reset out-of-data flag, unless read_restart_marker left us smack up - * against a marker. In that case we will end up treating the next data - * segment as empty, and we can avoid producing bogus output pixels by - * leaving the flag set. - */ - if (m_cinfo.m_unread_marker == 0) - m_insufficient_data = false; - - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/huff_entropy_encoder.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/huff_entropy_encoder.cs deleted file mode 100644 index f8737c19..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/huff_entropy_encoder.cs +++ /dev/null @@ -1,546 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains Huffman entropy encoding routines. - * - * Much of the complexity here has to do with supporting output suspension. - * If the data destination module demands suspension, we want to be able to - * back up to the start of the current MCU. To do this, we copy state - * variables into local working storage, and update them back to the - * permanent JPEG objects only upon successful completion of an MCU. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Expanded entropy encoder object for Huffman encoding. - /// - class huff_entropy_encoder : jpeg_entropy_encoder - { - /* The savable_state subrecord contains fields that change within an MCU, - * but must not be updated permanently until we complete the MCU. - */ - private class savable_state - { - public int put_buffer; /* current bit-accumulation buffer */ - public int put_bits; /* # of bits now in it */ - public int[] last_dc_val = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ - } - - private bool m_gather_statistics; - - private savable_state m_saved = new savable_state(); /* Bit buffer & DC state at start of MCU */ - - /* These fields are NOT loaded into local working state. */ - private int m_restarts_to_go; /* MCUs left in this restart interval */ - private int m_next_restart_num; /* next restart number to write (0-7) */ - - /* Pointers to derived tables (these workspaces have image lifespan) */ - private c_derived_tbl[] m_dc_derived_tbls = new c_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; - private c_derived_tbl[] m_ac_derived_tbls = new c_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; - - /* Statistics tables for optimization */ - private long[][] m_dc_count_ptrs = new long[JpegConstants.NUM_HUFF_TBLS][]; - private long[][] m_ac_count_ptrs = new long[JpegConstants.NUM_HUFF_TBLS][]; - - public huff_entropy_encoder(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - - /* Mark tables unallocated */ - for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) - { - m_dc_derived_tbls[i] = m_ac_derived_tbls[i] = null; - m_dc_count_ptrs[i] = m_ac_count_ptrs[i] = null; - } - } - - /// - /// Initialize for a Huffman-compressed scan. - /// If gather_statistics is true, we do not output anything during the scan, - /// just count the Huffman symbols used and generate Huffman code tables. - /// - public override void start_pass(bool gather_statistics) - { - m_gather_statistics = gather_statistics; - - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - int dctbl = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no; - int actbl = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Ac_tbl_no; - if (m_gather_statistics) - { - /* Check for invalid table indexes */ - /* (make_c_derived_tbl does this in the other path) */ - if (dctbl < 0 || dctbl >= JpegConstants.NUM_HUFF_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, dctbl); - - if (actbl < 0 || actbl >= JpegConstants.NUM_HUFF_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, actbl); - - /* Allocate and zero the statistics tables */ - /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ - if (m_dc_count_ptrs[dctbl] == null) - m_dc_count_ptrs[dctbl] = new long[257]; - - Array.Clear(m_dc_count_ptrs[dctbl], 0, m_dc_count_ptrs[dctbl].Length); - - if (m_ac_count_ptrs[actbl] == null) - m_ac_count_ptrs[actbl] = new long[257]; - - Array.Clear(m_ac_count_ptrs[actbl], 0, m_ac_count_ptrs[actbl].Length); - } - else - { - /* Compute derived values for Huffman tables */ - /* We may do this more than once for a table, but it's not expensive */ - jpeg_make_c_derived_tbl(true, dctbl, ref m_dc_derived_tbls[dctbl]); - jpeg_make_c_derived_tbl(false, actbl, ref m_ac_derived_tbls[actbl]); - } - - /* Initialize DC predictions to 0 */ - m_saved.last_dc_val[ci] = 0; - } - - /* Initialize bit buffer to empty */ - m_saved.put_buffer = 0; - m_saved.put_bits = 0; - - /* Initialize restart stuff */ - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num = 0; - } - - public override bool encode_mcu(JBLOCK[][] MCU_data) - { - if (m_gather_statistics) - return encode_mcu_gather(MCU_data); - - return encode_mcu_huff(MCU_data); - } - - public override void finish_pass() - { - if (m_gather_statistics) - finish_pass_gather(); - else - finish_pass_huff(); - } - - /// - /// Encode and output one MCU's worth of Huffman-compressed coefficients. - /// - private bool encode_mcu_huff(JBLOCK[][] MCU_data) - { - /* Load up working state */ - savable_state state; - state = m_saved; - - /* Emit restart marker if needed */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - if (!emit_restart(state, m_next_restart_num)) - return false; - } - } - - /* Encode the MCU data blocks */ - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - int ci = m_cinfo.m_MCU_membership[blkn]; - if (!encode_one_block(state, MCU_data[blkn][0].data, state.last_dc_val[ci], - m_dc_derived_tbls[m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no], - m_ac_derived_tbls[m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Ac_tbl_no])) - { - return false; - } - - /* Update last_dc_val */ - state.last_dc_val[ci] = MCU_data[blkn][0][0]; - } - - /* Completed MCU, so update state */ - m_saved = state; - - /* Update restart-interval state too */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num++; - m_next_restart_num &= 7; - } - - m_restarts_to_go--; - } - - return true; - } - - /// - /// Finish up at the end of a Huffman-compressed scan. - /// - private void finish_pass_huff() - { - /* Load up working state ... flush_bits needs it */ - savable_state state; - state = m_saved; - - /* Flush out the last data */ - if (!flush_bits(state)) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CANT_SUSPEND); - - /* Update state */ - m_saved = state; - } - - /// - /// Trial-encode one MCU's worth of Huffman-compressed coefficients. - /// No data is actually output, so no suspension return is possible. - /// - private bool encode_mcu_gather(JBLOCK[][] MCU_data) - { - /* Take care of restart intervals if needed */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - /* Re-initialize DC predictions to 0 */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - m_saved.last_dc_val[ci] = 0; - - /* Update restart state */ - m_restarts_to_go = m_cinfo.m_restart_interval; - } - - m_restarts_to_go--; - } - - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - int ci = m_cinfo.m_MCU_membership[blkn]; - htest_one_block(MCU_data[blkn][0].data, m_saved.last_dc_val[ci], - m_dc_count_ptrs[m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no], - m_ac_count_ptrs[m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Ac_tbl_no]); - m_saved.last_dc_val[ci] = MCU_data[blkn][0][0]; - } - - return true; - } - - /// - /// Finish up a statistics-gathering pass and create the new Huffman tables. - /// - private void finish_pass_gather() - { - /* It's important not to apply jpeg_gen_optimal_table more than once - * per table, because it clobbers the input frequency counts! - */ - bool[] did_dc = new bool [JpegConstants.NUM_HUFF_TBLS]; - bool[] did_ac = new bool[JpegConstants.NUM_HUFF_TBLS]; - - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - int dctbl = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no; - if (!did_dc[dctbl]) - { - if (m_cinfo.m_dc_huff_tbl_ptrs[dctbl] == null) - m_cinfo.m_dc_huff_tbl_ptrs[dctbl] = new JHUFF_TBL(); - - jpeg_gen_optimal_table(m_cinfo.m_dc_huff_tbl_ptrs[dctbl], m_dc_count_ptrs[dctbl]); - did_dc[dctbl] = true; - } - - int actbl = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Ac_tbl_no; - if (!did_ac[actbl]) - { - if (m_cinfo.m_ac_huff_tbl_ptrs[actbl] == null) - m_cinfo.m_ac_huff_tbl_ptrs[actbl] = new JHUFF_TBL(); - - jpeg_gen_optimal_table(m_cinfo.m_ac_huff_tbl_ptrs[actbl], m_ac_count_ptrs[actbl]); - did_ac[actbl] = true; - } - } - } - - /// - /// Encode a single block's worth of coefficients - /// - private bool encode_one_block(savable_state state, short[] block, int last_dc_val, c_derived_tbl dctbl, c_derived_tbl actbl) - { - /* Encode the DC coefficient difference per section F.1.2.1 */ - int temp = block[0] - last_dc_val; - int temp2 = temp; - if (temp < 0) - { - temp = -temp; /* temp is abs value of input */ - /* For a negative input, want temp2 = bitwise complement of abs(input) */ - /* This code assumes we are on a two's complement machine */ - temp2--; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - int nbits = 0; - while (temp != 0) - { - nbits++; - temp >>= 1; - } - - /* Check for out-of-range coefficient values. - * Since we're encoding a difference, the range limit is twice as much. - */ - if (nbits > MAX_HUFFMAN_COEF_BITS + 1) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); - - /* Emit the Huffman-coded symbol for the number of bits */ - if (!emit_bits(state, dctbl.ehufco[nbits], dctbl.ehufsi[nbits])) - return false; - - /* Emit that number of bits of the value, if positive, */ - /* or the complement of its magnitude, if negative. */ - if (nbits != 0) - { - /* emit_bits rejects calls with size 0 */ - if (!emit_bits(state, temp2, nbits)) - return false; - } - - /* Encode the AC coefficients per section F.1.2.2 */ - int r = 0; /* r = run length of zeros */ - for (int k = 1; k < JpegConstants.DCTSIZE2; k++) - { - temp = block[JpegUtils.jpeg_natural_order[k]]; - if (temp == 0) - { - r++; - } - else - { - /* if run length > 15, must emit special run-length-16 codes (0xF0) */ - while (r > 15) - { - if (!emit_bits(state, actbl.ehufco[0xF0], actbl.ehufsi[0xF0])) - return false; - r -= 16; - } - - temp2 = temp; - if (temp < 0) - { - temp = -temp; /* temp is abs value of input */ - /* This code assumes we are on a two's complement machine */ - temp2--; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 1; /* there must be at least one 1 bit */ - while ((temp >>= 1) != 0) - nbits++; - - /* Check for out-of-range coefficient values */ - if (nbits > MAX_HUFFMAN_COEF_BITS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); - - /* Emit Huffman symbol for run length / number of bits */ - int i = (r << 4) + nbits; - if (!emit_bits(state, actbl.ehufco[i], actbl.ehufsi[i])) - return false; - - /* Emit that number of bits of the value, if positive, */ - /* or the complement of its magnitude, if negative. */ - if (!emit_bits(state, temp2, nbits)) - return false; - - r = 0; - } - } - - /* If the last coef(s) were zero, emit an end-of-block code */ - if (r > 0) - { - if (!emit_bits(state, actbl.ehufco[0], actbl.ehufsi[0])) - return false; - } - - return true; - } - - /// - /// Huffman coding optimization. - /// - /// We first scan the supplied data and count the number of uses of each symbol - /// that is to be Huffman-coded. (This process MUST agree with the code above.) - /// Then we build a Huffman coding tree for the observed counts. - /// Symbols which are not needed at all for the particular image are not - /// assigned any code, which saves space in the DHT marker as well as in - /// the compressed data. - /// - private void htest_one_block(short[] block, int last_dc_val, long[] dc_counts, long[] ac_counts) - { - /* Encode the DC coefficient difference per section F.1.2.1 */ - int temp = block[0] - last_dc_val; - if (temp < 0) - temp = -temp; - - /* Find the number of bits needed for the magnitude of the coefficient */ - int nbits = 0; - while (temp != 0) - { - nbits++; - temp >>= 1; - } - - /* Check for out-of-range coefficient values. - * Since we're encoding a difference, the range limit is twice as much. - */ - if (nbits > MAX_HUFFMAN_COEF_BITS + 1) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); - - /* Count the Huffman symbol for the number of bits */ - dc_counts[nbits]++; - - /* Encode the AC coefficients per section F.1.2.2 */ - int r = 0; /* r = run length of zeros */ - for (int k = 1; k < JpegConstants.DCTSIZE2; k++) - { - temp = block[JpegUtils.jpeg_natural_order[k]]; - if (temp == 0) - { - r++; - } - else - { - /* if run length > 15, must emit special run-length-16 codes (0xF0) */ - while (r > 15) - { - ac_counts[0xF0]++; - r -= 16; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - if (temp < 0) - temp = -temp; - - /* Find the number of bits needed for the magnitude of the coefficient */ - nbits = 1; /* there must be at least one 1 bit */ - while ((temp >>= 1) != 0) - nbits++; - - /* Check for out-of-range coefficient values */ - if (nbits > MAX_HUFFMAN_COEF_BITS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); - - /* Count Huffman symbol for run length / number of bits */ - ac_counts[(r << 4) + nbits]++; - - r = 0; - } - } - - /* If the last coef(s) were zero, emit an end-of-block code */ - if (r > 0) - ac_counts[0]++; - } - - private bool emit_byte(int val) - { - return m_cinfo.m_dest.emit_byte(val); - } - - /// - /// Only the right 24 bits of put_buffer are used; the valid bits are - /// left-justified in this part. At most 16 bits can be passed to emit_bits - /// in one call, and we never retain more than 7 bits in put_buffer - /// between calls, so 24 bits are sufficient. - /// - private bool emit_bits(savable_state state, int code, int size) - { - // Emit some bits; return true if successful, false if must suspend - /* This routine is heavily used, so it's worth coding tightly. */ - int put_buffer = code; - int put_bits = state.put_bits; - - /* if size is 0, caller used an invalid Huffman table entry */ - if (size == 0) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_HUFF_MISSING_CODE); - - put_buffer &= (1 << size) - 1; /* mask off any extra bits in code */ - put_bits += size; /* new number of bits in buffer */ - put_buffer <<= 24 - put_bits; /* align incoming bits */ - put_buffer |= state.put_buffer; /* and merge with old buffer contents */ - - while (put_bits >= 8) - { - int c = (put_buffer >> 16) & 0xFF; - if (!emit_byte(c)) - return false; - - if (c == 0xFF) - { - /* need to stuff a zero byte? */ - if (!emit_byte(0)) - return false; - } - - put_buffer <<= 8; - put_bits -= 8; - } - - state.put_buffer = put_buffer; /* update state variables */ - state.put_bits = put_bits; - - return true; - } - - private bool flush_bits(savable_state state) - { - if (!emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ - return false; - - state.put_buffer = 0; /* and reset bit-buffer to empty */ - state.put_bits = 0; - return true; - } - - /// - /// Emit a restart marker and resynchronize predictions. - /// - private bool emit_restart(savable_state state, int restart_num) - { - if (!flush_bits(state)) - return false; - - if (!emit_byte(0xFF)) - return false; - - if (!emit_byte((int)(JPEG_MARKER.RST0 + restart_num))) - return false; - - /* Re-initialize DC predictions to 0 */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - state.last_dc_val[ci] = 0; - - /* The restart counter is not updated until we successfully write the MCU. */ - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_c_coef_controller.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_c_coef_controller.cs deleted file mode 100644 index fe85c562..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_c_coef_controller.cs +++ /dev/null @@ -1,28 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Coefficient buffer control - /// - interface jpeg_c_coef_controller - { - void start_pass(J_BUF_MODE pass_mode); - bool compress_data(byte[][][] input_buf); - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_c_main_controller.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_c_main_controller.cs deleted file mode 100644 index 66f93cd9..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_c_main_controller.cs +++ /dev/null @@ -1,123 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the main buffer controller for compression. - * The main buffer lies between the pre-processor and the JPEG - * compressor proper; it holds downsampled data in the JPEG colorspace. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Main buffer control (downsampled-data buffer) - /// - class jpeg_c_main_controller - { - private jpeg_compress_struct m_cinfo; - - private int m_cur_iMCU_row; /* number of current iMCU row */ - private int m_rowgroup_ctr; /* counts row groups received in iMCU row */ - private bool m_suspended; /* remember if we suspended output */ - - /* If using just a strip buffer, this points to the entire set of buffers - * (we allocate one for each component). In the full-image case, this - * points to the currently accessible strips of the virtual arrays. - */ - private byte[][][] m_buffer = new byte[JpegConstants.MAX_COMPONENTS][][]; - - public jpeg_c_main_controller(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - - /* Allocate a strip buffer for each component */ - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - m_buffer[ci] = jpeg_common_struct.AllocJpegSamples( - cinfo.Component_info[ci].Width_in_blocks * JpegConstants.DCTSIZE, - cinfo.Component_info[ci].V_samp_factor * JpegConstants.DCTSIZE); - } - } - - // Initialize for a processing pass. - public void start_pass(J_BUF_MODE pass_mode) - { - /* Do nothing in raw-data mode. */ - if (m_cinfo.m_raw_data_in) - return; - - m_cur_iMCU_row = 0; /* initialize counters */ - m_rowgroup_ctr = 0; - m_suspended = false; - - if (pass_mode != J_BUF_MODE.JBUF_PASS_THRU) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - } - - /// - /// Process some data. - /// This routine handles the simple pass-through mode, - /// where we have only a strip buffer. - /// - public void process_data(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail) - { - while (m_cur_iMCU_row < m_cinfo.m_total_iMCU_rows) - { - /* Read input data if we haven't filled the main buffer yet */ - if (m_rowgroup_ctr < JpegConstants.DCTSIZE) - m_cinfo.m_prep.pre_process_data(input_buf, ref in_row_ctr, in_rows_avail, m_buffer, ref m_rowgroup_ctr, JpegConstants.DCTSIZE); - - /* If we don't have a full iMCU row buffered, return to application for - * more data. Note that preprocessor will always pad to fill the iMCU row - * at the bottom of the image. - */ - if (m_rowgroup_ctr != JpegConstants.DCTSIZE) - return; - - /* Send the completed row to the compressor */ - if (!m_cinfo.m_coef.compress_data(m_buffer)) - { - /* If compressor did not consume the whole row, then we must need to - * suspend processing and return to the application. In this situation - * we pretend we didn't yet consume the last input row; otherwise, if - * it happened to be the last row of the image, the application would - * think we were done. - */ - if (!m_suspended) - { - in_row_ctr--; - m_suspended = true; - } - - return; - } - - /* We did finish the row. Undo our little suspension hack if a previous - * call suspended; then mark the main buffer empty. - */ - if (m_suspended) - { - in_row_ctr++; - m_suspended = false; - } - - m_rowgroup_ctr = 0; - m_cur_iMCU_row++; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_c_prep_controller.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_c_prep_controller.cs deleted file mode 100644 index a778ed19..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_c_prep_controller.cs +++ /dev/null @@ -1,291 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the compression preprocessing controller. - * This controller manages the color conversion, downsampling, - * and edge expansion steps. - * - * Most of the complexity here is associated with buffering input rows - * as required by the downsampler. See the comments at the head of - * my_downsampler for the downsampler's needs. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Compression preprocessing (downsampling input buffer control). - /// - /// For the simple (no-context-row) case, we just need to buffer one - /// row group's worth of pixels for the downsampling step. At the bottom of - /// the image, we pad to a full row group by replicating the last pixel row. - /// The downsampler's last output row is then replicated if needed to pad - /// out to a full iMCU row. - /// - /// When providing context rows, we must buffer three row groups' worth of - /// pixels. Three row groups are physically allocated, but the row pointer - /// arrays are made five row groups high, with the extra pointers above and - /// below "wrapping around" to point to the last and first real row groups. - /// This allows the downsampler to access the proper context rows. - /// At the top and bottom of the image, we create dummy context rows by - /// copying the first or last real pixel row. This copying could be avoided - /// by pointer hacking as is done in jdmainct.c, but it doesn't seem worth the - /// trouble on the compression side. - /// - class jpeg_c_prep_controller - { - private jpeg_compress_struct m_cinfo; - - /* Downsampling input buffer. This buffer holds color-converted data - * until we have enough to do a downsample step. - */ - private byte[][][] m_color_buf = new byte[JpegConstants.MAX_COMPONENTS][][]; - private int m_colorBufRowsOffset; - - private int m_rows_to_go; /* counts rows remaining in source image */ - private int m_next_buf_row; /* index of next row to store in color_buf */ - - private int m_this_row_group; /* starting row index of group to process */ - private int m_next_buf_stop; /* downsample when we reach this index */ - - public jpeg_c_prep_controller(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - - /* Allocate the color conversion buffer. - * We make the buffer wide enough to allow the downsampler to edge-expand - * horizontally within the buffer, if it so chooses. - */ - if (cinfo.m_downsample.NeedContextRows()) - { - /* Set up to provide context rows */ - create_context_buffer(); - } - else - { - /* No context, just make it tall enough for one row group */ - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - m_colorBufRowsOffset = 0; - m_color_buf[ci] = jpeg_compress_struct.AllocJpegSamples( - (cinfo.Component_info[ci].Width_in_blocks * JpegConstants.DCTSIZE * cinfo.m_max_h_samp_factor) / cinfo.Component_info[ci].H_samp_factor, - cinfo.m_max_v_samp_factor); - } - } - } - - /// - /// Initialize for a processing pass. - /// - public void start_pass(J_BUF_MODE pass_mode) - { - if (pass_mode != J_BUF_MODE.JBUF_PASS_THRU) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - - /* Initialize total-height counter for detecting bottom of image */ - m_rows_to_go = m_cinfo.m_image_height; - - /* Mark the conversion buffer empty */ - m_next_buf_row = 0; - - /* Preset additional state variables for context mode. - * These aren't used in non-context mode, so we needn't test which mode. - */ - m_this_row_group = 0; - - /* Set next_buf_stop to stop after two row groups have been read in. */ - m_next_buf_stop = 2 * m_cinfo.m_max_v_samp_factor; - } - - public void pre_process_data(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail, byte[][][] output_buf, ref int out_row_group_ctr, int out_row_groups_avail) - { - if (m_cinfo.m_downsample.NeedContextRows()) - pre_process_context(input_buf, ref in_row_ctr, in_rows_avail, output_buf, ref out_row_group_ctr, out_row_groups_avail); - else - pre_process_WithoutContext(input_buf, ref in_row_ctr, in_rows_avail, output_buf, ref out_row_group_ctr, out_row_groups_avail); - } - - /// - /// Create the wrapped-around downsampling input buffer needed for context mode. - /// - private void create_context_buffer() - { - int rgroup_height = m_cinfo.m_max_v_samp_factor; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - int samplesPerRow = (m_cinfo.Component_info[ci].Width_in_blocks * JpegConstants.DCTSIZE * m_cinfo.m_max_h_samp_factor) / m_cinfo.Component_info[ci].H_samp_factor; - - byte[][] fake_buffer = new byte[5 * rgroup_height][]; - for (int i = 1; i < 4 * rgroup_height; i++) - fake_buffer[i] = new byte [samplesPerRow]; - - /* Allocate the actual buffer space (3 row groups) for this component. - * We make the buffer wide enough to allow the downsampler to edge-expand - * horizontally within the buffer, if it so chooses. - */ - byte[][] true_buffer = jpeg_common_struct.AllocJpegSamples(samplesPerRow, 3 * rgroup_height); - - /* Copy true buffer row pointers into the middle of the fake row array */ - for (int i = 0; i < 3 * rgroup_height; i++) - fake_buffer[rgroup_height + i] = true_buffer[i]; - - /* Fill in the above and below wraparound pointers */ - for (int i = 0; i < rgroup_height; i++) - { - fake_buffer[i] = true_buffer[2 * rgroup_height + i]; - fake_buffer[4 * rgroup_height + i] = true_buffer[i]; - } - - m_color_buf[ci] = fake_buffer; - m_colorBufRowsOffset = rgroup_height; - } - } - - /// - /// Process some data in the simple no-context case. - /// - /// Preprocessor output data is counted in "row groups". A row group - /// is defined to be v_samp_factor sample rows of each component. - /// Downsampling will produce this much data from each max_v_samp_factor - /// input rows. - /// - private void pre_process_WithoutContext(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail, byte[][][] output_buf, ref int out_row_group_ctr, int out_row_groups_avail) - { - while (in_row_ctr < in_rows_avail && out_row_group_ctr < out_row_groups_avail) - { - /* Do color conversion to fill the conversion buffer. */ - int inrows = in_rows_avail - in_row_ctr; - int numrows = m_cinfo.m_max_v_samp_factor - m_next_buf_row; - numrows = Math.Min(numrows, inrows); - m_cinfo.m_cconvert.color_convert(input_buf, in_row_ctr, m_color_buf, m_colorBufRowsOffset + m_next_buf_row, numrows); - in_row_ctr += numrows; - m_next_buf_row += numrows; - m_rows_to_go -= numrows; - - /* If at bottom of image, pad to fill the conversion buffer. */ - if (m_rows_to_go == 0 && m_next_buf_row < m_cinfo.m_max_v_samp_factor) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - expand_bottom_edge(m_color_buf[ci], m_colorBufRowsOffset, m_cinfo.m_image_width, m_next_buf_row, m_cinfo.m_max_v_samp_factor); - - m_next_buf_row = m_cinfo.m_max_v_samp_factor; - } - - /* If we've filled the conversion buffer, empty it. */ - if (m_next_buf_row == m_cinfo.m_max_v_samp_factor) - { - m_cinfo.m_downsample.downsample(m_color_buf, m_colorBufRowsOffset, output_buf, out_row_group_ctr); - m_next_buf_row = 0; - out_row_group_ctr++; - } - - /* If at bottom of image, pad the output to a full iMCU height. - * Note we assume the caller is providing a one-iMCU-height output buffer! - */ - if (m_rows_to_go == 0 && out_row_group_ctr < out_row_groups_avail) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[ci]; - expand_bottom_edge(output_buf[ci], 0, componentInfo.Width_in_blocks * JpegConstants.DCTSIZE, - out_row_group_ctr * componentInfo.V_samp_factor, - out_row_groups_avail * componentInfo.V_samp_factor); - } - - out_row_group_ctr = out_row_groups_avail; - break; /* can exit outer loop without test */ - } - } - } - - /// - /// Process some data in the context case. - /// - private void pre_process_context(byte[][] input_buf, ref int in_row_ctr, int in_rows_avail, byte[][][] output_buf, ref int out_row_group_ctr, int out_row_groups_avail) - { - while (out_row_group_ctr < out_row_groups_avail) - { - if (in_row_ctr < in_rows_avail) - { - /* Do color conversion to fill the conversion buffer. */ - int inrows = in_rows_avail - in_row_ctr; - int numrows = m_next_buf_stop - m_next_buf_row; - numrows = Math.Min(numrows, inrows); - m_cinfo.m_cconvert.color_convert(input_buf, in_row_ctr, m_color_buf, m_colorBufRowsOffset + m_next_buf_row, numrows); - - /* Pad at top of image, if first time through */ - if (m_rows_to_go == m_cinfo.m_image_height) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - for (int row = 1; row <= m_cinfo.m_max_v_samp_factor; row++) - JpegUtils.jcopy_sample_rows(m_color_buf[ci], m_colorBufRowsOffset, m_color_buf[ci], m_colorBufRowsOffset - row, 1, m_cinfo.m_image_width); - } - } - - in_row_ctr += numrows; - m_next_buf_row += numrows; - m_rows_to_go -= numrows; - } - else - { - /* Return for more data, unless we are at the bottom of the image. */ - if (m_rows_to_go != 0) - break; - - /* When at bottom of image, pad to fill the conversion buffer. */ - if (m_next_buf_row < m_next_buf_stop) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - expand_bottom_edge(m_color_buf[ci], m_colorBufRowsOffset, m_cinfo.m_image_width, m_next_buf_row, m_next_buf_stop); - - m_next_buf_row = m_next_buf_stop; - } - } - - /* If we've gotten enough data, downsample a row group. */ - if (m_next_buf_row == m_next_buf_stop) - { - m_cinfo.m_downsample.downsample(m_color_buf, m_colorBufRowsOffset + m_this_row_group, output_buf, out_row_group_ctr); - out_row_group_ctr++; - - /* Advance pointers with wraparound as necessary. */ - m_this_row_group += m_cinfo.m_max_v_samp_factor; - int buf_height = m_cinfo.m_max_v_samp_factor * 3; - - if (m_this_row_group >= buf_height) - m_this_row_group = 0; - - if (m_next_buf_row >= buf_height) - m_next_buf_row = 0; - - m_next_buf_stop = m_next_buf_row + m_cinfo.m_max_v_samp_factor; - } - } - } - - /// - /// Expand an image vertically from height input_rows to height output_rows, - /// by duplicating the bottom row. - /// - private static void expand_bottom_edge(byte[][] image_data, int rowsOffset, int num_cols, int input_rows, int output_rows) - { - for (int row = input_rows; row < output_rows; row++) - JpegUtils.jcopy_sample_rows(image_data, rowsOffset + input_rows - 1, image_data, row, 1, num_cols); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_color_converter.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_color_converter.cs deleted file mode 100644 index 4cc64eba..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_color_converter.cs +++ /dev/null @@ -1,423 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains input colorspace conversion routines. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Colorspace conversion - /// - class jpeg_color_converter - { - private const int SCALEBITS = 16; /* speediest right-shift on some machines */ - private const int CBCR_OFFSET = JpegConstants.CENTERJSAMPLE << SCALEBITS; - private const int ONE_HALF = 1 << (SCALEBITS - 1); - - // We allocate one big table and divide it up into eight parts, instead of - // doing eight alloc_small requests. This lets us use a single table base - // address, which can be held in a register in the inner loops on many - // machines (more than can hold all eight addresses, anyway). - private const int R_Y_OFF = 0; /* offset to R => Y section */ - private const int G_Y_OFF = (1 * (JpegConstants.MAXJSAMPLE+1)); /* offset to G => Y section */ - private const int B_Y_OFF = (2 * (JpegConstants.MAXJSAMPLE+1)); /* etc. */ - private const int R_CB_OFF = (3 * (JpegConstants.MAXJSAMPLE+1)); - private const int G_CB_OFF = (4 * (JpegConstants.MAXJSAMPLE+1)); - private const int B_CB_OFF = (5 * (JpegConstants.MAXJSAMPLE+1)); - private const int R_CR_OFF = B_CB_OFF; /* B=>Cb, R=>Cr are the same */ - private const int G_CR_OFF = (6 * (JpegConstants.MAXJSAMPLE+1)); - private const int B_CR_OFF = (7 * (JpegConstants.MAXJSAMPLE+1)); - private const int TABLE_SIZE = (8 * (JpegConstants.MAXJSAMPLE + 1)); - - private jpeg_compress_struct m_cinfo; - - private bool m_useNullStart; - - private bool m_useCmykYcckConvert; - private bool m_useGrayscaleConvert; - private bool m_useNullConvert; - private bool m_useRgbGrayConvert; - private bool m_useRgbYccConvert; - - private int[] m_rgb_ycc_tab; /* => table for RGB to YCbCr conversion */ - - public jpeg_color_converter(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - - /* set start_pass to null method until we find out differently */ - m_useNullStart = true; - - /* Make sure input_components agrees with in_color_space */ - switch (cinfo.m_in_color_space) - { - case J_COLOR_SPACE.JCS_GRAYSCALE: - if (cinfo.m_input_components != 1) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); - break; - - case J_COLOR_SPACE.JCS_RGB: - case J_COLOR_SPACE.JCS_YCbCr: - if (cinfo.m_input_components != 3) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); - break; - - case J_COLOR_SPACE.JCS_CMYK: - case J_COLOR_SPACE.JCS_YCCK: - if (cinfo.m_input_components != 4) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); - break; - - default: - /* JCS_UNKNOWN can be anything */ - if (cinfo.m_input_components < 1) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); - break; - } - - /* Check num_components, set conversion method based on requested space */ - clearConvertFlags(); - switch (cinfo.m_jpeg_color_space) - { - case J_COLOR_SPACE.JCS_GRAYSCALE: - if (cinfo.m_num_components != 1) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - - if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_GRAYSCALE) - m_useGrayscaleConvert = true; - else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_RGB) - { - m_useNullStart = false; // use rgb_ycc_start - m_useRgbGrayConvert = true; - } - else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_YCbCr) - m_useGrayscaleConvert = true; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - case J_COLOR_SPACE.JCS_RGB: - if (cinfo.m_num_components != 3) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - - if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_RGB) - m_useNullConvert = true; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - case J_COLOR_SPACE.JCS_YCbCr: - if (cinfo.m_num_components != 3) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - - if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_RGB) - { - m_useNullStart = false; // use rgb_ycc_start - m_useRgbYccConvert = true; - } - else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_YCbCr) - m_useNullConvert = true; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - case J_COLOR_SPACE.JCS_CMYK: - if (cinfo.m_num_components != 4) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - - if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_CMYK) - m_useNullConvert = true; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - case J_COLOR_SPACE.JCS_YCCK: - if (cinfo.m_num_components != 4) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - - if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_CMYK) - { - m_useNullStart = false; // use rgb_ycc_start - m_useCmykYcckConvert = true; - } - else if (cinfo.m_in_color_space == J_COLOR_SPACE.JCS_YCCK) - m_useNullConvert = true; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - default: - /* allow null conversion of JCS_UNKNOWN */ - if (cinfo.m_jpeg_color_space != cinfo.m_in_color_space || cinfo.m_num_components != cinfo.m_input_components) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - - m_useNullConvert = true; - break; - } - } - - public void start_pass() - { - if (!m_useNullStart) - rgb_ycc_start(); - } - - /// - /// Convert some rows of samples to the JPEG colorspace. - /// - /// Note that we change from the application's interleaved-pixel format - /// to our internal noninterleaved, one-plane-per-component format. - /// The input buffer is therefore three times as wide as the output buffer. - /// - /// A starting row offset is provided only for the output buffer. The caller - /// can easily adjust the passed input_buf value to accommodate any row - /// offset required on that side. - /// - public void color_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) - { - if (m_useCmykYcckConvert) - cmyk_ycck_convert(input_buf, input_row, output_buf, output_row, num_rows); - else if (m_useGrayscaleConvert) - grayscale_convert(input_buf, input_row, output_buf, output_row, num_rows); - else if (m_useRgbGrayConvert) - rgb_gray_convert(input_buf, input_row, output_buf, output_row, num_rows); - else if (m_useRgbYccConvert) - rgb_ycc_convert(input_buf, input_row, output_buf, output_row, num_rows); - else if (m_useNullConvert) - null_convert(input_buf, input_row, output_buf, output_row, num_rows); - else - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - } - - /// - /// Initialize for RGB->YCC colorspace conversion. - /// - private void rgb_ycc_start() - { - /* Allocate and fill in the conversion tables. */ - m_rgb_ycc_tab = new int[TABLE_SIZE]; - - for (int i = 0; i <= JpegConstants.MAXJSAMPLE; i++) - { - m_rgb_ycc_tab[i + R_Y_OFF] = FIX(0.29900) * i; - m_rgb_ycc_tab[i + G_Y_OFF] = FIX(0.58700) * i; - m_rgb_ycc_tab[i + B_Y_OFF] = FIX(0.11400) * i + ONE_HALF; - m_rgb_ycc_tab[i + R_CB_OFF] = (-FIX(0.16874)) * i; - m_rgb_ycc_tab[i + G_CB_OFF] = (-FIX(0.33126)) * i; - - /* We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr. - * This ensures that the maximum output will round to MAXJSAMPLE - * not MAXJSAMPLE+1, and thus that we don't have to range-limit. - */ - m_rgb_ycc_tab[i + B_CB_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF - 1; - - /* B=>Cb and R=>Cr tables are the same - rgb_ycc_tab[i+R_CR_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1; - */ - m_rgb_ycc_tab[i + G_CR_OFF] = (-FIX(0.41869)) * i; - m_rgb_ycc_tab[i + B_CR_OFF] = (-FIX(0.08131)) * i; - } - } - - private void clearConvertFlags() - { - m_useCmykYcckConvert = false; - m_useGrayscaleConvert = false; - m_useNullConvert = false; - m_useRgbGrayConvert = false; - m_useRgbYccConvert = false; - } - - private static int FIX(double x) - { - return (int)(x * (1L << SCALEBITS) + 0.5); - } - - /// - /// RGB -> YCbCr conversion: most common case - /// YCbCr is defined per CCIR 601-1, except that Cb and Cr are - /// normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. - /// The conversion equations to be implemented are therefore - /// Y = 0.29900 * R + 0.58700 * G + 0.11400 * B - /// Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + CENTERJSAMPLE - /// Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + CENTERJSAMPLE - /// (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) - /// To avoid floating-point arithmetic, we represent the fractional constants - /// as integers scaled up by 2^16 (about 4 digits precision); we have to divide - /// the products by 2^16, with appropriate rounding, to get the correct answer. - /// For even more speed, we avoid doing any multiplications in the inner loop - /// by precalculating the constants times R,G,B for all possible values. - /// For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); - /// for 12-bit samples it is still acceptable. It's not very reasonable for - /// 16-bit samples, but if you want lossless storage you shouldn't be changing - /// colorspace anyway. - /// The CENTERJSAMPLE offsets and the rounding fudge-factor of 0.5 are included - /// in the tables to save adding them separately in the inner loop. - /// - private void rgb_ycc_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) - { - int num_cols = m_cinfo.m_image_width; - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - int r = input_buf[input_row + row][columnOffset + JpegConstants.RGB_RED]; - int g = input_buf[input_row + row][columnOffset + JpegConstants.RGB_GREEN]; - int b = input_buf[input_row + row][columnOffset + JpegConstants.RGB_BLUE]; - columnOffset += JpegConstants.RGB_PIXELSIZE; - - /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations - * must be too; we do not need an explicit range-limiting operation. - * Hence the value being shifted is never negative, and we don't - * need the general RIGHT_SHIFT macro. - */ - /* Y */ - output_buf[0][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_Y_OFF] + m_rgb_ycc_tab[g + G_Y_OFF] + m_rgb_ycc_tab[b + B_Y_OFF]) >> SCALEBITS); - /* Cb */ - output_buf[1][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_CB_OFF] + m_rgb_ycc_tab[g + G_CB_OFF] + m_rgb_ycc_tab[b + B_CB_OFF]) >> SCALEBITS); - /* Cr */ - output_buf[2][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_CR_OFF] + m_rgb_ycc_tab[g + G_CR_OFF] + m_rgb_ycc_tab[b + B_CR_OFF]) >> SCALEBITS); - } - - output_row++; - } - } - - /// - /// Convert some rows of samples to the JPEG colorspace. - /// This version handles RGB->grayscale conversion, which is the same - /// as the RGB->Y portion of RGB->YCbCr. - /// We assume rgb_ycc_start has been called (we only use the Y tables). - /// - private void rgb_gray_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) - { - int num_cols = m_cinfo.m_image_width; - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - int r = input_buf[input_row + row][columnOffset + JpegConstants.RGB_RED]; - int g = input_buf[input_row + row][columnOffset + JpegConstants.RGB_GREEN]; - int b = input_buf[input_row + row][columnOffset + JpegConstants.RGB_BLUE]; - columnOffset += JpegConstants.RGB_PIXELSIZE; - - /* Y */ - output_buf[0][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_Y_OFF] + m_rgb_ycc_tab[g + G_Y_OFF] + m_rgb_ycc_tab[b + B_Y_OFF]) >> SCALEBITS); - } - - output_row++; - } - } - - /// - /// Convert some rows of samples to the JPEG colorspace. - /// This version handles Adobe-style CMYK->YCCK conversion, - /// where we convert R=1-C, G=1-M, and B=1-Y to YCbCr using the same - /// conversion as above, while passing K (black) unchanged. - /// We assume rgb_ycc_start has been called. - /// - private void cmyk_ycck_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) - { - int num_cols = m_cinfo.m_image_width; - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - int r = JpegConstants.MAXJSAMPLE - input_buf[input_row + row][columnOffset]; - int g = JpegConstants.MAXJSAMPLE - input_buf[input_row + row][columnOffset + 1]; - int b = JpegConstants.MAXJSAMPLE - input_buf[input_row + row][columnOffset + 2]; - - /* K passes through as-is */ - /* don't need GETJSAMPLE here */ - output_buf[3][output_row][col] = input_buf[input_row + row][columnOffset + 3]; - columnOffset += 4; - - /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations - * must be too; we do not need an explicit range-limiting operation. - * Hence the value being shifted is never negative, and we don't - * need the general RIGHT_SHIFT macro. - */ - /* Y */ - output_buf[0][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_Y_OFF] + m_rgb_ycc_tab[g + G_Y_OFF] + m_rgb_ycc_tab[b + B_Y_OFF]) >> SCALEBITS); - /* Cb */ - output_buf[1][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_CB_OFF] + m_rgb_ycc_tab[g + G_CB_OFF] + m_rgb_ycc_tab[b + B_CB_OFF]) >> SCALEBITS); - /* Cr */ - output_buf[2][output_row][col] = (byte)((m_rgb_ycc_tab[r + R_CR_OFF] + m_rgb_ycc_tab[g + G_CR_OFF] + m_rgb_ycc_tab[b + B_CR_OFF]) >> SCALEBITS); - } - - output_row++; - } - } - - /// - /// Convert some rows of samples to the JPEG colorspace. - /// This version handles grayscale output with no conversion. - /// The source can be either plain grayscale or YCbCr (since Y == gray). - /// - private void grayscale_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) - { - int num_cols = m_cinfo.m_image_width; - int instride = m_cinfo.m_input_components; - - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - /* don't need GETJSAMPLE() here */ - output_buf[0][output_row][col] = input_buf[input_row + row][columnOffset]; - columnOffset += instride; - } - - output_row++; - } - } - - /// - /// Convert some rows of samples to the JPEG colorspace. - /// This version handles multi-component colorspaces without conversion. - /// We assume input_components == num_components. - /// - private void null_convert(byte[][] input_buf, int input_row, byte[][][] output_buf, int output_row, int num_rows) - { - int nc = m_cinfo.m_num_components; - int num_cols = m_cinfo.m_image_width; - - for (int row = 0; row < num_rows; row++) - { - /* It seems fastest to make a separate pass for each component. */ - for (int ci = 0; ci < nc; ci++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - /* don't need GETJSAMPLE() here */ - output_buf[ci][output_row][col] = input_buf[input_row + row][columnOffset + ci]; - columnOffset += nc; - } - } - - output_row++; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_color_deconverter.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_color_deconverter.cs deleted file mode 100644 index 2a4cf6f8..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_color_deconverter.cs +++ /dev/null @@ -1,392 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains output colorspace conversion routines. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Colorspace conversion - /// - class jpeg_color_deconverter - { - private const int SCALEBITS = 16; /* speediest right-shift on some machines */ - private const int ONE_HALF = 1 << (SCALEBITS - 1); - - private enum ColorConverter - { - grayscale_converter, - ycc_rgb_converter, - gray_rgb_converter, - null_converter, - ycck_cmyk_converter - } - - private ColorConverter m_converter; - private jpeg_decompress_struct m_cinfo; - - private int[] m_perComponentOffsets; - - /* Private state for YCC->RGB conversion */ - private int[] m_Cr_r_tab; /* => table for Cr to R conversion */ - private int[] m_Cb_b_tab; /* => table for Cb to B conversion */ - private int[] m_Cr_g_tab; /* => table for Cr to G conversion */ - private int[] m_Cb_g_tab; /* => table for Cb to G conversion */ - - /// - /// Module initialization routine for output colorspace conversion. - /// - public jpeg_color_deconverter(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Make sure num_components agrees with jpeg_color_space */ - switch (cinfo.m_jpeg_color_space) - { - case J_COLOR_SPACE.JCS_GRAYSCALE: - if (cinfo.m_num_components != 1) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - break; - - case J_COLOR_SPACE.JCS_RGB: - case J_COLOR_SPACE.JCS_YCbCr: - if (cinfo.m_num_components != 3) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - break; - - case J_COLOR_SPACE.JCS_CMYK: - case J_COLOR_SPACE.JCS_YCCK: - if (cinfo.m_num_components != 4) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - break; - - default: - /* JCS_UNKNOWN can be anything */ - if (cinfo.m_num_components < 1) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - break; - } - - /* Set out_color_components and conversion method based on requested space. - * Also clear the component_needed flags for any unused components, - * so that earlier pipeline stages can avoid useless computation. - */ - - switch (cinfo.m_out_color_space) - { - case J_COLOR_SPACE.JCS_GRAYSCALE: - cinfo.m_out_color_components = 1; - if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_GRAYSCALE || cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_YCbCr) - { - m_converter = ColorConverter.grayscale_converter; - /* For color->grayscale conversion, only the Y (0) component is needed */ - for (int ci = 1; ci < cinfo.m_num_components; ci++) - cinfo.Comp_info[ci].component_needed = false; - } - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - case J_COLOR_SPACE.JCS_RGB: - cinfo.m_out_color_components = JpegConstants.RGB_PIXELSIZE; - if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_YCbCr) - { - m_converter = ColorConverter.ycc_rgb_converter; - build_ycc_rgb_table(); - } - else if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_GRAYSCALE) - m_converter = ColorConverter.gray_rgb_converter; - else if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_RGB) - m_converter = ColorConverter.null_converter; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - case J_COLOR_SPACE.JCS_CMYK: - cinfo.m_out_color_components = 4; - if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_YCCK) - { - m_converter = ColorConverter.ycck_cmyk_converter; - build_ycc_rgb_table(); - } - else if (cinfo.m_jpeg_color_space == J_COLOR_SPACE.JCS_CMYK) - m_converter = ColorConverter.null_converter; - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - - default: - /* Permit null conversion to same output space */ - if (cinfo.m_out_color_space == cinfo.m_jpeg_color_space) - { - cinfo.m_out_color_components = cinfo.m_num_components; - m_converter = ColorConverter.null_converter; - } - else - { - /* unsupported non-null conversion */ - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - } - break; - } - - if (cinfo.m_quantize_colors) - cinfo.m_output_components = 1; /* single colormapped output component */ - else - cinfo.m_output_components = cinfo.m_out_color_components; - } - - /// - /// Convert some rows of samples to the output colorspace. - /// - /// Note that we change from noninterleaved, one-plane-per-component format - /// to interleaved-pixel format. The output buffer is therefore three times - /// as wide as the input buffer. - /// A starting row offset is provided only for the input buffer. The caller - /// can easily adjust the passed output_buf value to accommodate any row - /// offset required on that side. - /// - public void color_convert(ComponentBuffer[] input_buf, int[] perComponentOffsets, int input_row, byte[][] output_buf, int output_row, int num_rows) - { - m_perComponentOffsets = perComponentOffsets; - - switch (m_converter) - { - case ColorConverter.grayscale_converter: - grayscale_convert(input_buf, input_row, output_buf, output_row, num_rows); - break; - case ColorConverter.ycc_rgb_converter: - ycc_rgb_convert(input_buf, input_row, output_buf, output_row, num_rows); - break; - case ColorConverter.gray_rgb_converter: - gray_rgb_convert(input_buf, input_row, output_buf, output_row, num_rows); - break; - case ColorConverter.null_converter: - null_convert(input_buf, input_row, output_buf, output_row, num_rows); - break; - case ColorConverter.ycck_cmyk_converter: - ycck_cmyk_convert(input_buf, input_row, output_buf, output_row, num_rows); - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL); - break; - } - } - - /**************** YCbCr -> RGB conversion: most common case **************/ - - /* - * YCbCr is defined per CCIR 601-1, except that Cb and Cr are - * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. - * The conversion equations to be implemented are therefore - * R = Y + 1.40200 * Cr - * G = Y - 0.34414 * Cb - 0.71414 * Cr - * B = Y + 1.77200 * Cb - * where Cb and Cr represent the incoming values less CENTERJSAMPLE. - * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) - * - * To avoid floating-point arithmetic, we represent the fractional constants - * as integers scaled up by 2^16 (about 4 digits precision); we have to divide - * the products by 2^16, with appropriate rounding, to get the correct answer. - * Notice that Y, being an integral input, does not contribute any fraction - * so it need not participate in the rounding. - * - * For even more speed, we avoid doing any multiplications in the inner loop - * by precalculating the constants times Cb and Cr for all possible values. - * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); - * for 12-bit samples it is still acceptable. It's not very reasonable for - * 16-bit samples, but if you want lossless storage you shouldn't be changing - * colorspace anyway. - * The Cr=>R and Cb=>B values can be rounded to integers in advance; the - * values for the G calculation are left scaled up, since we must add them - * together before rounding. - */ - - /// - /// Initialize tables for YCC->RGB colorspace conversion. - /// - private void build_ycc_rgb_table() - { - m_Cr_r_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - m_Cb_b_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - m_Cr_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - m_Cb_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - - for (int i = 0, x = -JpegConstants.CENTERJSAMPLE; i <= JpegConstants.MAXJSAMPLE; i++, x++) - { - /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ - /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ - /* Cr=>R value is nearest int to 1.40200 * x */ - m_Cr_r_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); - - /* Cb=>B value is nearest int to 1.77200 * x */ - m_Cb_b_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); - - /* Cr=>G value is scaled-up -0.71414 * x */ - m_Cr_g_tab[i] = (-FIX(0.71414)) * x; - - /* Cb=>G value is scaled-up -0.34414 * x */ - /* We also add in ONE_HALF so that need not do it in inner loop */ - m_Cb_g_tab[i] = (-FIX(0.34414)) * x + ONE_HALF; - } - } - - private void ycc_rgb_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) - { - int component0RowOffset = m_perComponentOffsets[0]; - int component1RowOffset = m_perComponentOffsets[1]; - int component2RowOffset = m_perComponentOffsets[2]; - - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset; - - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < m_cinfo.m_output_width; col++) - { - int y = input_buf[0][input_row + component0RowOffset][col]; - int cb = input_buf[1][input_row + component1RowOffset][col]; - int cr = input_buf[2][input_row + component2RowOffset][col]; - - /* Range-limiting is essential due to noise introduced by DCT losses. */ - output_buf[output_row + row][columnOffset + JpegConstants.RGB_RED] = limit[limitOffset + y + m_Cr_r_tab[cr]]; - output_buf[output_row + row][columnOffset + JpegConstants.RGB_GREEN] = limit[limitOffset + y + JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS)]; - output_buf[output_row + row][columnOffset + JpegConstants.RGB_BLUE] = limit[limitOffset + y + m_Cb_b_tab[cb]]; - columnOffset += JpegConstants.RGB_PIXELSIZE; - } - - input_row++; - } - } - - /**************** Cases other than YCbCr -> RGB **************/ - - /// - /// Adobe-style YCCK->CMYK conversion. - /// We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same - /// conversion as above, while passing K (black) unchanged. - /// We assume build_ycc_rgb_table has been called. - /// - private void ycck_cmyk_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) - { - int component0RowOffset = m_perComponentOffsets[0]; - int component1RowOffset = m_perComponentOffsets[1]; - int component2RowOffset = m_perComponentOffsets[2]; - int component3RowOffset = m_perComponentOffsets[3]; - - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset; - - int num_cols = m_cinfo.m_output_width; - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - int y = input_buf[0][input_row + component0RowOffset][col]; - int cb = input_buf[1][input_row + component1RowOffset][col]; - int cr = input_buf[2][input_row + component2RowOffset][col]; - - /* Range-limiting is essential due to noise introduced by DCT losses. */ - output_buf[output_row + row][columnOffset] = limit[limitOffset + JpegConstants.MAXJSAMPLE - (y + m_Cr_r_tab[cr])]; /* red */ - output_buf[output_row + row][columnOffset + 1] = limit[limitOffset + JpegConstants.MAXJSAMPLE - (y + JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS))]; /* green */ - output_buf[output_row + row][columnOffset + 2] = limit[limitOffset + JpegConstants.MAXJSAMPLE - (y + m_Cb_b_tab[cb])]; /* blue */ - - /* K passes through unchanged */ - /* don't need GETJSAMPLE here */ - output_buf[output_row + row][columnOffset + 3] = input_buf[3][input_row + component3RowOffset][col]; - columnOffset += 4; - } - - input_row++; - } - } - - /// - /// Convert grayscale to RGB: just duplicate the graylevel three times. - /// This is provided to support applications that don't want to cope - /// with grayscale as a separate case. - /// - private void gray_rgb_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) - { - int component0RowOffset = m_perComponentOffsets[0]; - int component1RowOffset = m_perComponentOffsets[1]; - int component2RowOffset = m_perComponentOffsets[2]; - - int num_cols = m_cinfo.m_output_width; - for (int row = 0; row < num_rows; row++) - { - int columnOffset = 0; - for (int col = 0; col < num_cols; col++) - { - /* We can dispense with GETJSAMPLE() here */ - output_buf[output_row + row][columnOffset + JpegConstants.RGB_RED] = input_buf[0][input_row + component0RowOffset][col]; - output_buf[output_row + row][columnOffset + JpegConstants.RGB_GREEN] = input_buf[0][input_row + component1RowOffset][col]; - output_buf[output_row + row][columnOffset + JpegConstants.RGB_BLUE] = input_buf[0][input_row + component2RowOffset][col]; - columnOffset += JpegConstants.RGB_PIXELSIZE; - } - - input_row++; - } - } - - /// - /// Color conversion for grayscale: just copy the data. - /// This also works for YCbCr -> grayscale conversion, in which - /// we just copy the Y (luminance) component and ignore chrominance. - /// - private void grayscale_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) - { - JpegUtils.jcopy_sample_rows(input_buf[0], input_row + m_perComponentOffsets[0], output_buf, output_row, num_rows, m_cinfo.m_output_width); - } - - /// - /// Color conversion for no colorspace change: just copy the data, - /// converting from separate-planes to interleaved representation. - /// - private void null_convert(ComponentBuffer[] input_buf, int input_row, byte[][] output_buf, int output_row, int num_rows) - { - for (int row = 0; row < num_rows; row++) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - int columnIndex = 0; - int componentOffset = 0; - int perComponentOffset = m_perComponentOffsets[ci]; - - for (int count = m_cinfo.m_output_width; count > 0; count--) - { - /* needn't bother with GETJSAMPLE() here */ - output_buf[output_row + row][ci + componentOffset] = input_buf[ci][input_row + perComponentOffset][columnIndex]; - componentOffset += m_cinfo.m_num_components; - columnIndex++; - } - } - - input_row++; - } - } - - private static int FIX(double x) - { - return (int)(x * (1L << SCALEBITS) + 0.5); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_color_quantizer.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_color_quantizer.cs deleted file mode 100644 index fe6ab75a..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_color_quantizer.cs +++ /dev/null @@ -1,32 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Color quantization or color precision reduction - /// - interface jpeg_color_quantizer - { - void start_pass(bool is_pre_scan); - - void color_quantize(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows); - - void finish_pass(); - void new_color_map(); - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_comp_master.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_comp_master.cs deleted file mode 100644 index 4ec01e87..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_comp_master.cs +++ /dev/null @@ -1,368 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Master control module - /// - class jpeg_comp_master - { - private enum c_pass_type - { - main_pass, /* input data, also do first output step */ - huff_opt_pass, /* Huffman code optimization pass */ - output_pass /* data output pass */ - } - - private jpeg_compress_struct m_cinfo; - - private bool m_call_pass_startup; /* True if pass_startup must be called */ - private bool m_is_last_pass; /* True during last pass */ - - private c_pass_type m_pass_type; /* the type of the current pass */ - - private int m_pass_number; /* # of passes completed */ - private int m_total_passes; /* total # of passes needed */ - - private int m_scan_number; /* current index in scan_info[] */ - - public jpeg_comp_master(jpeg_compress_struct cinfo, bool transcode_only) - { - m_cinfo = cinfo; - - if (transcode_only) - { - /* no main pass in transcoding */ - if (cinfo.m_optimize_coding) - m_pass_type = c_pass_type.huff_opt_pass; - else - m_pass_type = c_pass_type.output_pass; - } - else - { - /* for normal compression, first pass is always this type: */ - m_pass_type = c_pass_type.main_pass; - } - - if (cinfo.m_optimize_coding) - m_total_passes = cinfo.m_num_scans * 2; - else - m_total_passes = cinfo.m_num_scans; - } - - /// - /// Per-pass setup. - /// - /// This is called at the beginning of each pass. We determine which - /// modules will be active during this pass and give them appropriate - /// start_pass calls. - /// We also set is_last_pass to indicate whether any more passes will - /// be required. - /// - public void prepare_for_pass() - { - switch (m_pass_type) - { - case c_pass_type.main_pass: - prepare_for_main_pass(); - break; - case c_pass_type.huff_opt_pass: - if (!prepare_for_huff_opt_pass()) - break; - prepare_for_output_pass(); - break; - case c_pass_type.output_pass: - prepare_for_output_pass(); - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); - break; - } - - m_is_last_pass = (m_pass_number == m_total_passes - 1); - - /* Set up progress monitor's pass info if present */ - if (m_cinfo.m_progress != null) - { - m_cinfo.m_progress.Completed_passes = m_pass_number; - m_cinfo.m_progress.Total_passes = m_total_passes; - } - } - - /// - /// Special start-of-pass hook. - /// - /// This is called by jpeg_write_scanlines if call_pass_startup is true. - /// In single-pass processing, we need this hook because we don't want to - /// write frame/scan headers during jpeg_start_compress; we want to let the - /// application write COM markers etc. between jpeg_start_compress and the - /// jpeg_write_scanlines loop. - /// In multi-pass processing, this routine is not used. - /// - public void pass_startup() - { - m_cinfo.m_master.m_call_pass_startup = false; /* reset flag so call only once */ - - m_cinfo.m_marker.write_frame_header(); - m_cinfo.m_marker.write_scan_header(); - } - - /// - /// Finish up at end of pass. - /// - public void finish_pass() - { - /* The entropy coder always needs an end-of-pass call, - * either to analyze statistics or to flush its output buffer. - */ - m_cinfo.m_entropy.finish_pass(); - - /* Update state for next pass */ - switch (m_pass_type) - { - case c_pass_type.main_pass: - /* next pass is either output of scan 0 (after optimization) - * or output of scan 1 (if no optimization). - */ - m_pass_type = c_pass_type.output_pass; - if (!m_cinfo.m_optimize_coding) - m_scan_number++; - break; - case c_pass_type.huff_opt_pass: - /* next pass is always output of current scan */ - m_pass_type = c_pass_type.output_pass; - break; - case c_pass_type.output_pass: - /* next pass is either optimization or output of next scan */ - if (m_cinfo.m_optimize_coding) - m_pass_type = c_pass_type.huff_opt_pass; - m_scan_number++; - break; - } - - m_pass_number++; - } - - public bool IsLastPass() - { - return m_is_last_pass; - } - - public bool MustCallPassStartup() - { - return m_call_pass_startup; - } - - private void prepare_for_main_pass() - { - /* Initial pass: will collect input data, and do either Huffman - * optimization or data output for the first scan. - */ - select_scan_parameters(); - per_scan_setup(); - - if (!m_cinfo.m_raw_data_in) - { - m_cinfo.m_cconvert.start_pass(); - m_cinfo.m_prep.start_pass(J_BUF_MODE.JBUF_PASS_THRU); - } - - m_cinfo.m_fdct.start_pass(); - m_cinfo.m_entropy.start_pass(m_cinfo.m_optimize_coding); - m_cinfo.m_coef.start_pass((m_total_passes > 1 ? J_BUF_MODE.JBUF_SAVE_AND_PASS : J_BUF_MODE.JBUF_PASS_THRU)); - m_cinfo.m_main.start_pass(J_BUF_MODE.JBUF_PASS_THRU); - - if (m_cinfo.m_optimize_coding) - { - /* No immediate data output; postpone writing frame/scan headers */ - m_call_pass_startup = false; - } - else - { - /* Will write frame/scan headers at first jpeg_write_scanlines call */ - m_call_pass_startup = true; - } - } - - private bool prepare_for_huff_opt_pass() - { - /* Do Huffman optimization for a scan after the first one. */ - select_scan_parameters(); - per_scan_setup(); - - if (m_cinfo.m_Ss != 0 || m_cinfo.m_Ah == 0) - { - m_cinfo.m_entropy.start_pass(true); - m_cinfo.m_coef.start_pass(J_BUF_MODE.JBUF_CRANK_DEST); - m_call_pass_startup = false; - return false; - } - - /* Special case: Huffman DC refinement scans need no Huffman table - * and therefore we can skip the optimization pass for them. - */ - m_pass_type = c_pass_type.output_pass; - m_pass_number++; - return true; - } - - private void prepare_for_output_pass() - { - /* Do a data-output pass. */ - /* We need not repeat per-scan setup if prior optimization pass did it. */ - if (!m_cinfo.m_optimize_coding) - { - select_scan_parameters(); - per_scan_setup(); - } - - m_cinfo.m_entropy.start_pass(false); - m_cinfo.m_coef.start_pass(J_BUF_MODE.JBUF_CRANK_DEST); - - /* We emit frame/scan headers now */ - if (m_scan_number == 0) - m_cinfo.m_marker.write_frame_header(); - - m_cinfo.m_marker.write_scan_header(); - m_call_pass_startup = false; - } - - // Set up the scan parameters for the current scan - private void select_scan_parameters() - { - if (m_cinfo.m_scan_info != null) - { - /* Prepare for current scan --- the script is already validated */ - jpeg_scan_info scanInfo = m_cinfo.m_scan_info[m_scan_number]; - - m_cinfo.m_comps_in_scan = scanInfo.comps_in_scan; - for (int ci = 0; ci < scanInfo.comps_in_scan; ci++) - m_cinfo.m_cur_comp_info[ci] = scanInfo.component_index[ci]; - - m_cinfo.m_Ss = scanInfo.Ss; - m_cinfo.m_Se = scanInfo.Se; - m_cinfo.m_Ah = scanInfo.Ah; - m_cinfo.m_Al = scanInfo.Al; - } - else - { - /* Prepare for single sequential-JPEG scan containing all components */ - if (m_cinfo.m_num_components > JpegConstants.MAX_COMPS_IN_SCAN) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_num_components, JpegConstants.MAX_COMPS_IN_SCAN); - - m_cinfo.m_comps_in_scan = m_cinfo.m_num_components; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - m_cinfo.m_cur_comp_info[ci] = ci; - - m_cinfo.m_Ss = 0; - m_cinfo.m_Se = JpegConstants.DCTSIZE2 - 1; - m_cinfo.m_Ah = 0; - m_cinfo.m_Al = 0; - } - } - - /// - /// Do computations that are needed before processing a JPEG scan - /// cinfo.comps_in_scan and cinfo.cur_comp_info[] are already set - /// - private void per_scan_setup() - { - if (m_cinfo.m_comps_in_scan == 1) - { - /* Noninterleaved (single-component) scan */ - int compIndex = m_cinfo.m_cur_comp_info[0]; - - /* Overall image size in MCUs */ - m_cinfo.m_MCUs_per_row = m_cinfo.Component_info[compIndex].Width_in_blocks; - m_cinfo.m_MCU_rows_in_scan = m_cinfo.Component_info[compIndex].height_in_blocks; - - /* For noninterleaved scan, always one block per MCU */ - m_cinfo.Component_info[compIndex].MCU_width = 1; - m_cinfo.Component_info[compIndex].MCU_height = 1; - m_cinfo.Component_info[compIndex].MCU_blocks = 1; - m_cinfo.Component_info[compIndex].MCU_sample_width = JpegConstants.DCTSIZE; - m_cinfo.Component_info[compIndex].last_col_width = 1; - - /* For noninterleaved scans, it is convenient to define last_row_height - * as the number of block rows present in the last iMCU row. - */ - int tmp = m_cinfo.Component_info[compIndex].height_in_blocks % m_cinfo.Component_info[compIndex].V_samp_factor; - if (tmp == 0) - tmp = m_cinfo.Component_info[compIndex].V_samp_factor; - m_cinfo.Component_info[compIndex].last_row_height = tmp; - - /* Prepare array describing MCU composition */ - m_cinfo.m_blocks_in_MCU = 1; - m_cinfo.m_MCU_membership[0] = 0; - } - else - { - /* Interleaved (multi-component) scan */ - if (m_cinfo.m_comps_in_scan <= 0 || m_cinfo.m_comps_in_scan > JpegConstants.MAX_COMPS_IN_SCAN) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_comps_in_scan, JpegConstants.MAX_COMPS_IN_SCAN); - - /* Overall image size in MCUs */ - m_cinfo.m_MCUs_per_row = JpegUtils.jdiv_round_up( - m_cinfo.m_image_width, m_cinfo.m_max_h_samp_factor * JpegConstants.DCTSIZE); - - m_cinfo.m_MCU_rows_in_scan = JpegUtils.jdiv_round_up(m_cinfo.m_image_height, - m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); - - m_cinfo.m_blocks_in_MCU = 0; - - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - int compIndex = m_cinfo.m_cur_comp_info[ci]; - - /* Sampling factors give # of blocks of component in each MCU */ - m_cinfo.Component_info[compIndex].MCU_width = m_cinfo.Component_info[compIndex].H_samp_factor; - m_cinfo.Component_info[compIndex].MCU_height = m_cinfo.Component_info[compIndex].V_samp_factor; - m_cinfo.Component_info[compIndex].MCU_blocks = m_cinfo.Component_info[compIndex].MCU_width * m_cinfo.Component_info[compIndex].MCU_height; - m_cinfo.Component_info[compIndex].MCU_sample_width = m_cinfo.Component_info[compIndex].MCU_width * JpegConstants.DCTSIZE; - - /* Figure number of non-dummy blocks in last MCU column & row */ - int tmp = m_cinfo.Component_info[compIndex].Width_in_blocks % m_cinfo.Component_info[compIndex].MCU_width; - if (tmp == 0) - tmp = m_cinfo.Component_info[compIndex].MCU_width; - m_cinfo.Component_info[compIndex].last_col_width = tmp; - - tmp = m_cinfo.Component_info[compIndex].height_in_blocks % m_cinfo.Component_info[compIndex].MCU_height; - if (tmp == 0) - tmp = m_cinfo.Component_info[compIndex].MCU_height; - m_cinfo.Component_info[compIndex].last_row_height = tmp; - - /* Prepare array describing MCU composition */ - int mcublks = m_cinfo.Component_info[compIndex].MCU_blocks; - if (m_cinfo.m_blocks_in_MCU + mcublks > JpegConstants.C_MAX_BLOCKS_IN_MCU) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_MCU_SIZE); - - while (mcublks-- > 0) - m_cinfo.m_MCU_membership[m_cinfo.m_blocks_in_MCU++] = ci; - } - } - - /* Convert restart specified in rows to actual MCU count. */ - /* Note that count must fit in 16 bits, so we provide limiting. */ - if (m_cinfo.m_restart_in_rows > 0) - { - int nominal = m_cinfo.m_restart_in_rows * m_cinfo.m_MCUs_per_row; - m_cinfo.m_restart_interval = Math.Min(nominal, 65535); - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_d_coef_controller.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_d_coef_controller.cs deleted file mode 100644 index e23e4dcd..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_d_coef_controller.cs +++ /dev/null @@ -1,765 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the coefficient buffer controller for decompression. - * This controller is the top level of the JPEG decompressor proper. - * The coefficient buffer lies between entropy decoding and inverse-DCT steps. - * - * In buffered-image mode, this controller is the interface between - * input-oriented processing and output-oriented processing. - * Also, the input side (only) is used when reading a file for transcoding. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Coefficient buffer control - /// - /// This code applies interblock smoothing as described by section K.8 - /// of the JPEG standard: the first 5 AC coefficients are estimated from - /// the DC values of a DCT block and its 8 neighboring blocks. - /// We apply smoothing only for progressive JPEG decoding, and only if - /// the coefficients it can estimate are not yet known to full precision. - /// - class jpeg_d_coef_controller - { - private const int SAVED_COEFS = 6; /* we save coef_bits[0..5] */ - - /* Natural-order array positions of the first 5 zigzag-order coefficients */ - private const int Q01_POS = 1; - private const int Q10_POS = 8; - private const int Q20_POS = 16; - private const int Q11_POS = 9; - private const int Q02_POS = 2; - - private enum DecompressorType - { - Ordinary, - Smooth, - OnePass - } - - private jpeg_decompress_struct m_cinfo; - private bool m_useDummyConsumeData; - private DecompressorType m_decompressor; - - /* These variables keep track of the current location of the input side. */ - /* cinfo.input_iMCU_row is also used for this. */ - private int m_MCU_ctr; /* counts MCUs processed in current row */ - private int m_MCU_vert_offset; /* counts MCU rows within iMCU row */ - private int m_MCU_rows_per_iMCU_row; /* number of such rows needed */ - - /* The output side's location is represented by cinfo.output_iMCU_row. */ - - /* In single-pass modes, it's sufficient to buffer just one MCU. - * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, - * and let the entropy decoder write into that workspace each time. - * (On 80x86, the workspace is FAR even though it's not really very big; - * this is to keep the module interfaces unchanged when a large coefficient - * buffer is necessary.) - * In multi-pass modes, this array points to the current MCU's blocks - * within the virtual arrays; it is used only by the input side. - */ - private JBLOCK[] m_MCU_buffer = new JBLOCK[JpegConstants.D_MAX_BLOCKS_IN_MCU]; - - /* In multi-pass modes, we need a virtual block array for each component. */ - private jvirt_array[] m_whole_image = new jvirt_array[JpegConstants.MAX_COMPONENTS]; - private jvirt_array[] m_coef_arrays; - - /* When doing block smoothing, we latch coefficient Al values here */ - private int[] m_coef_bits_latch; - private int m_coef_bits_savedOffset; - - public jpeg_d_coef_controller(jpeg_decompress_struct cinfo, bool need_full_buffer) - { - m_cinfo = cinfo; - - /* Create the coefficient buffer. */ - if (need_full_buffer) - { - /* Allocate a full-image virtual array for each component, */ - /* padded to a multiple of samp_factor DCT blocks in each direction. */ - /* Note we ask for a pre-zeroed array. */ - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - m_whole_image[ci] = jpeg_common_struct.CreateBlocksArray( - JpegUtils.jround_up(cinfo.Comp_info[ci].Width_in_blocks, cinfo.Comp_info[ci].H_samp_factor), - JpegUtils.jround_up(cinfo.Comp_info[ci].height_in_blocks, cinfo.Comp_info[ci].V_samp_factor)); - m_whole_image[ci].ErrorProcessor = cinfo; - } - - m_useDummyConsumeData = false; - m_decompressor = DecompressorType.Ordinary; - m_coef_arrays = m_whole_image; /* link to virtual arrays */ - } - else - { - /* We only need a single-MCU buffer. */ - JBLOCK[] buffer = new JBLOCK[JpegConstants.D_MAX_BLOCKS_IN_MCU]; - for (int i = 0; i < JpegConstants.D_MAX_BLOCKS_IN_MCU; i++) - { - buffer[i] = new JBLOCK(); - for (int ii = 0; ii < buffer[i].data.Length; ii++) - buffer[i].data[ii] = -12851; - - m_MCU_buffer[i] = buffer[i]; - } - - m_useDummyConsumeData = true; - m_decompressor = DecompressorType.OnePass; - m_coef_arrays = null; /* flag for no virtual arrays */ - } - } - - /// - /// Initialize for an input processing pass. - /// - public void start_input_pass() - { - m_cinfo.m_input_iMCU_row = 0; - start_iMCU_row(); - } - - /// - /// Consume input data and store it in the full-image coefficient buffer. - /// We read as much as one fully interleaved MCU row ("iMCU" row) per call, - /// ie, v_samp_factor block rows for each component in the scan. - /// - public ReadResult consume_data() - { - if (m_useDummyConsumeData) - return ReadResult.JPEG_SUSPENDED; /* Always indicate nothing was done */ - - JBLOCK[][][] buffer = new JBLOCK[JpegConstants.MAX_COMPS_IN_SCAN][][]; - - /* Align the virtual buffers for the components used in this scan. */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - - buffer[ci] = m_whole_image[componentInfo.Component_index].Access( - m_cinfo.m_input_iMCU_row * componentInfo.V_samp_factor, componentInfo.V_samp_factor); - - /* Note: entropy decoder expects buffer to be zeroed, - * but this is handled automatically by the memory manager - * because we requested a pre-zeroed array. - */ - } - - /* Loop to process one whole iMCU row */ - for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) - { - for (int MCU_col_num = m_MCU_ctr; MCU_col_num < m_cinfo.m_MCUs_per_row; MCU_col_num++) - { - /* Construct list of pointers to DCT blocks belonging to this MCU */ - int blkn = 0; /* index of current DCT block within MCU */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - int start_col = MCU_col_num * componentInfo.MCU_width; - for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) - { - for (int xindex = 0; xindex < componentInfo.MCU_width; xindex++) - { - m_MCU_buffer[blkn] = buffer[ci][yindex + yoffset][start_col + xindex]; - blkn++; - } - } - } - - /* Try to fetch the MCU. */ - if (!m_cinfo.m_entropy.decode_mcu(m_MCU_buffer)) - { - /* Suspension forced; update state counters and exit */ - m_MCU_vert_offset = yoffset; - m_MCU_ctr = MCU_col_num; - return ReadResult.JPEG_SUSPENDED; - } - } - - /* Completed an MCU row, but perhaps not an iMCU row */ - m_MCU_ctr = 0; - } - - /* Completed the iMCU row, advance counters for next one */ - m_cinfo.m_input_iMCU_row++; - if (m_cinfo.m_input_iMCU_row < m_cinfo.m_total_iMCU_rows) - { - start_iMCU_row(); - return ReadResult.JPEG_ROW_COMPLETED; - } - - /* Completed the scan */ - m_cinfo.m_inputctl.finish_input_pass(); - return ReadResult.JPEG_SCAN_COMPLETED; - } - - /// - /// Initialize for an output processing pass. - /// - public void start_output_pass() - { - /* If multipass, check to see whether to use block smoothing on this pass */ - if (m_coef_arrays != null) - { - if (m_cinfo.m_do_block_smoothing && smoothing_ok()) - m_decompressor = DecompressorType.Smooth; - else - m_decompressor = DecompressorType.Ordinary; - } - - m_cinfo.m_output_iMCU_row = 0; - } - - public ReadResult decompress_data(ComponentBuffer[] output_buf) - { - switch (m_decompressor) - { - case DecompressorType.Ordinary: - return decompress_data_ordinary(output_buf); - - case DecompressorType.Smooth: - return decompress_smooth_data(output_buf); - - case DecompressorType.OnePass: - return decompress_onepass(output_buf); - } - - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - return 0; - } - - /* Pointer to array of coefficient virtual arrays, or null if none */ - public jvirt_array[] GetCoefArrays() - { - return m_coef_arrays; - } - - /// - /// Decompress and return some data in the single-pass case. - /// Always attempts to emit one fully interleaved MCU row ("iMCU" row). - /// Input and output must run in lockstep since we have only a one-MCU buffer. - /// Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. - /// - /// NB: output_buf contains a plane for each component in image, - /// which we index according to the component's SOF position. - /// - private ReadResult decompress_onepass(ComponentBuffer[] output_buf) - { - int last_MCU_col = m_cinfo.m_MCUs_per_row - 1; - int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; - - /* Loop to process as much as one whole iMCU row */ - for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) - { - for (int MCU_col_num = m_MCU_ctr; MCU_col_num <= last_MCU_col; MCU_col_num++) - { - /* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */ - for (int i = 0; i < m_cinfo.m_blocks_in_MCU; i++) - Array.Clear(m_MCU_buffer[i].data, 0, m_MCU_buffer[i].data.Length); - - if (!m_cinfo.m_entropy.decode_mcu(m_MCU_buffer)) - { - /* Suspension forced; update state counters and exit */ - m_MCU_vert_offset = yoffset; - m_MCU_ctr = MCU_col_num; - return ReadResult.JPEG_SUSPENDED; - } - - /* Determine where data should go in output_buf and do the IDCT thing. - * We skip dummy blocks at the right and bottom edges (but blkn gets - * incremented past them!). Note the inner loop relies on having - * allocated the MCU_buffer[] blocks sequentially. - */ - int blkn = 0; /* index of current DCT block within MCU */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - - /* Don't bother to IDCT an uninteresting component. */ - if (!componentInfo.component_needed) - { - blkn += componentInfo.MCU_blocks; - continue; - } - - int useful_width = (MCU_col_num < last_MCU_col) ? componentInfo.MCU_width : componentInfo.last_col_width; - int outputIndex = yoffset * componentInfo.DCT_scaled_size; - int start_col = MCU_col_num * componentInfo.MCU_sample_width; - for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) - { - if (m_cinfo.m_input_iMCU_row < last_iMCU_row || yoffset + yindex < componentInfo.last_row_height) - { - int output_col = start_col; - for (int xindex = 0; xindex < useful_width; xindex++) - { - m_cinfo.m_idct.inverse(componentInfo.Component_index, - m_MCU_buffer[blkn + xindex].data, output_buf[componentInfo.Component_index], - outputIndex, output_col); - - output_col += componentInfo.DCT_scaled_size; - } - } - - blkn += componentInfo.MCU_width; - outputIndex += componentInfo.DCT_scaled_size; - } - } - } - - /* Completed an MCU row, but perhaps not an iMCU row */ - m_MCU_ctr = 0; - } - - /* Completed the iMCU row, advance counters for next one */ - m_cinfo.m_output_iMCU_row++; - m_cinfo.m_input_iMCU_row++; - if (m_cinfo.m_input_iMCU_row < m_cinfo.m_total_iMCU_rows) - { - start_iMCU_row(); - return ReadResult.JPEG_ROW_COMPLETED; - } - - /* Completed the scan */ - m_cinfo.m_inputctl.finish_input_pass(); - return ReadResult.JPEG_SCAN_COMPLETED; - } - - /// - /// Decompress and return some data in the multi-pass case. - /// Always attempts to emit one fully interleaved MCU row ("iMCU" row). - /// Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. - /// - /// NB: output_buf contains a plane for each component in image. - /// - private ReadResult decompress_data_ordinary(ComponentBuffer[] output_buf) - { - /* Force some input to be done if we are getting ahead of the input. */ - while (m_cinfo.m_input_scan_number < m_cinfo.m_output_scan_number || - (m_cinfo.m_input_scan_number == m_cinfo.m_output_scan_number && - m_cinfo.m_input_iMCU_row <= m_cinfo.m_output_iMCU_row)) - { - if (m_cinfo.m_inputctl.consume_input() == ReadResult.JPEG_SUSPENDED) - return ReadResult.JPEG_SUSPENDED; - } - - int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; - - /* OK, output from the virtual arrays. */ - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[ci]; - - /* Don't bother to IDCT an uninteresting component. */ - if (!componentInfo.component_needed) - continue; - - /* Align the virtual buffer for this component. */ - JBLOCK[][] buffer = m_whole_image[ci].Access(m_cinfo.m_output_iMCU_row * componentInfo.V_samp_factor, - componentInfo.V_samp_factor); - - /* Count non-dummy DCT block rows in this iMCU row. */ - int block_rows; - if (m_cinfo.m_output_iMCU_row < last_iMCU_row) - block_rows = componentInfo.V_samp_factor; - else - { - /* NB: can't use last_row_height here; it is input-side-dependent! */ - block_rows = componentInfo.height_in_blocks % componentInfo.V_samp_factor; - if (block_rows == 0) - block_rows = componentInfo.V_samp_factor; - } - - /* Loop over all DCT blocks to be processed. */ - int rowIndex = 0; - for (int block_row = 0; block_row < block_rows; block_row++) - { - int output_col = 0; - for (int block_num = 0; block_num < componentInfo.Width_in_blocks; block_num++) - { - m_cinfo.m_idct.inverse(componentInfo.Component_index, - buffer[block_row][block_num].data, output_buf[ci], rowIndex, output_col); - - output_col += componentInfo.DCT_scaled_size; - } - - rowIndex += componentInfo.DCT_scaled_size; - } - } - - m_cinfo.m_output_iMCU_row++; - if (m_cinfo.m_output_iMCU_row < m_cinfo.m_total_iMCU_rows) - return ReadResult.JPEG_ROW_COMPLETED; - - return ReadResult.JPEG_SCAN_COMPLETED; - } - - /// - /// Variant of decompress_data for use when doing block smoothing. - /// - private ReadResult decompress_smooth_data(ComponentBuffer[] output_buf) - { - /* Force some input to be done if we are getting ahead of the input. */ - while (m_cinfo.m_input_scan_number <= m_cinfo.m_output_scan_number && !m_cinfo.m_inputctl.EOIReached()) - { - if (m_cinfo.m_input_scan_number == m_cinfo.m_output_scan_number) - { - /* If input is working on current scan, we ordinarily want it to - * have completed the current row. But if input scan is DC, - * we want it to keep one row ahead so that next block row's DC - * values are up to date. - */ - int delta = (m_cinfo.m_Ss == 0) ? 1 : 0; - if (m_cinfo.m_input_iMCU_row > m_cinfo.m_output_iMCU_row + delta) - break; - } - - if (m_cinfo.m_inputctl.consume_input() == ReadResult.JPEG_SUSPENDED) - return ReadResult.JPEG_SUSPENDED; - } - - int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; - - /* OK, output from the virtual arrays. */ - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[ci]; - - /* Don't bother to IDCT an uninteresting component. */ - if (!componentInfo.component_needed) - continue; - - int block_rows; - int access_rows; - bool last_row; - /* Count non-dummy DCT block rows in this iMCU row. */ - if (m_cinfo.m_output_iMCU_row < last_iMCU_row) - { - block_rows = componentInfo.V_samp_factor; - access_rows = block_rows * 2; /* this and next iMCU row */ - last_row = false; - } - else - { - /* NB: can't use last_row_height here; it is input-side-dependent! */ - block_rows = componentInfo.height_in_blocks % componentInfo.V_samp_factor; - if (block_rows == 0) - block_rows = componentInfo.V_samp_factor; - access_rows = block_rows; /* this iMCU row only */ - last_row = true; - } - - /* Align the virtual buffer for this component. */ - JBLOCK[][] buffer = null; - bool first_row; - int bufferRowOffset = 0; - if (m_cinfo.m_output_iMCU_row > 0) - { - access_rows += componentInfo.V_samp_factor; /* prior iMCU row too */ - buffer = m_whole_image[ci].Access((m_cinfo.m_output_iMCU_row - 1) * componentInfo.V_samp_factor, access_rows); - bufferRowOffset = componentInfo.V_samp_factor; /* point to current iMCU row */ - first_row = false; - } - else - { - buffer = m_whole_image[ci].Access(0, access_rows); - first_row = true; - } - - /* Fetch component-dependent info */ - int coefBitsOffset = ci * SAVED_COEFS; - int Q00 = componentInfo.quant_table.quantval[0]; - int Q01 = componentInfo.quant_table.quantval[Q01_POS]; - int Q10 = componentInfo.quant_table.quantval[Q10_POS]; - int Q20 = componentInfo.quant_table.quantval[Q20_POS]; - int Q11 = componentInfo.quant_table.quantval[Q11_POS]; - int Q02 = componentInfo.quant_table.quantval[Q02_POS]; - int outputIndex = ci; - - /* Loop over all DCT blocks to be processed. */ - for (int block_row = 0; block_row < block_rows; block_row++) - { - int bufferIndex = bufferRowOffset + block_row; - - int prev_block_row = 0; - if (first_row && block_row == 0) - prev_block_row = bufferIndex; - else - prev_block_row = bufferIndex - 1; - - int next_block_row = 0; - if (last_row && block_row == block_rows - 1) - next_block_row = bufferIndex; - else - next_block_row = bufferIndex + 1; - - /* We fetch the surrounding DC values using a sliding-register approach. - * Initialize all nine here so as to do the right thing on narrow pics. - */ - int DC1 = buffer[prev_block_row][0][0]; - int DC2 = DC1; - int DC3 = DC1; - - int DC4 = buffer[bufferIndex][0][0]; - int DC5 = DC4; - int DC6 = DC4; - - int DC7 = buffer[next_block_row][0][0]; - int DC8 = DC7; - int DC9 = DC7; - - int output_col = 0; - int last_block_column = componentInfo.Width_in_blocks - 1; - for (int block_num = 0; block_num <= last_block_column; block_num++) - { - /* Fetch current DCT block into workspace so we can modify it. */ - JBLOCK workspace = new JBLOCK(); - Buffer.BlockCopy(buffer[bufferIndex][0].data, 0, workspace.data, 0, workspace.data.Length * sizeof(short)); - - /* Update DC values */ - if (block_num < last_block_column) - { - DC3 = buffer[prev_block_row][1][0]; - DC6 = buffer[bufferIndex][1][0]; - DC9 = buffer[next_block_row][1][0]; - } - - /* Compute coefficient estimates per K.8. - * An estimate is applied only if coefficient is still zero, - * and is not known to be fully accurate. - */ - /* AC01 */ - int Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 1]; - if (Al != 0 && workspace[1] == 0) - { - int pred; - int num = 36 * Q00 * (DC4 - DC6); - if (num >= 0) - { - pred = ((Q01 << 7) + num) / (Q01 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - } - else - { - pred = ((Q01 << 7) - num) / (Q01 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - pred = -pred; - } - workspace[1] = (short) pred; - } - - /* AC10 */ - Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 2]; - if (Al != 0 && workspace[8] == 0) - { - int pred; - int num = 36 * Q00 * (DC2 - DC8); - if (num >= 0) - { - pred = ((Q10 << 7) + num) / (Q10 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - } - else - { - pred = ((Q10 << 7) - num) / (Q10 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - pred = -pred; - } - workspace[8] = (short) pred; - } - - /* AC20 */ - Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 3]; - if (Al != 0 && workspace[16] == 0) - { - int pred; - int num = 9 * Q00 * (DC2 + DC8 - 2 * DC5); - if (num >= 0) - { - pred = ((Q20 << 7) + num) / (Q20 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - } - else - { - pred = ((Q20 << 7) - num) / (Q20 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - pred = -pred; - } - workspace[16] = (short) pred; - } - - /* AC11 */ - Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 4]; - if (Al != 0 && workspace[9] == 0) - { - int pred; - int num = 5 * Q00 * (DC1 - DC3 - DC7 + DC9); - if (num >= 0) - { - pred = ((Q11 << 7) + num) / (Q11 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - } - else - { - pred = ((Q11 << 7) - num) / (Q11 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - pred = -pred; - } - workspace[9] = (short) pred; - } - - /* AC02 */ - Al = m_coef_bits_latch[m_coef_bits_savedOffset + coefBitsOffset + 5]; - if (Al != 0 && workspace[2] == 0) - { - int pred; - int num = 9 * Q00 * (DC4 + DC6 - 2 * DC5); - if (num >= 0) - { - pred = ((Q02 << 7) + num) / (Q02 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - } - else - { - pred = ((Q02 << 7) - num) / (Q02 << 8); - if (Al > 0 && pred >= (1 << Al)) - pred = (1 << Al) - 1; - pred = -pred; - } - workspace[2] = (short) pred; - } - - /* OK, do the IDCT */ - m_cinfo.m_idct.inverse(componentInfo.Component_index, workspace.data, output_buf[outputIndex], 0, output_col); - - /* Advance for next column */ - DC1 = DC2; - DC2 = DC3; - DC4 = DC5; - DC5 = DC6; - DC7 = DC8; - DC8 = DC9; - - bufferIndex++; - prev_block_row++; - next_block_row++; - - output_col += componentInfo.DCT_scaled_size; - } - - outputIndex += componentInfo.DCT_scaled_size; - } - } - - m_cinfo.m_output_iMCU_row++; - if (m_cinfo.m_output_iMCU_row < m_cinfo.m_total_iMCU_rows) - return ReadResult.JPEG_ROW_COMPLETED; - - return ReadResult.JPEG_SCAN_COMPLETED; - } - - /// - /// Determine whether block smoothing is applicable and safe. - /// We also latch the current states of the coef_bits[] entries for the - /// AC coefficients; otherwise, if the input side of the decompressor - /// advances into a new scan, we might think the coefficients are known - /// more accurately than they really are. - /// - private bool smoothing_ok() - { - if (!m_cinfo.m_progressive_mode || m_cinfo.m_coef_bits == null) - return false; - - /* Allocate latch area if not already done */ - if (m_coef_bits_latch == null) - { - m_coef_bits_latch = new int[m_cinfo.m_num_components * SAVED_COEFS]; - m_coef_bits_savedOffset = 0; - } - - bool smoothing_useful = false; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - /* All components' quantization values must already be latched. */ - JQUANT_TBL qtable = m_cinfo.Comp_info[ci].quant_table; - if (qtable == null) - return false; - - /* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */ - if (qtable.quantval[0] == 0 || qtable.quantval[Q01_POS] == 0 || - qtable.quantval[Q10_POS] == 0 || qtable.quantval[Q20_POS] == 0 || - qtable.quantval[Q11_POS] == 0 || qtable.quantval[Q02_POS] == 0) - { - return false; - } - - /* DC values must be at least partly known for all components. */ - if (m_cinfo.m_coef_bits[ci][0] < 0) - return false; - - /* Block smoothing is helpful if some AC coefficients remain inaccurate. */ - for (int coefi = 1; coefi <= 5; coefi++) - { - m_coef_bits_latch[m_coef_bits_savedOffset + coefi] = m_cinfo.m_coef_bits[ci][coefi]; - if (m_cinfo.m_coef_bits[ci][coefi] != 0) - smoothing_useful = true; - } - - m_coef_bits_savedOffset += SAVED_COEFS; - } - - return smoothing_useful; - } - - /// - /// Reset within-iMCU-row counters for a new row (input side) - /// - private void start_iMCU_row() - { - /* In an interleaved scan, an MCU row is the same as an iMCU row. - * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. - * But at the bottom of the image, process only what's left. - */ - if (m_cinfo.m_comps_in_scan > 1) - { - m_MCU_rows_per_iMCU_row = 1; - } - else - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[0]]; - - if (m_cinfo.m_input_iMCU_row < (m_cinfo.m_total_iMCU_rows - 1)) - m_MCU_rows_per_iMCU_row = componentInfo.V_samp_factor; - else - m_MCU_rows_per_iMCU_row = componentInfo.last_row_height; - } - - m_MCU_ctr = 0; - m_MCU_vert_offset = 0; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_d_main_controller.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_d_main_controller.cs deleted file mode 100644 index 5c7aaf8e..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_d_main_controller.cs +++ /dev/null @@ -1,514 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the main buffer controller for decompression. - * The main buffer lies between the JPEG decompressor proper and the - * post-processor; it holds downsampled data in the JPEG colorspace. - * - * Note that this code is bypassed in raw-data mode, since the application - * supplies the equivalent of the main buffer in that case. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Main buffer control (downsampled-data buffer) - /// - /// In the current system design, the main buffer need never be a full-image - /// buffer; any full-height buffers will be found inside the coefficient or - /// postprocessing controllers. Nonetheless, the main controller is not - /// trivial. Its responsibility is to provide context rows for upsampling/ - /// rescaling, and doing this in an efficient fashion is a bit tricky. - /// - /// Postprocessor input data is counted in "row groups". A row group - /// is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) - /// sample rows of each component. (We require DCT_scaled_size values to be - /// chosen such that these numbers are integers. In practice DCT_scaled_size - /// values will likely be powers of two, so we actually have the stronger - /// condition that DCT_scaled_size / min_DCT_scaled_size is an integer.) - /// Upsampling will typically produce max_v_samp_factor pixel rows from each - /// row group (times any additional scale factor that the upsampler is - /// applying). - /// - /// The coefficient controller will deliver data to us one iMCU row at a time; - /// each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or - /// exactly min_DCT_scaled_size row groups. (This amount of data corresponds - /// to one row of MCUs when the image is fully interleaved.) Note that the - /// number of sample rows varies across components, but the number of row - /// groups does not. Some garbage sample rows may be included in the last iMCU - /// row at the bottom of the image. - /// - /// Depending on the vertical scaling algorithm used, the upsampler may need - /// access to the sample row(s) above and below its current input row group. - /// The upsampler is required to set need_context_rows true at global selection - /// time if so. When need_context_rows is false, this controller can simply - /// obtain one iMCU row at a time from the coefficient controller and dole it - /// out as row groups to the postprocessor. - /// - /// When need_context_rows is true, this controller guarantees that the buffer - /// passed to postprocessing contains at least one row group's worth of samples - /// above and below the row group(s) being processed. Note that the context - /// rows "above" the first passed row group appear at negative row offsets in - /// the passed buffer. At the top and bottom of the image, the required - /// context rows are manufactured by duplicating the first or last real sample - /// row; this avoids having special cases in the upsampling inner loops. - /// - /// The amount of context is fixed at one row group just because that's a - /// convenient number for this controller to work with. The existing - /// upsamplers really only need one sample row of context. An upsampler - /// supporting arbitrary output rescaling might wish for more than one row - /// group of context when shrinking the image; tough, we don't handle that. - /// (This is justified by the assumption that downsizing will be handled mostly - /// by adjusting the DCT_scaled_size values, so that the actual scale factor at - /// the upsample step needn't be much less than one.) - /// - /// To provide the desired context, we have to retain the last two row groups - /// of one iMCU row while reading in the next iMCU row. (The last row group - /// can't be processed until we have another row group for its below-context, - /// and so we have to save the next-to-last group too for its above-context.) - /// We could do this most simply by copying data around in our buffer, but - /// that'd be very slow. We can avoid copying any data by creating a rather - /// strange pointer structure. Here's how it works. We allocate a workspace - /// consisting of M+2 row groups (where M = min_DCT_scaled_size is the number - /// of row groups per iMCU row). We create two sets of redundant pointers to - /// the workspace. Labeling the physical row groups 0 to M+1, the synthesized - /// pointer lists look like this: - /// M+1 M-1 - /// master pointer --> 0 master pointer --> 0 - /// 1 1 - /// ... ... - /// M-3 M-3 - /// M-2 M - /// M-1 M+1 - /// M M-2 - /// M+1 M-1 - /// 0 0 - /// We read alternate iMCU rows using each master pointer; thus the last two - /// row groups of the previous iMCU row remain un-overwritten in the workspace. - /// The pointer lists are set up so that the required context rows appear to - /// be adjacent to the proper places when we pass the pointer lists to the - /// upsampler. - /// - /// The above pictures describe the normal state of the pointer lists. - /// At top and bottom of the image, we diddle the pointer lists to duplicate - /// the first or last sample row as necessary (this is cheaper than copying - /// sample rows around). - /// - /// This scheme breaks down if M less than 2, ie, min_DCT_scaled_size is 1. In that - /// situation each iMCU row provides only one row group so the buffering logic - /// must be different (eg, we must read two iMCU rows before we can emit the - /// first row group). For now, we simply do not support providing context - /// rows when min_DCT_scaled_size is 1. That combination seems unlikely to - /// be worth providing --- if someone wants a 1/8th-size preview, they probably - /// want it quick and dirty, so a context-free upsampler is sufficient. - /// - class jpeg_d_main_controller - { - private enum DataProcessor - { - context_main, - simple_main, - crank_post - } - - /* context_state values: */ - private const int CTX_PREPARE_FOR_IMCU = 0; /* need to prepare for MCU row */ - private const int CTX_PROCESS_IMCU = 1; /* feeding iMCU to postprocessor */ - private const int CTX_POSTPONED_ROW = 2; /* feeding postponed row group */ - - private DataProcessor m_dataProcessor; - private jpeg_decompress_struct m_cinfo; - - /* Pointer to allocated workspace (M or M+2 row groups). */ - private byte[][][] m_buffer = new byte[JpegConstants.MAX_COMPONENTS][][]; - - private bool m_buffer_full; /* Have we gotten an iMCU row from decoder? */ - private int m_rowgroup_ctr; /* counts row groups output to postprocessor */ - - /* Remaining fields are only used in the context case. */ - - private int[][][] m_funnyIndices = new int[2][][] { new int[JpegConstants.MAX_COMPONENTS][], new int[JpegConstants.MAX_COMPONENTS][]}; - private int[] m_funnyOffsets = new int[JpegConstants.MAX_COMPONENTS]; - private int m_whichFunny; /* indicates which funny indices set is now in use */ - - private int m_context_state; /* process_data state machine status */ - private int m_rowgroups_avail; /* row groups available to postprocessor */ - private int m_iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ - - public jpeg_d_main_controller(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Allocate the workspace. - * ngroups is the number of row groups we need. - */ - int ngroups = cinfo.m_min_DCT_scaled_size; - if (cinfo.m_upsample.NeedContextRows()) - { - if (cinfo.m_min_DCT_scaled_size < 2) /* unsupported, see comments above */ - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - - alloc_funny_pointers(); /* Alloc space for xbuffer[] lists */ - ngroups = cinfo.m_min_DCT_scaled_size + 2; - } - - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - /* height of a row group of component */ - int rgroup = (cinfo.Comp_info[ci].V_samp_factor * cinfo.Comp_info[ci].DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; - - m_buffer[ci] = jpeg_common_struct.AllocJpegSamples( - cinfo.Comp_info[ci].Width_in_blocks * cinfo.Comp_info[ci].DCT_scaled_size, - rgroup * ngroups); - } - } - - /// - /// Initialize for a processing pass. - /// - public void start_pass(J_BUF_MODE pass_mode) - { - switch (pass_mode) - { - case J_BUF_MODE.JBUF_PASS_THRU: - if (m_cinfo.m_upsample.NeedContextRows()) - { - m_dataProcessor = DataProcessor.context_main; - make_funny_pointers(); /* Create the xbuffer[] lists */ - m_whichFunny = 0; /* Read first iMCU row into xbuffer[0] */ - m_context_state = CTX_PREPARE_FOR_IMCU; - m_iMCU_row_ctr = 0; - } - else - { - /* Simple case with no context needed */ - m_dataProcessor = DataProcessor.simple_main; - } - m_buffer_full = false; /* Mark buffer empty */ - m_rowgroup_ctr = 0; - break; - case J_BUF_MODE.JBUF_CRANK_DEST: - /* For last pass of 2-pass quantization, just crank the postprocessor */ - m_dataProcessor = DataProcessor.crank_post; - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - break; - } - } - - public void process_data(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - switch (m_dataProcessor) - { - case DataProcessor.simple_main: - process_data_simple_main(output_buf, ref out_row_ctr, out_rows_avail); - break; - - case DataProcessor.context_main: - process_data_context_main(output_buf, ref out_row_ctr, out_rows_avail); - break; - - case DataProcessor.crank_post: - process_data_crank_post(output_buf, ref out_row_ctr, out_rows_avail); - break; - - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - break; - } - } - - /// - /// Process some data. - /// This handles the simple case where no context is required. - /// - private void process_data_simple_main(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - ComponentBuffer[] cb = new ComponentBuffer[JpegConstants.MAX_COMPONENTS]; - for (int i = 0; i < JpegConstants.MAX_COMPONENTS; i++) - { - cb[i] = new ComponentBuffer(); - cb[i].SetBuffer(m_buffer[i], null, 0); - } - - /* Read input data if we haven't filled the main buffer yet */ - if (!m_buffer_full) - { - if (m_cinfo.m_coef.decompress_data(cb) == ReadResult.JPEG_SUSPENDED) - { - /* suspension forced, can do nothing more */ - return; - } - - /* OK, we have an iMCU row to work with */ - m_buffer_full = true; - } - - /* There are always min_DCT_scaled_size row groups in an iMCU row. */ - int rowgroups_avail = m_cinfo.m_min_DCT_scaled_size; - - /* Note: at the bottom of the image, we may pass extra garbage row groups - * to the postprocessor. The postprocessor has to check for bottom - * of image anyway (at row resolution), so no point in us doing it too. - */ - - /* Feed the postprocessor */ - m_cinfo.m_post.post_process_data(cb, ref m_rowgroup_ctr, rowgroups_avail, output_buf, ref out_row_ctr, out_rows_avail); - - /* Has postprocessor consumed all the data yet? If so, mark buffer empty */ - if (m_rowgroup_ctr >= rowgroups_avail) - { - m_buffer_full = false; - m_rowgroup_ctr = 0; - } - } - - /// - /// Process some data. - /// This handles the case where context rows must be provided. - /// - private void process_data_context_main(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - ComponentBuffer[] cb = new ComponentBuffer[m_cinfo.m_num_components]; - for (int i = 0; i < m_cinfo.m_num_components; i++) - { - cb[i] = new ComponentBuffer(); - cb[i].SetBuffer(m_buffer[i], m_funnyIndices[m_whichFunny][i], m_funnyOffsets[i]); - } - - /* Read input data if we haven't filled the main buffer yet */ - if (!m_buffer_full) - { - if (m_cinfo.m_coef.decompress_data(cb) == ReadResult.JPEG_SUSPENDED) - { - /* suspension forced, can do nothing more */ - return; - } - - /* OK, we have an iMCU row to work with */ - m_buffer_full = true; - - /* count rows received */ - m_iMCU_row_ctr++; - } - - /* Postprocessor typically will not swallow all the input data it is handed - * in one call (due to filling the output buffer first). Must be prepared - * to exit and restart. - - - This switch lets us keep track of how far we got. - * Note that each case falls through to the next on successful completion. - */ - if (m_context_state == CTX_POSTPONED_ROW) - { - /* Call postprocessor using previously set pointers for postponed row */ - m_cinfo.m_post.post_process_data(cb, ref m_rowgroup_ctr, - m_rowgroups_avail, output_buf, ref out_row_ctr, out_rows_avail); - - if (m_rowgroup_ctr < m_rowgroups_avail) - { - /* Need to suspend */ - return; - } - - m_context_state = CTX_PREPARE_FOR_IMCU; - - if (out_row_ctr >= out_rows_avail) - { - /* Postprocessor exactly filled output buf */ - return; - } - } - - if (m_context_state == CTX_PREPARE_FOR_IMCU) - { - /* Prepare to process first M-1 row groups of this iMCU row */ - m_rowgroup_ctr = 0; - m_rowgroups_avail = m_cinfo.m_min_DCT_scaled_size - 1; - - /* Check for bottom of image: if so, tweak pointers to "duplicate" - * the last sample row, and adjust rowgroups_avail to ignore padding rows. - */ - if (m_iMCU_row_ctr == m_cinfo.m_total_iMCU_rows) - set_bottom_pointers(); - - m_context_state = CTX_PROCESS_IMCU; - } - - if (m_context_state == CTX_PROCESS_IMCU) - { - /* Call postprocessor using previously set pointers */ - m_cinfo.m_post.post_process_data(cb, ref m_rowgroup_ctr, - m_rowgroups_avail, output_buf, ref out_row_ctr, out_rows_avail); - - if (m_rowgroup_ctr < m_rowgroups_avail) - { - /* Need to suspend */ - return; - } - - /* After the first iMCU, change wraparound pointers to normal state */ - if (m_iMCU_row_ctr == 1) - set_wraparound_pointers(); - - /* Prepare to load new iMCU row using other xbuffer list */ - m_whichFunny ^= 1; /* 0=>1 or 1=>0 */ - m_buffer_full = false; - - /* Still need to process last row group of this iMCU row, */ - /* which is saved at index M+1 of the other xbuffer */ - m_rowgroup_ctr = m_cinfo.m_min_DCT_scaled_size + 1; - m_rowgroups_avail = m_cinfo.m_min_DCT_scaled_size + 2; - m_context_state = CTX_POSTPONED_ROW; - } - } - - /// - /// Process some data. - /// Final pass of two-pass quantization: just call the postprocessor. - /// Source data will be the postprocessor controller's internal buffer. - /// - private void process_data_crank_post(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - int dummy = 0; - m_cinfo.m_post.post_process_data(null, ref dummy, 0, output_buf, ref out_row_ctr, out_rows_avail); - } - - /// - /// Allocate space for the funny pointer lists. - /// This is done only once, not once per pass. - /// - private void alloc_funny_pointers() - { - int M = m_cinfo.m_min_DCT_scaled_size; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - /* height of a row group of component */ - int rgroup = (m_cinfo.Comp_info[ci].V_samp_factor * m_cinfo.Comp_info[ci].DCT_scaled_size) / m_cinfo.m_min_DCT_scaled_size; - - /* Get space for pointer lists --- M+4 row groups in each list. - */ - m_funnyIndices[0][ci] = new int[rgroup * (M + 4)]; - m_funnyIndices[1][ci] = new int[rgroup * (M + 4)]; - m_funnyOffsets[ci] = rgroup; - } - } - - /// - /// Create the funny pointer lists discussed in the comments above. - /// The actual workspace is already allocated (in main.buffer), - /// and the space for the pointer lists is allocated too. - /// This routine just fills in the curiously ordered lists. - /// This will be repeated at the beginning of each pass. - /// - private void make_funny_pointers() - { - int M = m_cinfo.m_min_DCT_scaled_size; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - /* height of a row group of component */ - int rgroup = (m_cinfo.Comp_info[ci].V_samp_factor * m_cinfo.Comp_info[ci].DCT_scaled_size) / m_cinfo.m_min_DCT_scaled_size; - - int[] ind0 = m_funnyIndices[0][ci]; - int[] ind1 = m_funnyIndices[1][ci]; - - /* First copy the workspace pointers as-is */ - for (int i = 0; i < rgroup * (M + 2); i++) - { - ind0[i + rgroup] = i; - ind1[i + rgroup] = i; - } - - /* In the second list, put the last four row groups in swapped order */ - for (int i = 0; i < rgroup * 2; i++) - { - ind1[rgroup * (M - 1) + i] = rgroup * M + i; - ind1[rgroup * (M + 1) + i] = rgroup * (M - 2) + i; - } - - /* The wraparound pointers at top and bottom will be filled later - * (see set_wraparound_pointers, below). Initially we want the "above" - * pointers to duplicate the first actual data line. This only needs - * to happen in xbuffer[0]. - */ - for (int i = 0; i < rgroup; i++) - ind0[i] = ind0[rgroup]; - } - } - - /// - /// Set up the "wraparound" pointers at top and bottom of the pointer lists. - /// This changes the pointer list state from top-of-image to the normal state. - /// - private void set_wraparound_pointers() - { - int M = m_cinfo.m_min_DCT_scaled_size; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - /* height of a row group of component */ - int rgroup = (m_cinfo.Comp_info[ci].V_samp_factor * m_cinfo.Comp_info[ci].DCT_scaled_size) / m_cinfo.m_min_DCT_scaled_size; - - int[] ind0 = m_funnyIndices[0][ci]; - int[] ind1 = m_funnyIndices[1][ci]; - - for (int i = 0; i < rgroup; i++) - { - ind0[i] = ind0[rgroup * (M + 2) + i]; - ind1[i] = ind1[rgroup * (M + 2) + i]; - - ind0[rgroup * (M + 3) + i] = ind0[i + rgroup]; - ind1[rgroup * (M + 3) + i] = ind1[i + rgroup]; - } - } - } - - /// - /// Change the pointer lists to duplicate the last sample row at the bottom - /// of the image. m_whichFunny indicates which m_funnyIndices holds the final iMCU row. - /// Also sets rowgroups_avail to indicate number of nondummy row groups in row. - /// - private void set_bottom_pointers() - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - /* Count sample rows in one iMCU row and in one row group */ - int iMCUheight = m_cinfo.Comp_info[ci].V_samp_factor * m_cinfo.Comp_info[ci].DCT_scaled_size; - int rgroup = iMCUheight / m_cinfo.m_min_DCT_scaled_size; - - /* Count nondummy sample rows remaining for this component */ - int rows_left = m_cinfo.Comp_info[ci].downsampled_height % iMCUheight; - if (rows_left == 0) - rows_left = iMCUheight; - - /* Count nondummy row groups. Should get same answer for each component, - * so we need only do it once. - */ - if (ci == 0) - m_rowgroups_avail = (rows_left - 1) / rgroup + 1; - - /* Duplicate the last real sample row rgroup*2 times; this pads out the - * last partial rowgroup and ensures at least one full rowgroup of context. - */ - for (int i = 0; i < rgroup * 2; i++) - m_funnyIndices[m_whichFunny][ci][rows_left + i + rgroup] = m_funnyIndices[m_whichFunny][ci][rows_left - 1 + rgroup]; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_d_post_controller.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_d_post_controller.cs deleted file mode 100644 index dc885c56..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_d_post_controller.cs +++ /dev/null @@ -1,252 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the decompression postprocessing controller. - * This controller manages the upsampling, color conversion, and color - * quantization/reduction steps; specifically, it controls the buffering - * between upsample/color conversion and color quantization/reduction. - * - * If no color quantization/reduction is required, then this module has no - * work to do, and it just hands off to the upsample/color conversion code. - * An integrated upsample/convert/quantize process would replace this module - * entirely. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Decompression postprocessing (color quantization buffer control) - /// - class jpeg_d_post_controller - { - private enum ProcessorType - { - OnePass, - PrePass, - Upsample, - SecondPass - } - - private ProcessorType m_processor; - - private jpeg_decompress_struct m_cinfo; - - /* Color quantization source buffer: this holds output data from - * the upsample/color conversion step to be passed to the quantizer. - * For two-pass color quantization, we need a full-image buffer; - * for one-pass operation, a strip buffer is sufficient. - */ - private jvirt_array m_whole_image; /* virtual array, or null if one-pass */ - private byte[][] m_buffer; /* strip buffer, or current strip of virtual */ - private int m_strip_height; /* buffer size in rows */ - /* for two-pass mode only: */ - private int m_starting_row; /* row # of first row in current strip */ - private int m_next_row; /* index of next row to fill/empty in strip */ - - /// - /// Initialize postprocessing controller. - /// - public jpeg_d_post_controller(jpeg_decompress_struct cinfo, bool need_full_buffer) - { - m_cinfo = cinfo; - - /* Create the quantization buffer, if needed */ - if (cinfo.m_quantize_colors) - { - /* The buffer strip height is max_v_samp_factor, which is typically - * an efficient number of rows for upsampling to return. - * (In the presence of output rescaling, we might want to be smarter?) - */ - m_strip_height = cinfo.m_max_v_samp_factor; - - if (need_full_buffer) - { - /* Two-pass color quantization: need full-image storage. */ - /* We round up the number of rows to a multiple of the strip height. */ - m_whole_image = jpeg_common_struct.CreateSamplesArray( - cinfo.m_output_width * cinfo.m_out_color_components, - JpegUtils.jround_up(cinfo.m_output_height, m_strip_height)); - m_whole_image.ErrorProcessor = cinfo; - } - else - { - /* One-pass color quantization: just make a strip buffer. */ - m_buffer = jpeg_common_struct.AllocJpegSamples( - cinfo.m_output_width * cinfo.m_out_color_components, m_strip_height); - } - } - } - - /// - /// Initialize for a processing pass. - /// - public void start_pass(J_BUF_MODE pass_mode) - { - switch (pass_mode) - { - case J_BUF_MODE.JBUF_PASS_THRU: - if (m_cinfo.m_quantize_colors) - { - /* Single-pass processing with color quantization. */ - m_processor = ProcessorType.OnePass; - /* We could be doing buffered-image output before starting a 2-pass - * color quantization; in that case, jinit_d_post_controller did not - * allocate a strip buffer. Use the virtual-array buffer as workspace. - */ - if (m_buffer == null) - m_buffer = m_whole_image.Access(0, m_strip_height); - } - else - { - /* For single-pass processing without color quantization, - * I have no work to do; just call the upsampler directly. - */ - m_processor = ProcessorType.Upsample; - } - break; - case J_BUF_MODE.JBUF_SAVE_AND_PASS: - /* First pass of 2-pass quantization */ - if (m_whole_image == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - - m_processor = ProcessorType.PrePass; - break; - case J_BUF_MODE.JBUF_CRANK_DEST: - /* Second pass of 2-pass quantization */ - if (m_whole_image == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - - m_processor = ProcessorType.SecondPass; - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - break; - } - m_starting_row = m_next_row = 0; - } - - public void post_process_data(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - switch (m_processor) - { - case ProcessorType.OnePass: - post_process_1pass(input_buf, ref in_row_group_ctr, in_row_groups_avail, output_buf, ref out_row_ctr, out_rows_avail); - break; - case ProcessorType.PrePass: - post_process_prepass(input_buf, ref in_row_group_ctr, in_row_groups_avail, ref out_row_ctr); - break; - case ProcessorType.Upsample: - m_cinfo.m_upsample.upsample(input_buf, ref in_row_group_ctr, in_row_groups_avail, output_buf, ref out_row_ctr, out_rows_avail); - break; - case ProcessorType.SecondPass: - post_process_2pass(output_buf, ref out_row_ctr, out_rows_avail); - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - break; - } - } - - /// - /// Process some data in the one-pass (strip buffer) case. - /// This is used for color precision reduction as well as one-pass quantization. - /// - private void post_process_1pass(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - /* Fill the buffer, but not more than what we can dump out in one go. */ - /* Note we rely on the upsampler to detect bottom of image. */ - int max_rows = out_rows_avail - out_row_ctr; - if (max_rows > m_strip_height) - max_rows = m_strip_height; - - int num_rows = 0; - m_cinfo.m_upsample.upsample(input_buf, ref in_row_group_ctr, in_row_groups_avail, m_buffer, ref num_rows, max_rows); - - /* Quantize and emit data. */ - m_cinfo.m_cquantize.color_quantize(m_buffer, 0, output_buf, out_row_ctr, num_rows); - out_row_ctr += num_rows; - } - - /// - /// Process some data in the first pass of 2-pass quantization. - /// - private void post_process_prepass(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, ref int out_row_ctr) - { - int old_next_row, num_rows; - - /* Reposition virtual buffer if at start of strip. */ - if (m_next_row == 0) - m_buffer = m_whole_image.Access(m_starting_row, m_strip_height); - - /* Upsample some data (up to a strip height's worth). */ - old_next_row = m_next_row; - m_cinfo.m_upsample.upsample(input_buf, ref in_row_group_ctr, in_row_groups_avail, m_buffer, ref m_next_row, m_strip_height); - - /* Allow quantizer to scan new data. No data is emitted, */ - /* but we advance out_row_ctr so outer loop can tell when we're done. */ - if (m_next_row > old_next_row) - { - num_rows = m_next_row - old_next_row; - m_cinfo.m_cquantize.color_quantize(m_buffer, old_next_row, null, 0, num_rows); - out_row_ctr += num_rows; - } - - /* Advance if we filled the strip. */ - if (m_next_row >= m_strip_height) - { - m_starting_row += m_strip_height; - m_next_row = 0; - } - } - - /// - /// Process some data in the second pass of 2-pass quantization. - /// - private void post_process_2pass(byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - int num_rows, max_rows; - - /* Reposition virtual buffer if at start of strip. */ - if (m_next_row == 0) - m_buffer = m_whole_image.Access(m_starting_row, m_strip_height); - - /* Determine number of rows to emit. */ - num_rows = m_strip_height - m_next_row; /* available in strip */ - max_rows = out_rows_avail - out_row_ctr; /* available in output area */ - if (num_rows > max_rows) - num_rows = max_rows; - - /* We have to check bottom of image here, can't depend on upsampler. */ - max_rows = m_cinfo.m_output_height - m_starting_row; - if (num_rows > max_rows) - num_rows = max_rows; - - /* Quantize and emit data. */ - m_cinfo.m_cquantize.color_quantize(m_buffer, m_next_row, output_buf, out_row_ctr, num_rows); - out_row_ctr += num_rows; - - /* Advance if we filled the strip. */ - m_next_row += num_rows; - if (m_next_row >= m_strip_height) - { - m_starting_row += m_strip_height; - m_next_row = 0; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_decomp_master.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_decomp_master.cs deleted file mode 100644 index af3cb1c1..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_decomp_master.cs +++ /dev/null @@ -1,348 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains master control logic for the JPEG decompressor. - * These routines are concerned with selecting the modules to be executed - * and with determining the number of passes and the work to be done in each - * pass. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Master control module - /// - class jpeg_decomp_master - { - private jpeg_decompress_struct m_cinfo; - - private int m_pass_number; /* # of passes completed */ - private bool m_is_dummy_pass; /* True during 1st pass for 2-pass quant */ - - private bool m_using_merged_upsample; /* true if using merged upsample/cconvert */ - - /* Saved references to initialized quantizer modules, - * in case we need to switch modes. - */ - private jpeg_color_quantizer m_quantizer_1pass; - private jpeg_color_quantizer m_quantizer_2pass; - - public jpeg_decomp_master(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - master_selection(); - } - - /// - /// Per-pass setup. - /// This is called at the beginning of each output pass. We determine which - /// modules will be active during this pass and give them appropriate - /// start_pass calls. We also set is_dummy_pass to indicate whether this - /// is a "real" output pass or a dummy pass for color quantization. - /// (In the latter case, we will crank the pass to completion.) - /// - public void prepare_for_output_pass() - { - if (m_is_dummy_pass) - { - /* Final pass of 2-pass quantization */ - m_is_dummy_pass = false; - m_cinfo.m_cquantize.start_pass(false); - m_cinfo.m_post.start_pass(J_BUF_MODE.JBUF_CRANK_DEST); - m_cinfo.m_main.start_pass(J_BUF_MODE.JBUF_CRANK_DEST); - } - else - { - if (m_cinfo.m_quantize_colors && m_cinfo.m_colormap == null) - { - /* Select new quantization method */ - if (m_cinfo.m_two_pass_quantize && m_cinfo.m_enable_2pass_quant) - { - m_cinfo.m_cquantize = m_quantizer_2pass; - m_is_dummy_pass = true; - } - else if (m_cinfo.m_enable_1pass_quant) - m_cinfo.m_cquantize = m_quantizer_1pass; - else - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_MODE_CHANGE); - } - - m_cinfo.m_idct.start_pass(); - m_cinfo.m_coef.start_output_pass(); - - if (!m_cinfo.m_raw_data_out) - { - m_cinfo.m_upsample.start_pass(); - - if (m_cinfo.m_quantize_colors) - m_cinfo.m_cquantize.start_pass(m_is_dummy_pass); - - m_cinfo.m_post.start_pass((m_is_dummy_pass ? J_BUF_MODE.JBUF_SAVE_AND_PASS : J_BUF_MODE.JBUF_PASS_THRU)); - m_cinfo.m_main.start_pass(J_BUF_MODE.JBUF_PASS_THRU); - } - } - - /* Set up progress monitor's pass info if present */ - if (m_cinfo.m_progress != null) - { - m_cinfo.m_progress.Completed_passes = m_pass_number; - m_cinfo.m_progress.Total_passes = m_pass_number + (m_is_dummy_pass ? 2 : 1); - - /* In buffered-image mode, we assume one more output pass if EOI not - * yet reached, but no more passes if EOI has been reached. - */ - if (m_cinfo.m_buffered_image && !m_cinfo.m_inputctl.EOIReached()) - m_cinfo.m_progress.Total_passes += (m_cinfo.m_enable_2pass_quant ? 2 : 1); - } - } - - /// - /// Finish up at end of an output pass. - /// - public void finish_output_pass() - { - if (m_cinfo.m_quantize_colors) - m_cinfo.m_cquantize.finish_pass(); - - m_pass_number++; - } - - public bool IsDummyPass() - { - return m_is_dummy_pass; - } - - /// - /// Master selection of decompression modules. - /// This is done once at jpeg_start_decompress time. We determine - /// which modules will be used and give them appropriate initialization calls. - /// We also initialize the decompressor input side to begin consuming data. - /// - /// Since jpeg_read_header has finished, we know what is in the SOF - /// and (first) SOS markers. We also have all the application parameter - /// settings. - /// - private void master_selection() - { - /* Initialize dimensions and other stuff */ - m_cinfo.jpeg_calc_output_dimensions(); - prepare_range_limit_table(); - - /* Width of an output scanline must be representable as int. */ - long samplesperrow = m_cinfo.m_output_width * m_cinfo.m_out_color_components; - int jd_samplesperrow = (int)samplesperrow; - if ((long)jd_samplesperrow != samplesperrow) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_WIDTH_OVERFLOW); - - /* Initialize my private state */ - m_pass_number = 0; - m_using_merged_upsample = m_cinfo.use_merged_upsample(); - - /* Color quantizer selection */ - m_quantizer_1pass = null; - m_quantizer_2pass = null; - - /* No mode changes if not using buffered-image mode. */ - if (!m_cinfo.m_quantize_colors || !m_cinfo.m_buffered_image) - { - m_cinfo.m_enable_1pass_quant = false; - m_cinfo.m_enable_external_quant = false; - m_cinfo.m_enable_2pass_quant = false; - } - - if (m_cinfo.m_quantize_colors) - { - if (m_cinfo.m_raw_data_out) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - - /* 2-pass quantizer only works in 3-component color space. */ - if (m_cinfo.m_out_color_components != 3) - { - m_cinfo.m_enable_1pass_quant = true; - m_cinfo.m_enable_external_quant = false; - m_cinfo.m_enable_2pass_quant = false; - m_cinfo.m_colormap = null; - } - else if (m_cinfo.m_colormap != null) - m_cinfo.m_enable_external_quant = true; - else if (m_cinfo.m_two_pass_quantize) - m_cinfo.m_enable_2pass_quant = true; - else - m_cinfo.m_enable_1pass_quant = true; - - if (m_cinfo.m_enable_1pass_quant) - { - m_cinfo.m_cquantize = new my_1pass_cquantizer(m_cinfo); - m_quantizer_1pass = m_cinfo.m_cquantize; - } - - /* We use the 2-pass code to map to external colormaps. */ - if (m_cinfo.m_enable_2pass_quant || m_cinfo.m_enable_external_quant) - { - m_cinfo.m_cquantize = new my_2pass_cquantizer(m_cinfo); - m_quantizer_2pass = m_cinfo.m_cquantize; - } - /* If both quantizers are initialized, the 2-pass one is left active; - * this is necessary for starting with quantization to an external map. - */ - } - - /* Post-processing: in particular, color conversion first */ - if (!m_cinfo.m_raw_data_out) - { - if (m_using_merged_upsample) - { - /* does color conversion too */ - m_cinfo.m_upsample = new my_merged_upsampler(m_cinfo); - } - else - { - m_cinfo.m_cconvert = new jpeg_color_deconverter(m_cinfo); - m_cinfo.m_upsample = new my_upsampler(m_cinfo); - } - - m_cinfo.m_post = new jpeg_d_post_controller(m_cinfo, m_cinfo.m_enable_2pass_quant); - } - - /* Inverse DCT */ - m_cinfo.m_idct = new jpeg_inverse_dct(m_cinfo); - - if (m_cinfo.m_progressive_mode) - m_cinfo.m_entropy = new phuff_entropy_decoder(m_cinfo); - else - m_cinfo.m_entropy = new huff_entropy_decoder(m_cinfo); - - /* Initialize principal buffer controllers. */ - bool use_c_buffer = m_cinfo.m_inputctl.HasMultipleScans() || m_cinfo.m_buffered_image; - m_cinfo.m_coef = new jpeg_d_coef_controller(m_cinfo, use_c_buffer); - - if (!m_cinfo.m_raw_data_out) - m_cinfo.m_main = new jpeg_d_main_controller(m_cinfo); - - /* Initialize input side of decompressor to consume first scan. */ - m_cinfo.m_inputctl.start_input_pass(); - - /* If jpeg_start_decompress will read the whole file, initialize - * progress monitoring appropriately. The input step is counted - * as one pass. - */ - if (m_cinfo.m_progress != null && !m_cinfo.m_buffered_image && m_cinfo.m_inputctl.HasMultipleScans()) - { - /* Estimate number of scans to set pass_limit. */ - int nscans; - if (m_cinfo.m_progressive_mode) - { - /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ - nscans = 2 + 3 * m_cinfo.m_num_components; - } - else - { - /* For a non progressive multiscan file, estimate 1 scan per component. */ - nscans = m_cinfo.m_num_components; - } - - m_cinfo.m_progress.Pass_counter = 0; - m_cinfo.m_progress.Pass_limit = m_cinfo.m_total_iMCU_rows * nscans; - m_cinfo.m_progress.Completed_passes = 0; - m_cinfo.m_progress.Total_passes = (m_cinfo.m_enable_2pass_quant ? 3 : 2); - - /* Count the input pass as done */ - m_pass_number++; - } - } - - /// - /// Allocate and fill in the sample_range_limit table. - /// - /// Several decompression processes need to range-limit values to the range - /// 0..MAXJSAMPLE; the input value may fall somewhat outside this range - /// due to noise introduced by quantization, roundoff error, etc. These - /// processes are inner loops and need to be as fast as possible. On most - /// machines, particularly CPUs with pipelines or instruction prefetch, - /// a (subscript-check-less) C table lookup - /// x = sample_range_limit[x]; - /// is faster than explicit tests - /// - /// if (x & 0) - /// x = 0; - /// else if (x > MAXJSAMPLE) - /// x = MAXJSAMPLE; - /// - /// These processes all use a common table prepared by the routine below. - /// - /// For most steps we can mathematically guarantee that the initial value - /// of x is within MAXJSAMPLE + 1 of the legal range, so a table running from - /// -(MAXJSAMPLE + 1) to 2 * MAXJSAMPLE + 1 is sufficient. But for the initial - /// limiting step (just after the IDCT), a wildly out-of-range value is - /// possible if the input data is corrupt. To avoid any chance of indexing - /// off the end of memory and getting a bad-pointer trap, we perform the - /// post-IDCT limiting thus: x = range_limit[x & MASK]; - /// where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit - /// samples. Under normal circumstances this is more than enough range and - /// a correct output will be generated; with bogus input data the mask will - /// cause wraparound, and we will safely generate a bogus-but-in-range output. - /// For the post-IDCT step, we want to convert the data from signed to unsigned - /// representation by adding CENTERJSAMPLE at the same time that we limit it. - /// So the post-IDCT limiting table ends up looking like this: - ///
-        ///     CENTERJSAMPLE, CENTERJSAMPLE + 1, ..., MAXJSAMPLE,
-        ///     MAXJSAMPLE (repeat 2 * (MAXJSAMPLE + 1) - CENTERJSAMPLE times),
-        ///     0          (repeat 2 * (MAXJSAMPLE + 1) - CENTERJSAMPLE times),
-        ///     0, 1, ..., CENTERJSAMPLE - 1
-        /// 
- /// Negative inputs select values from the upper half of the table after - /// masking. - /// - /// We can save some space by overlapping the start of the post-IDCT table - /// with the simpler range limiting table. The post-IDCT table begins at - /// sample_range_limit + CENTERJSAMPLE. - /// - /// Note that the table is allocated in near data space on PCs; it's small - /// enough and used often enough to justify this. - ///
- private void prepare_range_limit_table() - { - byte[] table = new byte[5 * (JpegConstants.MAXJSAMPLE + 1) + JpegConstants.CENTERJSAMPLE]; - - /* allow negative subscripts of simple table */ - int tableOffset = JpegConstants.MAXJSAMPLE + 1; - m_cinfo.m_sample_range_limit = table; - m_cinfo.m_sampleRangeLimitOffset = tableOffset; - - /* First segment of "simple" table: limit[x] = 0 for x < 0 */ - Array.Clear(table, 0, JpegConstants.MAXJSAMPLE + 1); - - /* Main part of "simple" table: limit[x] = x */ - for (int i = 0; i <= JpegConstants.MAXJSAMPLE; i++) - table[tableOffset + i] = (byte) i; - - tableOffset += JpegConstants.CENTERJSAMPLE; /* Point to where post-IDCT table starts */ - - /* End of simple table, rest of first half of post-IDCT table */ - for (int i = JpegConstants.CENTERJSAMPLE; i < 2 * (JpegConstants.MAXJSAMPLE + 1); i++) - table[tableOffset + i] = JpegConstants.MAXJSAMPLE; - - /* Second half of post-IDCT table */ - Array.Clear(table, tableOffset + 2 * (JpegConstants.MAXJSAMPLE + 1), - 2 * (JpegConstants.MAXJSAMPLE + 1) - JpegConstants.CENTERJSAMPLE); - - Buffer.BlockCopy(m_cinfo.m_sample_range_limit, 0, table, - tableOffset + 4 * (JpegConstants.MAXJSAMPLE + 1) - JpegConstants.CENTERJSAMPLE, JpegConstants.CENTERJSAMPLE); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_downsampler.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_downsampler.cs deleted file mode 100644 index 166e2da7..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_downsampler.cs +++ /dev/null @@ -1,550 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains downsampling routines. - * - * Downsampling input data is counted in "row groups". A row group - * is defined to be max_v_samp_factor pixel rows of each component, - * from which the downsampler produces v_samp_factor sample rows. - * A single row group is processed in each call to the downsampler module. - * - * The downsampler is responsible for edge-expansion of its output data - * to fill an integral number of DCT blocks horizontally. The source buffer - * may be modified if it is helpful for this purpose (the source buffer is - * allocated wide enough to correspond to the desired output width). - * The caller (the prep controller) is responsible for vertical padding. - * - * The downsampler may request "context rows" by setting need_context_rows - * during startup. In this case, the input arrays will contain at least - * one row group's worth of pixels above and below the passed-in data; - * the caller will create dummy rows at image top and bottom by replicating - * the first or last real pixel row. - * - * An excellent reference for image resampling is - * Digital Image Warping, George Wolberg, 1990. - * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. - * - * The downsampling algorithm used here is a simple average of the source - * pixels covered by the output pixel. The hi-falutin sampling literature - * refers to this as a "box filter". In general the characteristics of a box - * filter are not very good, but for the specific cases we normally use (1:1 - * and 2:1 ratios) the box is equivalent to a "triangle filter" which is not - * nearly so bad. If you intend to use other sampling ratios, you'd be well - * advised to improve this code. - * - * A simple input-smoothing capability is provided. This is mainly intended - * for cleaning up color-dithered GIF input files (if you find it inadequate, - * we suggest using an external filtering program such as pnmconvol). When - * enabled, each input pixel P is replaced by a weighted sum of itself and its - * eight neighbors. P's weight is 1-8*SF and each neighbor's weight is SF, - * where SF = (smoothing_factor / 1024). - * Currently, smoothing is only supported for 2h2v sampling factors. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Downsampling - /// - class jpeg_downsampler - { - private enum downSampleMethod - { - fullsize_smooth_downsampler, - fullsize_downsampler, - h2v1_downsampler, - h2v2_smooth_downsampler, - h2v2_downsampler, - int_downsampler - }; - - /* Downsamplers, one per component */ - private downSampleMethod[] m_downSamplers = new downSampleMethod[JpegConstants.MAX_COMPONENTS]; - - private jpeg_compress_struct m_cinfo; - private bool m_need_context_rows; /* true if need rows above & below */ - - public jpeg_downsampler(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - m_need_context_rows = false; - - if (cinfo.m_CCIR601_sampling) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL); - - /* Verify we can handle the sampling factors, and set up method pointers */ - bool smoothok = true; - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = cinfo.Component_info[ci]; - - if (componentInfo.H_samp_factor == cinfo.m_max_h_samp_factor && - componentInfo.V_samp_factor == cinfo.m_max_v_samp_factor) - { - if (cinfo.m_smoothing_factor != 0) - { - m_downSamplers[ci] = downSampleMethod.fullsize_smooth_downsampler; - m_need_context_rows = true; - } - else - { - m_downSamplers[ci] = downSampleMethod.fullsize_downsampler; - } - } - else if (componentInfo.H_samp_factor * 2 == cinfo.m_max_h_samp_factor && - componentInfo.V_samp_factor == cinfo.m_max_v_samp_factor) - { - smoothok = false; - m_downSamplers[ci] = downSampleMethod.h2v1_downsampler; - } - else if (componentInfo.H_samp_factor * 2 == cinfo.m_max_h_samp_factor && - componentInfo.V_samp_factor * 2 == cinfo.m_max_v_samp_factor) - { - if (cinfo.m_smoothing_factor != 0) - { - m_downSamplers[ci] = downSampleMethod.h2v2_smooth_downsampler; - m_need_context_rows = true; - } - else - { - m_downSamplers[ci] = downSampleMethod.h2v2_downsampler; - } - } - else if ((cinfo.m_max_h_samp_factor % componentInfo.H_samp_factor) == 0 && - (cinfo.m_max_v_samp_factor % componentInfo.V_samp_factor) == 0) - { - smoothok = false; - m_downSamplers[ci] = downSampleMethod.int_downsampler; - } - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL); - } - - if (cinfo.m_smoothing_factor != 0 && !smoothok) - cinfo.TRACEMS(0, J_MESSAGE_CODE.JTRC_SMOOTH_NOTIMPL); - } - - /// - /// Do downsampling for a whole row group (all components). - /// - /// In this version we simply downsample each component independently. - /// - public void downsample(byte[][][] input_buf, int in_row_index, byte[][][] output_buf, int out_row_group_index) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - int outIndex = out_row_group_index * m_cinfo.Component_info[ci].V_samp_factor; - switch (m_downSamplers[ci]) - { - case downSampleMethod.fullsize_smooth_downsampler: - fullsize_smooth_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); - break; - - case downSampleMethod.fullsize_downsampler: - fullsize_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); - break; - - case downSampleMethod.h2v1_downsampler: - h2v1_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); - break; - - case downSampleMethod.h2v2_smooth_downsampler: - h2v2_smooth_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); - break; - - case downSampleMethod.h2v2_downsampler: - h2v2_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); - break; - - case downSampleMethod.int_downsampler: - int_downsample(ci, input_buf[ci], in_row_index, output_buf[ci], outIndex); - break; - }; - } - } - - public bool NeedContextRows() - { - return m_need_context_rows; - } - - /// - /// Downsample pixel values of a single component. - /// One row group is processed per call. - /// This version handles arbitrary integral sampling ratios, without smoothing. - /// Note that this version is not actually used for customary sampling ratios. - /// - private void int_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) - { - /* Expand input data enough to let all the output samples be generated - * by the standard loop. Special-casing padded output would be more - * efficient. - */ - int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; - int h_expand = m_cinfo.m_max_h_samp_factor / m_cinfo.Component_info[componentIndex].H_samp_factor; - expand_right_edge(input_data, startInputRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, output_cols * h_expand); - - int v_expand = m_cinfo.m_max_v_samp_factor / m_cinfo.Component_info[componentIndex].V_samp_factor; - int numpix = h_expand * v_expand; - int numpix2 = numpix / 2; - int inrow = 0; - for (int outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) - { - for (int outcol = 0, outcol_h = 0; outcol < output_cols; outcol++, outcol_h += h_expand) - { - int outvalue = 0; - for (int v = 0; v < v_expand; v++) - { - for (int h = 0; h < h_expand; h++) - outvalue += input_data[startInputRow + inrow + v][outcol_h + h]; - } - - output_data[startOutRow + outrow][outcol] = (byte)((outvalue + numpix2) / numpix); - } - - inrow += v_expand; - } - } - - /// - /// Downsample pixel values of a single component. - /// This version handles the special case of a full-size component, - /// without smoothing. - /// - private void fullsize_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) - { - /* Copy the data */ - JpegUtils.jcopy_sample_rows(input_data, startInputRow, output_data, startOutRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width); - - /* Edge-expand */ - expand_right_edge(output_data, startOutRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE); - } - - /// - /// Downsample pixel values of a single component. - /// This version handles the common case of 2:1 horizontal and 1:1 vertical, - /// without smoothing. - /// - /// A note about the "bias" calculations: when rounding fractional values to - /// integer, we do not want to always round 0.5 up to the next integer. - /// If we did that, we'd introduce a noticeable bias towards larger values. - /// Instead, this code is arranged so that 0.5 will be rounded up or down at - /// alternate pixel locations (a simple ordered dither pattern). - /// - private void h2v1_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) - { - /* Expand input data enough to let all the output samples be generated - * by the standard loop. Special-casing padded output would be more - * efficient. - */ - int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; - expand_right_edge(input_data, startInputRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, output_cols * 2); - - for (int outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) - { - /* bias = 0,1,0,1,... for successive samples */ - int bias = 0; - int inputColumn = 0; - for (int outcol = 0; outcol < output_cols; outcol++) - { - output_data[startOutRow + outrow][outcol] = (byte)( - ((int)input_data[startInputRow + outrow][inputColumn] + - (int)input_data[startInputRow + outrow][inputColumn + 1] + bias) >> 1); - - bias ^= 1; /* 0=>1, 1=>0 */ - inputColumn += 2; - } - } - } - - /// - /// Downsample pixel values of a single component. - /// This version handles the standard case of 2:1 horizontal and 2:1 vertical, - /// without smoothing. - /// - private void h2v2_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) - { - /* Expand input data enough to let all the output samples be generated - * by the standard loop. Special-casing padded output would be more - * efficient. - */ - int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; - expand_right_edge(input_data, startInputRow, m_cinfo.m_max_v_samp_factor, m_cinfo.m_image_width, output_cols * 2); - - int inrow = 0; - for (int outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) - { - /* bias = 1,2,1,2,... for successive samples */ - int bias = 1; - int inputColumn = 0; - for (int outcol = 0; outcol < output_cols; outcol++) - { - output_data[startOutRow + outrow][outcol] = (byte)(( - (int)input_data[startInputRow + inrow][inputColumn] + - (int)input_data[startInputRow + inrow][inputColumn + 1] + - (int)input_data[startInputRow + inrow + 1][inputColumn] + - (int)input_data[startInputRow + inrow + 1][inputColumn + 1] + bias) >> 2); - - bias ^= 3; /* 1=>2, 2=>1 */ - inputColumn += 2; - } - - inrow += 2; - } - } - - /// - /// Downsample pixel values of a single component. - /// This version handles the standard case of 2:1 horizontal and 2:1 vertical, - /// with smoothing. One row of context is required. - /// - private void h2v2_smooth_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) - { - /* Expand input data enough to let all the output samples be generated - * by the standard loop. Special-casing padded output would be more - * efficient. - */ - int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; - expand_right_edge(input_data, startInputRow - 1, m_cinfo.m_max_v_samp_factor + 2, m_cinfo.m_image_width, output_cols * 2); - - /* We don't bother to form the individual "smoothed" input pixel values; - * we can directly compute the output which is the average of the four - * smoothed values. Each of the four member pixels contributes a fraction - * (1-8*SF) to its own smoothed image and a fraction SF to each of the three - * other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final - * output. The four corner-adjacent neighbor pixels contribute a fraction - * SF to just one smoothed pixel, or SF/4 to the final output; while the - * eight edge-adjacent neighbors contribute SF to each of two smoothed - * pixels, or SF/2 overall. In order to use integer arithmetic, these - * factors are scaled by 2^16 = 65536. - * Also recall that SF = smoothing_factor / 1024. - */ - - int memberscale = 16384 - m_cinfo.m_smoothing_factor * 80; /* scaled (1-5*SF)/4 */ - int neighscale = m_cinfo.m_smoothing_factor * 16; /* scaled SF/4 */ - - for (int inrow = 0, outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) - { - int outIndex = 0; - int inIndex0 = 0; - int inIndex1 = 0; - int aboveIndex = 0; - int belowIndex = 0; - - /* Special case for first column: pretend column -1 is same as column 0 */ - int membersum = input_data[startInputRow + inrow][inIndex0] + - input_data[startInputRow + inrow][inIndex0 + 1] + - input_data[startInputRow + inrow + 1][inIndex1] + - input_data[startInputRow + inrow + 1][inIndex1 + 1]; - - int neighsum = input_data[startInputRow + inrow - 1][aboveIndex] + - input_data[startInputRow + inrow - 1][aboveIndex + 1] + - input_data[startInputRow + inrow + 2][belowIndex] + - input_data[startInputRow + inrow + 2][belowIndex + 1] + - input_data[startInputRow + inrow][inIndex0] + - input_data[startInputRow + inrow][inIndex0 + 2] + - input_data[startInputRow + inrow + 1][inIndex1] + - input_data[startInputRow + inrow + 1][inIndex1 + 2]; - - neighsum += neighsum; - neighsum += input_data[startInputRow + inrow - 1][aboveIndex] + - input_data[startInputRow + inrow - 1][aboveIndex + 2] + - input_data[startInputRow + inrow + 2][belowIndex] + - input_data[startInputRow + inrow + 2][belowIndex + 2]; - - membersum = membersum * memberscale + neighsum * neighscale; - output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); - outIndex++; - - inIndex0 += 2; - inIndex1 += 2; - aboveIndex += 2; - belowIndex += 2; - - for (int colctr = output_cols - 2; colctr > 0; colctr--) - { - /* sum of pixels directly mapped to this output element */ - membersum = input_data[startInputRow + inrow][inIndex0] + - input_data[startInputRow + inrow][inIndex0 + 1] + - input_data[startInputRow + inrow + 1][inIndex1] + - input_data[startInputRow + inrow + 1][inIndex1 + 1]; - - /* sum of edge-neighbor pixels */ - neighsum = input_data[startInputRow + inrow - 1][aboveIndex] + - input_data[startInputRow + inrow - 1][aboveIndex + 1] + - input_data[startInputRow + inrow + 2][belowIndex] + - input_data[startInputRow + inrow + 2][belowIndex + 1] + - input_data[startInputRow + inrow][inIndex0 - 1] + - input_data[startInputRow + inrow][inIndex0 + 2] + - input_data[startInputRow + inrow + 1][inIndex1 - 1] + - input_data[startInputRow + inrow + 1][inIndex1 + 2]; - - /* The edge-neighbors count twice as much as corner-neighbors */ - neighsum += neighsum; - - /* Add in the corner-neighbors */ - neighsum += input_data[startInputRow + inrow - 1][aboveIndex - 1] + - input_data[startInputRow + inrow - 1][aboveIndex + 2] + - input_data[startInputRow + inrow + 2][belowIndex - 1] + - input_data[startInputRow + inrow + 2][belowIndex + 2]; - - /* form final output scaled up by 2^16 */ - membersum = membersum * memberscale + neighsum * neighscale; - - /* round, descale and output it */ - output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); - outIndex++; - - inIndex0 += 2; - inIndex1 += 2; - aboveIndex += 2; - belowIndex += 2; - } - - /* Special case for last column */ - membersum = input_data[startInputRow + inrow][inIndex0] + - input_data[startInputRow + inrow][inIndex0 + 1] + - input_data[startInputRow + inrow + 1][inIndex1] + - input_data[startInputRow + inrow + 1][inIndex1 + 1]; - - neighsum = input_data[startInputRow + inrow - 1][aboveIndex] + - input_data[startInputRow + inrow - 1][aboveIndex + 1] + - input_data[startInputRow + inrow + 2][belowIndex] + - input_data[startInputRow + inrow + 2][belowIndex + 1] + - input_data[startInputRow + inrow][inIndex0 - 1] + - input_data[startInputRow + inrow][inIndex0 + 1] + - input_data[startInputRow + inrow + 1][inIndex1 - 1] + - input_data[startInputRow + inrow + 1][inIndex1 + 1]; - - neighsum += neighsum; - neighsum += input_data[startInputRow + inrow - 1][aboveIndex - 1] + - input_data[startInputRow + inrow - 1][aboveIndex + 1] + - input_data[startInputRow + inrow + 2][belowIndex - 1] + - input_data[startInputRow + inrow + 2][belowIndex + 1]; - - membersum = membersum * memberscale + neighsum * neighscale; - output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); - - inrow += 2; - } - } - - /// - /// Downsample pixel values of a single component. - /// This version handles the special case of a full-size component, - /// with smoothing. One row of context is required. - /// - private void fullsize_smooth_downsample(int componentIndex, byte[][] input_data, int startInputRow, byte[][] output_data, int startOutRow) - { - /* Expand input data enough to let all the output samples be generated - * by the standard loop. Special-casing padded output would be more - * efficient. - */ - int output_cols = m_cinfo.Component_info[componentIndex].Width_in_blocks * JpegConstants.DCTSIZE; - expand_right_edge(input_data, startInputRow - 1, m_cinfo.m_max_v_samp_factor + 2, m_cinfo.m_image_width, output_cols); - - /* Each of the eight neighbor pixels contributes a fraction SF to the - * smoothed pixel, while the main pixel contributes (1-8*SF). In order - * to use integer arithmetic, these factors are multiplied by 2^16 = 65536. - * Also recall that SF = smoothing_factor / 1024. - */ - - int memberscale = 65536 - m_cinfo.m_smoothing_factor * 512; /* scaled 1-8*SF */ - int neighscale = m_cinfo.m_smoothing_factor * 64; /* scaled SF */ - - for (int outrow = 0; outrow < m_cinfo.Component_info[componentIndex].V_samp_factor; outrow++) - { - int outIndex = 0; - int inIndex = 0; - int aboveIndex = 0; - int belowIndex = 0; - - /* Special case for first column */ - int colsum = input_data[startInputRow + outrow - 1][aboveIndex] + - input_data[startInputRow + outrow + 1][belowIndex] + - input_data[startInputRow + outrow][inIndex]; - - aboveIndex++; - belowIndex++; - - int membersum = input_data[startInputRow + outrow][inIndex]; - inIndex++; - - int nextcolsum = input_data[startInputRow + outrow - 1][aboveIndex] + - input_data[startInputRow + outrow + 1][belowIndex] + - input_data[startInputRow + outrow][inIndex]; - - int neighsum = colsum + (colsum - membersum) + nextcolsum; - - membersum = membersum * memberscale + neighsum * neighscale; - output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); - outIndex++; - - int lastcolsum = colsum; - colsum = nextcolsum; - - for (int colctr = output_cols - 2; colctr > 0; colctr--) - { - membersum = input_data[startInputRow + outrow][inIndex]; - - inIndex++; - aboveIndex++; - belowIndex++; - - nextcolsum = input_data[startInputRow + outrow - 1][aboveIndex] + - input_data[startInputRow + outrow + 1][belowIndex] + - input_data[startInputRow + outrow][inIndex]; - - neighsum = lastcolsum + (colsum - membersum) + nextcolsum; - membersum = membersum * memberscale + neighsum * neighscale; - - output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); - outIndex++; - - lastcolsum = colsum; - colsum = nextcolsum; - } - - /* Special case for last column */ - membersum = input_data[startInputRow + outrow][inIndex]; - neighsum = lastcolsum + (colsum - membersum) + colsum; - membersum = membersum * memberscale + neighsum * neighscale; - output_data[startOutRow + outrow][outIndex] = (byte)((membersum + 32768) >> 16); - } - } - - /// - /// Expand a component horizontally from width input_cols to width output_cols, - /// by duplicating the rightmost samples. - /// - private static void expand_right_edge(byte[][] image_data, int startInputRow, int num_rows, int input_cols, int output_cols) - { - int numcols = output_cols - input_cols; - if (numcols > 0) - { - for (int row = startInputRow; row < (startInputRow + num_rows); row++) - { - /* don't need GETJSAMPLE() here */ - byte pixval = image_data[row][input_cols - 1]; - for (int count = 0; count < numcols; count++) - image_data[row][input_cols + count] = pixval; - } - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_entropy_decoder.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_entropy_decoder.cs deleted file mode 100644 index 5acf69f2..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_entropy_decoder.cs +++ /dev/null @@ -1,477 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Entropy decoding - /// - abstract class jpeg_entropy_decoder - { - // Figure F.12: extend sign bit. - // entry n is 2**(n-1) - private static int[] extend_test = - { - 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, - 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, - 0x1000, 0x2000, 0x4000 - }; - - // entry n is (-1 << n) + 1 - private static int[] extend_offset = - { - 0, (-1 << 1) + 1, (-1 << 2) + 1, - (-1 << 3) + 1, (-1 << 4) + 1, (-1 << 5) + 1, - (-1 << 6) + 1, (-1 << 7) + 1, (-1 << 8) + 1, - (-1 << 9) + 1, (-1 << 10) + 1, - (-1 << 11) + 1, (-1 << 12) + 1, - (-1 << 13) + 1, (-1 << 14) + 1, - (-1 << 15) + 1 - }; - - /* Fetching the next N bits from the input stream is a time-critical operation - * for the Huffman decoders. We implement it with a combination of inline - * macros and out-of-line subroutines. Note that N (the number of bits - * demanded at one time) never exceeds 15 for JPEG use. - * - * We read source bytes into get_buffer and dole out bits as needed. - * If get_buffer already contains enough bits, they are fetched in-line - * by the macros CHECK_BIT_BUFFER and GET_BITS. When there aren't enough - * bits, jpeg_fill_bit_buffer is called; it will attempt to fill get_buffer - * as full as possible (not just to the number of bits needed; this - * prefetching reduces the overhead cost of calling jpeg_fill_bit_buffer). - * Note that jpeg_fill_bit_buffer may return false to indicate suspension. - * On true return, jpeg_fill_bit_buffer guarantees that get_buffer contains - * at least the requested number of bits --- dummy zeroes are inserted if - * necessary. - */ - protected const int BIT_BUF_SIZE = 32; /* size of buffer in bits */ - - /* - * Out-of-line code for bit fetching (shared with jdphuff.c). - * See jdhuff.h for info about usage. - * Note: current values of get_buffer and bits_left are passed as parameters, - * but are returned in the corresponding fields of the state struct. - * - * On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width - * of get_buffer to be used. (On machines with wider words, an even larger - * buffer could be used.) However, on some machines 32-bit shifts are - * quite slow and take time proportional to the number of places shifted. - * (This is true with most PC compilers, for instance.) In this case it may - * be a win to set MIN_GET_BITS to the minimum value of 15. This reduces the - * average shift distance at the cost of more calls to jpeg_fill_bit_buffer. - */ - - protected const int MIN_GET_BITS = BIT_BUF_SIZE - 7; - - protected jpeg_decompress_struct m_cinfo; - - /* This is here to share code between baseline and progressive decoders; */ - /* other modules probably should not use it */ - protected bool m_insufficient_data; /* set true after emitting warning */ - - public abstract void start_pass(); - public abstract bool decode_mcu(JBLOCK[] MCU_data); - - protected static int HUFF_EXTEND(int x, int s) - { - return ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)); - } - - protected void BITREAD_LOAD_STATE(bitread_perm_state bitstate, out int get_buffer, out int bits_left, ref bitread_working_state br_state) - { - br_state.cinfo = m_cinfo; - get_buffer = bitstate.get_buffer; - bits_left = bitstate.bits_left; - } - - protected static void BITREAD_SAVE_STATE(ref bitread_perm_state bitstate, int get_buffer, int bits_left) - { - bitstate.get_buffer = get_buffer; - bitstate.bits_left = bits_left; - } - - /// - /// Expand a Huffman table definition into the derived format - /// This routine also performs some validation checks on the table. - /// - protected void jpeg_make_d_derived_tbl(bool isDC, int tblno, ref d_derived_tbl dtbl) - { - /* Note that huffsize[] and huffcode[] are filled in code-length order, - * paralleling the order of the symbols themselves in htbl.huffval[]. - */ - - /* Find the input Huffman table */ - if (tblno < 0 || tblno >= JpegConstants.NUM_HUFF_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tblno); - - JHUFF_TBL htbl = isDC ? m_cinfo.m_dc_huff_tbl_ptrs[tblno] : m_cinfo.m_ac_huff_tbl_ptrs[tblno]; - if (htbl == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tblno); - - /* Allocate a workspace if we haven't already done so. */ - if (dtbl == null) - dtbl = new d_derived_tbl(); - - dtbl.pub = htbl; /* fill in back link */ - - /* Figure C.1: make table of Huffman code length for each symbol */ - - int p = 0; - char[] huffsize = new char[257]; - for (int l = 1; l <= 16; l++) - { - int i = htbl.Bits[l]; - if (i < 0 || p + i> 256) /* protect against table overrun */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - - while ((i--) != 0) - huffsize[p++] = (char) l; - } - huffsize[p] = (char)0; - int numsymbols = p; - - /* Figure C.2: generate the codes themselves */ - /* We also validate that the counts represent a legal Huffman code tree. */ - - int code = 0; - int si = huffsize[0]; - int[] huffcode = new int[257]; - p = 0; - while (huffsize[p] != 0) - { - while (((int)huffsize[p]) == si) - { - huffcode[p++] = code; - code++; - } - - /* code is now 1 more than the last code used for codelength si; but - * it must still fit in si bits, since no code is allowed to be all ones. - */ - if (code >= (1 << si)) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - code <<= 1; - si++; - } - - /* Figure F.15: generate decoding tables for bit-sequential decoding */ - - p = 0; - for (int l = 1; l <= 16; l++) - { - if (htbl.Bits[l] != 0) - { - /* valoffset[l] = huffval[] index of 1st symbol of code length l, - * minus the minimum code of length l - */ - dtbl.valoffset[l] = p - huffcode[p]; - p += htbl.Bits[l]; - dtbl.maxcode[l] = huffcode[p - 1]; /* maximum code of length l */ - } - else - { - /* -1 if no codes of this length */ - dtbl.maxcode[l] = -1; - } - } - dtbl.maxcode[17] = 0xFFFFF; /* ensures jpeg_huff_decode terminates */ - - /* Compute lookahead tables to speed up decoding. - * First we set all the table entries to 0, indicating "too long"; - * then we iterate through the Huffman codes that are short enough and - * fill in all the entries that correspond to bit sequences starting - * with that code. - */ - - Array.Clear(dtbl.look_nbits, 0, dtbl.look_nbits.Length); - p = 0; - for (int l = 1; l <= JpegConstants.HUFF_LOOKAHEAD; l++) - { - for (int i = 1; i <= htbl.Bits[l]; i++, p++) - { - /* l = current code's length, p = its index in huffcode[] & huffval[]. */ - /* Generate left-justified code followed by all possible bit sequences */ - int lookbits = huffcode[p] << (JpegConstants.HUFF_LOOKAHEAD - l); - for (int ctr = 1 << (JpegConstants.HUFF_LOOKAHEAD - l); ctr > 0; ctr--) - { - dtbl.look_nbits[lookbits] = l; - dtbl.look_sym[lookbits] = htbl.Huffval[p]; - lookbits++; - } - } - } - - /* Validate symbols as being reasonable. - * For AC tables, we make no check, but accept all byte values 0..255. - * For DC tables, we require the symbols to be in range 0..15. - * (Tighter bounds could be applied depending on the data depth and mode, - * but this is sufficient to ensure safe decoding.) - */ - if (isDC) - { - for (int i = 0; i < numsymbols; i++) - { - int sym = htbl.Huffval[i]; - if (sym < 0 || sym> 15) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - } - } - } - - /* - * These methods provide the in-line portion of bit fetching. - * Use CHECK_BIT_BUFFER to ensure there are N bits in get_buffer - * before using GET_BITS, PEEK_BITS, or DROP_BITS. - * The variables get_buffer and bits_left are assumed to be locals, - * but the state struct might not be (jpeg_huff_decode needs this). - * CHECK_BIT_BUFFER(state,n,action); - * Ensure there are N bits in get_buffer; if suspend, take action. - * val = GET_BITS(n); - * Fetch next N bits. - * val = PEEK_BITS(n); - * Fetch next N bits without removing them from the buffer. - * DROP_BITS(n); - * Discard next N bits. - * The value N should be a simple variable, not an expression, because it - * is evaluated multiple times. - */ - - protected static bool CHECK_BIT_BUFFER(ref bitread_working_state state, int nbits, ref int get_buffer, ref int bits_left) - { - if (bits_left < nbits) - { - if (!jpeg_fill_bit_buffer(ref state, get_buffer, bits_left, nbits)) - return false; - - get_buffer = state.get_buffer; - bits_left = state.bits_left; - } - - return true; - } - - protected static int GET_BITS(int nbits, int get_buffer, ref int bits_left) - { - return (((int)(get_buffer >> (bits_left -= nbits))) & ((1 << nbits) - 1)); - } - - protected static int PEEK_BITS(int nbits, int get_buffer, int bits_left) - { - return (((int)(get_buffer >> (bits_left - nbits))) & ((1 << nbits) - 1)); - } - - protected static void DROP_BITS(int nbits, ref int bits_left) - { - bits_left -= nbits; - } - - /* Load up the bit buffer to a depth of at least nbits */ - protected static bool jpeg_fill_bit_buffer(ref bitread_working_state state, int get_buffer, int bits_left, int nbits) - { - /* Attempt to load at least MIN_GET_BITS bits into get_buffer. */ - /* (It is assumed that no request will be for more than that many bits.) */ - /* We fail to do so only if we hit a marker or are forced to suspend. */ - - bool noMoreBytes = false; - - if (state.cinfo.m_unread_marker == 0) - { - /* cannot advance past a marker */ - while (bits_left < MIN_GET_BITS) - { - int c; - state.cinfo.m_src.GetByte(out c); - - /* If it's 0xFF, check and discard stuffed zero byte */ - if (c == 0xFF) - { - /* Loop here to discard any padding FF's on terminating marker, - * so that we can save a valid unread_marker value. NOTE: we will - * accept multiple FF's followed by a 0 as meaning a single FF data - * byte. This data pattern is not valid according to the standard. - */ - do - { - state.cinfo.m_src.GetByte(out c); - } - while (c == 0xFF); - - if (c == 0) - { - /* Found FF/00, which represents an FF data byte */ - c = 0xFF; - } - else - { - /* Oops, it's actually a marker indicating end of compressed data. - * Save the marker code for later use. - * Fine point: it might appear that we should save the marker into - * bitread working state, not straight into permanent state. But - * once we have hit a marker, we cannot need to suspend within the - * current MCU, because we will read no more bytes from the data - * source. So it is OK to update permanent state right away. - */ - state.cinfo.m_unread_marker = c; - /* See if we need to insert some fake zero bits. */ - noMoreBytes = true; - break; - } - } - - /* OK, load c into get_buffer */ - get_buffer = (get_buffer << 8) | c; - bits_left += 8; - } /* end while */ - } - else - noMoreBytes = true; - - if (noMoreBytes) - { - /* We get here if we've read the marker that terminates the compressed - * data segment. There should be enough bits in the buffer register - * to satisfy the request; if so, no problem. - */ - if (nbits > bits_left) - { - /* Uh-oh. Report corrupted data to user and stuff zeroes into - * the data stream, so that we can produce some kind of image. - * We use a nonvolatile flag to ensure that only one warning message - * appears per data segment. - */ - if (!state.cinfo.m_entropy.m_insufficient_data) - { - state.cinfo.WARNMS(J_MESSAGE_CODE.JWRN_HIT_MARKER); - state.cinfo.m_entropy.m_insufficient_data = true; - } - - /* Fill the buffer with zero bits */ - get_buffer <<= MIN_GET_BITS - bits_left; - bits_left = MIN_GET_BITS; - } - } - - /* Unload the local registers */ - state.get_buffer = get_buffer; - state.bits_left = bits_left; - - return true; - } - - /* - * Code for extracting next Huffman-coded symbol from input bit stream. - * Again, this is time-critical and we make the main paths be macros. - * - * We use a lookahead table to process codes of up to HUFF_LOOKAHEAD bits - * without looping. Usually, more than 95% of the Huffman codes will be 8 - * or fewer bits long. The few overlength codes are handled with a loop, - * which need not be inline code. - * - * Notes about the HUFF_DECODE macro: - * 1. Near the end of the data segment, we may fail to get enough bits - * for a lookahead. In that case, we do it the hard way. - * 2. If the lookahead table contains no entry, the next code must be - * more than HUFF_LOOKAHEAD bits long. - * 3. jpeg_huff_decode returns -1 if forced to suspend. - */ - protected static bool HUFF_DECODE(out int result, ref bitread_working_state state, d_derived_tbl htbl, ref int get_buffer, ref int bits_left) - { - int nb = 0; - bool doSlow = false; - - if (bits_left < JpegConstants.HUFF_LOOKAHEAD) - { - if (!jpeg_fill_bit_buffer(ref state, get_buffer, bits_left, 0)) - { - result = -1; - return false; - } - - get_buffer = state.get_buffer; - bits_left = state.bits_left; - if (bits_left < JpegConstants.HUFF_LOOKAHEAD) - { - nb = 1; - doSlow = true; - } - } - - if (!doSlow) - { - int look = PEEK_BITS(JpegConstants.HUFF_LOOKAHEAD, get_buffer, bits_left); - if ((nb = htbl.look_nbits[look]) != 0) - { - DROP_BITS(nb, ref bits_left); - result = htbl.look_sym[look]; - return true; - } - - nb = JpegConstants.HUFF_LOOKAHEAD + 1; - } - - result = jpeg_huff_decode(ref state, get_buffer, bits_left, htbl, nb); - if (result < 0) - return false; - - get_buffer = state.get_buffer; - bits_left = state.bits_left; - - return true; - } - - /* Out-of-line case for Huffman code fetching */ - protected static int jpeg_huff_decode(ref bitread_working_state state, int get_buffer, int bits_left, d_derived_tbl htbl, int min_bits) - { - /* HUFF_DECODE has determined that the code is at least min_bits */ - /* bits long, so fetch that many bits in one swoop. */ - int l = min_bits; - if (!CHECK_BIT_BUFFER(ref state, l, ref get_buffer, ref bits_left)) - return -1; - - int code = GET_BITS(l, get_buffer, ref bits_left); - - /* Collect the rest of the Huffman code one bit at a time. */ - /* This is per Figure F.16 in the JPEG spec. */ - - while (code > htbl.maxcode[l]) - { - code <<= 1; - if (!CHECK_BIT_BUFFER(ref state, 1, ref get_buffer, ref bits_left)) - return -1; - - code |= GET_BITS(1, get_buffer, ref bits_left); - l++; - } - - /* Unload the local registers */ - state.get_buffer = get_buffer; - state.bits_left = bits_left; - - /* With garbage input we may reach the sentinel value l = 17. */ - - if (l > 16) - { - state.cinfo.WARNMS(J_MESSAGE_CODE.JWRN_HUFF_BAD_CODE); - /* fake a zero as the safest result */ - return 0; - } - - return htbl.pub.Huffval[code + htbl.valoffset[l]]; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_entropy_encoder.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_entropy_encoder.cs deleted file mode 100644 index e48b1703..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_entropy_encoder.cs +++ /dev/null @@ -1,307 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Entropy encoding - /// - abstract class jpeg_entropy_encoder - { - /* Derived data constructed for each Huffman table */ - protected class c_derived_tbl - { - public int[] ehufco = new int[256]; /* code for each symbol */ - public char[] ehufsi = new char[256]; /* length of code for each symbol */ - /* If no code has been allocated for a symbol S, ehufsi[S] contains 0 */ - } - - /* The legal range of a DCT coefficient is - * -1024 .. +1023 for 8-bit data; - * -16384 .. +16383 for 12-bit data. - * Hence the magnitude should always fit in 10 or 14 bits respectively. - */ - protected static int MAX_HUFFMAN_COEF_BITS = 10; - private static int MAX_CLEN = 32; /* assumed maximum initial code length */ - - protected jpeg_compress_struct m_cinfo; - - public abstract void start_pass(bool gather_statistics); - public abstract bool encode_mcu(JBLOCK[][] MCU_data); - public abstract void finish_pass(); - - /// - /// Expand a Huffman table definition into the derived format - /// Compute the derived values for a Huffman table. - /// This routine also performs some validation checks on the table. - /// - protected void jpeg_make_c_derived_tbl(bool isDC, int tblno, ref c_derived_tbl dtbl) - { - /* Note that huffsize[] and huffcode[] are filled in code-length order, - * paralleling the order of the symbols themselves in htbl.huffval[]. - */ - - /* Find the input Huffman table */ - if (tblno < 0 || tblno >= JpegConstants.NUM_HUFF_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tblno); - - JHUFF_TBL htbl = isDC ? m_cinfo.m_dc_huff_tbl_ptrs[tblno] : m_cinfo.m_ac_huff_tbl_ptrs[tblno]; - if (htbl == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tblno); - - /* Allocate a workspace if we haven't already done so. */ - if (dtbl == null) - dtbl = new c_derived_tbl(); - - /* Figure C.1: make table of Huffman code length for each symbol */ - - int p = 0; - char[] huffsize = new char[257]; - for (int l = 1; l <= 16; l++) - { - int i = htbl.Bits[l]; - if (i < 0 || p + i> 256) /* protect against table overrun */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - - while ((i--) != 0) - huffsize[p++] = (char) l; - } - huffsize[p] = (char)0; - int lastp = p; - - /* Figure C.2: generate the codes themselves */ - /* We also validate that the counts represent a legal Huffman code tree. */ - - int code = 0; - int si = huffsize[0]; - p = 0; - int[] huffcode = new int[257]; - while (huffsize[p] != 0) - { - while (((int)huffsize[p]) == si) - { - huffcode[p++] = code; - code++; - } - /* code is now 1 more than the last code used for codelength si; but - * it must still fit in si bits, since no code is allowed to be all ones. - */ - if (code >= (1 << si)) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - code <<= 1; - si++; - } - - /* Figure C.3: generate encoding tables */ - /* These are code and size indexed by symbol value */ - - /* Set all codeless symbols to have code length 0; - * this lets us detect duplicate VAL entries here, and later - * allows emit_bits to detect any attempt to emit such symbols. - */ - Array.Clear(dtbl.ehufsi, 0, dtbl.ehufsi.Length); - - /* This is also a convenient place to check for out-of-range - * and duplicated VAL entries. We allow 0..255 for AC symbols - * but only 0..15 for DC. (We could constrain them further - * based on data depth and mode, but this seems enough.) - */ - int maxsymbol = isDC ? 15 : 255; - - for (p = 0; p < lastp; p++) - { - int i = htbl.Huffval[p]; - if (i < 0 || i> maxsymbol || dtbl.ehufsi[i] != 0) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - - dtbl.ehufco[i] = huffcode[p]; - dtbl.ehufsi[i] = huffsize[p]; - } - } - - /// - /// Generate the best Huffman code table for the given counts, fill htbl. - /// - /// The JPEG standard requires that no symbol be assigned a codeword of all - /// one bits (so that padding bits added at the end of a compressed segment - /// can't look like a valid code). Because of the canonical ordering of - /// codewords, this just means that there must be an unused slot in the - /// longest codeword length category. Section K.2 of the JPEG spec suggests - /// reserving such a slot by pretending that symbol 256 is a valid symbol - /// with count 1. In theory that's not optimal; giving it count zero but - /// including it in the symbol set anyway should give a better Huffman code. - /// But the theoretically better code actually seems to come out worse in - /// practice, because it produces more all-ones bytes (which incur stuffed - /// zero bytes in the final file). In any case the difference is tiny. - /// - /// The JPEG standard requires Huffman codes to be no more than 16 bits long. - /// If some symbols have a very small but nonzero probability, the Huffman tree - /// must be adjusted to meet the code length restriction. We currently use - /// the adjustment method suggested in JPEG section K.2. This method is *not* - /// optimal; it may not choose the best possible limited-length code. But - /// typically only very-low-frequency symbols will be given less-than-optimal - /// lengths, so the code is almost optimal. Experimental comparisons against - /// an optimal limited-length-code algorithm indicate that the difference is - /// microscopic --- usually less than a hundredth of a percent of total size. - /// So the extra complexity of an optimal algorithm doesn't seem worthwhile. - /// - protected void jpeg_gen_optimal_table(JHUFF_TBL htbl, long[] freq) - { - byte[] bits = new byte[MAX_CLEN + 1]; /* bits[k] = # of symbols with code length k */ - int[] codesize = new int[257]; /* codesize[k] = code length of symbol k */ - int[] others = new int[257]; /* next symbol in current branch of tree */ - int c1, c2; - int p, i, j; - long v; - - /* This algorithm is explained in section K.2 of the JPEG standard */ - for (i = 0; i < 257; i++) - others[i] = -1; /* init links to empty */ - - freq[256] = 1; /* make sure 256 has a nonzero count */ - /* Including the pseudo-symbol 256 in the Huffman procedure guarantees - * that no real symbol is given code-value of all ones, because 256 - * will be placed last in the largest codeword category. - */ - - /* Huffman's basic algorithm to assign optimal code lengths to symbols */ - - for (; ;) - { - /* Find the smallest nonzero frequency, set c1 = its symbol */ - /* In case of ties, take the larger symbol number */ - c1 = -1; - v = 1000000000L; - for (i = 0; i <= 256; i++) - { - if (freq[i] != 0 && freq[i] <= v) - { - v = freq[i]; - c1 = i; - } - } - - /* Find the next smallest nonzero frequency, set c2 = its symbol */ - /* In case of ties, take the larger symbol number */ - c2 = -1; - v = 1000000000L; - for (i = 0; i <= 256; i++) - { - if (freq[i] != 0 && freq[i] <= v && i != c1) - { - v = freq[i]; - c2 = i; - } - } - - /* Done if we've merged everything into one frequency */ - if (c2 < 0) - break; - - /* Else merge the two counts/trees */ - freq[c1] += freq[c2]; - freq[c2] = 0; - - /* Increment the codesize of everything in c1's tree branch */ - codesize[c1]++; - while (others[c1] >= 0) - { - c1 = others[c1]; - codesize[c1]++; - } - - others[c1] = c2; /* chain c2 onto c1's tree branch */ - - /* Increment the codesize of everything in c2's tree branch */ - codesize[c2]++; - while (others[c2] >= 0) - { - c2 = others[c2]; - codesize[c2]++; - } - } - - /* Now count the number of symbols of each code length */ - for (i = 0; i <= 256; i++) - { - if (codesize[i] != 0) - { - /* The JPEG standard seems to think that this can't happen, */ - /* but I'm paranoid... */ - if (codesize[i] > MAX_CLEN) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_HUFF_CLEN_OVERFLOW); - - bits[codesize[i]]++; - } - } - - /* JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure - * Huffman procedure assigned any such lengths, we must adjust the coding. - * Here is what the JPEG spec says about how this next bit works: - * Since symbols are paired for the longest Huffman code, the symbols are - * removed from this length category two at a time. The prefix for the pair - * (which is one bit shorter) is allocated to one of the pair; then, - * skipping the BITS entry for that prefix length, a code word from the next - * shortest nonzero BITS entry is converted into a prefix for two code words - * one bit longer. - */ - - for (i = MAX_CLEN; i > 16; i--) - { - while (bits[i] > 0) - { - j = i - 2; /* find length of new prefix to be used */ - while (bits[j] == 0) - j--; - - bits[i] -= 2; /* remove two symbols */ - bits[i - 1]++; /* one goes in this length */ - bits[j + 1] += 2; /* two new symbols in this length */ - bits[j]--; /* symbol of this length is now a prefix */ - } - } - - /* Remove the count for the pseudo-symbol 256 from the largest codelength */ - while (bits[i] == 0) /* find largest codelength still in use */ - i--; - bits[i]--; - - /* Return final symbol counts (only for lengths 0..16) */ - Buffer.BlockCopy(bits, 0, htbl.Bits, 0, htbl.Bits.Length); - - /* Return a list of the symbols sorted by code length */ - /* It's not real clear to me why we don't need to consider the codelength - * changes made above, but the JPEG spec seems to think this works. - */ - p = 0; - for (i = 1; i <= MAX_CLEN; i++) - { - for (j = 0; j <= 255; j++) - { - if (codesize[j] == i) - { - htbl.Huffval[p] = (byte) j; - p++; - } - } - } - - /* Set sent_table false so updated table will be written to JPEG file. */ - htbl.Sent_table = false; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_forward_dct.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_forward_dct.cs deleted file mode 100644 index 31717942..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_forward_dct.cs +++ /dev/null @@ -1,817 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the forward-DCT management logic. - * This code selects a particular DCT implementation to be used, - * and it performs related housekeeping chores including coefficient - * quantization. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Forward DCT (also controls coefficient quantization) - /// - /// A forward DCT routine is given a pointer to a work area of type DCTELEM[]; - /// the DCT is to be performed in-place in that buffer. Type DCTELEM is int - /// for 8-bit samples, int for 12-bit samples. (NOTE: Floating-point DCT - /// implementations use an array of type float, instead.) - /// The DCT inputs are expected to be signed (range +-CENTERJSAMPLE). - /// The DCT outputs are returned scaled up by a factor of 8; they therefore - /// have a range of +-8K for 8-bit data, +-128K for 12-bit data. This - /// convention improves accuracy in integer implementations and saves some - /// work in floating-point ones. - /// - /// Each IDCT routine has its own ideas about the best dct_table element type. - /// - class jpeg_forward_dct - { - private const int FAST_INTEGER_CONST_BITS = 8; - - /* We use the following pre-calculated constants. - * If you change FAST_INTEGER_CONST_BITS you may want to add appropriate values. - * - * Convert a positive real constant to an integer scaled by CONST_SCALE. - * static int FAST_INTEGER_FIX(double x) - *{ - * return ((int) ((x) * (((int) 1) << FAST_INTEGER_CONST_BITS) + 0.5)); - *} - */ - private const int FAST_INTEGER_FIX_0_382683433 = 98; /* FIX(0.382683433) */ - private const int FAST_INTEGER_FIX_0_541196100 = 139; /* FIX(0.541196100) */ - private const int FAST_INTEGER_FIX_0_707106781 = 181; /* FIX(0.707106781) */ - private const int FAST_INTEGER_FIX_1_306562965 = 334; /* FIX(1.306562965) */ - - private const int SLOW_INTEGER_CONST_BITS = 13; - private const int SLOW_INTEGER_PASS1_BITS = 2; - - /* We use the following pre-calculated constants. - * If you change SLOW_INTEGER_CONST_BITS you may want to add appropriate values. - * - * Convert a positive real constant to an integer scaled by CONST_SCALE. - * - * static int SLOW_INTEGER_FIX(double x) - * { - * return ((int) ((x) * (((int) 1) << SLOW_INTEGER_CONST_BITS) + 0.5)); - * } - */ - private const int SLOW_INTEGER_FIX_0_298631336 = 2446; /* FIX(0.298631336) */ - private const int SLOW_INTEGER_FIX_0_390180644 = 3196; /* FIX(0.390180644) */ - private const int SLOW_INTEGER_FIX_0_541196100 = 4433; /* FIX(0.541196100) */ - private const int SLOW_INTEGER_FIX_0_765366865 = 6270; /* FIX(0.765366865) */ - private const int SLOW_INTEGER_FIX_0_899976223 = 7373; /* FIX(0.899976223) */ - private const int SLOW_INTEGER_FIX_1_175875602 = 9633; /* FIX(1.175875602) */ - private const int SLOW_INTEGER_FIX_1_501321110 = 12299; /* FIX(1.501321110) */ - private const int SLOW_INTEGER_FIX_1_847759065 = 15137; /* FIX(1.847759065) */ - private const int SLOW_INTEGER_FIX_1_961570560 = 16069; /* FIX(1.961570560) */ - private const int SLOW_INTEGER_FIX_2_053119869 = 16819; /* FIX(2.053119869) */ - private const int SLOW_INTEGER_FIX_2_562915447 = 20995; /* FIX(2.562915447) */ - private const int SLOW_INTEGER_FIX_3_072711026 = 25172; /* FIX(3.072711026) */ - - /* For AA&N IDCT method, divisors are equal to quantization - * coefficients scaled by scalefactor[row]*scalefactor[col], where - * scalefactor[0] = 1 - * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 - * We apply a further scale factor of 8. - */ - private const int CONST_BITS = 14; - - /* precomputed values scaled up by 14 bits */ - private static short[] aanscales = { - 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 22725, 31521, 29692, 26722, 22725, 17855, - 12299, 6270, 21407, 29692, 27969, 25172, 21407, 16819, 11585, - 5906, 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, - 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 12873, - 17855, 16819, 15137, 12873, 10114, 6967, 3552, 8867, 12299, - 11585, 10426, 8867, 6967, 4799, 2446, 4520, 6270, 5906, 5315, - 4520, 3552, 2446, 1247 }; - - /* For float AA&N IDCT method, divisors are equal to quantization - * coefficients scaled by scalefactor[row]*scalefactor[col], where - * scalefactor[0] = 1 - * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 - * We apply a further scale factor of 8. - * What's actually stored is 1/divisor so that the inner loop can - * use a multiplication rather than a division. - */ - private static double[] aanscalefactor = { - 1.0, 1.387039845, 1.306562965, 1.175875602, 1.0, - 0.785694958, 0.541196100, 0.275899379 }; - - private jpeg_compress_struct m_cinfo; - private bool m_useSlowMethod; - private bool m_useFloatMethod; - - /* The actual post-DCT divisors --- not identical to the quant table - * entries, because of scaling (especially for an unnormalized DCT). - * Each table is given in normal array order. - */ - private int[][] m_divisors = new int [JpegConstants.NUM_QUANT_TBLS][]; - - /* Same as above for the floating-point case. */ - private float[][] m_float_divisors = new float[JpegConstants.NUM_QUANT_TBLS][]; - - public jpeg_forward_dct(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - - switch (cinfo.m_dct_method) - { - case J_DCT_METHOD.JDCT_ISLOW: - m_useFloatMethod = false; - m_useSlowMethod = true; - break; - case J_DCT_METHOD.JDCT_IFAST: - m_useFloatMethod = false; - m_useSlowMethod = false; - break; - case J_DCT_METHOD.JDCT_FLOAT: - m_useFloatMethod = true; - break; - default: - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); - break; - } - - /* Mark divisor tables unallocated */ - for (int i = 0; i < JpegConstants.NUM_QUANT_TBLS; i++) - { - m_divisors[i] = null; - m_float_divisors[i] = null; - } - } - - /// - /// Initialize for a processing pass. - /// Verify that all referenced Q-tables are present, and set up - /// the divisor table for each one. - /// In the current implementation, DCT of all components is done during - /// the first pass, even if only some components will be output in the - /// first scan. Hence all components should be examined here. - /// - public virtual void start_pass() - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - int qtblno = m_cinfo.Component_info[ci].Quant_tbl_no; - - /* Make sure specified quantization table is present */ - if (qtblno < 0 || qtblno >= JpegConstants.NUM_QUANT_TBLS || m_cinfo.m_quant_tbl_ptrs[qtblno] == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, qtblno); - - JQUANT_TBL qtbl = m_cinfo.m_quant_tbl_ptrs[qtblno]; - - /* Compute divisors for this quant table */ - /* We may do this more than once for same table, but it's not a big deal */ - int i = 0; - switch (m_cinfo.m_dct_method) - { - case J_DCT_METHOD.JDCT_ISLOW: - /* For LL&M IDCT method, divisors are equal to raw quantization - * coefficients multiplied by 8 (to counteract scaling). - */ - if (m_divisors[qtblno] == null) - m_divisors[qtblno] = new int [JpegConstants.DCTSIZE2]; - - for (i = 0; i < JpegConstants.DCTSIZE2; i++) - m_divisors[qtblno][i] = ((int)qtbl.quantval[i]) << 3; - - break; - case J_DCT_METHOD.JDCT_IFAST: - if (m_divisors[qtblno] == null) - m_divisors[qtblno] = new int [JpegConstants.DCTSIZE2]; - - for (i = 0; i < JpegConstants.DCTSIZE2; i++) - m_divisors[qtblno][i] = JpegUtils.DESCALE((int)qtbl.quantval[i] * (int)aanscales[i], CONST_BITS - 3); - break; - case J_DCT_METHOD.JDCT_FLOAT: - if (m_float_divisors[qtblno] == null) - m_float_divisors[qtblno] = new float [JpegConstants.DCTSIZE2]; - - float[] fdtbl = m_float_divisors[qtblno]; - i = 0; - for (int row = 0; row < JpegConstants.DCTSIZE; row++) - { - for (int col = 0; col < JpegConstants.DCTSIZE; col++) - { - fdtbl[i] = (float)(1.0 / (((double) qtbl.quantval[i] * aanscalefactor[row] * aanscalefactor[col] * 8.0))); - i++; - } - } - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); - break; - } - } - } - - /// - /// Perform forward DCT on one or more blocks of a component. - /// - /// The input samples are taken from the sample_data[] array starting at - /// position start_row/start_col, and moving to the right for any additional - /// blocks. The quantized coefficients are returned in coef_blocks[]. - /// - public virtual void forward_DCT(int quant_tbl_no, byte[][] sample_data, JBLOCK[] coef_blocks, int start_row, int start_col, int num_blocks) - { - if (m_useFloatMethod) - forwardDCTFloatImpl(quant_tbl_no, sample_data, coef_blocks, start_row, start_col, num_blocks); - else - forwardDCTImpl(quant_tbl_no, sample_data, coef_blocks, start_row, start_col, num_blocks); - } - - // This version is used for integer DCT implementations. - private void forwardDCTImpl(int quant_tbl_no, byte[][] sample_data, JBLOCK[] coef_blocks, int start_row, int start_col, int num_blocks) - { - /* This routine is heavily used, so it's worth coding it tightly. */ - int[] workspace = new int [JpegConstants.DCTSIZE2]; /* work area for FDCT subroutine */ - for (int bi = 0; bi < num_blocks; bi++, start_col += JpegConstants.DCTSIZE) - { - /* Load data into workspace, applying unsigned->signed conversion */ - int workspaceIndex = 0; - for (int elemr = 0; elemr < JpegConstants.DCTSIZE; elemr++) - { - for (int column = 0; column < JpegConstants.DCTSIZE; column++) - { - workspace[workspaceIndex] = (int)sample_data[start_row + elemr][start_col + column] - JpegConstants.CENTERJSAMPLE; - workspaceIndex++; - } - } - - /* Perform the DCT */ - if (m_useSlowMethod) - jpeg_fdct_islow(workspace); - else - jpeg_fdct_ifast(workspace); - - /* Quantize/descale the coefficients, and store into coef_blocks[] */ - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - { - int qval = m_divisors[quant_tbl_no][i]; - int temp = workspace[i]; - - if (temp < 0) - { - temp = -temp; - temp += qval >> 1; /* for rounding */ - - if (temp >= qval) - temp /= qval; - else - temp = 0; - - temp = -temp; - } - else - { - temp += qval >> 1; /* for rounding */ - - if (temp >= qval) - temp /= qval; - else - temp = 0; - } - - coef_blocks[bi][i] = (short) temp; - } - } - } - - // This version is used for floating-point DCT implementations. - private void forwardDCTFloatImpl(int quant_tbl_no, byte[][] sample_data, JBLOCK[] coef_blocks, int start_row, int start_col, int num_blocks) - { - /* This routine is heavily used, so it's worth coding it tightly. */ - float[] workspace = new float [JpegConstants.DCTSIZE2]; /* work area for FDCT subroutine */ - for (int bi = 0; bi < num_blocks; bi++, start_col += JpegConstants.DCTSIZE) - { - /* Load data into workspace, applying unsigned->signed conversion */ - int workspaceIndex = 0; - for (int elemr = 0; elemr < JpegConstants.DCTSIZE; elemr++) - { - for (int column = 0; column < JpegConstants.DCTSIZE; column++) - { - workspace[workspaceIndex] = (float)((int)sample_data[start_row + elemr][start_col + column] - JpegConstants.CENTERJSAMPLE); - workspaceIndex++; - } - } - - /* Perform the DCT */ - jpeg_fdct_float(workspace); - - /* Quantize/descale the coefficients, and store into coef_blocks[] */ - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - { - /* Apply the quantization and scaling factor */ - float temp = workspace[i] * m_float_divisors[quant_tbl_no][i]; - - /* Round to nearest integer. - * Since C does not specify the direction of rounding for negative - * quotients, we have to force the dividend positive for portability. - * The maximum coefficient size is +-16K (for 12-bit data), so this - * code should work for either 16-bit or 32-bit ints. - */ - coef_blocks[bi][i] = (short)((int)(temp + (float)16384.5) - 16384); - } - } - } - - /// - /// Perform the forward DCT on one block of samples. - /// NOTE: this code only copes with 8x8 DCTs. - /// - /// A floating-point implementation of the - /// forward DCT (Discrete Cosine Transform). - /// - /// This implementation should be more accurate than either of the integer - /// DCT implementations. However, it may not give the same results on all - /// machines because of differences in roundoff behavior. Speed will depend - /// on the hardware's floating point capacity. - /// - /// A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT - /// on each column. Direct algorithms are also available, but they are - /// much more complex and seem not to be any faster when reduced to code. - /// - /// This implementation is based on Arai, Agui, and Nakajima's algorithm for - /// scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in - /// Japanese, but the algorithm is described in the Pennebaker & Mitchell - /// JPEG textbook (see REFERENCES section in file README). The following code - /// is based directly on figure 4-8 in P&M. - /// While an 8-point DCT cannot be done in less than 11 multiplies, it is - /// possible to arrange the computation so that many of the multiplies are - /// simple scalings of the final outputs. These multiplies can then be - /// folded into the multiplications or divisions by the JPEG quantization - /// table entries. The AA&N method leaves only 5 multiplies and 29 adds - /// to be done in the DCT itself. - /// The primary disadvantage of this method is that with a fixed-point - /// implementation, accuracy is lost due to imprecise representation of the - /// scaled quantization values. However, that problem does not arise if - /// we use floating point arithmetic. - /// - private static void jpeg_fdct_float(float[] data) - { - /* Pass 1: process rows. */ - int dataIndex = 0; - for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) - { - float tmp0 = data[dataIndex + 0] + data[dataIndex + 7]; - float tmp7 = data[dataIndex + 0] - data[dataIndex + 7]; - float tmp1 = data[dataIndex + 1] + data[dataIndex + 6]; - float tmp6 = data[dataIndex + 1] - data[dataIndex + 6]; - float tmp2 = data[dataIndex + 2] + data[dataIndex + 5]; - float tmp5 = data[dataIndex + 2] - data[dataIndex + 5]; - float tmp3 = data[dataIndex + 3] + data[dataIndex + 4]; - float tmp4 = data[dataIndex + 3] - data[dataIndex + 4]; - - /* Even part */ - - float tmp10 = tmp0 + tmp3; /* phase 2 */ - float tmp13 = tmp0 - tmp3; - float tmp11 = tmp1 + tmp2; - float tmp12 = tmp1 - tmp2; - - data[dataIndex + 0] = tmp10 + tmp11; /* phase 3 */ - data[dataIndex + 4] = tmp10 - tmp11; - - float z1 = (tmp12 + tmp13) * ((float)0.707106781); /* c4 */ - data[dataIndex + 2] = tmp13 + z1; /* phase 5 */ - data[dataIndex + 6] = tmp13 - z1; - - /* Odd part */ - - tmp10 = tmp4 + tmp5; /* phase 2 */ - tmp11 = tmp5 + tmp6; - tmp12 = tmp6 + tmp7; - - /* The rotator is modified from fig 4-8 to avoid extra negations. */ - float z5 = (tmp10 - tmp12) * ((float)0.382683433); /* c6 */ - float z2 = ((float)0.541196100) * tmp10 + z5; /* c2-c6 */ - float z4 = ((float)1.306562965) * tmp12 + z5; /* c2+c6 */ - float z3 = tmp11 * ((float)0.707106781); /* c4 */ - - float z11 = tmp7 + z3; /* phase 5 */ - float z13 = tmp7 - z3; - - data[dataIndex + 5] = z13 + z2; /* phase 6 */ - data[dataIndex + 3] = z13 - z2; - data[dataIndex + 1] = z11 + z4; - data[dataIndex + 7] = z11 - z4; - - dataIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ - } - - /* Pass 2: process columns. */ - - dataIndex = 0; - for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) - { - float tmp0 = data[dataIndex + JpegConstants.DCTSIZE * 0] + data[dataIndex + JpegConstants.DCTSIZE * 7]; - float tmp7 = data[dataIndex + JpegConstants.DCTSIZE * 0] - data[dataIndex + JpegConstants.DCTSIZE * 7]; - float tmp1 = data[dataIndex + JpegConstants.DCTSIZE * 1] + data[dataIndex + JpegConstants.DCTSIZE * 6]; - float tmp6 = data[dataIndex + JpegConstants.DCTSIZE * 1] - data[dataIndex + JpegConstants.DCTSIZE * 6]; - float tmp2 = data[dataIndex + JpegConstants.DCTSIZE * 2] + data[dataIndex + JpegConstants.DCTSIZE * 5]; - float tmp5 = data[dataIndex + JpegConstants.DCTSIZE * 2] - data[dataIndex + JpegConstants.DCTSIZE * 5]; - float tmp3 = data[dataIndex + JpegConstants.DCTSIZE * 3] + data[dataIndex + JpegConstants.DCTSIZE * 4]; - float tmp4 = data[dataIndex + JpegConstants.DCTSIZE * 3] - data[dataIndex + JpegConstants.DCTSIZE * 4]; - - /* Even part */ - - float tmp10 = tmp0 + tmp3; /* phase 2 */ - float tmp13 = tmp0 - tmp3; - float tmp11 = tmp1 + tmp2; - float tmp12 = tmp1 - tmp2; - - data[dataIndex + JpegConstants.DCTSIZE * 0] = tmp10 + tmp11; /* phase 3 */ - data[dataIndex + JpegConstants.DCTSIZE * 4] = tmp10 - tmp11; - - float z1 = (tmp12 + tmp13) * ((float)0.707106781); /* c4 */ - data[dataIndex + JpegConstants.DCTSIZE * 2] = tmp13 + z1; /* phase 5 */ - data[dataIndex + JpegConstants.DCTSIZE * 6] = tmp13 - z1; - - /* Odd part */ - - tmp10 = tmp4 + tmp5; /* phase 2 */ - tmp11 = tmp5 + tmp6; - tmp12 = tmp6 + tmp7; - - /* The rotator is modified from fig 4-8 to avoid extra negations. */ - float z5 = (tmp10 - tmp12) * ((float)0.382683433); /* c6 */ - float z2 = ((float)0.541196100) * tmp10 + z5; /* c2-c6 */ - float z4 = ((float)1.306562965) * tmp12 + z5; /* c2+c6 */ - float z3 = tmp11 * ((float)0.707106781); /* c4 */ - - float z11 = tmp7 + z3; /* phase 5 */ - float z13 = tmp7 - z3; - - data[dataIndex + JpegConstants.DCTSIZE * 5] = z13 + z2; /* phase 6 */ - data[dataIndex + JpegConstants.DCTSIZE * 3] = z13 - z2; - data[dataIndex + JpegConstants.DCTSIZE * 1] = z11 + z4; - data[dataIndex + JpegConstants.DCTSIZE * 7] = z11 - z4; - - dataIndex++; /* advance pointer to next column */ - } - } - - /// - /// Perform the forward DCT on one block of samples. - /// NOTE: this code only copes with 8x8 DCTs. - /// This file contains a fast, not so accurate integer implementation of the - /// forward DCT (Discrete Cosine Transform). - /// - /// A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT - /// on each column. Direct algorithms are also available, but they are - /// much more complex and seem not to be any faster when reduced to code. - /// - /// This implementation is based on Arai, Agui, and Nakajima's algorithm for - /// scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in - /// Japanese, but the algorithm is described in the Pennebaker & Mitchell - /// JPEG textbook (see REFERENCES section in file README). The following code - /// is based directly on figure 4-8 in P&M. - /// While an 8-point DCT cannot be done in less than 11 multiplies, it is - /// possible to arrange the computation so that many of the multiplies are - /// simple scalings of the final outputs. These multiplies can then be - /// folded into the multiplications or divisions by the JPEG quantization - /// table entries. The AA&N method leaves only 5 multiplies and 29 adds - /// to be done in the DCT itself. - /// The primary disadvantage of this method is that with fixed-point math, - /// accuracy is lost due to imprecise representation of the scaled - /// quantization values. The smaller the quantization table entry, the less - /// precise the scaled value, so this implementation does worse with high- - /// quality-setting files than with low-quality ones. - /// - /// Scaling decisions are generally the same as in the LL&M algorithm; - /// see jpeg_fdct_islow for more details. However, we choose to descale - /// (right shift) multiplication products as soon as they are formed, - /// rather than carrying additional fractional bits into subsequent additions. - /// This compromises accuracy slightly, but it lets us save a few shifts. - /// More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) - /// everywhere except in the multiplications proper; this saves a good deal - /// of work on 16-bit-int machines. - /// - /// Again to save a few shifts, the intermediate results between pass 1 and - /// pass 2 are not upscaled, but are represented only to integral precision. - /// - /// A final compromise is to represent the multiplicative constants to only - /// 8 fractional bits, rather than 13. This saves some shifting work on some - /// machines, and may also reduce the cost of multiplication (since there - /// are fewer one-bits in the constants). - /// - private static void jpeg_fdct_ifast(int[] data) - { - /* Pass 1: process rows. */ - int dataIndex = 0; - for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) - { - int tmp0 = data[dataIndex + 0] + data[dataIndex + 7]; - int tmp7 = data[dataIndex + 0] - data[dataIndex + 7]; - int tmp1 = data[dataIndex + 1] + data[dataIndex + 6]; - int tmp6 = data[dataIndex + 1] - data[dataIndex + 6]; - int tmp2 = data[dataIndex + 2] + data[dataIndex + 5]; - int tmp5 = data[dataIndex + 2] - data[dataIndex + 5]; - int tmp3 = data[dataIndex + 3] + data[dataIndex + 4]; - int tmp4 = data[dataIndex + 3] - data[dataIndex + 4]; - - /* Even part */ - - int tmp10 = tmp0 + tmp3; /* phase 2 */ - int tmp13 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp12 = tmp1 - tmp2; - - data[dataIndex + 0] = tmp10 + tmp11; /* phase 3 */ - data[dataIndex + 4] = tmp10 - tmp11; - - int z1 = FAST_INTEGER_MULTIPLY(tmp12 + tmp13, FAST_INTEGER_FIX_0_707106781); /* c4 */ - data[dataIndex + 2] = tmp13 + z1; /* phase 5 */ - data[dataIndex + 6] = tmp13 - z1; - - /* Odd part */ - - tmp10 = tmp4 + tmp5; /* phase 2 */ - tmp11 = tmp5 + tmp6; - tmp12 = tmp6 + tmp7; - - /* The rotator is modified from fig 4-8 to avoid extra negations. */ - int z5 = FAST_INTEGER_MULTIPLY(tmp10 - tmp12, FAST_INTEGER_FIX_0_382683433); /* c6 */ - int z2 = FAST_INTEGER_MULTIPLY(tmp10, FAST_INTEGER_FIX_0_541196100) + z5; /* c2-c6 */ - int z4 = FAST_INTEGER_MULTIPLY(tmp12, FAST_INTEGER_FIX_1_306562965) + z5; /* c2+c6 */ - int z3 = FAST_INTEGER_MULTIPLY(tmp11, FAST_INTEGER_FIX_0_707106781); /* c4 */ - - int z11 = tmp7 + z3; /* phase 5 */ - int z13 = tmp7 - z3; - - data[dataIndex + 5] = z13 + z2; /* phase 6 */ - data[dataIndex + 3] = z13 - z2; - data[dataIndex + 1] = z11 + z4; - data[dataIndex + 7] = z11 - z4; - - dataIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ - } - - /* Pass 2: process columns. */ - - dataIndex = 0; - for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) - { - int tmp0 = data[dataIndex + JpegConstants.DCTSIZE * 0] + data[dataIndex + JpegConstants.DCTSIZE * 7]; - int tmp7 = data[dataIndex + JpegConstants.DCTSIZE * 0] - data[dataIndex + JpegConstants.DCTSIZE * 7]; - int tmp1 = data[dataIndex + JpegConstants.DCTSIZE * 1] + data[dataIndex + JpegConstants.DCTSIZE * 6]; - int tmp6 = data[dataIndex + JpegConstants.DCTSIZE * 1] - data[dataIndex + JpegConstants.DCTSIZE * 6]; - int tmp2 = data[dataIndex + JpegConstants.DCTSIZE * 2] + data[dataIndex + JpegConstants.DCTSIZE * 5]; - int tmp5 = data[dataIndex + JpegConstants.DCTSIZE * 2] - data[dataIndex + JpegConstants.DCTSIZE * 5]; - int tmp3 = data[dataIndex + JpegConstants.DCTSIZE * 3] + data[dataIndex + JpegConstants.DCTSIZE * 4]; - int tmp4 = data[dataIndex + JpegConstants.DCTSIZE * 3] - data[dataIndex + JpegConstants.DCTSIZE * 4]; - - /* Even part */ - - int tmp10 = tmp0 + tmp3; /* phase 2 */ - int tmp13 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp12 = tmp1 - tmp2; - - data[dataIndex + JpegConstants.DCTSIZE * 0] = tmp10 + tmp11; /* phase 3 */ - data[dataIndex + JpegConstants.DCTSIZE * 4] = tmp10 - tmp11; - - int z1 = FAST_INTEGER_MULTIPLY(tmp12 + tmp13, FAST_INTEGER_FIX_0_707106781); /* c4 */ - data[dataIndex + JpegConstants.DCTSIZE * 2] = tmp13 + z1; /* phase 5 */ - data[dataIndex + JpegConstants.DCTSIZE * 6] = tmp13 - z1; - - /* Odd part */ - - tmp10 = tmp4 + tmp5; /* phase 2 */ - tmp11 = tmp5 + tmp6; - tmp12 = tmp6 + tmp7; - - /* The rotator is modified from fig 4-8 to avoid extra negations. */ - int z5 = FAST_INTEGER_MULTIPLY(tmp10 - tmp12, FAST_INTEGER_FIX_0_382683433); /* c6 */ - int z2 = FAST_INTEGER_MULTIPLY(tmp10, FAST_INTEGER_FIX_0_541196100) + z5; /* c2-c6 */ - int z4 = FAST_INTEGER_MULTIPLY(tmp12, FAST_INTEGER_FIX_1_306562965) + z5; /* c2+c6 */ - int z3 = FAST_INTEGER_MULTIPLY(tmp11, FAST_INTEGER_FIX_0_707106781); /* c4 */ - - int z11 = tmp7 + z3; /* phase 5 */ - int z13 = tmp7 - z3; - - data[dataIndex + JpegConstants.DCTSIZE * 5] = z13 + z2; /* phase 6 */ - data[dataIndex + JpegConstants.DCTSIZE * 3] = z13 - z2; - data[dataIndex + JpegConstants.DCTSIZE * 1] = z11 + z4; - data[dataIndex + JpegConstants.DCTSIZE * 7] = z11 - z4; - - dataIndex++; /* advance pointer to next column */ - } - } - - /// - /// Perform the forward DCT on one block of samples. - /// NOTE: this code only copes with 8x8 DCTs. - /// - /// A slow-but-accurate integer implementation of the - /// forward DCT (Discrete Cosine Transform). - /// - /// A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT - /// on each column. Direct algorithms are also available, but they are - /// much more complex and seem not to be any faster when reduced to code. - /// - /// This implementation is based on an algorithm described in - /// C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT - /// Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, - /// Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. - /// The primary algorithm described there uses 11 multiplies and 29 adds. - /// We use their alternate method with 12 multiplies and 32 adds. - /// The advantage of this method is that no data path contains more than one - /// multiplication; this allows a very simple and accurate implementation in - /// scaled fixed-point arithmetic, with a minimal number of shifts. - /// - /// The poop on this scaling stuff is as follows: - /// - /// Each 1-D DCT step produces outputs which are a factor of sqrt(N) - /// larger than the true DCT outputs. The final outputs are therefore - /// a factor of N larger than desired; since N=8 this can be cured by - /// a simple right shift at the end of the algorithm. The advantage of - /// this arrangement is that we save two multiplications per 1-D DCT, - /// because the y0 and y4 outputs need not be divided by sqrt(N). - /// In the IJG code, this factor of 8 is removed by the quantization - /// step, NOT here. - /// - /// We have to do addition and subtraction of the integer inputs, which - /// is no problem, and multiplication by fractional constants, which is - /// a problem to do in integer arithmetic. We multiply all the constants - /// by CONST_SCALE and convert them to integer constants (thus retaining - /// SLOW_INTEGER_CONST_BITS bits of precision in the constants). After doing a - /// multiplication we have to divide the product by CONST_SCALE, with proper - /// rounding, to produce the correct output. This division can be done - /// cheaply as a right shift of SLOW_INTEGER_CONST_BITS bits. We postpone shifting - /// as long as possible so that partial sums can be added together with - /// full fractional precision. - /// - /// The outputs of the first pass are scaled up by SLOW_INTEGER_PASS1_BITS bits so that - /// they are represented to better-than-integral precision. These outputs - /// require BITS_IN_JSAMPLE + SLOW_INTEGER_PASS1_BITS + 3 bits; this fits in a 16-bit word - /// with the recommended scaling. (For 12-bit sample data, the intermediate - /// array is int anyway.) - /// - /// To avoid overflow of the 32-bit intermediate results in pass 2, we must - /// have BITS_IN_JSAMPLE + SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS <= 26. Error analysis - /// shows that the values given below are the most effective. - /// - private static void jpeg_fdct_islow(int[] data) - { - /* Pass 1: process rows. */ - /* Note results are scaled up by sqrt(8) compared to a true DCT; */ - /* furthermore, we scale the results by 2**SLOW_INTEGER_PASS1_BITS. */ - int dataIndex = 0; - for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) - { - int tmp0 = data[dataIndex + 0] + data[dataIndex + 7]; - int tmp7 = data[dataIndex + 0] - data[dataIndex + 7]; - int tmp1 = data[dataIndex + 1] + data[dataIndex + 6]; - int tmp6 = data[dataIndex + 1] - data[dataIndex + 6]; - int tmp2 = data[dataIndex + 2] + data[dataIndex + 5]; - int tmp5 = data[dataIndex + 2] - data[dataIndex + 5]; - int tmp3 = data[dataIndex + 3] + data[dataIndex + 4]; - int tmp4 = data[dataIndex + 3] - data[dataIndex + 4]; - - /* Even part per LL&M figure 1 --- note that published figure is faulty; - * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". - */ - - int tmp10 = tmp0 + tmp3; - int tmp13 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp12 = tmp1 - tmp2; - - data[dataIndex + 0] = (tmp10 + tmp11) << SLOW_INTEGER_PASS1_BITS; - data[dataIndex + 4] = (tmp10 - tmp11) << SLOW_INTEGER_PASS1_BITS; - - int z1 = (tmp12 + tmp13) * SLOW_INTEGER_FIX_0_541196100; - data[dataIndex + 2] = JpegUtils.DESCALE(z1 + tmp13 * SLOW_INTEGER_FIX_0_765366865, - SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - data[dataIndex + 6] = JpegUtils.DESCALE(z1 + tmp12 * (-SLOW_INTEGER_FIX_1_847759065), - SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - - /* Odd part per figure 8 --- note paper omits factor of sqrt(2). - * cK represents cos(K*pi/16). - * i0..i3 in the paper are tmp4..tmp7 here. - */ - - z1 = tmp4 + tmp7; - int z2 = tmp5 + tmp6; - int z3 = tmp4 + tmp6; - int z4 = tmp5 + tmp7; - int z5 = (z3 + z4) * SLOW_INTEGER_FIX_1_175875602; /* sqrt(2) * c3 */ - - tmp4 = tmp4 * SLOW_INTEGER_FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */ - tmp5 = tmp5 * SLOW_INTEGER_FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */ - tmp6 = tmp6 * SLOW_INTEGER_FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */ - tmp7 = tmp7 * SLOW_INTEGER_FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */ - z1 = z1 * (-SLOW_INTEGER_FIX_0_899976223); /* sqrt(2) * (c7-c3) */ - z2 = z2 * (-SLOW_INTEGER_FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ - z3 = z3 * (-SLOW_INTEGER_FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ - z4 = z4 * (-SLOW_INTEGER_FIX_0_390180644); /* sqrt(2) * (c5-c3) */ - - z3 += z5; - z4 += z5; - - data[dataIndex + 7] = JpegUtils.DESCALE(tmp4 + z1 + z3, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - data[dataIndex + 5] = JpegUtils.DESCALE(tmp5 + z2 + z4, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - data[dataIndex + 3] = JpegUtils.DESCALE(tmp6 + z2 + z3, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - data[dataIndex + 1] = JpegUtils.DESCALE(tmp7 + z1 + z4, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - - dataIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ - } - - /* Pass 2: process columns. - * We remove the SLOW_INTEGER_PASS1_BITS scaling, but leave the results scaled up - * by an overall factor of 8. - */ - - dataIndex = 0; - for (int ctr = JpegConstants.DCTSIZE - 1; ctr >= 0; ctr--) - { - int tmp0 = data[dataIndex + JpegConstants.DCTSIZE * 0] + data[dataIndex + JpegConstants.DCTSIZE * 7]; - int tmp7 = data[dataIndex + JpegConstants.DCTSIZE * 0] - data[dataIndex + JpegConstants.DCTSIZE * 7]; - int tmp1 = data[dataIndex + JpegConstants.DCTSIZE * 1] + data[dataIndex + JpegConstants.DCTSIZE * 6]; - int tmp6 = data[dataIndex + JpegConstants.DCTSIZE * 1] - data[dataIndex + JpegConstants.DCTSIZE * 6]; - int tmp2 = data[dataIndex + JpegConstants.DCTSIZE * 2] + data[dataIndex + JpegConstants.DCTSIZE * 5]; - int tmp5 = data[dataIndex + JpegConstants.DCTSIZE * 2] - data[dataIndex + JpegConstants.DCTSIZE * 5]; - int tmp3 = data[dataIndex + JpegConstants.DCTSIZE * 3] + data[dataIndex + JpegConstants.DCTSIZE * 4]; - int tmp4 = data[dataIndex + JpegConstants.DCTSIZE * 3] - data[dataIndex + JpegConstants.DCTSIZE * 4]; - - /* Even part per LL&M figure 1 --- note that published figure is faulty; - * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". - */ - - int tmp10 = tmp0 + tmp3; - int tmp13 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp12 = tmp1 - tmp2; - - data[dataIndex + JpegConstants.DCTSIZE * 0] = JpegUtils.DESCALE(tmp10 + tmp11, SLOW_INTEGER_PASS1_BITS); - data[dataIndex + JpegConstants.DCTSIZE * 4] = JpegUtils.DESCALE(tmp10 - tmp11, SLOW_INTEGER_PASS1_BITS); - - int z1 = (tmp12 + tmp13) * SLOW_INTEGER_FIX_0_541196100; - data[dataIndex + JpegConstants.DCTSIZE * 2] = JpegUtils.DESCALE(z1 + tmp13 * SLOW_INTEGER_FIX_0_765366865, - SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); - data[dataIndex + JpegConstants.DCTSIZE * 6] = JpegUtils.DESCALE(z1 + tmp12 * (-SLOW_INTEGER_FIX_1_847759065), - SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); - - /* Odd part per figure 8 --- note paper omits factor of sqrt(2). - * cK represents cos(K*pi/16). - * i0..i3 in the paper are tmp4..tmp7 here. - */ - - z1 = tmp4 + tmp7; - int z2 = tmp5 + tmp6; - int z3 = tmp4 + tmp6; - int z4 = tmp5 + tmp7; - int z5 = (z3 + z4) * SLOW_INTEGER_FIX_1_175875602; /* sqrt(2) * c3 */ - - tmp4 = tmp4 * SLOW_INTEGER_FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */ - tmp5 = tmp5 * SLOW_INTEGER_FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */ - tmp6 = tmp6 * SLOW_INTEGER_FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */ - tmp7 = tmp7 * SLOW_INTEGER_FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */ - z1 = z1 * (-SLOW_INTEGER_FIX_0_899976223); /* sqrt(2) * (c7-c3) */ - z2 = z2 * (-SLOW_INTEGER_FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ - z3 = z3 * (-SLOW_INTEGER_FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ - z4 = z4 * (-SLOW_INTEGER_FIX_0_390180644); /* sqrt(2) * (c5-c3) */ - - z3 += z5; - z4 += z5; - - data[dataIndex + JpegConstants.DCTSIZE * 7] = JpegUtils.DESCALE(tmp4 + z1 + z3, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); - data[dataIndex + JpegConstants.DCTSIZE * 5] = JpegUtils.DESCALE(tmp5 + z2 + z4, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); - data[dataIndex + JpegConstants.DCTSIZE * 3] = JpegUtils.DESCALE(tmp6 + z2 + z3, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); - data[dataIndex + JpegConstants.DCTSIZE * 1] = JpegUtils.DESCALE(tmp7 + z1 + z4, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS); - - dataIndex++; /* advance pointer to next column */ - } - } - - /// - /// Multiply a DCTELEM variable by an int constant, and immediately - /// descale to yield a DCTELEM result. - /// - private static int FAST_INTEGER_MULTIPLY(int var, int c) - { -#if !USE_ACCURATE_ROUNDING - return (JpegUtils.RIGHT_SHIFT((var) * (c), FAST_INTEGER_CONST_BITS)); -#else - return (JpegUtils.DESCALE((var) * (c), FAST_INTEGER_CONST_BITS)); -#endif - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_input_controller.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_input_controller.cs deleted file mode 100644 index ab5546e8..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_input_controller.cs +++ /dev/null @@ -1,398 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains input control logic for the JPEG decompressor. - * These routines are concerned with controlling the decompressor's input - * processing (marker reading and coefficient decoding). - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Input control module - /// - class jpeg_input_controller - { - private jpeg_decompress_struct m_cinfo; - private bool m_consumeData; - private bool m_inheaders; /* true until first SOS is reached */ - private bool m_has_multiple_scans; /* True if file has multiple scans */ - private bool m_eoi_reached; /* True when EOI has been consumed */ - - /// - /// Initialize the input controller module. - /// This is called only once, when the decompression object is created. - /// - public jpeg_input_controller(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Initialize state: can't use reset_input_controller since we don't - * want to try to reset other modules yet. - */ - m_inheaders = true; - } - - public ReadResult consume_input() - { - if (m_consumeData) - return m_cinfo.m_coef.consume_data(); - - return consume_markers(); - } - - /// - /// Reset state to begin a fresh datastream. - /// - public void reset_input_controller() - { - m_consumeData = false; - m_has_multiple_scans = false; /* "unknown" would be better */ - m_eoi_reached = false; - m_inheaders = true; - - /* Reset other modules */ - m_cinfo.m_err.reset_error_mgr(); - m_cinfo.m_marker.reset_marker_reader(); - - /* Reset progression state -- would be cleaner if entropy decoder did this */ - m_cinfo.m_coef_bits = null; - } - - /// - /// Initialize the input modules to read a scan of compressed data. - /// The first call to this is done after initializing - /// the entire decompressor (during jpeg_start_decompress). - /// Subsequent calls come from consume_markers, below. - /// - public void start_input_pass() - { - per_scan_setup(); - latch_quant_tables(); - m_cinfo.m_entropy.start_pass(); - m_cinfo.m_coef.start_input_pass(); - m_consumeData = true; - } - - /// - /// Finish up after inputting a compressed-data scan. - /// This is called by the coefficient controller after it's read all - /// the expected data of the scan. - /// - public void finish_input_pass() - { - m_consumeData = false; - } - - public bool HasMultipleScans() - { - return m_has_multiple_scans; - } - - public bool EOIReached() - { - return m_eoi_reached; - } - - /// - /// Read JPEG markers before, between, or after compressed-data scans. - /// Change state as necessary when a new scan is reached. - /// Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. - /// - /// The consume_input method pointer points either here or to the - /// coefficient controller's consume_data routine, depending on whether - /// we are reading a compressed data segment or inter-segment markers. - /// - private ReadResult consume_markers() - { - ReadResult val; - - if (m_eoi_reached) /* After hitting EOI, read no further */ - return ReadResult.JPEG_REACHED_EOI; - - val = m_cinfo.m_marker.read_markers(); - - switch (val) - { - case ReadResult.JPEG_REACHED_SOS: - /* Found SOS */ - if (m_inheaders) - { - /* 1st SOS */ - initial_setup(); - m_inheaders = false; - /* Note: start_input_pass must be called by jpeg_decomp_master - * before any more input can be consumed. - */ - } - else - { - /* 2nd or later SOS marker */ - if (!m_has_multiple_scans) - { - /* Oops, I wasn't expecting this! */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_EOI_EXPECTED); - } - - m_cinfo.m_inputctl.start_input_pass(); - } - break; - case ReadResult.JPEG_REACHED_EOI: - /* Found EOI */ - m_eoi_reached = true; - if (m_inheaders) - { - /* Tables-only datastream, apparently */ - if (m_cinfo.m_marker.SawSOF()) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_SOF_NO_SOS); - } - else - { - /* Prevent infinite loop in coef ctlr's decompress_data routine - * if user set output_scan_number larger than number of scans. - */ - if (m_cinfo.m_output_scan_number > m_cinfo.m_input_scan_number) - m_cinfo.m_output_scan_number = m_cinfo.m_input_scan_number; - } - break; - case ReadResult.JPEG_SUSPENDED: - break; - } - - return val; - } - - /// - /// Routines to calculate various quantities related to the size of the image. - /// Called once, when first SOS marker is reached - /// - private void initial_setup() - { - /* Make sure image isn't bigger than I can handle */ - if (m_cinfo.m_image_height > JpegConstants.JPEG_MAX_DIMENSION || - m_cinfo.m_image_width > JpegConstants.JPEG_MAX_DIMENSION) - { - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG, (int)JpegConstants.JPEG_MAX_DIMENSION); - - } - - /* For now, precision must match compiled-in value... */ - if (m_cinfo.m_data_precision != JpegConstants.BITS_IN_JSAMPLE) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_PRECISION, m_cinfo.m_data_precision); - - /* Check that number of components won't exceed internal array sizes */ - if (m_cinfo.m_num_components > JpegConstants.MAX_COMPONENTS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_num_components, JpegConstants.MAX_COMPONENTS); - - /* Compute maximum sampling factors; check factor validity */ - m_cinfo.m_max_h_samp_factor = 1; - m_cinfo.m_max_v_samp_factor = 1; - - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - if (m_cinfo.Comp_info[ci].H_samp_factor <= 0 || m_cinfo.Comp_info[ci].H_samp_factor > JpegConstants.MAX_SAMP_FACTOR || - m_cinfo.Comp_info[ci].V_samp_factor <= 0 || m_cinfo.Comp_info[ci].V_samp_factor > JpegConstants.MAX_SAMP_FACTOR) - { - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_SAMPLING); - } - - m_cinfo.m_max_h_samp_factor = Math.Max(m_cinfo.m_max_h_samp_factor, m_cinfo.Comp_info[ci].H_samp_factor); - m_cinfo.m_max_v_samp_factor = Math.Max(m_cinfo.m_max_v_samp_factor, m_cinfo.Comp_info[ci].V_samp_factor); - } - - /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. - * In the full decompressor, this will be overridden jpeg_decomp_master; - * but in the transcoder, jpeg_decomp_master is not used, so we must do it here. - */ - m_cinfo.m_min_DCT_scaled_size = JpegConstants.DCTSIZE; - - /* Compute dimensions of components */ - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - m_cinfo.Comp_info[ci].DCT_scaled_size = JpegConstants.DCTSIZE; - - /* Size in DCT blocks */ - m_cinfo.Comp_info[ci].Width_in_blocks = JpegUtils.jdiv_round_up( - m_cinfo.m_image_width * m_cinfo.Comp_info[ci].H_samp_factor, - m_cinfo.m_max_h_samp_factor * JpegConstants.DCTSIZE); - - m_cinfo.Comp_info[ci].height_in_blocks = JpegUtils.jdiv_round_up( - m_cinfo.m_image_height * m_cinfo.Comp_info[ci].V_samp_factor, - m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); - - /* downsampled_width and downsampled_height will also be overridden by - * jpeg_decomp_master if we are doing full decompression. The transcoder library - * doesn't use these values, but the calling application might. - */ - /* Size in samples */ - m_cinfo.Comp_info[ci].downsampled_width = JpegUtils.jdiv_round_up( - m_cinfo.m_image_width * m_cinfo.Comp_info[ci].H_samp_factor, - m_cinfo.m_max_h_samp_factor); - - m_cinfo.Comp_info[ci].downsampled_height = JpegUtils.jdiv_round_up( - m_cinfo.m_image_height * m_cinfo.Comp_info[ci].V_samp_factor, - m_cinfo.m_max_v_samp_factor); - - /* Mark component needed, until color conversion says otherwise */ - m_cinfo.Comp_info[ci].component_needed = true; - - /* Mark no quantization table yet saved for component */ - m_cinfo.Comp_info[ci].quant_table = null; - } - - /* Compute number of fully interleaved MCU rows. */ - m_cinfo.m_total_iMCU_rows = JpegUtils.jdiv_round_up( - m_cinfo.m_image_height, m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); - - /* Decide whether file contains multiple scans */ - if (m_cinfo.m_comps_in_scan < m_cinfo.m_num_components || m_cinfo.m_progressive_mode) - m_cinfo.m_inputctl.m_has_multiple_scans = true; - else - m_cinfo.m_inputctl.m_has_multiple_scans = false; - } - - /// - /// Save away a copy of the Q-table referenced by each component present - /// in the current scan, unless already saved during a prior scan. - /// - /// In a multiple-scan JPEG file, the encoder could assign different components - /// the same Q-table slot number, but change table definitions between scans - /// so that each component uses a different Q-table. (The IJG encoder is not - /// currently capable of doing this, but other encoders might.) Since we want - /// to be able to dequantize all the components at the end of the file, this - /// means that we have to save away the table actually used for each component. - /// We do this by copying the table at the start of the first scan containing - /// the component. - /// The JPEG spec prohibits the encoder from changing the contents of a Q-table - /// slot between scans of a component using that slot. If the encoder does so - /// anyway, this decoder will simply use the Q-table values that were current - /// at the start of the first scan for the component. - /// - /// The decompressor output side looks only at the saved quant tables, - /// not at the current Q-table slots. - /// - private void latch_quant_tables() - { - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - - /* No work if we already saved Q-table for this component */ - if (componentInfo.quant_table != null) - continue; - - /* Make sure specified quantization table is present */ - int qtblno = componentInfo.Quant_tbl_no; - if (qtblno < 0 || qtblno >= JpegConstants.NUM_QUANT_TBLS || m_cinfo.m_quant_tbl_ptrs[qtblno] == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, qtblno); - - /* OK, save away the quantization table */ - JQUANT_TBL qtbl = new JQUANT_TBL(); - Buffer.BlockCopy(m_cinfo.m_quant_tbl_ptrs[qtblno].quantval, 0, - qtbl.quantval, 0, qtbl.quantval.Length * sizeof(short)); - qtbl.Sent_table = m_cinfo.m_quant_tbl_ptrs[qtblno].Sent_table; - componentInfo.quant_table = qtbl; - m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]] = componentInfo; - } - } - - /// - /// Do computations that are needed before processing a JPEG scan - /// cinfo.comps_in_scan and cinfo.cur_comp_info[] were set from SOS marker - /// - private void per_scan_setup() - { - if (m_cinfo.m_comps_in_scan == 1) - { - /* Noninterleaved (single-component) scan */ - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[0]]; - - /* Overall image size in MCUs */ - m_cinfo.m_MCUs_per_row = componentInfo.Width_in_blocks; - m_cinfo.m_MCU_rows_in_scan = componentInfo.height_in_blocks; - - /* For noninterleaved scan, always one block per MCU */ - componentInfo.MCU_width = 1; - componentInfo.MCU_height = 1; - componentInfo.MCU_blocks = 1; - componentInfo.MCU_sample_width = componentInfo.DCT_scaled_size; - componentInfo.last_col_width = 1; - - /* For noninterleaved scans, it is convenient to define last_row_height - * as the number of block rows present in the last iMCU row. - */ - int tmp = componentInfo.height_in_blocks % componentInfo.V_samp_factor; - if (tmp == 0) - tmp = componentInfo.V_samp_factor; - componentInfo.last_row_height = tmp; - m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[0]] = componentInfo; - - /* Prepare array describing MCU composition */ - m_cinfo.m_blocks_in_MCU = 1; - m_cinfo.m_MCU_membership[0] = 0; - } - else - { - /* Interleaved (multi-component) scan */ - if (m_cinfo.m_comps_in_scan <= 0 || m_cinfo.m_comps_in_scan > JpegConstants.MAX_COMPS_IN_SCAN) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_cinfo.m_comps_in_scan, JpegConstants.MAX_COMPS_IN_SCAN); - - /* Overall image size in MCUs */ - m_cinfo.m_MCUs_per_row = JpegUtils.jdiv_round_up( - m_cinfo.m_image_width, m_cinfo.m_max_h_samp_factor * JpegConstants.DCTSIZE); - - m_cinfo.m_MCU_rows_in_scan = JpegUtils.jdiv_round_up( - m_cinfo.m_image_height, m_cinfo.m_max_v_samp_factor * JpegConstants.DCTSIZE); - - m_cinfo.m_blocks_in_MCU = 0; - - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - - /* Sampling factors give # of blocks of component in each MCU */ - componentInfo.MCU_width = componentInfo.H_samp_factor; - componentInfo.MCU_height = componentInfo.V_samp_factor; - componentInfo.MCU_blocks = componentInfo.MCU_width * componentInfo.MCU_height; - componentInfo.MCU_sample_width = componentInfo.MCU_width * componentInfo.DCT_scaled_size; - - /* Figure number of non-dummy blocks in last MCU column & row */ - int tmp = componentInfo.Width_in_blocks % componentInfo.MCU_width; - if (tmp == 0) - tmp = componentInfo.MCU_width; - componentInfo.last_col_width = tmp; - - tmp = componentInfo.height_in_blocks % componentInfo.MCU_height; - if (tmp == 0) - tmp = componentInfo.MCU_height; - componentInfo.last_row_height = tmp; - - /* Prepare array describing MCU composition */ - int mcublks = componentInfo.MCU_blocks; - if (m_cinfo.m_blocks_in_MCU + mcublks > JpegConstants.D_MAX_BLOCKS_IN_MCU) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_MCU_SIZE); - - m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]] = componentInfo; - - while (mcublks-- > 0) - m_cinfo.m_MCU_membership[m_cinfo.m_blocks_in_MCU++] = ci; - } - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_inverse_dct.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_inverse_dct.cs deleted file mode 100644 index 5459e547..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_inverse_dct.cs +++ /dev/null @@ -1,1486 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the inverse-DCT management logic. - * This code selects a particular IDCT implementation to be used, - * and it performs related housekeeping chores. No code in this file - * is executed per IDCT step, only during output pass setup. - * - * Note that the IDCT routines are responsible for performing coefficient - * dequantization as well as the IDCT proper. This module sets up the - * dequantization multiplier table needed by the IDCT routine. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// An inverse DCT routine is given a pointer to the input JBLOCK and a pointer - /// to an output sample array. The routine must dequantize the input data as - /// well as perform the IDCT; for dequantization, it uses the multiplier table - /// pointed to by componentInfo.dct_table. The output data is to be placed into the - /// sample array starting at a specified column. (Any row offset needed will - /// be applied to the array pointer before it is passed to the IDCT code) - /// Note that the number of samples emitted by the IDCT routine is - /// DCT_scaled_size * DCT_scaled_size. - /// - /// Each IDCT routine has its own ideas about the best dct_table element type. - /// - /// The decompressor input side saves away the appropriate - /// quantization table for each component at the start of the first scan - /// involving that component. (This is necessary in order to correctly - /// decode files that reuse Q-table slots.) - /// When we are ready to make an output pass, the saved Q-table is converted - /// to a multiplier table that will actually be used by the IDCT routine. - /// The multiplier table contents are IDCT-method-dependent. To support - /// application changes in IDCT method between scans, we can remake the - /// multiplier tables if necessary. - /// In buffered-image mode, the first output pass may occur before any data - /// has been seen for some components, and thus before their Q-tables have - /// been saved away. To handle this case, multiplier tables are preset - /// to zeroes; the result of the IDCT will be a neutral gray level. - /// - class jpeg_inverse_dct - { - private const int IFAST_SCALE_BITS = 2; /* fractional bits in scale factors */ - - /* - * Each IDCT routine is responsible for range-limiting its results and - * converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could - * be quite far out of range if the input data is corrupt, so a bulletproof - * range-limiting step is required. We use a mask-and-table-lookup method - * to do the combined operations quickly. See the comments with - * prepare_range_limit_table (in jdmaster.c) for more info. - */ - private const int RANGE_MASK = (JpegConstants.MAXJSAMPLE * 4 + 3); /* 2 bits wider than legal samples */ - - private const int SLOW_INTEGER_CONST_BITS = 13; - private const int SLOW_INTEGER_PASS1_BITS = 2; - - /* We use the following pre-calculated constants. - * If you change SLOW_INTEGER_CONST_BITS you may want to add appropriate values. - * - * Convert a positive real constant to an integer scaled by CONST_SCALE. - * static int SLOW_INTEGER_FIX(double x) - * { - * return ((int) ((x) * (((int) 1) << SLOW_INTEGER_CONST_BITS) + 0.5)); - * } - */ - - private const int SLOW_INTEGER_FIX_0_298631336 = 2446; /* SLOW_INTEGER_FIX(0.298631336) */ - private const int SLOW_INTEGER_FIX_0_390180644 = 3196; /* SLOW_INTEGER_FIX(0.390180644) */ - private const int SLOW_INTEGER_FIX_0_541196100 = 4433; /* SLOW_INTEGER_FIX(0.541196100) */ - private const int SLOW_INTEGER_FIX_0_765366865 = 6270; /* SLOW_INTEGER_FIX(0.765366865) */ - private const int SLOW_INTEGER_FIX_0_899976223 = 7373; /* SLOW_INTEGER_FIX(0.899976223) */ - private const int SLOW_INTEGER_FIX_1_175875602 = 9633; /* SLOW_INTEGER_FIX(1.175875602) */ - private const int SLOW_INTEGER_FIX_1_501321110 = 12299; /* SLOW_INTEGER_FIX(1.501321110) */ - private const int SLOW_INTEGER_FIX_1_847759065 = 15137; /* SLOW_INTEGER_FIX(1.847759065) */ - private const int SLOW_INTEGER_FIX_1_961570560 = 16069; /* SLOW_INTEGER_FIX(1.961570560) */ - private const int SLOW_INTEGER_FIX_2_053119869 = 16819; /* SLOW_INTEGER_FIX(2.053119869) */ - private const int SLOW_INTEGER_FIX_2_562915447 = 20995; /* SLOW_INTEGER_FIX(2.562915447) */ - private const int SLOW_INTEGER_FIX_3_072711026 = 25172; /* SLOW_INTEGER_FIX(3.072711026) */ - - private const int FAST_INTEGER_CONST_BITS = 8; - private const int FAST_INTEGER_PASS1_BITS = 2; - - /* We use the following pre-calculated constants. - * If you change FAST_INTEGER_CONST_BITS you may want to add appropriate values. - */ - private const int FAST_INTEGER_FIX_1_082392200 = 277; /* FAST_INTEGER_FIX(1.082392200) */ - private const int FAST_INTEGER_FIX_1_414213562 = 362; /* FAST_INTEGER_FIX(1.414213562) */ - private const int FAST_INTEGER_FIX_1_847759065 = 473; /* FAST_INTEGER_FIX(1.847759065) */ - private const int FAST_INTEGER_FIX_2_613125930 = 669; /* FAST_INTEGER_FIX(2.613125930) */ - - private const int REDUCED_CONST_BITS = 13; - private const int REDUCED_PASS1_BITS = 2; - - /* We use the following pre-calculated constants. - * If you change REDUCED_CONST_BITS you may want to add appropriate values. - * Convert a positive real constant to an integer scaled by CONST_SCALE. - * static int REDUCED_FIX(double x) - * { - * return ((int) ((x) * (((int) 1) << REDUCED_CONST_BITS) + 0.5)); - * } - */ - - private const int REDUCED_FIX_0_211164243 = 1730; /* REDUCED_FIX(0.211164243) */ - private const int REDUCED_FIX_0_509795579 = 4176; /* REDUCED_FIX(0.509795579) */ - private const int REDUCED_FIX_0_601344887 = 4926; /* REDUCED_FIX(0.601344887) */ - private const int REDUCED_FIX_0_720959822 = 5906; /* REDUCED_FIX(0.720959822) */ - private const int REDUCED_FIX_0_765366865 = 6270; /* REDUCED_FIX(0.765366865) */ - private const int REDUCED_FIX_0_850430095 = 6967; /* REDUCED_FIX(0.850430095) */ - private const int REDUCED_FIX_0_899976223 = 7373; /* REDUCED_FIX(0.899976223) */ - private const int REDUCED_FIX_1_061594337 = 8697; /* REDUCED_FIX(1.061594337) */ - private const int REDUCED_FIX_1_272758580 = 10426; /* REDUCED_FIX(1.272758580) */ - private const int REDUCED_FIX_1_451774981 = 11893; /* REDUCED_FIX(1.451774981) */ - private const int REDUCED_FIX_1_847759065 = 15137; /* REDUCED_FIX(1.847759065) */ - private const int REDUCED_FIX_2_172734803 = 17799; /* REDUCED_FIX(2.172734803) */ - private const int REDUCED_FIX_2_562915447 = 20995; /* REDUCED_FIX(2.562915447) */ - private const int REDUCED_FIX_3_624509785 = 29692; /* REDUCED_FIX(3.624509785) */ - - /* precomputed values scaled up by 14 bits */ - private static short[] aanscales = - { - 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 22725, 31521, 29692, 26722, 22725, 17855, - 12299, 6270, 21407, 29692, 27969, 25172, 21407, 16819, 11585, - 5906, 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, - 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, 12873, - 17855, 16819, 15137, 12873, 10114, 6967, 3552, 8867, 12299, - 11585, 10426, 8867, 6967, 4799, 2446, 4520, 6270, 5906, 5315, - 4520, 3552, 2446, 1247 - }; - - private const int CONST_BITS = 14; - - private static double[] aanscalefactor = - { - 1.0, 1.387039845, 1.306562965, 1.175875602, 1.0, - 0.785694958, 0.541196100, 0.275899379 - }; - - private enum InverseMethod - { - Unknown, - idct_1x1_method, - idct_2x2_method, - idct_4x4_method, - idct_islow_method, - idct_ifast_method, - idct_float_method - } - - /* It is useful to allow each component to have a separate IDCT method. */ - private InverseMethod[] m_inverse_DCT_method = new InverseMethod[JpegConstants.MAX_COMPONENTS]; - - /* Allocated multiplier tables: big enough for any supported variant */ - private class multiplier_table - { - public int[] int_array = new int[JpegConstants.DCTSIZE2]; - public float[] float_array = new float[JpegConstants.DCTSIZE2]; - }; - - private multiplier_table[] m_dctTables; - - private jpeg_decompress_struct m_cinfo; - - /* This array contains the IDCT method code that each multiplier table - * is currently set up for, or -1 if it's not yet set up. - * The actual multiplier tables are pointed to by dct_table in the - * per-component comp_info structures. - */ - private int[] m_cur_method = new int[JpegConstants.MAX_COMPONENTS]; - - private ComponentBuffer m_componentBuffer; - - public jpeg_inverse_dct(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - m_dctTables = new multiplier_table[cinfo.m_num_components]; - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - /* Allocate and pre-zero a multiplier table for each component */ - m_dctTables[ci] = new multiplier_table(); - - /* Mark multiplier table not yet set up for any method */ - m_cur_method[ci] = -1; - } - } - - /// - /// Prepare for an output pass. - /// Here we select the proper IDCT routine for each component and build - /// a matching multiplier table. - /// - public void start_pass() - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[ci]; - - InverseMethod im = InverseMethod.Unknown; - int method = 0; - /* Select the proper IDCT routine for this component's scaling */ - switch (componentInfo.DCT_scaled_size) - { - case 1: - im = InverseMethod.idct_1x1_method; - method = (int)J_DCT_METHOD.JDCT_ISLOW; /* jidctred uses islow-style table */ - break; - case 2: - im = InverseMethod.idct_2x2_method; - method = (int)J_DCT_METHOD.JDCT_ISLOW; /* jidctred uses islow-style table */ - break; - case 4: - im = InverseMethod.idct_4x4_method; - method = (int)J_DCT_METHOD.JDCT_ISLOW; /* jidctred uses islow-style table */ - break; - case JpegConstants.DCTSIZE: - switch (m_cinfo.m_dct_method) - { - case J_DCT_METHOD.JDCT_ISLOW: - im = InverseMethod.idct_islow_method; - method = (int)J_DCT_METHOD.JDCT_ISLOW; - break; - case J_DCT_METHOD.JDCT_IFAST: - im = InverseMethod.idct_ifast_method; - method = (int)J_DCT_METHOD.JDCT_IFAST; - break; - case J_DCT_METHOD.JDCT_FLOAT: - im = InverseMethod.idct_float_method; - method = (int)J_DCT_METHOD.JDCT_FLOAT; - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); - break; - } - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCTSIZE, componentInfo.DCT_scaled_size); - break; - } - - m_inverse_DCT_method[ci] = im; - - /* Create multiplier table from quant table. - * However, we can skip this if the component is uninteresting - * or if we already built the table. Also, if no quant table - * has yet been saved for the component, we leave the - * multiplier table all-zero; we'll be reading zeroes from the - * coefficient controller's buffer anyway. - */ - if (!componentInfo.component_needed || m_cur_method[ci] == method) - continue; - - if (componentInfo.quant_table == null) - { - /* happens if no data yet for component */ - continue; - } - - m_cur_method[ci] = method; - switch ((J_DCT_METHOD)method) - { - case J_DCT_METHOD.JDCT_ISLOW: - /* For LL&M IDCT method, multipliers are equal to raw quantization - * coefficients, but are stored as ints to ensure access efficiency. - */ - int[] ismtbl = m_dctTables[ci].int_array; - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - ismtbl[i] = componentInfo.quant_table.quantval[i]; - break; - - case J_DCT_METHOD.JDCT_IFAST: - /* For AA&N IDCT method, multipliers are equal to quantization - * coefficients scaled by scalefactor[row]*scalefactor[col], where - * scalefactor[0] = 1 - * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 - * For integer operation, the multiplier table is to be scaled by - * IFAST_SCALE_BITS. - */ - int[] ifmtbl = m_dctTables[ci].int_array; - - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - { - ifmtbl[i] = JpegUtils.DESCALE((int)componentInfo.quant_table.quantval[i] * (int)aanscales[i], CONST_BITS - IFAST_SCALE_BITS); - } - break; - - case J_DCT_METHOD.JDCT_FLOAT: - /* For float AA&N IDCT method, multipliers are equal to quantization - * coefficients scaled by scalefactor[row]*scalefactor[col], where - * scalefactor[0] = 1 - * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 - */ - float[] fmtbl = m_dctTables[ci].float_array; - int ii = 0; - for (int row = 0; row < JpegConstants.DCTSIZE; row++) - { - for (int col = 0; col < JpegConstants.DCTSIZE; col++) - { - fmtbl[ii] = (float) ((double) componentInfo.quant_table.quantval[ii] * aanscalefactor[row] * aanscalefactor[col]); - ii++; - } - } - break; - - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); - break; - } - } - } - - /* Inverse DCT (also performs dequantization) */ - public void inverse(int component_index, short[] coef_block, ComponentBuffer output_buf, int output_row, int output_col) - { - m_componentBuffer = output_buf; - switch (m_inverse_DCT_method[component_index]) - { - case InverseMethod.idct_1x1_method: - jpeg_idct_1x1(component_index, coef_block, output_row, output_col); - break; - case InverseMethod.idct_2x2_method: - jpeg_idct_2x2(component_index, coef_block, output_row, output_col); - break; - case InverseMethod.idct_4x4_method: - jpeg_idct_4x4(component_index, coef_block, output_row, output_col); - break; - case InverseMethod.idct_islow_method: - jpeg_idct_islow(component_index, coef_block, output_row, output_col); - break; - case InverseMethod.idct_ifast_method: - jpeg_idct_ifast(component_index, coef_block, output_row, output_col); - break; - case InverseMethod.idct_float_method: - jpeg_idct_float(component_index, coef_block, output_row, output_col); - break; - case InverseMethod.Unknown: - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); - break; - } - } - - /// - /// Perform dequantization and inverse DCT on one block of coefficients. - /// NOTE: this code only copes with 8x8 DCTs. - /// A slow-but-accurate integer implementation of the - /// inverse DCT (Discrete Cosine Transform). In the IJG code, this routine - /// must also perform dequantization of the input coefficients. - /// - /// A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT - /// on each row (or vice versa, but it's more convenient to emit a row at - /// a time). Direct algorithms are also available, but they are much more - /// complex and seem not to be any faster when reduced to code. - /// - /// This implementation is based on an algorithm described in - /// C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT - /// Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, - /// Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. - /// The primary algorithm described there uses 11 multiplies and 29 adds. - /// We use their alternate method with 12 multiplies and 32 adds. - /// The advantage of this method is that no data path contains more than one - /// multiplication; this allows a very simple and accurate implementation in - /// scaled fixed-point arithmetic, with a minimal number of shifts. - /// - /// The poop on this scaling stuff is as follows: - /// - /// Each 1-D IDCT step produces outputs which are a factor of sqrt(N) - /// larger than the true IDCT outputs. The final outputs are therefore - /// a factor of N larger than desired; since N=8 this can be cured by - /// a simple right shift at the end of the algorithm. The advantage of - /// this arrangement is that we save two multiplications per 1-D IDCT, - /// because the y0 and y4 inputs need not be divided by sqrt(N). - /// - /// We have to do addition and subtraction of the integer inputs, which - /// is no problem, and multiplication by fractional constants, which is - /// a problem to do in integer arithmetic. We multiply all the constants - /// by CONST_SCALE and convert them to integer constants (thus retaining - /// SLOW_INTEGER_CONST_BITS bits of precision in the constants). After doing a - /// multiplication we have to divide the product by CONST_SCALE, with proper - /// rounding, to produce the correct output. This division can be done - /// cheaply as a right shift of SLOW_INTEGER_CONST_BITS bits. We postpone shifting - /// as long as possible so that partial sums can be added together with - /// full fractional precision. - /// - /// The outputs of the first pass are scaled up by SLOW_INTEGER_PASS1_BITS bits so that - /// they are represented to better-than-integral precision. These outputs - /// require BITS_IN_JSAMPLE + SLOW_INTEGER_PASS1_BITS + 3 bits; this fits in a 16-bit word - /// with the recommended scaling. (To scale up 12-bit sample data further, an - /// intermediate int array would be needed.) - /// - /// To avoid overflow of the 32-bit intermediate results in pass 2, we must - /// have BITS_IN_JSAMPLE + SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS <= 26. Error analysis - /// shows that the values given below are the most effective. - /// - private void jpeg_idct_islow(int component_index, short[] coef_block, int output_row, int output_col) - { - /* buffers data between passes */ - int[] workspace = new int[JpegConstants.DCTSIZE2]; - - /* Pass 1: process columns from input, store into work array. */ - /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ - /* furthermore, we scale the results by 2**SLOW_INTEGER_PASS1_BITS. */ - - int coefBlockIndex = 0; - - int[] quantTable = m_dctTables[component_index].int_array; - int quantTableIndex = 0; - - int workspaceIndex = 0; - - for (int ctr = JpegConstants.DCTSIZE; ctr > 0; ctr--) - { - /* Due to quantization, we will usually find that many of the input - * coefficients are zero, especially the AC terms. We can exploit this - * by short-circuiting the IDCT calculation for any column in which all - * the AC terms are zero. In that case each output is equal to the - * DC coefficient (with scale factor as needed). - * With typical images and quantization tables, half or more of the - * column DCT calculations can be simplified this way. - */ - - if (coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 1] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 2] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 3] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 4] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 5] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 6] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 7] == 0) - { - /* AC terms all zero */ - int dcval = SLOW_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 0], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 0]) << SLOW_INTEGER_PASS1_BITS; - - workspace[workspaceIndex + JpegConstants.DCTSIZE * 0] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 1] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 2] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 3] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 4] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 5] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 6] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 7] = dcval; - - /* advance pointers to next column */ - coefBlockIndex++; - quantTableIndex++; - workspaceIndex++; - continue; - } - - /* Even part: reverse the even part of the forward DCT. */ - /* The rotator is sqrt(2)*c(-6). */ - - int z2 = SLOW_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 2], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 2]); - int z3 = SLOW_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 6], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 6]); - - int z1 = (z2 + z3) * SLOW_INTEGER_FIX_0_541196100; - int tmp2 = z1 + z3 * (-SLOW_INTEGER_FIX_1_847759065); - int tmp3 = z1 + z2 * SLOW_INTEGER_FIX_0_765366865; - - z2 = SLOW_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 0], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 0]); - z3 = SLOW_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 4], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 4]); - - int tmp0 = (z2 + z3) << SLOW_INTEGER_CONST_BITS; - int tmp1 = (z2 - z3) << SLOW_INTEGER_CONST_BITS; - - int tmp10 = tmp0 + tmp3; - int tmp13 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp12 = tmp1 - tmp2; - - /* Odd part per figure 8; the matrix is unitary and hence its - * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. - */ - - tmp0 = SLOW_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 7], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 7]); - tmp1 = SLOW_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 5], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 5]); - tmp2 = SLOW_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 3], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 3]); - tmp3 = SLOW_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 1], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 1]); - - z1 = tmp0 + tmp3; - z2 = tmp1 + tmp2; - z3 = tmp0 + tmp2; - int z4 = tmp1 + tmp3; - int z5 = (z3 + z4) * SLOW_INTEGER_FIX_1_175875602; /* sqrt(2) * c3 */ - - tmp0 = tmp0 * SLOW_INTEGER_FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */ - tmp1 = tmp1 * SLOW_INTEGER_FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */ - tmp2 = tmp2 * SLOW_INTEGER_FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */ - tmp3 = tmp3 * SLOW_INTEGER_FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */ - z1 = z1 * (-SLOW_INTEGER_FIX_0_899976223); /* sqrt(2) * (c7-c3) */ - z2 = z2 * (-SLOW_INTEGER_FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ - z3 = z3 * (-SLOW_INTEGER_FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ - z4 = z4 * (-SLOW_INTEGER_FIX_0_390180644); /* sqrt(2) * (c5-c3) */ - - z3 += z5; - z4 += z5; - - tmp0 += z1 + z3; - tmp1 += z2 + z4; - tmp2 += z2 + z3; - tmp3 += z1 + z4; - - /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ - - workspace[workspaceIndex + JpegConstants.DCTSIZE * 0] = JpegUtils.DESCALE(tmp10 + tmp3, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - workspace[workspaceIndex + JpegConstants.DCTSIZE * 7] = JpegUtils.DESCALE(tmp10 - tmp3, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - workspace[workspaceIndex + JpegConstants.DCTSIZE * 1] = JpegUtils.DESCALE(tmp11 + tmp2, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - workspace[workspaceIndex + JpegConstants.DCTSIZE * 6] = JpegUtils.DESCALE(tmp11 - tmp2, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - workspace[workspaceIndex + JpegConstants.DCTSIZE * 2] = JpegUtils.DESCALE(tmp12 + tmp1, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - workspace[workspaceIndex + JpegConstants.DCTSIZE * 5] = JpegUtils.DESCALE(tmp12 - tmp1, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - workspace[workspaceIndex + JpegConstants.DCTSIZE * 3] = JpegUtils.DESCALE(tmp13 + tmp0, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - workspace[workspaceIndex + JpegConstants.DCTSIZE * 4] = JpegUtils.DESCALE(tmp13 - tmp0, SLOW_INTEGER_CONST_BITS - SLOW_INTEGER_PASS1_BITS); - - /* advance pointers to next column */ - coefBlockIndex++; - quantTableIndex++; - workspaceIndex++; - } - - /* Pass 2: process rows from work array, store into output array. */ - /* Note that we must descale the results by a factor of 8 == 2**3, */ - /* and also undo the SLOW_INTEGER_PASS1_BITS scaling. */ - - workspaceIndex = 0; - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset + JpegConstants.CENTERJSAMPLE; - - for (int ctr = 0; ctr < JpegConstants.DCTSIZE; ctr++) - { - /* Rows of zeroes can be exploited in the same way as we did with columns. - * However, the column calculation has created many nonzero AC terms, so - * the simplification applies less often (typically 5% to 10% of the time). - * On machines with very fast multiplication, it's possible that the - * test takes more time than it's worth. In that case this section - * may be commented out. - */ - int currentOutRow = output_row + ctr; - if (workspace[workspaceIndex + 1] == 0 && - workspace[workspaceIndex + 2] == 0 && - workspace[workspaceIndex + 3] == 0 && - workspace[workspaceIndex + 4] == 0 && - workspace[workspaceIndex + 5] == 0 && - workspace[workspaceIndex + 6] == 0 && - workspace[workspaceIndex + 7] == 0) - { - /* AC terms all zero */ - byte dcval = limit[limitOffset + JpegUtils.DESCALE(workspace[workspaceIndex + 0], SLOW_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - - m_componentBuffer[currentOutRow][output_col + 0] = dcval; - m_componentBuffer[currentOutRow][output_col + 1] = dcval; - m_componentBuffer[currentOutRow][output_col + 2] = dcval; - m_componentBuffer[currentOutRow][output_col + 3] = dcval; - m_componentBuffer[currentOutRow][output_col + 4] = dcval; - m_componentBuffer[currentOutRow][output_col + 5] = dcval; - m_componentBuffer[currentOutRow][output_col + 6] = dcval; - m_componentBuffer[currentOutRow][output_col + 7] = dcval; - - workspaceIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ - continue; - } - - /* Even part: reverse the even part of the forward DCT. */ - /* The rotator is sqrt(2)*c(-6). */ - - int z2 = workspace[workspaceIndex + 2]; - int z3 = workspace[workspaceIndex + 6]; - - int z1 = (z2 + z3) * SLOW_INTEGER_FIX_0_541196100; - int tmp2 = z1 + z3 * (-SLOW_INTEGER_FIX_1_847759065); - int tmp3 = z1 + z2 * SLOW_INTEGER_FIX_0_765366865; - - int tmp0 = (workspace[workspaceIndex + 0] + workspace[workspaceIndex + 4]) << SLOW_INTEGER_CONST_BITS; - int tmp1 = (workspace[workspaceIndex + 0] - workspace[workspaceIndex + 4]) << SLOW_INTEGER_CONST_BITS; - - int tmp10 = tmp0 + tmp3; - int tmp13 = tmp0 - tmp3; - int tmp11 = tmp1 + tmp2; - int tmp12 = tmp1 - tmp2; - - /* Odd part per figure 8; the matrix is unitary and hence its - * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. - */ - - tmp0 = workspace[workspaceIndex + 7]; - tmp1 = workspace[workspaceIndex + 5]; - tmp2 = workspace[workspaceIndex + 3]; - tmp3 = workspace[workspaceIndex + 1]; - - z1 = tmp0 + tmp3; - z2 = tmp1 + tmp2; - z3 = tmp0 + tmp2; - int z4 = tmp1 + tmp3; - int z5 = (z3 + z4) * SLOW_INTEGER_FIX_1_175875602; /* sqrt(2) * c3 */ - - tmp0 = tmp0 * SLOW_INTEGER_FIX_0_298631336; /* sqrt(2) * (-c1+c3+c5-c7) */ - tmp1 = tmp1 * SLOW_INTEGER_FIX_2_053119869; /* sqrt(2) * ( c1+c3-c5+c7) */ - tmp2 = tmp2 * SLOW_INTEGER_FIX_3_072711026; /* sqrt(2) * ( c1+c3+c5-c7) */ - tmp3 = tmp3 * SLOW_INTEGER_FIX_1_501321110; /* sqrt(2) * ( c1+c3-c5-c7) */ - z1 = z1 * (-SLOW_INTEGER_FIX_0_899976223); /* sqrt(2) * (c7-c3) */ - z2 = z2 * (-SLOW_INTEGER_FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ - z3 = z3 * (-SLOW_INTEGER_FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ - z4 = z4 * (-SLOW_INTEGER_FIX_0_390180644); /* sqrt(2) * (c5-c3) */ - - z3 += z5; - z4 += z5; - - tmp0 += z1 + z3; - tmp1 += z2 + z4; - tmp2 += z2 + z3; - tmp3 += z1 + z4; - - /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ - - m_componentBuffer[currentOutRow][output_col + 0] = limit[limitOffset + JpegUtils.DESCALE(tmp10 + tmp3, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 7] = limit[limitOffset + JpegUtils.DESCALE(tmp10 - tmp3, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 1] = limit[limitOffset + JpegUtils.DESCALE(tmp11 + tmp2, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 6] = limit[limitOffset + JpegUtils.DESCALE(tmp11 - tmp2, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 2] = limit[limitOffset + JpegUtils.DESCALE(tmp12 + tmp1, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 5] = limit[limitOffset + JpegUtils.DESCALE(tmp12 - tmp1, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 3] = limit[limitOffset + JpegUtils.DESCALE(tmp13 + tmp0, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 4] = limit[limitOffset + JpegUtils.DESCALE(tmp13 - tmp0, SLOW_INTEGER_CONST_BITS + SLOW_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - - /* advance pointer to next row */ - workspaceIndex += JpegConstants.DCTSIZE; - } - } - - /// - /// Dequantize a coefficient by multiplying it by the multiplier-table - /// entry; produce an int result. In this module, both inputs and result - /// are 16 bits or less, so either int or short multiply will work. - /// - private static int SLOW_INTEGER_DEQUANTIZE(int coef, int quantval) - { - return (coef * quantval); - } - - /// - /// Perform dequantization and inverse DCT on one block of coefficients. - /// NOTE: this code only copes with 8x8 DCTs. - /// - /// A fast, not so accurate integer implementation of the - /// inverse DCT (Discrete Cosine Transform). In the IJG code, this routine - /// must also perform dequantization of the input coefficients. - /// - /// A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT - /// on each row (or vice versa, but it's more convenient to emit a row at - /// a time). Direct algorithms are also available, but they are much more - /// complex and seem not to be any faster when reduced to code. - /// - /// This implementation is based on Arai, Agui, and Nakajima's algorithm for - /// scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in - /// Japanese, but the algorithm is described in the Pennebaker & Mitchell - /// JPEG textbook (see REFERENCES section in file README). The following code - /// is based directly on figure 4-8 in P&M. - /// While an 8-point DCT cannot be done in less than 11 multiplies, it is - /// possible to arrange the computation so that many of the multiplies are - /// simple scalings of the final outputs. These multiplies can then be - /// folded into the multiplications or divisions by the JPEG quantization - /// table entries. The AA&N method leaves only 5 multiplies and 29 adds - /// to be done in the DCT itself. - /// The primary disadvantage of this method is that with fixed-point math, - /// accuracy is lost due to imprecise representation of the scaled - /// quantization values. The smaller the quantization table entry, the less - /// precise the scaled value, so this implementation does worse with high- - /// quality-setting files than with low-quality ones. - /// - /// Scaling decisions are generally the same as in the LL&M algorithm; - /// However, we choose to descale - /// (right shift) multiplication products as soon as they are formed, - /// rather than carrying additional fractional bits into subsequent additions. - /// This compromises accuracy slightly, but it lets us save a few shifts. - /// More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) - /// everywhere except in the multiplications proper; this saves a good deal - /// of work on 16-bit-int machines. - /// - /// The dequantized coefficients are not integers because the AA&N scaling - /// factors have been incorporated. We represent them scaled up by FAST_INTEGER_PASS1_BITS, - /// so that the first and second IDCT rounds have the same input scaling. - /// For 8-bit JSAMPLEs, we choose IFAST_SCALE_BITS = FAST_INTEGER_PASS1_BITS so as to - /// avoid a descaling shift; this compromises accuracy rather drastically - /// for small quantization table entries, but it saves a lot of shifts. - /// For 12-bit JSAMPLEs, there's no hope of using 16x16 multiplies anyway, - /// so we use a much larger scaling factor to preserve accuracy. - /// - /// A final compromise is to represent the multiplicative constants to only - /// 8 fractional bits, rather than 13. This saves some shifting work on some - /// machines, and may also reduce the cost of multiplication (since there - /// are fewer one-bits in the constants). - /// - private void jpeg_idct_ifast(int component_index, short[] coef_block, int output_row, int output_col) - { - /* buffers data between passes */ - int[] workspace = new int[JpegConstants.DCTSIZE2]; - - /* Pass 1: process columns from input, store into work array. */ - - int coefBlockIndex = 0; - int workspaceIndex = 0; - - int[] quantTable = m_dctTables[component_index].int_array; - int quantTableIndex = 0; - - for (int ctr = JpegConstants.DCTSIZE; ctr > 0; ctr--) - { - /* Due to quantization, we will usually find that many of the input - * coefficients are zero, especially the AC terms. We can exploit this - * by short-circuiting the IDCT calculation for any column in which all - * the AC terms are zero. In that case each output is equal to the - * DC coefficient (with scale factor as needed). - * With typical images and quantization tables, half or more of the - * column DCT calculations can be simplified this way. - */ - - if (coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 1] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 2] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 3] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 4] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 5] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 6] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 7] == 0) - { - /* AC terms all zero */ - int dcval = FAST_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 0], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 0]); - - workspace[workspaceIndex + JpegConstants.DCTSIZE * 0] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 1] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 2] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 3] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 4] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 5] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 6] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 7] = dcval; - - /* advance pointers to next column */ - coefBlockIndex++; - quantTableIndex++; - workspaceIndex++; - continue; - } - - /* Even part */ - - int tmp0 = FAST_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 0], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 0]); - int tmp1 = FAST_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 2], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 2]); - int tmp2 = FAST_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 4], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 4]); - int tmp3 = FAST_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 6], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 6]); - - int tmp10 = tmp0 + tmp2; /* phase 3 */ - int tmp11 = tmp0 - tmp2; - - int tmp13 = tmp1 + tmp3; /* phases 5-3 */ - int tmp12 = FAST_INTEGER_MULTIPLY(tmp1 - tmp3, FAST_INTEGER_FIX_1_414213562) - tmp13; /* 2*c4 */ - - tmp0 = tmp10 + tmp13; /* phase 2 */ - tmp3 = tmp10 - tmp13; - tmp1 = tmp11 + tmp12; - tmp2 = tmp11 - tmp12; - - /* Odd part */ - - int tmp4 = FAST_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 1], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 1]); - int tmp5 = FAST_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 3], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 3]); - int tmp6 = FAST_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 5], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 5]); - int tmp7 = FAST_INTEGER_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 7], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 7]); - - int z13 = tmp6 + tmp5; /* phase 6 */ - int z10 = tmp6 - tmp5; - int z11 = tmp4 + tmp7; - int z12 = tmp4 - tmp7; - - tmp7 = z11 + z13; /* phase 5 */ - tmp11 = FAST_INTEGER_MULTIPLY(z11 - z13, FAST_INTEGER_FIX_1_414213562); /* 2*c4 */ - - int z5 = FAST_INTEGER_MULTIPLY(z10 + z12, FAST_INTEGER_FIX_1_847759065); /* 2*c2 */ - tmp10 = FAST_INTEGER_MULTIPLY(z12, FAST_INTEGER_FIX_1_082392200) - z5; /* 2*(c2-c6) */ - tmp12 = FAST_INTEGER_MULTIPLY(z10, -FAST_INTEGER_FIX_2_613125930) + z5; /* -2*(c2+c6) */ - - tmp6 = tmp12 - tmp7; /* phase 2 */ - tmp5 = tmp11 - tmp6; - tmp4 = tmp10 + tmp5; - - workspace[workspaceIndex + JpegConstants.DCTSIZE * 0] = tmp0 + tmp7; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 7] = tmp0 - tmp7; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 1] = tmp1 + tmp6; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 6] = tmp1 - tmp6; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 2] = tmp2 + tmp5; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 5] = tmp2 - tmp5; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 4] = tmp3 + tmp4; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 3] = tmp3 - tmp4; - - /* advance pointers to next column */ - coefBlockIndex++; - quantTableIndex++; - workspaceIndex++; - } - - /* Pass 2: process rows from work array, store into output array. */ - /* Note that we must descale the results by a factor of 8 == 2**3, */ - /* and also undo the FAST_INTEGER_PASS1_BITS scaling. */ - - workspaceIndex = 0; - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset + JpegConstants.CENTERJSAMPLE; - - for (int ctr = 0; ctr < JpegConstants.DCTSIZE; ctr++) - { - int currentOutRow = output_row + ctr; - - /* Rows of zeroes can be exploited in the same way as we did with columns. - * However, the column calculation has created many nonzero AC terms, so - * the simplification applies less often (typically 5% to 10% of the time). - * On machines with very fast multiplication, it's possible that the - * test takes more time than it's worth. In that case this section - * may be commented out. - */ - - if (workspace[workspaceIndex + 1] == 0 && - workspace[workspaceIndex + 2] == 0 && - workspace[workspaceIndex + 3] == 0 && - workspace[workspaceIndex + 4] == 0 && - workspace[workspaceIndex + 5] == 0 && - workspace[workspaceIndex + 6] == 0 && - workspace[workspaceIndex + 7] == 0) - { - /* AC terms all zero */ - byte dcval = limit[limitOffset + FAST_INTEGER_IDESCALE(workspace[workspaceIndex + 0], FAST_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - - m_componentBuffer[currentOutRow][output_col + 0] = dcval; - m_componentBuffer[currentOutRow][output_col + 1] = dcval; - m_componentBuffer[currentOutRow][output_col + 2] = dcval; - m_componentBuffer[currentOutRow][output_col + 3] = dcval; - m_componentBuffer[currentOutRow][output_col + 4] = dcval; - m_componentBuffer[currentOutRow][output_col + 5] = dcval; - m_componentBuffer[currentOutRow][output_col + 6] = dcval; - m_componentBuffer[currentOutRow][output_col + 7] = dcval; - - /* advance pointer to next row */ - workspaceIndex += JpegConstants.DCTSIZE; - continue; - } - - /* Even part */ - - int tmp10 = workspace[workspaceIndex + 0] + workspace[workspaceIndex + 4]; - int tmp11 = workspace[workspaceIndex + 0] - workspace[workspaceIndex + 4]; - - int tmp13 = workspace[workspaceIndex + 2] + workspace[workspaceIndex + 6]; - int tmp12 = FAST_INTEGER_MULTIPLY(workspace[workspaceIndex + 2] - workspace[workspaceIndex + 6], FAST_INTEGER_FIX_1_414213562) - tmp13; - - int tmp0 = tmp10 + tmp13; - int tmp3 = tmp10 - tmp13; - int tmp1 = tmp11 + tmp12; - int tmp2 = tmp11 - tmp12; - - /* Odd part */ - - int z13 = workspace[workspaceIndex + 5] + workspace[workspaceIndex + 3]; - int z10 = workspace[workspaceIndex + 5] - workspace[workspaceIndex + 3]; - int z11 = workspace[workspaceIndex + 1] + workspace[workspaceIndex + 7]; - int z12 = workspace[workspaceIndex + 1] - workspace[workspaceIndex + 7]; - - int tmp7 = z11 + z13; /* phase 5 */ - tmp11 = FAST_INTEGER_MULTIPLY(z11 - z13, FAST_INTEGER_FIX_1_414213562); /* 2*c4 */ - - int z5 = FAST_INTEGER_MULTIPLY(z10 + z12, FAST_INTEGER_FIX_1_847759065); /* 2*c2 */ - tmp10 = FAST_INTEGER_MULTIPLY(z12, FAST_INTEGER_FIX_1_082392200) - z5; /* 2*(c2-c6) */ - tmp12 = FAST_INTEGER_MULTIPLY(z10, -FAST_INTEGER_FIX_2_613125930) + z5; /* -2*(c2+c6) */ - - int tmp6 = tmp12 - tmp7; /* phase 2 */ - int tmp5 = tmp11 - tmp6; - int tmp4 = tmp10 + tmp5; - - /* Final output stage: scale down by a factor of 8 and range-limit */ - - m_componentBuffer[currentOutRow][output_col + 0] = limit[limitOffset + FAST_INTEGER_IDESCALE(tmp0 + tmp7, FAST_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 7] = limit[limitOffset + FAST_INTEGER_IDESCALE(tmp0 - tmp7, FAST_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 1] = limit[limitOffset + FAST_INTEGER_IDESCALE(tmp1 + tmp6, FAST_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 6] = limit[limitOffset + FAST_INTEGER_IDESCALE(tmp1 - tmp6, FAST_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 2] = limit[limitOffset + FAST_INTEGER_IDESCALE(tmp2 + tmp5, FAST_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 5] = limit[limitOffset + FAST_INTEGER_IDESCALE(tmp2 - tmp5, FAST_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 4] = limit[limitOffset + FAST_INTEGER_IDESCALE(tmp3 + tmp4, FAST_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 3] = limit[limitOffset + FAST_INTEGER_IDESCALE(tmp3 - tmp4, FAST_INTEGER_PASS1_BITS + 3) & RANGE_MASK]; - - /* advance pointer to next row */ - workspaceIndex += JpegConstants.DCTSIZE; - } - } - - /// - /// Multiply a DCTELEM variable by an int constant, and immediately - /// descale to yield a DCTELEM result. - /// - private static int FAST_INTEGER_MULTIPLY(int var, int c) - { -#if !USE_ACCURATE_ROUNDING - return (JpegUtils.RIGHT_SHIFT(var * c, FAST_INTEGER_CONST_BITS)); -#else - return (JpegUtils.DESCALE(var * c, FAST_INTEGER_CONST_BITS)); -#endif - } - - /// - /// Dequantize a coefficient by multiplying it by the multiplier-table - /// entry; produce a DCTELEM result. For 8-bit data a 16x16->16 - /// multiplication will do. For 12-bit data, the multiplier table is - /// declared int, so a 32-bit multiply will be used. - /// - private static int FAST_INTEGER_DEQUANTIZE(short coef, int quantval) - { - return ((int)coef * quantval); - } - - /// - /// Like DESCALE, but applies to a DCTELEM and produces an int. - /// We assume that int right shift is unsigned if int right shift is. - /// - private static int FAST_INTEGER_IRIGHT_SHIFT(int x, int shft) - { - return (x >> shft); - } - - private static int FAST_INTEGER_IDESCALE(int x, int n) - { -#if USE_ACCURATE_ROUNDING - return (FAST_INTEGER_IRIGHT_SHIFT((x) + (1 << ((n) - 1)), n)); -#else - return (FAST_INTEGER_IRIGHT_SHIFT(x, n)); -#endif - } - - /// - /// Perform dequantization and inverse DCT on one block of coefficients. - /// NOTE: this code only copes with 8x8 DCTs. - /// - /// A floating-point implementation of the - /// inverse DCT (Discrete Cosine Transform). In the IJG code, this routine - /// must also perform dequantization of the input coefficients. - /// - /// This implementation should be more accurate than either of the integer - /// IDCT implementations. However, it may not give the same results on all - /// machines because of differences in roundoff behavior. Speed will depend - /// on the hardware's floating point capacity. - /// - /// A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT - /// on each row (or vice versa, but it's more convenient to emit a row at - /// a time). Direct algorithms are also available, but they are much more - /// complex and seem not to be any faster when reduced to code. - /// - /// This implementation is based on Arai, Agui, and Nakajima's algorithm for - /// scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in - /// Japanese, but the algorithm is described in the Pennebaker & Mitchell - /// JPEG textbook (see REFERENCES section in file README). The following code - /// is based directly on figure 4-8 in P&M. - /// While an 8-point DCT cannot be done in less than 11 multiplies, it is - /// possible to arrange the computation so that many of the multiplies are - /// simple scalings of the final outputs. These multiplies can then be - /// folded into the multiplications or divisions by the JPEG quantization - /// table entries. The AA&N method leaves only 5 multiplies and 29 adds - /// to be done in the DCT itself. - /// The primary disadvantage of this method is that with a fixed-point - /// implementation, accuracy is lost due to imprecise representation of the - /// scaled quantization values. However, that problem does not arise if - /// we use floating point arithmetic. - /// - private void jpeg_idct_float(int component_index, short[] coef_block, int output_row, int output_col) - { - /* buffers data between passes */ - float[] workspace = new float[JpegConstants.DCTSIZE2]; - - /* Pass 1: process columns from input, store into work array. */ - - int coefBlockIndex = 0; - int workspaceIndex = 0; - - float[] quantTable = m_dctTables[component_index].float_array; - int quantTableIndex = 0; - - for (int ctr = JpegConstants.DCTSIZE; ctr > 0; ctr--) - { - /* Due to quantization, we will usually find that many of the input - * coefficients are zero, especially the AC terms. We can exploit this - * by short-circuiting the IDCT calculation for any column in which all - * the AC terms are zero. In that case each output is equal to the - * DC coefficient (with scale factor as needed). - * With typical images and quantization tables, half or more of the - * column DCT calculations can be simplified this way. - */ - - if (coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 1] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 2] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 3] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 4] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 5] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 6] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 7] == 0) - { - /* AC terms all zero */ - float dcval = FLOAT_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 0], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 0]); - - workspace[workspaceIndex + JpegConstants.DCTSIZE * 0] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 1] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 2] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 3] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 4] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 5] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 6] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 7] = dcval; - - coefBlockIndex++; /* advance pointers to next column */ - quantTableIndex++; - workspaceIndex++; - continue; - } - - /* Even part */ - - float tmp0 = FLOAT_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 0], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 0]); - float tmp1 = FLOAT_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 2], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 2]); - float tmp2 = FLOAT_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 4], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 4]); - float tmp3 = FLOAT_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 6], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 6]); - - float tmp10 = tmp0 + tmp2; /* phase 3 */ - float tmp11 = tmp0 - tmp2; - - float tmp13 = tmp1 + tmp3; /* phases 5-3 */ - float tmp12 = (tmp1 - tmp3) * 1.414213562f - tmp13; /* 2*c4 */ - - tmp0 = tmp10 + tmp13; /* phase 2 */ - tmp3 = tmp10 - tmp13; - tmp1 = tmp11 + tmp12; - tmp2 = tmp11 - tmp12; - - /* Odd part */ - - float tmp4 = FLOAT_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 1], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 1]); - float tmp5 = FLOAT_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 3], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 3]); - float tmp6 = FLOAT_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 5], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 5]); - float tmp7 = FLOAT_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 7], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 7]); - - float z13 = tmp6 + tmp5; /* phase 6 */ - float z10 = tmp6 - tmp5; - float z11 = tmp4 + tmp7; - float z12 = tmp4 - tmp7; - - tmp7 = z11 + z13; /* phase 5 */ - tmp11 = (z11 - z13) * 1.414213562f; /* 2*c4 */ - - float z5 = (z10 + z12) * 1.847759065f; /* 2*c2 */ - tmp10 = 1.082392200f * z12 - z5; /* 2*(c2-c6) */ - tmp12 = -2.613125930f * z10 + z5; /* -2*(c2+c6) */ - - tmp6 = tmp12 - tmp7; /* phase 2 */ - tmp5 = tmp11 - tmp6; - tmp4 = tmp10 + tmp5; - - workspace[workspaceIndex + JpegConstants.DCTSIZE * 0] = tmp0 + tmp7; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 7] = tmp0 - tmp7; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 1] = tmp1 + tmp6; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 6] = tmp1 - tmp6; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 2] = tmp2 + tmp5; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 5] = tmp2 - tmp5; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 4] = tmp3 + tmp4; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 3] = tmp3 - tmp4; - - coefBlockIndex++; /* advance pointers to next column */ - quantTableIndex++; - workspaceIndex++; - } - - /* Pass 2: process rows from work array, store into output array. */ - /* Note that we must descale the results by a factor of 8 == 2**3. */ - workspaceIndex = 0; - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset + JpegConstants.CENTERJSAMPLE; - - for (int ctr = 0; ctr < JpegConstants.DCTSIZE; ctr++) - { - /* Rows of zeroes can be exploited in the same way as we did with columns. - * However, the column calculation has created many nonzero AC terms, so - * the simplification applies less often (typically 5% to 10% of the time). - * And testing floats for zero is relatively expensive, so we don't bother. - */ - - /* Even part */ - - float tmp10 = workspace[workspaceIndex + 0] + workspace[workspaceIndex + 4]; - float tmp11 = workspace[workspaceIndex + 0] - workspace[workspaceIndex + 4]; - - float tmp13 = workspace[workspaceIndex + 2] + workspace[workspaceIndex + 6]; - float tmp12 = (workspace[workspaceIndex + 2] - workspace[workspaceIndex + 6]) * 1.414213562f - tmp13; - - float tmp0 = tmp10 + tmp13; - float tmp3 = tmp10 - tmp13; - float tmp1 = tmp11 + tmp12; - float tmp2 = tmp11 - tmp12; - - /* Odd part */ - - float z13 = workspace[workspaceIndex + 5] + workspace[workspaceIndex + 3]; - float z10 = workspace[workspaceIndex + 5] - workspace[workspaceIndex + 3]; - float z11 = workspace[workspaceIndex + 1] + workspace[workspaceIndex + 7]; - float z12 = workspace[workspaceIndex + 1] - workspace[workspaceIndex + 7]; - - float tmp7 = z11 + z13; - tmp11 = (z11 - z13) * 1.414213562f; - - float z5 = (z10 + z12) * 1.847759065f; /* 2*c2 */ - tmp10 = 1.082392200f * z12 - z5; /* 2*(c2-c6) */ - tmp12 = -2.613125930f * z10 + z5; /* -2*(c2+c6) */ - - float tmp6 = tmp12 - tmp7; - float tmp5 = tmp11 - tmp6; - float tmp4 = tmp10 + tmp5; - - /* Final output stage: scale down by a factor of 8 and range-limit */ - int currentOutRow = output_row + ctr; - m_componentBuffer[currentOutRow][output_col + 0] = limit[limitOffset + JpegUtils.DESCALE((int)(tmp0 + tmp7), 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 7] = limit[limitOffset + JpegUtils.DESCALE((int)(tmp0 - tmp7), 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 1] = limit[limitOffset + JpegUtils.DESCALE((int)(tmp1 + tmp6), 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 6] = limit[limitOffset + JpegUtils.DESCALE((int)(tmp1 - tmp6), 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 2] = limit[limitOffset + JpegUtils.DESCALE((int)(tmp2 + tmp5), 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 5] = limit[limitOffset + JpegUtils.DESCALE((int)(tmp2 - tmp5), 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 4] = limit[limitOffset + JpegUtils.DESCALE((int)(tmp3 + tmp4), 3) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 3] = limit[limitOffset + JpegUtils.DESCALE((int)(tmp3 - tmp4), 3) & RANGE_MASK]; - - workspaceIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ - } - } - - /// - /// Dequantize a coefficient by multiplying it by the multiplier-table - /// entry; produce a float result. - /// - private static float FLOAT_DEQUANTIZE(short coef, float quantval) - { - return (((float)(coef)) * (quantval)); - } - - /// - /// Inverse-DCT routines that produce reduced-size output: - /// either 4x4, 2x2, or 1x1 pixels from an 8x8 DCT block. - /// - /// NOTE: this code only copes with 8x8 DCTs. - /// - /// The implementation is based on the Loeffler, Ligtenberg and Moschytz (LL&M) - /// algorithm. We simply replace each 8-to-8 1-D IDCT step - /// with an 8-to-4 step that produces the four averages of two adjacent outputs - /// (or an 8-to-2 step producing two averages of four outputs, for 2x2 output). - /// These steps were derived by computing the corresponding values at the end - /// of the normal LL&M code, then simplifying as much as possible. - /// - /// 1x1 is trivial: just take the DC coefficient divided by 8. - /// - /// Perform dequantization and inverse DCT on one block of coefficients, - /// producing a reduced-size 4x4 output block. - /// - private void jpeg_idct_4x4(int component_index, short[] coef_block, int output_row, int output_col) - { - /* buffers data between passes */ - int[] workspace = new int[JpegConstants.DCTSIZE * 4]; - - /* Pass 1: process columns from input, store into work array. */ - int coefBlockIndex = 0; - int workspaceIndex = 0; - - int[] quantTable = m_dctTables[component_index].int_array; - int quantTableIndex = 0; - - for (int ctr = JpegConstants.DCTSIZE; ctr > 0; coefBlockIndex++, quantTableIndex++, workspaceIndex++, ctr--) - { - /* Don't bother to process column 4, because second pass won't use it */ - if (ctr == JpegConstants.DCTSIZE - 4) - continue; - - if (coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 1] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 2] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 3] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 5] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 6] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 7] == 0) - { - /* AC terms all zero; we need not examine term 4 for 4x4 output */ - int dcval = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 0], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 0]) << REDUCED_PASS1_BITS; - - workspace[workspaceIndex + JpegConstants.DCTSIZE * 0] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 1] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 2] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 3] = dcval; - - continue; - } - - /* Even part */ - - int tmp0 = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 0], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 0]); - tmp0 <<= (REDUCED_CONST_BITS + 1); - - int z2 = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 2], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 2]); - int z3 = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 6], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 6]); - - int tmp2 = z2 * REDUCED_FIX_1_847759065 + z3 * (-REDUCED_FIX_0_765366865); - - int tmp10 = tmp0 + tmp2; - int tmp12 = tmp0 - tmp2; - - /* Odd part */ - - int z1 = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 7], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 7]); - z2 = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 5], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 5]); - z3 = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 3], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 3]); - int z4 = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 1], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 1]); - - tmp0 = z1 * (-REDUCED_FIX_0_211164243) /* sqrt(2) * (c3-c1) */ + - z2 * REDUCED_FIX_1_451774981 /* sqrt(2) * (c3+c7) */ + - z3 * (-REDUCED_FIX_2_172734803) /* sqrt(2) * (-c1-c5) */ + - z4 * REDUCED_FIX_1_061594337; /* sqrt(2) * (c5+c7) */ - - tmp2 = z1 * (-REDUCED_FIX_0_509795579) /* sqrt(2) * (c7-c5) */ + - z2 * (-REDUCED_FIX_0_601344887) /* sqrt(2) * (c5-c1) */ + - z3 * REDUCED_FIX_0_899976223 /* sqrt(2) * (c3-c7) */ + - z4 * REDUCED_FIX_2_562915447; /* sqrt(2) * (c1+c3) */ - - /* Final output stage */ - - workspace[workspaceIndex + JpegConstants.DCTSIZE * 0] = JpegUtils.DESCALE(tmp10 + tmp2, REDUCED_CONST_BITS - REDUCED_PASS1_BITS + 1); - workspace[workspaceIndex + JpegConstants.DCTSIZE * 3] = JpegUtils.DESCALE(tmp10 - tmp2, REDUCED_CONST_BITS - REDUCED_PASS1_BITS + 1); - workspace[workspaceIndex + JpegConstants.DCTSIZE * 1] = JpegUtils.DESCALE(tmp12 + tmp0, REDUCED_CONST_BITS - REDUCED_PASS1_BITS + 1); - workspace[workspaceIndex + JpegConstants.DCTSIZE * 2] = JpegUtils.DESCALE(tmp12 - tmp0, REDUCED_CONST_BITS - REDUCED_PASS1_BITS + 1); - } - - /* Pass 2: process 4 rows from work array, store into output array. */ - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset + JpegConstants.CENTERJSAMPLE; - - workspaceIndex = 0; - for (int ctr = 0; ctr < 4; ctr++) - { - int currentOutRow = output_row + ctr; - /* It's not clear whether a zero row test is worthwhile here ... */ - - if (workspace[workspaceIndex + 1] == 0 && - workspace[workspaceIndex + 2] == 0 && - workspace[workspaceIndex + 3] == 0 && - workspace[workspaceIndex + 5] == 0 && - workspace[workspaceIndex + 6] == 0 && - workspace[workspaceIndex + 7] == 0) - { - /* AC terms all zero */ - byte dcval = limit[limitOffset + JpegUtils.DESCALE(workspace[workspaceIndex + 0], REDUCED_PASS1_BITS + 3) & RANGE_MASK]; - - m_componentBuffer[currentOutRow][output_col + 0] = dcval; - m_componentBuffer[currentOutRow][output_col + 1] = dcval; - m_componentBuffer[currentOutRow][output_col + 2] = dcval; - m_componentBuffer[currentOutRow][output_col + 3] = dcval; - - workspaceIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ - continue; - } - - /* Even part */ - - int tmp0 = (workspace[workspaceIndex + 0]) << (REDUCED_CONST_BITS + 1); - - int tmp2 = workspace[workspaceIndex + 2] * REDUCED_FIX_1_847759065 + workspace[workspaceIndex + 6] * (-REDUCED_FIX_0_765366865); - - int tmp10 = tmp0 + tmp2; - int tmp12 = tmp0 - tmp2; - - /* Odd part */ - - int z1 = workspace[workspaceIndex + 7]; - int z2 = workspace[workspaceIndex + 5]; - int z3 = workspace[workspaceIndex + 3]; - int z4 = workspace[workspaceIndex + 1]; - - tmp0 = z1 * (-REDUCED_FIX_0_211164243) /* sqrt(2) * (c3-c1) */ + - z2 * REDUCED_FIX_1_451774981 /* sqrt(2) * (c3+c7) */ + - z3 * (-REDUCED_FIX_2_172734803) /* sqrt(2) * (-c1-c5) */ + - z4 * REDUCED_FIX_1_061594337; /* sqrt(2) * (c5+c7) */ - - tmp2 = z1 * (-REDUCED_FIX_0_509795579) /* sqrt(2) * (c7-c5) */ + - z2 * (-REDUCED_FIX_0_601344887) /* sqrt(2) * (c5-c1) */ + - z3 * REDUCED_FIX_0_899976223 /* sqrt(2) * (c3-c7) */ + - z4 * REDUCED_FIX_2_562915447; /* sqrt(2) * (c1+c3) */ - - /* Final output stage */ - - m_componentBuffer[currentOutRow][output_col + 0] = limit[limitOffset + JpegUtils.DESCALE(tmp10 + tmp2, REDUCED_CONST_BITS + REDUCED_PASS1_BITS + 3 + 1) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 3] = limit[limitOffset + JpegUtils.DESCALE(tmp10 - tmp2, REDUCED_CONST_BITS + REDUCED_PASS1_BITS + 3 + 1) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 1] = limit[limitOffset + JpegUtils.DESCALE(tmp12 + tmp0, REDUCED_CONST_BITS + REDUCED_PASS1_BITS + 3 + 1) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 2] = limit[limitOffset + JpegUtils.DESCALE(tmp12 - tmp0, REDUCED_CONST_BITS + REDUCED_PASS1_BITS + 3 + 1) & RANGE_MASK]; - - workspaceIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ - } - } - - /// - /// Perform dequantization and inverse DCT on one block of coefficients, - /// producing a reduced-size 2x2 output block. - /// - private void jpeg_idct_2x2(int component_index, short[] coef_block, int output_row, int output_col) - { - /* buffers data between passes */ - int[] workspace = new int[JpegConstants.DCTSIZE * 2]; - - /* Pass 1: process columns from input, store into work array. */ - int coefBlockIndex = 0; - int workspaceIndex = 0; - - int[] quantTable = m_dctTables[component_index].int_array; - int quantTableIndex = 0; - - for (int ctr = JpegConstants.DCTSIZE; ctr > 0; coefBlockIndex++, quantTableIndex++, workspaceIndex++, ctr--) - { - /* Don't bother to process columns 2,4,6 */ - if (ctr == JpegConstants.DCTSIZE - 2 || ctr == JpegConstants.DCTSIZE - 4 || ctr == JpegConstants.DCTSIZE - 6) - continue; - - if (coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 1] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 3] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 5] == 0 && - coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 7] == 0) - { - /* AC terms all zero; we need not examine terms 2,4,6 for 2x2 output */ - int dcval = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 0], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 0]) << REDUCED_PASS1_BITS; - - workspace[workspaceIndex + JpegConstants.DCTSIZE * 0] = dcval; - workspace[workspaceIndex + JpegConstants.DCTSIZE * 1] = dcval; - - continue; - } - - /* Even part */ - - int z1 = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 0], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 0]); - int tmp10 = z1 << (REDUCED_CONST_BITS + 2); - - /* Odd part */ - - z1 = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 7], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 7]); - int tmp0 = z1 * -REDUCED_FIX_0_720959822; /* sqrt(2) * (c7-c5+c3-c1) */ - z1 = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 5], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 5]); - tmp0 += z1 * REDUCED_FIX_0_850430095; /* sqrt(2) * (-c1+c3+c5+c7) */ - z1 = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 3], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 3]); - tmp0 += z1 * (-REDUCED_FIX_1_272758580); /* sqrt(2) * (-c1+c3-c5-c7) */ - z1 = REDUCED_DEQUANTIZE(coef_block[coefBlockIndex + JpegConstants.DCTSIZE * 1], - quantTable[quantTableIndex + JpegConstants.DCTSIZE * 1]); - tmp0 += z1 * REDUCED_FIX_3_624509785; /* sqrt(2) * (c1+c3+c5+c7) */ - - /* Final output stage */ - - workspace[workspaceIndex + JpegConstants.DCTSIZE * 0] = JpegUtils.DESCALE(tmp10 + tmp0, REDUCED_CONST_BITS - REDUCED_PASS1_BITS + 2); - workspace[workspaceIndex + JpegConstants.DCTSIZE * 1] = JpegUtils.DESCALE(tmp10 - tmp0, REDUCED_CONST_BITS - REDUCED_PASS1_BITS + 2); - } - - /* Pass 2: process 2 rows from work array, store into output array. */ - workspaceIndex = 0; - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset + JpegConstants.CENTERJSAMPLE; - - for (int ctr = 0; ctr < 2; ctr++) - { - int currentOutRow = output_row + ctr; - /* It's not clear whether a zero row test is worthwhile here ... */ - - if (workspace[workspaceIndex + 1] == 0 && - workspace[workspaceIndex + 3] == 0 && - workspace[workspaceIndex + 5] == 0 && - workspace[workspaceIndex + 7] == 0) - { - /* AC terms all zero */ - byte dcval = limit[limitOffset + JpegUtils.DESCALE(workspace[workspaceIndex + 0], REDUCED_PASS1_BITS + 3) & RANGE_MASK]; - - m_componentBuffer[currentOutRow][output_col + 0] = dcval; - m_componentBuffer[currentOutRow][output_col + 1] = dcval; - - workspaceIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ - continue; - } - - /* Even part */ - - int tmp10 = (workspace[workspaceIndex + 0]) << (REDUCED_CONST_BITS + 2); - - /* Odd part */ - - int tmp0 = workspace[workspaceIndex + 7] * (-REDUCED_FIX_0_720959822) /* sqrt(2) * (c7-c5+c3-c1) */ + - workspace[workspaceIndex + 5] * REDUCED_FIX_0_850430095 /* sqrt(2) * (-c1+c3+c5+c7) */ + - workspace[workspaceIndex + 3] * (-REDUCED_FIX_1_272758580) /* sqrt(2) * (-c1+c3-c5-c7) */ + - workspace[workspaceIndex + 1] * REDUCED_FIX_3_624509785; /* sqrt(2) * (c1+c3+c5+c7) */ - - /* Final output stage */ - - m_componentBuffer[currentOutRow][output_col + 0] = limit[limitOffset + JpegUtils.DESCALE(tmp10 + tmp0, REDUCED_CONST_BITS + REDUCED_PASS1_BITS + 3 + 2) & RANGE_MASK]; - m_componentBuffer[currentOutRow][output_col + 1] = limit[limitOffset + JpegUtils.DESCALE(tmp10 - tmp0, REDUCED_CONST_BITS + REDUCED_PASS1_BITS + 3 + 2) & RANGE_MASK]; - - workspaceIndex += JpegConstants.DCTSIZE; /* advance pointer to next row */ - } - } - - /// - /// Perform dequantization and inverse DCT on one block of coefficients, - /// producing a reduced-size 1x1 output block. - /// - private void jpeg_idct_1x1(int component_index, short[] coef_block, int output_row, int output_col) - { - /* We hardly need an inverse DCT routine for this: just take the - * average pixel value, which is one-eighth of the DC coefficient. - */ - int[] quantptr = m_dctTables[component_index].int_array; - int dcval = REDUCED_DEQUANTIZE(coef_block[0], quantptr[0]); - dcval = JpegUtils.DESCALE(dcval, 3); - - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset + JpegConstants.CENTERJSAMPLE; - - m_componentBuffer[output_row + 0][output_col] = limit[limitOffset + dcval & RANGE_MASK]; - } - - /// - /// Dequantize a coefficient by multiplying it by the multiplier-table - /// entry; produce an int result. In this module, both inputs and result - /// are 16 bits or less, so either int or short multiply will work. - /// - private static int REDUCED_DEQUANTIZE(short coef, int quantval) - { - return ((int)coef * quantval); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_marker_reader.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_marker_reader.cs deleted file mode 100644 index e484bdfc..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_marker_reader.cs +++ /dev/null @@ -1,1192 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains routines to decode JPEG datastream markers. - * Most of the complexity arises from our desire to support input - * suspension: if not all of the data for a marker is available, - * we must exit back to the application. On resumption, we reprocess - * the marker. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Marker reading and parsing - /// - class jpeg_marker_reader - { - private const int APP0_DATA_LEN = 14; /* Length of interesting data in APP0 */ - private const int APP14_DATA_LEN = 12; /* Length of interesting data in APP14 */ - private const int APPN_DATA_LEN = 14; /* Must be the largest of the above!! */ - - private jpeg_decompress_struct m_cinfo; - - /* Application-overridable marker processing methods */ - private jpeg_decompress_struct.jpeg_marker_parser_method m_process_COM; - private jpeg_decompress_struct.jpeg_marker_parser_method[] m_process_APPn = new jpeg_decompress_struct.jpeg_marker_parser_method[16]; - - /* Limit on marker data length to save for each marker type */ - private int m_length_limit_COM; - private int[] m_length_limit_APPn = new int[16]; - - private bool m_saw_SOI; /* found SOI? */ - private bool m_saw_SOF; /* found SOF? */ - private int m_next_restart_num; /* next restart number expected (0-7) */ - private int m_discarded_bytes; /* # of bytes skipped looking for a marker */ - - /* Status of COM/APPn marker saving */ - private jpeg_marker_struct m_cur_marker; /* null if not processing a marker */ - private int m_bytes_read; /* data bytes read so far in marker */ - /* Note: cur_marker is not linked into marker_list until it's all read. */ - - /// - /// Initialize the marker reader module. - /// This is called only once, when the decompression object is created. - /// - public jpeg_marker_reader(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Initialize COM/APPn processing. - * By default, we examine and then discard APP0 and APP14, - * but simply discard COM and all other APPn. - */ - m_process_COM = skip_variable; - - for (int i = 0; i < 16; i++) - { - m_process_APPn[i] = skip_variable; - m_length_limit_APPn[i] = 0; - } - - m_process_APPn[0] = get_interesting_appn; - m_process_APPn[14] = get_interesting_appn; - - /* Reset marker processing state */ - reset_marker_reader(); - } - - /// - /// Reset marker processing state to begin a fresh datastream. - /// - public void reset_marker_reader() - { - m_cinfo.Comp_info = null; /* until allocated by get_sof */ - m_cinfo.m_input_scan_number = 0; /* no SOS seen yet */ - m_cinfo.m_unread_marker = 0; /* no pending marker */ - m_saw_SOI = false; /* set internal state too */ - m_saw_SOF = false; - m_discarded_bytes = 0; - m_cur_marker = null; - } - - /// - /// Read markers until SOS or EOI. - /// - /// Returns same codes as are defined for jpeg_consume_input: - /// JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. - /// - public ReadResult read_markers() - { - /* Outer loop repeats once for each marker. */ - for ( ; ; ) - { - /* Collect the marker proper, unless we already did. */ - /* NB: first_marker() enforces the requirement that SOI appear first. */ - if (m_cinfo.m_unread_marker == 0) - { - if (!m_cinfo.m_marker.m_saw_SOI) - { - if (!first_marker()) - return ReadResult.JPEG_SUSPENDED; - } - else - { - if (!next_marker()) - return ReadResult.JPEG_SUSPENDED; - } - } - - /* At this point m_cinfo.unread_marker contains the marker code and the - * input point is just past the marker proper, but before any parameters. - * A suspension will cause us to return with this state still true. - */ - switch ((JPEG_MARKER)m_cinfo.m_unread_marker) - { - case JPEG_MARKER.SOI: - if (!get_soi()) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.SOF0: - /* Baseline */ - case JPEG_MARKER.SOF1: - /* Extended sequential, Huffman */ - if (!get_sof(false)) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.SOF2: - /* Progressive, Huffman */ - if (!get_sof(true)) - return ReadResult.JPEG_SUSPENDED; - break; - - /* Currently unsupported SOFn types */ - case JPEG_MARKER.SOF3: - /* Lossless, Huffman */ - case JPEG_MARKER.SOF5: - /* Differential sequential, Huffman */ - case JPEG_MARKER.SOF6: - /* Differential progressive, Huffman */ - case JPEG_MARKER.SOF7: - /* Differential lossless, Huffman */ - case JPEG_MARKER.SOF9: - /* Extended sequential, arithmetic */ - case JPEG_MARKER.SOF10: - /* Progressive, arithmetic */ - case JPEG_MARKER.JPG: - /* Reserved for JPEG extensions */ - case JPEG_MARKER.SOF11: - /* Lossless, arithmetic */ - case JPEG_MARKER.SOF13: - /* Differential sequential, arithmetic */ - case JPEG_MARKER.SOF14: - /* Differential progressive, arithmetic */ - case JPEG_MARKER.SOF15: - /* Differential lossless, arithmetic */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_SOF_UNSUPPORTED, m_cinfo.m_unread_marker); - break; - - case JPEG_MARKER.SOS: - if (!get_sos()) - return ReadResult.JPEG_SUSPENDED; - m_cinfo.m_unread_marker = 0; /* processed the marker */ - return ReadResult.JPEG_REACHED_SOS; - - case JPEG_MARKER.EOI: - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_EOI); - m_cinfo.m_unread_marker = 0; /* processed the marker */ - return ReadResult.JPEG_REACHED_EOI; - - case JPEG_MARKER.DAC: - if (!skip_variable(m_cinfo)) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.DHT: - if (!get_dht()) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.DQT: - if (!get_dqt()) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.DRI: - if (!get_dri()) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.APP0: - case JPEG_MARKER.APP1: - case JPEG_MARKER.APP2: - case JPEG_MARKER.APP3: - case JPEG_MARKER.APP4: - case JPEG_MARKER.APP5: - case JPEG_MARKER.APP6: - case JPEG_MARKER.APP7: - case JPEG_MARKER.APP8: - case JPEG_MARKER.APP9: - case JPEG_MARKER.APP10: - case JPEG_MARKER.APP11: - case JPEG_MARKER.APP12: - case JPEG_MARKER.APP13: - case JPEG_MARKER.APP14: - case JPEG_MARKER.APP15: - if (!m_cinfo.m_marker.m_process_APPn[m_cinfo.m_unread_marker - (int)JPEG_MARKER.APP0](m_cinfo)) - return ReadResult.JPEG_SUSPENDED; - break; - - case JPEG_MARKER.COM: - if (!m_cinfo.m_marker.m_process_COM(m_cinfo)) - return ReadResult.JPEG_SUSPENDED; - break; - - /* these are all parameterless */ - case JPEG_MARKER.RST0: - case JPEG_MARKER.RST1: - case JPEG_MARKER.RST2: - case JPEG_MARKER.RST3: - case JPEG_MARKER.RST4: - case JPEG_MARKER.RST5: - case JPEG_MARKER.RST6: - case JPEG_MARKER.RST7: - case JPEG_MARKER.TEM: - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_PARMLESS_MARKER, m_cinfo.m_unread_marker); - break; - - case JPEG_MARKER.DNL: - /* Ignore DNL ... perhaps the wrong thing */ - if (!skip_variable(m_cinfo)) - return ReadResult.JPEG_SUSPENDED; - break; - - default: - /* must be DHP, EXP, JPGn, or RESn */ - /* For now, we treat the reserved markers as fatal errors since they are - * likely to be used to signal incompatible JPEG Part 3 extensions. - * Once the JPEG 3 version-number marker is well defined, this code - * ought to change! - */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_UNKNOWN_MARKER, m_cinfo.m_unread_marker); - break; - } - - /* Successfully processed marker, so reset state variable */ - m_cinfo.m_unread_marker = 0; - } /* end loop */ - } - - /// - /// Read a restart marker, which is expected to appear next in the datastream; - /// if the marker is not there, take appropriate recovery action. - /// Returns false if suspension is required. - /// - /// Made public for use by entropy decoder only - /// - /// This is called by the entropy decoder after it has read an appropriate - /// number of MCUs. cinfo.unread_marker may be nonzero if the entropy decoder - /// has already read a marker from the data source. Under normal conditions - /// cinfo.unread_marker will be reset to 0 before returning; if not reset, - /// it holds a marker which the decoder will be unable to read past. - /// - public bool read_restart_marker() - { - /* Obtain a marker unless we already did. */ - /* Note that next_marker will complain if it skips any data. */ - if (m_cinfo.m_unread_marker == 0) - { - if (!next_marker()) - return false; - } - - if (m_cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + m_cinfo.m_marker.m_next_restart_num)) - { - /* Normal case --- swallow the marker and let entropy decoder continue */ - m_cinfo.TRACEMS(3, J_MESSAGE_CODE.JTRC_RST, m_cinfo.m_marker.m_next_restart_num); - m_cinfo.m_unread_marker = 0; - } - else - { - /* Uh-oh, the restart markers have been messed up. */ - /* Let the data source manager determine how to resync. */ - if (!m_cinfo.m_src.resync_to_restart(m_cinfo, m_cinfo.m_marker.m_next_restart_num)) - return false; - } - - /* Update next-restart state */ - m_cinfo.m_marker.m_next_restart_num = (m_cinfo.m_marker.m_next_restart_num + 1) & 7; - - return true; - } - - /// - /// Find the next JPEG marker, save it in cinfo.unread_marker. - /// Returns false if had to suspend before reaching a marker; - /// in that case cinfo.unread_marker is unchanged. - /// - /// Note that the result might not be a valid marker code, - /// but it will never be 0 or FF. - /// - public bool next_marker() - { - int c; - for ( ; ; ) - { - if (!m_cinfo.m_src.GetByte(out c)) - return false; - - /* Skip any non-FF bytes. - * This may look a bit inefficient, but it will not occur in a valid file. - * We sync after each discarded byte so that a suspending data source - * can discard the byte from its buffer. - */ - while (c != 0xFF) - { - m_cinfo.m_marker.m_discarded_bytes++; - if (!m_cinfo.m_src.GetByte(out c)) - return false; - } - - /* This loop swallows any duplicate FF bytes. Extra FFs are legal as - * pad bytes, so don't count them in discarded_bytes. We assume there - * will not be so many consecutive FF bytes as to overflow a suspending - * data source's input buffer. - */ - do - { - if (!m_cinfo.m_src.GetByte(out c)) - return false; - } - while (c == 0xFF); - - if (c != 0) - { - /* found a valid marker, exit loop */ - break; - } - - /* Reach here if we found a stuffed-zero data sequence (FF/00). - * Discard it and loop back to try again. - */ - m_cinfo.m_marker.m_discarded_bytes += 2; - } - - if (m_cinfo.m_marker.m_discarded_bytes != 0) - { - m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_EXTRANEOUS_DATA, m_cinfo.m_marker.m_discarded_bytes, c); - m_cinfo.m_marker.m_discarded_bytes = 0; - } - - m_cinfo.m_unread_marker = c; - return true; - } - - /// - /// Install a special processing method for COM or APPn markers. - /// - public void jpeg_set_marker_processor(int marker_code, jpeg_decompress_struct.jpeg_marker_parser_method routine) - { - if (marker_code == (int)JPEG_MARKER.COM) - m_process_COM = routine; - else if (marker_code >= (int)JPEG_MARKER.APP0 && marker_code <= (int)JPEG_MARKER.APP15) - m_process_APPn[marker_code - (int)JPEG_MARKER.APP0] = routine; - else - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_UNKNOWN_MARKER, marker_code); - } - - public void jpeg_save_markers(int marker_code, int length_limit) - { - /* Choose processor routine to use. - * APP0/APP14 have special requirements. - */ - jpeg_decompress_struct.jpeg_marker_parser_method processor; - if (length_limit != 0) - { - processor = save_marker; - /* If saving APP0/APP14, save at least enough for our internal use. */ - if (marker_code == (int)JPEG_MARKER.APP0 && length_limit < APP0_DATA_LEN) - length_limit = APP0_DATA_LEN; - else if (marker_code == (int)JPEG_MARKER.APP14 && length_limit < APP14_DATA_LEN) - length_limit = APP14_DATA_LEN; - } - else - { - processor = skip_variable; - /* If discarding APP0/APP14, use our regular on-the-fly processor. */ - if (marker_code == (int)JPEG_MARKER.APP0 || marker_code == (int)JPEG_MARKER.APP14) - processor = get_interesting_appn; - } - - if (marker_code == (int)JPEG_MARKER.COM) - { - m_process_COM = processor; - m_length_limit_COM = length_limit; - } - else if (marker_code >= (int)JPEG_MARKER.APP0 && marker_code <= (int)JPEG_MARKER.APP15) - { - m_process_APPn[marker_code - (int)JPEG_MARKER.APP0] = processor; - m_length_limit_APPn[marker_code - (int)JPEG_MARKER.APP0] = length_limit; - } - else - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_UNKNOWN_MARKER, marker_code); - } - - /* State of marker reader, applications - * supplying COM or APPn handlers might like to know the state. - */ - public bool SawSOI() - { - return m_saw_SOI; - } - - public bool SawSOF() - { - return m_saw_SOF; - } - - public int NextRestartNumber() - { - return m_next_restart_num; - } - - public int DiscardedByteCount() - { - return m_discarded_bytes; - } - - public void SkipBytes(int count) - { - m_discarded_bytes += count; - } - - /// - /// Save an APPn or COM marker into the marker list - /// - private static bool save_marker(jpeg_decompress_struct cinfo) - { - jpeg_marker_struct cur_marker = cinfo.m_marker.m_cur_marker; - - byte[] data = null; - int length = 0; - int bytes_read; - int data_length; - int dataOffset = 0; - - if (cur_marker == null) - { - /* begin reading a marker */ - if (!cinfo.m_src.GetTwoBytes(out length)) - return false; - - length -= 2; - if (length >= 0) - { - /* watch out for bogus length word */ - /* figure out how much we want to save */ - int limit; - if (cinfo.m_unread_marker == (int)JPEG_MARKER.COM) - limit = cinfo.m_marker.m_length_limit_COM; - else - limit = cinfo.m_marker.m_length_limit_APPn[cinfo.m_unread_marker - (int)JPEG_MARKER.APP0]; - - if (length < limit) - limit = length; - - /* allocate and initialize the marker item */ - cur_marker = new jpeg_marker_struct((byte)cinfo.m_unread_marker, length, limit); - - /* data area is just beyond the jpeg_marker_struct */ - data = cur_marker.Data; - cinfo.m_marker.m_cur_marker = cur_marker; - cinfo.m_marker.m_bytes_read = 0; - bytes_read = 0; - data_length = limit; - } - else - { - /* deal with bogus length word */ - bytes_read = data_length = 0; - data = null; - } - } - else - { - /* resume reading a marker */ - bytes_read = cinfo.m_marker.m_bytes_read; - data_length = cur_marker.Data.Length; - data = cur_marker.Data; - dataOffset = bytes_read; - } - - byte[] tempData = null; - if (data_length != 0) - tempData = new byte[data.Length]; - - while (bytes_read < data_length) - { - /* move the restart point to here */ - cinfo.m_marker.m_bytes_read = bytes_read; - - /* If there's not at least one byte in buffer, suspend */ - if (!cinfo.m_src.MakeByteAvailable()) - return false; - - /* Copy bytes with reasonable rapidity */ - int read = cinfo.m_src.GetBytes(tempData, data_length - bytes_read); - Buffer.BlockCopy(tempData, 0, data, dataOffset, data_length - bytes_read); - bytes_read += read; - } - - /* Done reading what we want to read */ - if (cur_marker != null) - { - /* will be null if bogus length word */ - /* Add new marker to end of list */ - cinfo.m_marker_list.Add(cur_marker); - - /* Reset pointer & calc remaining data length */ - data = cur_marker.Data; - dataOffset = 0; - length = cur_marker.OriginalLength - data_length; - } - - /* Reset to initial state for next marker */ - cinfo.m_marker.m_cur_marker = null; - - JPEG_MARKER currentMarker = (JPEG_MARKER)cinfo.m_unread_marker; - if (data_length != 0 && (currentMarker == JPEG_MARKER.APP0 || currentMarker == JPEG_MARKER.APP14)) - { - tempData = new byte[data.Length]; - Buffer.BlockCopy(data, dataOffset, tempData, 0, data.Length - dataOffset); - } - - /* Process the marker if interesting; else just make a generic trace msg */ - switch ((JPEG_MARKER)cinfo.m_unread_marker) - { - case JPEG_MARKER.APP0: - examine_app0(cinfo, tempData, data_length, length); - break; - case JPEG_MARKER.APP14: - examine_app14(cinfo, tempData, data_length, length); - break; - default: - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_MISC_MARKER, cinfo.m_unread_marker, data_length + length); - break; - } - - /* skip any remaining data -- could be lots */ - if (length > 0) - cinfo.m_src.skip_input_data(length); - - return true; - } - - /// - /// Skip over an unknown or uninteresting variable-length marker - /// - private static bool skip_variable(jpeg_decompress_struct cinfo) - { - int length; - if (!cinfo.m_src.GetTwoBytes(out length)) - return false; - - length -= 2; - - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_MISC_MARKER, cinfo.m_unread_marker, length); - - if (length > 0) - cinfo.m_src.skip_input_data(length); - - return true; - } - - /// - /// Process an APP0 or APP14 marker without saving it - /// - private static bool get_interesting_appn(jpeg_decompress_struct cinfo) - { - int length; - if (!cinfo.m_src.GetTwoBytes(out length)) - return false; - - length -= 2; - - /* get the interesting part of the marker data */ - int numtoread = 0; - if (length >= APPN_DATA_LEN) - numtoread = APPN_DATA_LEN; - else if (length > 0) - numtoread = length; - - byte[] b = new byte[APPN_DATA_LEN]; - for (int i = 0; i < numtoread; i++) - { - int temp = 0; - if (!cinfo.m_src.GetByte(out temp)) - return false; - - b[i] = (byte) temp; - } - - length -= numtoread; - - /* process it */ - switch ((JPEG_MARKER)cinfo.m_unread_marker) - { - case JPEG_MARKER.APP0: - examine_app0(cinfo, b, numtoread, length); - break; - case JPEG_MARKER.APP14: - examine_app14(cinfo, b, numtoread, length); - break; - default: - /* can't get here unless jpeg_save_markers chooses wrong processor */ - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_UNKNOWN_MARKER, cinfo.m_unread_marker); - break; - } - - /* skip any remaining data -- could be lots */ - if (length > 0) - cinfo.m_src.skip_input_data(length); - - return true; - } - - /* - * Routines for processing APPn and COM markers. - * These are either saved in memory or discarded, per application request. - * APP0 and APP14 are specially checked to see if they are - * JFIF and Adobe markers, respectively. - */ - - /// - /// Examine first few bytes from an APP0. - /// Take appropriate action if it is a JFIF marker. - /// datalen is # of bytes at data[], remaining is length of rest of marker data. - /// - private static void examine_app0(jpeg_decompress_struct cinfo, byte[] data, int datalen, int remaining) - { - int totallen = datalen + remaining; - - if (datalen >= APP0_DATA_LEN && - data[0] == 0x4A && - data[1] == 0x46 && - data[2] == 0x49 && - data[3] == 0x46 && - data[4] == 0) - { - /* Found JFIF APP0 marker: save info */ - cinfo.m_saw_JFIF_marker = true; - cinfo.m_JFIF_major_version = data[5]; - cinfo.m_JFIF_minor_version = data[6]; - cinfo.m_density_unit = (DensityUnit)data[7]; - cinfo.m_X_density = (short)((data[8] << 8) + data[9]); - cinfo.m_Y_density = (short)((data[10] << 8) + data[11]); - - /* Check version. - * Major version must be 1, anything else signals an incompatible change. - * (We used to treat this as an error, but now it's a nonfatal warning, - * because some bozo at Hijaak couldn't read the spec.) - * Minor version should be 0..2, but process anyway if newer. - */ - if (cinfo.m_JFIF_major_version != 1) - cinfo.WARNMS(J_MESSAGE_CODE.JWRN_JFIF_MAJOR, cinfo.m_JFIF_major_version, cinfo.m_JFIF_minor_version); - - /* Generate trace messages */ - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF, cinfo.m_JFIF_major_version, cinfo.m_JFIF_minor_version, cinfo.m_X_density, - cinfo.m_Y_density, cinfo.m_density_unit); - - /* Validate thumbnail dimensions and issue appropriate messages */ - if ((data[12] | data[13]) != 0) - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_THUMBNAIL, data[12], data[13]); - - totallen -= APP0_DATA_LEN; - if (totallen != ((int)data[12] * (int)data[13] * 3)) - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_BADTHUMBNAILSIZE, totallen); - } - else if (datalen >= 6 && data[0] == 0x4A && data[1] == 0x46 && data[2] == 0x58 && data[3] == 0x58 && data[4] == 0) - { - /* Found JFIF "JFXX" extension APP0 marker */ - /* The library doesn't actually do anything with these, - * but we try to produce a helpful trace message. - */ - switch (data[5]) - { - case 0x10: - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_JPEG, totallen); - break; - case 0x11: - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_PALETTE, totallen); - break; - case 0x13: - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_THUMB_RGB, totallen); - break; - default: - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_JFIF_EXTENSION, data[5], totallen); - break; - } - } - else - { - /* Start of APP0 does not match "JFIF" or "JFXX", or too short */ - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_APP0, totallen); - } - } - - /// - /// Examine first few bytes from an APP14. - /// Take appropriate action if it is an Adobe marker. - /// datalen is # of bytes at data[], remaining is length of rest of marker data. - /// - private static void examine_app14(jpeg_decompress_struct cinfo, byte[] data, int datalen, int remaining) - { - if (datalen >= APP14_DATA_LEN && - data[0] == 0x41 && - data[1] == 0x64 && - data[2] == 0x6F && - data[3] == 0x62 && - data[4] == 0x65) - { - /* Found Adobe APP14 marker */ - int version = (data[5] << 8) + data[6]; - int flags0 = (data[7] << 8) + data[8]; - int flags1 = (data[9] << 8) + data[10]; - int transform = data[11]; - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_ADOBE, version, flags0, flags1, transform); - cinfo.m_saw_Adobe_marker = true; - cinfo.m_Adobe_transform = (byte) transform; - } - else - { - /* Start of APP14 does not match "Adobe", or too short */ - cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_APP14, datalen + remaining); - } - } - - /* - * Routines to process JPEG markers. - * - * Entry condition: JPEG marker itself has been read and its code saved - * in cinfo.unread_marker; input restart point is just after the marker. - * - * Exit: if return true, have read and processed any parameters, and have - * updated the restart point to point after the parameters. - * If return false, was forced to suspend before reaching end of - * marker parameters; restart point has not been moved. Same routine - * will be called again after application supplies more input data. - * - * This approach to suspension assumes that all of a marker's parameters - * can fit into a single input bufferload. This should hold for "normal" - * markers. Some COM/APPn markers might have large parameter segments - * that might not fit. If we are simply dropping such a marker, we use - * skip_input_data to get past it, and thereby put the problem on the - * source manager's shoulders. If we are saving the marker's contents - * into memory, we use a slightly different convention: when forced to - * suspend, the marker processor updates the restart point to the end of - * what it's consumed (ie, the end of the buffer) before returning false. - * On resumption, cinfo.unread_marker still contains the marker code, - * but the data source will point to the next chunk of marker data. - * The marker processor must retain internal state to deal with this. - * - * Note that we don't bother to avoid duplicate trace messages if a - * suspension occurs within marker parameters. Other side effects - * require more care. - */ - - - /// - /// Process an SOI marker - /// - private bool get_soi() - { - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_SOI); - - if (m_cinfo.m_marker.m_saw_SOI) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_SOI_DUPLICATE); - - /* Reset all parameters that are defined to be reset by SOI */ - m_cinfo.m_restart_interval = 0; - - /* Set initial assumptions for colorspace etc */ - - m_cinfo.m_jpeg_color_space = J_COLOR_SPACE.JCS_UNKNOWN; - m_cinfo.m_CCIR601_sampling = false; /* Assume non-CCIR sampling??? */ - - m_cinfo.m_saw_JFIF_marker = false; - m_cinfo.m_JFIF_major_version = 1; /* set default JFIF APP0 values */ - m_cinfo.m_JFIF_minor_version = 1; - m_cinfo.m_density_unit = DensityUnit.Unknown; - m_cinfo.m_X_density = 1; - m_cinfo.m_Y_density = 1; - m_cinfo.m_saw_Adobe_marker = false; - m_cinfo.m_Adobe_transform = 0; - - m_cinfo.m_marker.m_saw_SOI = true; - - return true; - } - - /// - /// Process a SOFn marker - /// - private bool get_sof(bool is_prog) - { - m_cinfo.m_progressive_mode = is_prog; - - int length; - if (!m_cinfo.m_src.GetTwoBytes(out length)) - return false; - - if (!m_cinfo.m_src.GetByte(out m_cinfo.m_data_precision)) - return false; - - int temp = 0; - if (!m_cinfo.m_src.GetTwoBytes(out temp)) - return false; - m_cinfo.m_image_height = temp; - - if (!m_cinfo.m_src.GetTwoBytes(out temp)) - return false; - m_cinfo.m_image_width = temp; - - if (!m_cinfo.m_src.GetByte(out m_cinfo.m_num_components)) - return false; - - length -= 8; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_SOF, m_cinfo.m_unread_marker, m_cinfo.m_image_width, m_cinfo.m_image_height, - m_cinfo.m_num_components); - - if (m_cinfo.m_marker.m_saw_SOF) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_SOF_DUPLICATE); - - /* We don't support files in which the image height is initially specified */ - /* as 0 and is later redefined by DNL. As long as we have to check that, */ - /* might as well have a general sanity check. */ - if (m_cinfo.m_image_height <= 0 || m_cinfo.m_image_width <= 0 || m_cinfo.m_num_components <= 0) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_EMPTY_IMAGE); - - if (length != (m_cinfo.m_num_components * 3)) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_LENGTH); - - if (m_cinfo.Comp_info == null) - { - /* do only once, even if suspend */ - m_cinfo.Comp_info = jpeg_component_info.createArrayOfComponents(m_cinfo.m_num_components); - } - - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - m_cinfo.Comp_info[ci].Component_index = ci; - - int component_id; - if (!m_cinfo.m_src.GetByte(out component_id)) - return false; - - m_cinfo.Comp_info[ci].Component_id = component_id; - - int c; - if (!m_cinfo.m_src.GetByte(out c)) - return false; - - m_cinfo.Comp_info[ci].H_samp_factor = (c >> 4) & 15; - m_cinfo.Comp_info[ci].V_samp_factor = (c) & 15; - - int quant_tbl_no; - if (!m_cinfo.m_src.GetByte(out quant_tbl_no)) - return false; - - m_cinfo.Comp_info[ci].Quant_tbl_no = quant_tbl_no; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_SOF_COMPONENT, m_cinfo.Comp_info[ci].Component_id, - m_cinfo.Comp_info[ci].H_samp_factor, m_cinfo.Comp_info[ci].V_samp_factor, - m_cinfo.Comp_info[ci].Quant_tbl_no); - } - - m_cinfo.m_marker.m_saw_SOF = true; - return true; - } - - /// - /// Process a SOS marker - /// - private bool get_sos() - { - if (!m_cinfo.m_marker.m_saw_SOF) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_SOS_NO_SOF); - - int length; - if (!m_cinfo.m_src.GetTwoBytes(out length)) - return false; - - /* Number of components */ - int n; - if (!m_cinfo.m_src.GetByte(out n)) - return false; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_SOS, n); - - if (length != (n * 2 + 6) || n < 1 || n > JpegConstants.MAX_COMPS_IN_SCAN) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_LENGTH); - - m_cinfo.m_comps_in_scan = n; - - /* Collect the component-spec parameters */ - - for (int i = 0; i < n; i++) - { - int cc; - if (!m_cinfo.m_src.GetByte(out cc)) - return false; - - int c; - if (!m_cinfo.m_src.GetByte(out c)) - return false; - - bool idFound = false; - int foundIndex = -1; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - if (cc == m_cinfo.Comp_info[ci].Component_id) - { - foundIndex = ci; - idFound = true; - break; - } - } - - if (!idFound) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_COMPONENT_ID, cc); - - m_cinfo.m_cur_comp_info[i] = foundIndex; - m_cinfo.Comp_info[foundIndex].Dc_tbl_no = (c >> 4) & 15; - m_cinfo.Comp_info[foundIndex].Ac_tbl_no = (c) & 15; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_SOS_COMPONENT, cc, - m_cinfo.Comp_info[foundIndex].Dc_tbl_no, m_cinfo.Comp_info[foundIndex].Ac_tbl_no); - } - - /* Collect the additional scan parameters Ss, Se, Ah/Al. */ - int temp; - if (!m_cinfo.m_src.GetByte(out temp)) - return false; - - m_cinfo.m_Ss = temp; - if (!m_cinfo.m_src.GetByte(out temp)) - return false; - - m_cinfo.m_Se = temp; - if (!m_cinfo.m_src.GetByte(out temp)) - return false; - - m_cinfo.m_Ah = (temp >> 4) & 15; - m_cinfo.m_Al = (temp) & 15; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_SOS_PARAMS, m_cinfo.m_Ss, m_cinfo.m_Se, m_cinfo.m_Ah, m_cinfo.m_Al); - - /* Prepare to scan data & restart markers */ - m_cinfo.m_marker.m_next_restart_num = 0; - - /* Count another SOS marker */ - m_cinfo.m_input_scan_number++; - return true; - } - - /// - /// Process a DHT marker - /// - private bool get_dht() - { - int length; - if (!m_cinfo.m_src.GetTwoBytes(out length)) - return false; - - length -= 2; - - byte[] bits = new byte[17]; - byte[] huffval = new byte[256]; - while (length > 16) - { - int index; - if (!m_cinfo.m_src.GetByte(out index)) - return false; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_DHT, index); - - bits[0] = 0; - int count = 0; - for (int i = 1; i <= 16; i++) - { - int temp = 0; - if (!m_cinfo.m_src.GetByte(out temp)) - return false; - - bits[i] = (byte) temp; - count += bits[i]; - } - - length -= 1 + 16; - - m_cinfo.TRACEMS(2, J_MESSAGE_CODE.JTRC_HUFFBITS, bits[1], bits[2], bits[3], bits[4], bits[5], bits[6], bits[7], bits[8]); - m_cinfo.TRACEMS(2, J_MESSAGE_CODE.JTRC_HUFFBITS, bits[9], bits[10], bits[11], bits[12], bits[13], bits[14], bits[15], bits[16]); - - /* Here we just do minimal validation of the counts to avoid walking - * off the end of our table space. huff_entropy_decoder will check more carefully. - */ - if (count > 256 || count > length) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - - for (int i = 0; i < count; i++) - { - int temp = 0; - if (!m_cinfo.m_src.GetByte(out temp)) - return false; - - huffval[i] = (byte) temp; - } - - length -= count; - - JHUFF_TBL htblptr = null; - if ((index & 0x10) != 0) - { - /* AC table definition */ - index -= 0x10; - if (m_cinfo.m_ac_huff_tbl_ptrs[index] == null) - m_cinfo.m_ac_huff_tbl_ptrs[index] = new JHUFF_TBL(); - - htblptr = m_cinfo.m_ac_huff_tbl_ptrs[index]; - } - else - { - /* DC table definition */ - if (m_cinfo.m_dc_huff_tbl_ptrs[index] == null) - m_cinfo.m_dc_huff_tbl_ptrs[index] = new JHUFF_TBL(); - - htblptr = m_cinfo.m_dc_huff_tbl_ptrs[index]; - } - - if (index < 0 || index >= JpegConstants.NUM_HUFF_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_DHT_INDEX, index); - - Buffer.BlockCopy(bits, 0, htblptr.Bits, 0, htblptr.Bits.Length); - Buffer.BlockCopy(huffval, 0, htblptr.Huffval, 0, htblptr.Huffval.Length); - } - - if (length != 0) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_LENGTH); - - return true; - } - - /// - /// Process a DQT marker - /// - private bool get_dqt() - { - int length; - if (!m_cinfo.m_src.GetTwoBytes(out length)) - return false; - - length -= 2; - while (length > 0) - { - int n; - if (!m_cinfo.m_src.GetByte(out n)) - return false; - - int prec = n >> 4; - n &= 0x0F; - - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_DQT, n, prec); - - if (n >= JpegConstants.NUM_QUANT_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_DQT_INDEX, n); - - if (m_cinfo.m_quant_tbl_ptrs[n] == null) - m_cinfo.m_quant_tbl_ptrs[n] = new JQUANT_TBL(); - - JQUANT_TBL quant_ptr = m_cinfo.m_quant_tbl_ptrs[n]; - - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - { - int tmp; - if (prec != 0) - { - int temp = 0; - if (!m_cinfo.m_src.GetTwoBytes(out temp)) - return false; - - tmp = temp; - } - else - { - int temp = 0; - if (!m_cinfo.m_src.GetByte(out temp)) - return false; - - tmp = temp; - } - - /* We convert the zigzag-order table to natural array order. */ - quant_ptr.quantval[JpegUtils.jpeg_natural_order[i]] = (short) tmp; - } - - if (m_cinfo.m_err.m_trace_level >= 2) - { - for (int i = 0; i < JpegConstants.DCTSIZE2; i += 8) - { - m_cinfo.TRACEMS(2, J_MESSAGE_CODE.JTRC_QUANTVALS, quant_ptr.quantval[i], - quant_ptr.quantval[i + 1], quant_ptr.quantval[i + 2], - quant_ptr.quantval[i + 3], quant_ptr.quantval[i + 4], - quant_ptr.quantval[i + 5], quant_ptr.quantval[i + 6], quant_ptr.quantval[i + 7]); - } - } - - length -= JpegConstants.DCTSIZE2 + 1; - if (prec != 0) - length -= JpegConstants.DCTSIZE2; - } - - if (length != 0) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_LENGTH); - - return true; - } - - /// - /// Process a DRI marker - /// - private bool get_dri() - { - int length; - if (!m_cinfo.m_src.GetTwoBytes(out length)) - return false; - - if (length != 4) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_LENGTH); - - int temp = 0; - if (!m_cinfo.m_src.GetTwoBytes(out temp)) - return false; - - int tmp = temp; - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_DRI, tmp); - m_cinfo.m_restart_interval = tmp; - - return true; - } - - /// - /// Like next_marker, but used to obtain the initial SOI marker. - /// For this marker, we do not allow preceding garbage or fill; otherwise, - /// we might well scan an entire input file before realizing it ain't JPEG. - /// If an application wants to process non-JFIF files, it must seek to the - /// SOI before calling the JPEG library. - /// - private bool first_marker() - { - int c; - if (!m_cinfo.m_src.GetByte(out c)) - return false; - - int c2; - if (!m_cinfo.m_src.GetByte(out c2)) - return false; - - if (c != 0xFF || c2 != (int)JPEG_MARKER.SOI) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_SOI, c, c2); - - m_cinfo.m_unread_marker = c2; - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_marker_writer.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_marker_writer.cs deleted file mode 100644 index b1295d7a..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_marker_writer.cs +++ /dev/null @@ -1,519 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains routines to write JPEG datastream markers. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Marker writing - /// - class jpeg_marker_writer - { - private jpeg_compress_struct m_cinfo; - private int m_last_restart_interval; /* last DRI value emitted; 0 after SOI */ - - public jpeg_marker_writer(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - } - - /// - /// Write datastream header. - /// This consists of an SOI and optional APPn markers. - /// We recommend use of the JFIF marker, but not the Adobe marker, - /// when using YCbCr or grayscale data. The JFIF marker should NOT - /// be used for any other JPEG colorspace. The Adobe marker is helpful - /// to distinguish RGB, CMYK, and YCCK colorspaces. - /// Note that an application can write additional header markers after - /// jpeg_start_compress returns. - /// - public void write_file_header() - { - emit_marker(JPEG_MARKER.SOI); /* first the SOI */ - - /* SOI is defined to reset restart interval to 0 */ - m_last_restart_interval = 0; - - if (m_cinfo.m_write_JFIF_header) /* next an optional JFIF APP0 */ - emit_jfif_app0(); - if (m_cinfo.m_write_Adobe_marker) /* next an optional Adobe APP14 */ - emit_adobe_app14(); - } - - /// - /// Write frame header. - /// This consists of DQT and SOFn markers. - /// Note that we do not emit the SOF until we have emitted the DQT(s). - /// This avoids compatibility problems with incorrect implementations that - /// try to error-check the quant table numbers as soon as they see the SOF. - /// - public void write_frame_header() - { - /* Emit DQT for each quantization table. - * Note that emit_dqt() suppresses any duplicate tables. - */ - int prec = 0; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - prec += emit_dqt(m_cinfo.Component_info[ci].Quant_tbl_no); - - /* now prec is nonzero iff there are any 16-bit quant tables. */ - - /* Check for a non-baseline specification. - * Note we assume that Huffman table numbers won't be changed later. - */ - bool is_baseline; - if (m_cinfo.m_progressive_mode || m_cinfo.m_data_precision != 8) - { - is_baseline = false; - } - else - { - is_baseline = true; - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - if (m_cinfo.Component_info[ci].Dc_tbl_no > 1 || m_cinfo.Component_info[ci].Ac_tbl_no > 1) - is_baseline = false; - } - - if (prec != 0 && is_baseline) - { - is_baseline = false; - /* If it's baseline except for quantizer size, warn the user */ - m_cinfo.TRACEMS(0, J_MESSAGE_CODE.JTRC_16BIT_TABLES); - } - } - - /* Emit the proper SOF marker */ - if (m_cinfo.m_progressive_mode) - emit_sof(JPEG_MARKER.SOF2); /* SOF code for progressive Huffman */ - else if (is_baseline) - emit_sof(JPEG_MARKER.SOF0); /* SOF code for baseline implementation */ - else - emit_sof(JPEG_MARKER.SOF1); /* SOF code for non-baseline Huffman file */ - } - - /// - /// Write scan header. - /// This consists of DHT or DAC markers, optional DRI, and SOS. - /// Compressed data will be written following the SOS. - /// - public void write_scan_header() - { - /* Emit Huffman tables. - * Note that emit_dht() suppresses any duplicate tables. - */ - for (int i = 0; i < m_cinfo.m_comps_in_scan; i++) - { - int ac_tbl_no = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[i]].Ac_tbl_no; - int dc_tbl_no = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[i]].Dc_tbl_no; - if (m_cinfo.m_progressive_mode) - { - /* Progressive mode: only DC or only AC tables are used in one scan */ - if (m_cinfo.m_Ss == 0) - { - if (m_cinfo.m_Ah == 0) - { - /* DC needs no table for refinement scan */ - emit_dht(dc_tbl_no, false); - } - } - else - { - emit_dht(ac_tbl_no, true); - } - } - else - { - /* Sequential mode: need both DC and AC tables */ - emit_dht(dc_tbl_no, false); - emit_dht(ac_tbl_no, true); - } - } - - /* Emit DRI if required --- note that DRI value could change for each scan. - * We avoid wasting space with unnecessary DRIs, however. - */ - if (m_cinfo.m_restart_interval != m_last_restart_interval) - { - emit_dri(); - m_last_restart_interval = m_cinfo.m_restart_interval; - } - - emit_sos(); - } - - /// - /// Write datastream trailer. - /// - public void write_file_trailer() - { - emit_marker(JPEG_MARKER.EOI); - } - - /// - /// Write an abbreviated table-specification datastream. - /// This consists of SOI, DQT and DHT tables, and EOI. - /// Any table that is defined and not marked sent_table = true will be - /// emitted. Note that all tables will be marked sent_table = true at exit. - /// - public void write_tables_only() - { - emit_marker(JPEG_MARKER.SOI); - - for (int i = 0; i < JpegConstants.NUM_QUANT_TBLS; i++) - { - if (m_cinfo.m_quant_tbl_ptrs[i] != null) - emit_dqt(i); - } - - for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) - { - if (m_cinfo.m_dc_huff_tbl_ptrs[i] != null) - emit_dht(i, false); - if (m_cinfo.m_ac_huff_tbl_ptrs[i] != null) - emit_dht(i, true); - } - - emit_marker(JPEG_MARKER.EOI); - } - - ////////////////////////////////////////////////////////////////////////// - // These routines allow writing an arbitrary marker with parameters. - // The only intended use is to emit COM or APPn markers after calling - // write_file_header and before calling write_frame_header. - // Other uses are not guaranteed to produce desirable results. - // Counting the parameter bytes properly is the caller's responsibility. - - /// - /// Emit an arbitrary marker header - /// - public void write_marker_header(int marker, int datalen) - { - if (datalen > 65533) /* safety check */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_LENGTH); - - emit_marker((JPEG_MARKER) marker); - - emit_2bytes(datalen + 2); /* total length */ - } - - /// - /// Emit one byte of marker parameters following write_marker_header - /// - public void write_marker_byte(byte val) - { - emit_byte(val); - } - - ////////////////////////////////////////////////////////////////////////// - // Routines to write specific marker types. - // - - /// - /// Emit a SOS marker - /// - private void emit_sos() - { - emit_marker(JPEG_MARKER.SOS); - - emit_2bytes(2 * m_cinfo.m_comps_in_scan + 2 + 1 + 3); /* length */ - - emit_byte(m_cinfo.m_comps_in_scan); - - for (int i = 0; i < m_cinfo.m_comps_in_scan; i++) - { - int componentIndex = m_cinfo.m_cur_comp_info[i]; - emit_byte(m_cinfo.Component_info[componentIndex].Component_id); - - int td = m_cinfo.Component_info[componentIndex].Dc_tbl_no; - int ta = m_cinfo.Component_info[componentIndex].Ac_tbl_no; - if (m_cinfo.m_progressive_mode) - { - /* Progressive mode: only DC or only AC tables are used in one scan; - * furthermore, Huffman coding of DC refinement uses no table at all. - * We emit 0 for unused field(s); this is recommended by the P&M text - * but does not seem to be specified in the standard. - */ - if (m_cinfo.m_Ss == 0) - { - /* DC scan */ - ta = 0; - if (m_cinfo.m_Ah != 0) - { - /* no DC table either */ - td = 0; - } - } - else - { - /* AC scan */ - td = 0; - } - } - - emit_byte((td << 4) + ta); - } - - emit_byte(m_cinfo.m_Ss); - emit_byte(m_cinfo.m_Se); - emit_byte((m_cinfo.m_Ah << 4) + m_cinfo.m_Al); - } - - /// - /// Emit a SOF marker - /// - private void emit_sof(JPEG_MARKER code) - { - emit_marker(code); - - emit_2bytes(3 * m_cinfo.m_num_components + 2 + 5 + 1); /* length */ - - /* Make sure image isn't bigger than SOF field can handle */ - if (m_cinfo.m_image_height > 65535 || m_cinfo.m_image_width > 65535) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG, 65535); - - emit_byte(m_cinfo.m_data_precision); - emit_2bytes(m_cinfo.m_image_height); - emit_2bytes(m_cinfo.m_image_width); - - emit_byte(m_cinfo.m_num_components); - - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[ci]; - emit_byte(componentInfo.Component_id); - emit_byte((componentInfo.H_samp_factor << 4) + componentInfo.V_samp_factor); - emit_byte(componentInfo.Quant_tbl_no); - } - } - - /// - /// Emit an Adobe APP14 marker - /// - private void emit_adobe_app14() - { - /* - * Length of APP14 block (2 bytes) - * Block ID (5 bytes - ASCII "Adobe") - * Version Number (2 bytes - currently 100) - * Flags0 (2 bytes - currently 0) - * Flags1 (2 bytes - currently 0) - * Color transform (1 byte) - * - * Although Adobe TN 5116 mentions Version = 101, all the Adobe files - * now in circulation seem to use Version = 100, so that's what we write. - * - * We write the color transform byte as 1 if the JPEG color space is - * YCbCr, 2 if it's YCCK, 0 otherwise. Adobe's definition has to do with - * whether the encoder performed a transformation, which is pretty useless. - */ - - emit_marker(JPEG_MARKER.APP14); - - emit_2bytes(2 + 5 + 2 + 2 + 2 + 1); /* length */ - - emit_byte(0x41); /* Identifier: ASCII "Adobe" */ - emit_byte(0x64); - emit_byte(0x6F); - emit_byte(0x62); - emit_byte(0x65); - emit_2bytes(100); /* Version */ - emit_2bytes(0); /* Flags0 */ - emit_2bytes(0); /* Flags1 */ - switch (m_cinfo.m_jpeg_color_space) - { - case J_COLOR_SPACE.JCS_YCbCr: - emit_byte(1); /* Color transform = 1 */ - break; - case J_COLOR_SPACE.JCS_YCCK: - emit_byte(2); /* Color transform = 2 */ - break; - default: - emit_byte(0); /* Color transform = 0 */ - break; - } - } - - /// - /// Emit a DRI marker - /// - private void emit_dri() - { - emit_marker(JPEG_MARKER.DRI); - - emit_2bytes(4); /* fixed length */ - - emit_2bytes(m_cinfo.m_restart_interval); - } - - /// - /// Emit a DHT marker - /// - private void emit_dht(int index, bool is_ac) - { - JHUFF_TBL htbl = m_cinfo.m_dc_huff_tbl_ptrs[index]; - if (is_ac) - { - htbl = m_cinfo.m_ac_huff_tbl_ptrs[index]; - index += 0x10; /* output index has AC bit set */ - } - - if (htbl == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, index); - - if (!htbl.Sent_table) - { - emit_marker(JPEG_MARKER.DHT); - - int length = 0; - for (int i = 1; i <= 16; i++) - length += htbl.Bits[i]; - - emit_2bytes(length + 2 + 1 + 16); - emit_byte(index); - - for (int i = 1; i <= 16; i++) - emit_byte(htbl.Bits[i]); - - for (int i = 0; i < length; i++) - emit_byte(htbl.Huffval[i]); - - htbl.Sent_table = true; - } - } - - /// - /// Emit a DQT marker - /// - /// The index. - /// the precision used (0 = 8bits, 1 = 16bits) for baseline checking - private int emit_dqt(int index) - { - JQUANT_TBL qtbl = m_cinfo.m_quant_tbl_ptrs[index]; - if (qtbl == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, index); - - int prec = 0; - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - { - if (qtbl.quantval[i] > 255) - prec = 1; - } - - if (!qtbl.Sent_table) - { - emit_marker(JPEG_MARKER.DQT); - - emit_2bytes(prec != 0 ? JpegConstants.DCTSIZE2 * 2 + 1 + 2 : JpegConstants.DCTSIZE2 + 1 + 2); - - emit_byte(index + (prec << 4)); - - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - { - /* The table entries must be emitted in zigzag order. */ - int qval = qtbl.quantval[JpegUtils.jpeg_natural_order[i]]; - - if (prec != 0) - emit_byte(qval >> 8); - - emit_byte(qval & 0xFF); - } - - qtbl.Sent_table = true; - } - - return prec; - } - - /// - /// Emit a JFIF-compliant APP0 marker - /// - private void emit_jfif_app0() - { - /* - * Length of APP0 block (2 bytes) - * Block ID (4 bytes - ASCII "JFIF") - * Zero byte (1 byte to terminate the ID string) - * Version Major, Minor (2 bytes - major first) - * Units (1 byte - 0x00 = none, 0x01 = inch, 0x02 = cm) - * Xdpu (2 bytes - dots per unit horizontal) - * Ydpu (2 bytes - dots per unit vertical) - * Thumbnail X size (1 byte) - * Thumbnail Y size (1 byte) - */ - - emit_marker(JPEG_MARKER.APP0); - - emit_2bytes(2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); /* length */ - - emit_byte(0x4A); /* Identifier: ASCII "JFIF" */ - emit_byte(0x46); - emit_byte(0x49); - emit_byte(0x46); - emit_byte(0); - emit_byte(m_cinfo.m_JFIF_major_version); /* Version fields */ - emit_byte(m_cinfo.m_JFIF_minor_version); - emit_byte((int)m_cinfo.m_density_unit); /* Pixel size information */ - emit_2bytes(m_cinfo.m_X_density); - emit_2bytes(m_cinfo.m_Y_density); - emit_byte(0); /* No thumbnail image */ - emit_byte(0); - } - - ////////////////////////////////////////////////////////////////////////// - // Basic output routines. - // - // Note that we do not support suspension while writing a marker. - // Therefore, an application using suspension must ensure that there is - // enough buffer space for the initial markers (typ. 600-700 bytes) before - // calling jpeg_start_compress, and enough space to write the trailing EOI - // (a few bytes) before calling jpeg_finish_compress. Multipass compression - // modes are not supported at all with suspension, so those two are the only - // points where markers will be written. - - - /// - /// Emit a marker code - /// - private void emit_marker(JPEG_MARKER mark) - { - emit_byte(0xFF); - emit_byte((int)mark); - } - - /// - /// Emit a 2-byte integer; these are always MSB first in JPEG files - /// - private void emit_2bytes(int value) - { - emit_byte((value >> 8) & 0xFF); - emit_byte(value & 0xFF); - } - - /// - /// Emit a byte - /// - private void emit_byte(int val) - { - if (!m_cinfo.m_dest.emit_byte(val)) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CANT_SUSPEND); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_scan_info.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_scan_info.cs deleted file mode 100644 index 1d0e9406..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_scan_info.cs +++ /dev/null @@ -1,32 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// The script for encoding a multiple-scan file is an array of these: - /// - class jpeg_scan_info - { - public int comps_in_scan; /* number of components encoded in this scan */ - public int[] component_index = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ - public int Ss; - public int Se; /* progressive JPEG spectral selection parms */ - public int Ah; - public int Al; /* progressive JPEG successive approx. parms */ - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_upsampler.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_upsampler.cs deleted file mode 100644 index f80c05e9..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/jpeg_upsampler.cs +++ /dev/null @@ -1,35 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Upsampling (note that upsampler must also call color converter) - /// - abstract class jpeg_upsampler - { - protected bool m_need_context_rows; /* true if need rows above & below */ - - public abstract void start_pass(); - public abstract void upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail); - - public bool NeedContextRows() - { - return m_need_context_rows; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_1pass_cquantizer.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_1pass_cquantizer.cs deleted file mode 100644 index 257b3f7c..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_1pass_cquantizer.cs +++ /dev/null @@ -1,858 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains 1-pass color quantization (color mapping) routines. - * These routines provide mapping to a fixed color map using equally spaced - * color values. Optional Floyd-Steinberg or ordered dithering is available. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// The main purpose of 1-pass quantization is to provide a fast, if not very - /// high quality, colormapped output capability. A 2-pass quantizer usually - /// gives better visual quality; however, for quantized grayscale output this - /// quantizer is perfectly adequate. Dithering is highly recommended with this - /// quantizer, though you can turn it off if you really want to. - /// - /// In 1-pass quantization the colormap must be chosen in advance of seeing the - /// image. We use a map consisting of all combinations of Ncolors[i] color - /// values for the i'th component. The Ncolors[] values are chosen so that - /// their product, the total number of colors, is no more than that requested. - /// (In most cases, the product will be somewhat less.) - /// - /// Since the colormap is orthogonal, the representative value for each color - /// component can be determined without considering the other components; - /// then these indexes can be combined into a colormap index by a standard - /// N-dimensional-array-subscript calculation. Most of the arithmetic involved - /// can be precalculated and stored in the lookup table colorindex[]. - /// colorindex[i][j] maps pixel value j in component i to the nearest - /// representative value (grid plane) for that component; this index is - /// multiplied by the array stride for component i, so that the - /// index of the colormap entry closest to a given pixel value is just - /// sum( colorindex[component-number][pixel-component-value] ) - /// Aside from being fast, this scheme allows for variable spacing between - /// representative values with no additional lookup cost. - /// - /// If gamma correction has been applied in color conversion, it might be wise - /// to adjust the color grid spacing so that the representative colors are - /// equidistant in linear space. At this writing, gamma correction is not - /// implemented, so nothing is done here. - /// - /// - /// Declarations for Floyd-Steinberg dithering. - /// - /// Errors are accumulated into the array fserrors[], at a resolution of - /// 1/16th of a pixel count. The error at a given pixel is propagated - /// to its not-yet-processed neighbors using the standard F-S fractions, - /// ... (here) 7/16 - /// 3/16 5/16 1/16 - /// We work left-to-right on even rows, right-to-left on odd rows. - /// - /// We can get away with a single array (holding one row's worth of errors) - /// by using it to store the current row's errors at pixel columns not yet - /// processed, but the next row's errors at columns already processed. We - /// need only a few extra variables to hold the errors immediately around the - /// current column. (If we are lucky, those variables are in registers, but - /// even if not, they're probably cheaper to access than array elements are.) - /// - /// The fserrors[] array is indexed [component#][position]. - /// We provide (#columns + 2) entries per component; the extra entry at each - /// end saves us from special-casing the first and last pixels. - /// - /// - /// Declarations for ordered dithering. - /// - /// We use a standard 16x16 ordered dither array. The basic concept of ordered - /// dithering is described in many references, for instance Dale Schumacher's - /// chapter II.2 of Graphics Gems II (James Arvo, ed. Academic Press, 1991). - /// In place of Schumacher's comparisons against a "threshold" value, we add a - /// "dither" value to the input pixel and then round the result to the nearest - /// output value. The dither value is equivalent to (0.5 - threshold) times - /// the distance between output values. For ordered dithering, we assume that - /// the output colors are equally spaced; if not, results will probably be - /// worse, since the dither may be too much or too little at a given point. - /// - /// The normal calculation would be to form pixel value + dither, range-limit - /// this to 0..MAXJSAMPLE, and then index into the colorindex table as usual. - /// We can skip the separate range-limiting step by extending the colorindex - /// table in both directions. - /// - class my_1pass_cquantizer : jpeg_color_quantizer - { - private enum QuantizerType - { - color_quantizer3, - color_quantizer, - quantize3_ord_dither_quantizer, - quantize_ord_dither_quantizer, - quantize_fs_dither_quantizer - } - - private static int[] RGB_order = { JpegConstants.RGB_GREEN, JpegConstants.RGB_RED, JpegConstants.RGB_BLUE }; - private const int MAX_Q_COMPS = 4; /* max components I can handle */ - - private const int ODITHER_SIZE = 16; /* dimension of dither matrix */ - - /* NB: if ODITHER_SIZE is not a power of 2, ODITHER_MASK uses will break */ - private const int ODITHER_CELLS = (ODITHER_SIZE * ODITHER_SIZE); /* # cells in matrix */ - private const int ODITHER_MASK = (ODITHER_SIZE-1); /* mask for wrapping around counters */ - - /* Bayer's order-4 dither array. Generated by the code given in - * Stephen Hawley's article "Ordered Dithering" in Graphics Gems I. - * The values in this array must range from 0 to ODITHER_CELLS-1. - */ - private static byte[][] base_dither_matrix = new byte[][] - { - new byte[] { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, - new byte[] { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, - new byte[] { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, - new byte[] { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, - new byte[] { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, - new byte[] { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, - new byte[] { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, - new byte[] { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, - new byte[] { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, - new byte[] { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, - new byte[] { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, - new byte[] { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, - new byte[] { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, - new byte[] { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, - new byte[] { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, - new byte[] { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } - }; - - private QuantizerType m_quantizer; - - private jpeg_decompress_struct m_cinfo; - - /* Initially allocated colormap is saved here */ - private byte[][] m_sv_colormap; /* The color map as a 2-D pixel array */ - private int m_sv_actual; /* number of entries in use */ - - private byte[][] m_colorindex; /* Precomputed mapping for speed */ - private int[] m_colorindexOffset; - - /* colorindex[i][j] = index of color closest to pixel value j in component i, - * premultiplied as described above. Since colormap indexes must fit into - * bytes, the entries of this array will too. - */ - private bool m_is_padded; /* is the colorindex padded for odither? */ - - private int[] m_Ncolors = new int[MAX_Q_COMPS]; /* # of values alloced to each component */ - - /* Variables for ordered dithering */ - private int m_row_index; /* cur row's vertical index in dither matrix */ - private int[][][] m_odither = new int[MAX_Q_COMPS][][]; /* one dither array per component */ - - /* Variables for Floyd-Steinberg dithering */ - private short[][] m_fserrors = new short[MAX_Q_COMPS][]; /* accumulated errors */ - private bool m_on_odd_row; /* flag to remember which row we are on */ - - /// - /// Module initialization routine for 1-pass color quantization. - /// - /// The cinfo. - public my_1pass_cquantizer(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - m_fserrors[0] = null; /* Flag FS workspace not allocated */ - m_odither[0] = null; /* Also flag odither arrays not allocated */ - - /* Make sure my internal arrays won't overflow */ - if (cinfo.m_out_color_components > MAX_Q_COMPS) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_COMPONENTS, MAX_Q_COMPS); - - /* Make sure colormap indexes can be represented by JSAMPLEs */ - if (cinfo.m_desired_number_of_colors > (JpegConstants.MAXJSAMPLE + 1)) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_MANY_COLORS, JpegConstants.MAXJSAMPLE + 1); - - /* Create the colormap and color index table. */ - create_colormap(); - create_colorindex(); - - /* Allocate Floyd-Steinberg workspace now if requested. - * We do this now since it is FAR storage and may affect the memory - * manager's space calculations. If the user changes to FS dither - * mode in a later pass, we will allocate the space then, and will - * possibly overrun the max_memory_to_use setting. - */ - if (cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_FS) - alloc_fs_workspace(); - } - - /// - /// Initialize for one-pass color quantization. - /// - public virtual void start_pass(bool is_pre_scan) - { - /* Install my colormap. */ - m_cinfo.m_colormap = m_sv_colormap; - m_cinfo.m_actual_number_of_colors = m_sv_actual; - - /* Initialize for desired dithering mode. */ - switch (m_cinfo.m_dither_mode) - { - case J_DITHER_MODE.JDITHER_NONE: - if (m_cinfo.m_out_color_components == 3) - m_quantizer = QuantizerType.color_quantizer3; - else - m_quantizer = QuantizerType.color_quantizer; - - break; - case J_DITHER_MODE.JDITHER_ORDERED: - if (m_cinfo.m_out_color_components == 3) - m_quantizer = QuantizerType.quantize3_ord_dither_quantizer; - else - m_quantizer = QuantizerType.quantize3_ord_dither_quantizer; - - /* initialize state for ordered dither */ - m_row_index = 0; - - /* If user changed to ordered dither from another mode, - * we must recreate the color index table with padding. - * This will cost extra space, but probably isn't very likely. - */ - if (!m_is_padded) - create_colorindex(); - - /* Create ordered-dither tables if we didn't already. */ - if (m_odither[0] == null) - create_odither_tables(); - - break; - case J_DITHER_MODE.JDITHER_FS: - m_quantizer = QuantizerType.quantize_fs_dither_quantizer; - - /* initialize state for F-S dither */ - m_on_odd_row = false; - - /* Allocate Floyd-Steinberg workspace if didn't already. */ - if (m_fserrors[0] == null) - alloc_fs_workspace(); - - /* Initialize the propagated errors to zero. */ - int arraysize = m_cinfo.m_output_width + 2; - for (int i = 0; i < m_cinfo.m_out_color_components; i++) - Array.Clear(m_fserrors[i], 0, arraysize); - - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOT_COMPILED); - break; - } - } - - public virtual void color_quantize(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - switch (m_quantizer) - { - case QuantizerType.color_quantizer3: - quantize3(input_buf, in_row, output_buf, out_row, num_rows); - break; - case QuantizerType.color_quantizer: - quantize(input_buf, in_row, output_buf, out_row, num_rows); - break; - case QuantizerType.quantize3_ord_dither_quantizer: - quantize3_ord_dither(input_buf, in_row, output_buf, out_row, num_rows); - break; - case QuantizerType.quantize_ord_dither_quantizer: - quantize_ord_dither(input_buf, in_row, output_buf, out_row, num_rows); - break; - case QuantizerType.quantize_fs_dither_quantizer: - quantize_fs_dither(input_buf, in_row, output_buf, out_row, num_rows); - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - break; - } - } - - /// - /// Finish up at the end of the pass. - /// - public virtual void finish_pass() - { - /* no work in 1-pass case */ - } - - /// - /// Switch to a new external colormap between output passes. - /// Shouldn't get to this! - /// - public virtual void new_color_map() - { - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_MODE_CHANGE); - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// General case, no dithering. - /// - private void quantize(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - int nc = m_cinfo.m_out_color_components; - - for (int row = 0; row < num_rows; row++) - { - int inIndex = 0; - int inRow = in_row + row; - - int outIndex = 0; - int outRow = out_row + row; - - for (int col = m_cinfo.m_output_width; col > 0; col--) - { - int pixcode = 0; - for (int ci = 0; ci < nc; ci++) - { - pixcode += m_colorindex[ci][m_colorindexOffset[ci] + input_buf[inRow][inIndex]]; - inIndex++; - } - - output_buf[outRow][outIndex] = (byte)pixcode; - outIndex++; - } - } - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// Fast path for out_color_components==3, no dithering - /// - private void quantize3(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - int width = m_cinfo.m_output_width; - - for (int row = 0; row < num_rows; row++) - { - int inIndex = 0; - int inRow = in_row + row; - - int outIndex = 0; - int outRow = out_row + row; - - for (int col = width; col > 0; col--) - { - int pixcode = m_colorindex[0][m_colorindexOffset[0] + input_buf[inRow][inIndex]]; - inIndex++; - - pixcode += m_colorindex[1][m_colorindexOffset[1] + input_buf[inRow][inIndex]]; - inIndex++; - - pixcode += m_colorindex[2][m_colorindexOffset[2] + input_buf[inRow][inIndex]]; - inIndex++; - - output_buf[outRow][outIndex] = (byte)pixcode; - outIndex++; - } - } - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// General case, with ordered dithering. - /// - private void quantize_ord_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - int nc = m_cinfo.m_out_color_components; - int width = m_cinfo.m_output_width; - - for (int row = 0; row < num_rows; row++) - { - /* Initialize output values to 0 so can process components separately */ - Array.Clear(output_buf[out_row + row], 0, width); - - int row_index = m_row_index; - for (int ci = 0; ci < nc; ci++) - { - int inputIndex = ci; - int outIndex = 0; - int outRow = out_row + row; - - int col_index = 0; - for (int col = width; col > 0; col--) - { - /* Form pixel value + dither, range-limit to 0..MAXJSAMPLE, - * select output value, accumulate into output code for this pixel. - * Range-limiting need not be done explicitly, as we have extended - * the colorindex table to produce the right answers for out-of-range - * inputs. The maximum dither is +- MAXJSAMPLE; this sets the - * required amount of padding. - */ - output_buf[outRow][outIndex] += m_colorindex[ci][m_colorindexOffset[ci] + input_buf[in_row + row][inputIndex] + m_odither[ci][row_index][col_index]]; - inputIndex += nc; - outIndex++; - col_index = (col_index + 1) & ODITHER_MASK; - } - } - - /* Advance row index for next row */ - row_index = (row_index + 1) & ODITHER_MASK; - m_row_index = row_index; - } - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// Fast path for out_color_components==3, with ordered dithering - /// - private void quantize3_ord_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - int width = m_cinfo.m_output_width; - - for (int row = 0; row < num_rows; row++) - { - int row_index = m_row_index; - int inRow = in_row + row; - int inIndex = 0; - - int outIndex = 0; - int outRow = out_row + row; - - int col_index = 0; - for (int col = width; col > 0; col--) - { - int pixcode = m_colorindex[0][m_colorindexOffset[0] + input_buf[inRow][inIndex] + m_odither[0][row_index][col_index]]; - inIndex++; - - pixcode += m_colorindex[1][m_colorindexOffset[1] + input_buf[inRow][inIndex] + m_odither[1][row_index][col_index]]; - inIndex++; - - pixcode += m_colorindex[2][m_colorindexOffset[2] + input_buf[inRow][inIndex] + m_odither[2][row_index][col_index]]; - inIndex++; - - output_buf[outRow][outIndex] = (byte)pixcode; - outIndex++; - - col_index = (col_index + 1) & ODITHER_MASK; - } - - row_index = (row_index + 1) & ODITHER_MASK; - m_row_index = row_index; - } - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// General case, with Floyd-Steinberg dithering - /// - private void quantize_fs_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - int nc = m_cinfo.m_out_color_components; - int width = m_cinfo.m_output_width; - - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset; - - for (int row = 0; row < num_rows; row++) - { - /* Initialize output values to 0 so can process components separately */ - Array.Clear(output_buf[out_row + row], 0, width); - - for (int ci = 0; ci < nc; ci++) - { - int inRow = in_row + row; - int inIndex = ci; - - int outIndex = 0; - int outRow = out_row + row; - - int errorIndex = 0; - int dir; /* 1 for left-to-right, -1 for right-to-left */ - if (m_on_odd_row) - { - /* work right to left in this row */ - inIndex += (width - 1) * nc; /* so point to rightmost pixel */ - outIndex += width - 1; - dir = -1; - errorIndex = width + 1; /* => entry after last column */ - } - else - { - /* work left to right in this row */ - dir = 1; - errorIndex = 0; /* => entry before first column */ - } - int dirnc = dir * nc; - - /* Preset error values: no error propagated to first pixel from left */ - int cur = 0; - /* and no error propagated to row below yet */ - int belowerr = 0; - int bpreverr = 0; - - for (int col = width; col > 0; col--) - { - /* cur holds the error propagated from the previous pixel on the - * current line. Add the error propagated from the previous line - * to form the complete error correction term for this pixel, and - * round the error term (which is expressed * 16) to an integer. - * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct - * for either sign of the error value. - * Note: errorIndex is for *previous* column's array entry. - */ - cur = JpegUtils.RIGHT_SHIFT(cur + m_fserrors[ci][errorIndex + dir] + 8, 4); - - /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. - * The maximum error is +- MAXJSAMPLE; this sets the required size - * of the range_limit array. - */ - cur += input_buf[inRow][inIndex]; - cur = limit[limitOffset + cur]; - - /* Select output value, accumulate into output code for this pixel */ - int pixcode = m_colorindex[ci][m_colorindexOffset[ci] + cur]; - output_buf[outRow][outIndex] += (byte)pixcode; - - /* Compute actual representation error at this pixel */ - /* Note: we can do this even though we don't have the final */ - /* pixel code, because the colormap is orthogonal. */ - cur -= m_sv_colormap[ci][pixcode]; - - /* Compute error fractions to be propagated to adjacent pixels. - * Add these into the running sums, and simultaneously shift the - * next-line error sums left by 1 column. - */ - int bnexterr = cur; - int delta = cur * 2; - cur += delta; /* form error * 3 */ - m_fserrors[ci][errorIndex + 0] = (short) (bpreverr + cur); - cur += delta; /* form error * 5 */ - bpreverr = belowerr + cur; - belowerr = bnexterr; - cur += delta; /* form error * 7 */ - - /* At this point cur contains the 7/16 error value to be propagated - * to the next pixel on the current line, and all the errors for the - * next line have been shifted over. We are therefore ready to move on. - */ - inIndex += dirnc; /* advance input to next column */ - outIndex += dir; /* advance output to next column */ - errorIndex += dir; /* advance errorIndex to current column */ - } - - /* Post-loop cleanup: we must unload the final error value into the - * final fserrors[] entry. Note we need not unload belowerr because - * it is for the dummy column before or after the actual array. - */ - m_fserrors[ci][errorIndex + 0] = (short) bpreverr; /* unload prev err into array */ - } - - m_on_odd_row = (m_on_odd_row ? false : true); - } - } - - /// - /// Create the colormap. - /// - private void create_colormap() - { - /* Select number of colors for each component */ - int total_colors = select_ncolors(m_Ncolors); - - /* Report selected color counts */ - if (m_cinfo.m_out_color_components == 3) - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_QUANT_3_NCOLORS, total_colors, m_Ncolors[0], m_Ncolors[1], m_Ncolors[2]); - else - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_QUANT_NCOLORS, total_colors); - - /* Allocate and fill in the colormap. */ - /* The colors are ordered in the map in standard row-major order, */ - /* i.e. rightmost (highest-indexed) color changes most rapidly. */ - byte[][] colormap = jpeg_common_struct.AllocJpegSamples(total_colors, m_cinfo.m_out_color_components); - - /* blksize is number of adjacent repeated entries for a component */ - /* blkdist is distance between groups of identical entries for a component */ - int blkdist = total_colors; - for (int i = 0; i < m_cinfo.m_out_color_components; i++) - { - /* fill in colormap entries for i'th color component */ - int nci = m_Ncolors[i]; /* # of distinct values for this color */ - int blksize = blkdist / nci; - for (int j = 0; j < nci; j++) - { - /* Compute j'th output value (out of nci) for component */ - int val = output_value(j, nci - 1); - - /* Fill in all colormap entries that have this value of this component */ - for (int ptr = j * blksize; ptr < total_colors; ptr += blkdist) - { - /* fill in blksize entries beginning at ptr */ - for (int k = 0; k < blksize; k++) - colormap[i][ptr + k] = (byte)val; - } - } - - /* blksize of this color is blkdist of next */ - blkdist = blksize; - } - - /* Save the colormap in private storage, - * where it will survive color quantization mode changes. - */ - m_sv_colormap = colormap; - m_sv_actual = total_colors; - } - - /// - /// Create the color index table. - /// - private void create_colorindex() - { - /* For ordered dither, we pad the color index tables by MAXJSAMPLE in - * each direction (input index values can be -MAXJSAMPLE .. 2*MAXJSAMPLE). - * This is not necessary in the other dithering modes. However, we - * flag whether it was done in case user changes dithering mode. - */ - int pad; - if (m_cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_ORDERED) - { - pad = JpegConstants.MAXJSAMPLE * 2; - m_is_padded = true; - } - else - { - pad = 0; - m_is_padded = false; - } - - m_colorindex = jpeg_common_struct.AllocJpegSamples(JpegConstants.MAXJSAMPLE + 1 + pad, m_cinfo.m_out_color_components); - m_colorindexOffset = new int[m_cinfo.m_out_color_components]; - - /* blksize is number of adjacent repeated entries for a component */ - int blksize = m_sv_actual; - for (int i = 0; i < m_cinfo.m_out_color_components; i++) - { - /* fill in colorindex entries for i'th color component */ - int nci = m_Ncolors[i]; /* # of distinct values for this color */ - blksize = blksize / nci; - - /* adjust colorindex pointers to provide padding at negative indexes. */ - if (pad != 0) - m_colorindexOffset[i] += JpegConstants.MAXJSAMPLE; - - /* in loop, val = index of current output value, */ - /* and k = largest j that maps to current val */ - int val = 0; - int k = largest_input_value(0, nci - 1); - for (int j = 0; j <= JpegConstants.MAXJSAMPLE; j++) - { - while (j > k) - { - /* advance val if past boundary */ - k = largest_input_value(++val, nci - 1); - } - - /* premultiply so that no multiplication needed in main processing */ - m_colorindex[i][m_colorindexOffset[i] + j] = (byte)(val * blksize); - } - - /* Pad at both ends if necessary */ - if (pad != 0) - { - for (int j = 1; j <= JpegConstants.MAXJSAMPLE; j++) - { - m_colorindex[i][m_colorindexOffset[i] + -j] = m_colorindex[i][m_colorindexOffset[i]]; - m_colorindex[i][m_colorindexOffset[i] + JpegConstants.MAXJSAMPLE + j] = m_colorindex[i][m_colorindexOffset[i] + JpegConstants.MAXJSAMPLE]; - } - } - } - } - - /// - /// Create the ordered-dither tables. - /// Components having the same number of representative colors may - /// share a dither table. - /// - private void create_odither_tables() - { - for (int i = 0; i < m_cinfo.m_out_color_components; i++) - { - int nci = m_Ncolors[i]; /* # of distinct values for this color */ - - /* search for matching prior component */ - int foundPos = -1; - for (int j = 0; j < i; j++) - { - if (nci == m_Ncolors[j]) - { - foundPos = j; - break; - } - } - - if (foundPos == -1) - { - /* need a new table? */ - m_odither[i] = make_odither_array(nci); - } - else - m_odither[i] = m_odither[foundPos]; - } - } - - /// - /// Allocate workspace for Floyd-Steinberg errors. - /// - private void alloc_fs_workspace() - { - for (int i = 0; i < m_cinfo.m_out_color_components; i++) - m_fserrors[i] = new short[m_cinfo.m_output_width + 2]; - } - - /* - * Policy-making subroutines for create_colormap and create_colorindex. - * These routines determine the colormap to be used. The rest of the module - * only assumes that the colormap is orthogonal. - * - * * select_ncolors decides how to divvy up the available colors - * among the components. - * * output_value defines the set of representative values for a component. - * * largest_input_value defines the mapping from input values to - * representative values for a component. - * Note that the latter two routines may impose different policies for - * different components, though this is not currently done. - */ - - /// - /// Return largest input value that should map to j'th output value - /// Must have largest(j=0) >= 0, and largest(j=maxj) >= MAXJSAMPLE - /// - private static int largest_input_value(int j, int maxj) - { - /* Breakpoints are halfway between values returned by output_value */ - return (int)(((2 * j + 1) * JpegConstants.MAXJSAMPLE + maxj) / (2 * maxj)); - } - - /// - /// Return j'th output value, where j will range from 0 to maxj - /// The output values must fall in 0..MAXJSAMPLE in increasing order - /// - private static int output_value(int j, int maxj) - { - /* We always provide values 0 and MAXJSAMPLE for each component; - * any additional values are equally spaced between these limits. - * (Forcing the upper and lower values to the limits ensures that - * dithering can't produce a color outside the selected gamut.) - */ - return (int)((j * JpegConstants.MAXJSAMPLE + maxj / 2) / maxj); - } - - /// - /// Determine allocation of desired colors to components, - /// and fill in Ncolors[] array to indicate choice. - /// Return value is total number of colors (product of Ncolors[] values). - /// - private int select_ncolors(int[] Ncolors) - { - int nc = m_cinfo.m_out_color_components; /* number of color components */ - int max_colors = m_cinfo.m_desired_number_of_colors; - - /* We can allocate at least the nc'th root of max_colors per component. */ - /* Compute floor(nc'th root of max_colors). */ - int iroot = 1; - long temp = 0; - do - { - iroot++; - temp = iroot; /* set temp = iroot ** nc */ - for (int i = 1; i < nc; i++) - temp *= iroot; - } - while (temp <= max_colors); /* repeat till iroot exceeds root */ - - /* now iroot = floor(root) */ - iroot--; - - /* Must have at least 2 color values per component */ - if (iroot < 2) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_FEW_COLORS, (int)temp); - - /* Initialize to iroot color values for each component */ - int total_colors = 1; - for (int i = 0; i < nc; i++) - { - Ncolors[i] = iroot; - total_colors *= iroot; - } - - /* We may be able to increment the count for one or more components without - * exceeding max_colors, though we know not all can be incremented. - * Sometimes, the first component can be incremented more than once! - * (Example: for 16 colors, we start at 2*2*2, go to 3*2*2, then 4*2*2.) - * In RGB colorspace, try to increment G first, then R, then B. - */ - bool changed = false; - do - { - changed = false; - for (int i = 0; i < nc; i++) - { - int j = (m_cinfo.m_out_color_space == J_COLOR_SPACE.JCS_RGB ? RGB_order[i] : i); - /* calculate new total_colors if Ncolors[j] is incremented */ - temp = total_colors / Ncolors[j]; - temp *= Ncolors[j] + 1; /* done in long arith to avoid oflo */ - - if (temp > max_colors) - break; /* won't fit, done with this pass */ - - Ncolors[j]++; /* OK, apply the increment */ - total_colors = (int)temp; - changed = true; - } - } - while (changed); - - return total_colors; - } - - /// - /// Create an ordered-dither array for a component having ncolors - /// distinct output values. - /// - private static int[][] make_odither_array(int ncolors) - { - int[][] odither = new int[ODITHER_SIZE][]; - for (int i = 0; i < ODITHER_SIZE; i++) - odither[i] = new int[ODITHER_SIZE]; - - /* The inter-value distance for this color is MAXJSAMPLE/(ncolors-1). - * Hence the dither value for the matrix cell with fill order f - * (f=0..N-1) should be (N-1-2*f)/(2*N) * MAXJSAMPLE/(ncolors-1). - * On 16-bit-int machine, be careful to avoid overflow. - */ - int den = 2 * ODITHER_CELLS * (ncolors - 1); - for (int j = 0; j < ODITHER_SIZE; j++) - { - for (int k = 0; k < ODITHER_SIZE; k++) - { - int num = ((int)(ODITHER_CELLS - 1 - 2 * ((int)base_dither_matrix[j][k]))) * JpegConstants.MAXJSAMPLE; - - /* Ensure round towards zero despite C's lack of consistency - * about rounding negative values in integer division... - */ - odither[j][k] = num < 0 ? -((-num) / den) : num / den; - } - } - - return odither; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_2pass_cquantizer.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_2pass_cquantizer.cs deleted file mode 100644 index 191bf909..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_2pass_cquantizer.cs +++ /dev/null @@ -1,1394 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains 2-pass color quantization (color mapping) routines. - * These routines provide selection of a custom color map for an image, - * followed by mapping of the image to that color map, with optional - * Floyd-Steinberg dithering. - * It is also possible to use just the second pass to map to an arbitrary - * externally-given color map. - * - * Note: ordered dithering is not supported, since there isn't any fast - * way to compute intercolor distances; it's unclear that ordered dither's - * fundamental assumptions even hold with an irregularly spaced color map. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// This module implements the well-known Heckbert paradigm for color - /// quantization. Most of the ideas used here can be traced back to - /// Heckbert's seminal paper - /// Heckbert, Paul. "Color Image Quantization for Frame Buffer Display", - /// Proc. SIGGRAPH '82, Computer Graphics v.16 #3 (July 1982), pp 297-304. - /// - /// In the first pass over the image, we accumulate a histogram showing the - /// usage count of each possible color. To keep the histogram to a reasonable - /// size, we reduce the precision of the input; typical practice is to retain - /// 5 or 6 bits per color, so that 8 or 4 different input values are counted - /// in the same histogram cell. - /// - /// Next, the color-selection step begins with a box representing the whole - /// color space, and repeatedly splits the "largest" remaining box until we - /// have as many boxes as desired colors. Then the mean color in each - /// remaining box becomes one of the possible output colors. - /// - /// The second pass over the image maps each input pixel to the closest output - /// color (optionally after applying a Floyd-Steinberg dithering correction). - /// This mapping is logically trivial, but making it go fast enough requires - /// considerable care. - /// - /// Heckbert-style quantizers vary a good deal in their policies for choosing - /// the "largest" box and deciding where to cut it. The particular policies - /// used here have proved out well in experimental comparisons, but better ones - /// may yet be found. - /// - /// In earlier versions of the IJG code, this module quantized in YCbCr color - /// space, processing the raw upsampled data without a color conversion step. - /// This allowed the color conversion math to be done only once per colormap - /// entry, not once per pixel. However, that optimization precluded other - /// useful optimizations (such as merging color conversion with upsampling) - /// and it also interfered with desired capabilities such as quantizing to an - /// externally-supplied colormap. We have therefore abandoned that approach. - /// The present code works in the post-conversion color space, typically RGB. - /// - /// To improve the visual quality of the results, we actually work in scaled - /// RGB space, giving G distances more weight than R, and R in turn more than - /// B. To do everything in integer math, we must use integer scale factors. - /// The 2/3/1 scale factors used here correspond loosely to the relative - /// weights of the colors in the NTSC grayscale equation. - /// If you want to use this code to quantize a non-RGB color space, you'll - /// probably need to change these scale factors. - /// - /// First we have the histogram data structure and routines for creating it. - /// - /// The number of bits of precision can be adjusted by changing these symbols. - /// We recommend keeping 6 bits for G and 5 each for R and B. - /// If you have plenty of memory and cycles, 6 bits all around gives marginally - /// better results; if you are short of memory, 5 bits all around will save - /// some space but degrade the results. - /// To maintain a fully accurate histogram, we'd need to allocate a "long" - /// (preferably unsigned long) for each cell. In practice this is overkill; - /// we can get by with 16 bits per cell. Few of the cell counts will overflow, - /// and clamping those that do overflow to the maximum value will give close- - /// enough results. This reduces the recommended histogram size from 256Kb - /// to 128Kb, which is a useful savings on PC-class machines. - /// (In the second pass the histogram space is re-used for pixel mapping data; - /// in that capacity, each cell must be able to store zero to the number of - /// desired colors. 16 bits/cell is plenty for that too.) - /// Since the JPEG code is intended to run in small memory model on 80x86 - /// machines, we can't just allocate the histogram in one chunk. Instead - /// of a true 3-D array, we use a row of pointers to 2-D arrays. Each - /// pointer corresponds to a C0 value (typically 2^5 = 32 pointers) and - /// each 2-D array has 2^6*2^5 = 2048 or 2^6*2^6 = 4096 entries. Note that - /// on 80x86 machines, the pointer row is in near memory but the actual - /// arrays are in far memory (same arrangement as we use for image arrays). - /// - /// - /// Declarations for Floyd-Steinberg dithering. - /// - /// Errors are accumulated into the array fserrors[], at a resolution of - /// 1/16th of a pixel count. The error at a given pixel is propagated - /// to its not-yet-processed neighbors using the standard F-S fractions, - /// ... (here) 7/16 - /// 3/16 5/16 1/16 - /// We work left-to-right on even rows, right-to-left on odd rows. - /// - /// We can get away with a single array (holding one row's worth of errors) - /// by using it to store the current row's errors at pixel columns not yet - /// processed, but the next row's errors at columns already processed. We - /// need only a few extra variables to hold the errors immediately around the - /// current column. (If we are lucky, those variables are in registers, but - /// even if not, they're probably cheaper to access than array elements are.) - /// - /// The fserrors[] array has (#columns + 2) entries; the extra entry at - /// each end saves us from special-casing the first and last pixels. - /// Each entry is three values long, one value for each color component. - /// - class my_2pass_cquantizer : jpeg_color_quantizer - { - private struct box - { - /* The bounds of the box (inclusive); expressed as histogram indexes */ - public int c0min; - public int c0max; - public int c1min; - public int c1max; - public int c2min; - public int c2max; - /* The volume (actually 2-norm) of the box */ - public int volume; - /* The number of nonzero histogram cells within this box */ - public long colorcount; - } - - private enum QuantizerType - { - prescan_quantizer, - pass2_fs_dither_quantizer, - pass2_no_dither_quantizer - } - - private const int MAXNUMCOLORS = (JpegConstants.MAXJSAMPLE+1); /* maximum size of colormap */ - - /* These will do the right thing for either R,G,B or B,G,R color order, - * but you may not like the results for other color orders. - */ - private const int HIST_C0_BITS = 5; /* bits of precision in R/B histogram */ - private const int HIST_C1_BITS = 6; /* bits of precision in G histogram */ - private const int HIST_C2_BITS = 5; /* bits of precision in B/R histogram */ - - /* Number of elements along histogram axes. */ - private const int HIST_C0_ELEMS = (1< - /// Module initialization routine for 2-pass color quantization. - /// - public my_2pass_cquantizer(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Make sure jdmaster didn't give me a case I can't handle */ - if (cinfo.m_out_color_components != 3) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - - /* Allocate the histogram/inverse colormap storage */ - m_histogram = new ushort[HIST_C0_ELEMS][]; - for (int i = 0; i < HIST_C0_ELEMS; i++) - m_histogram[i] = new ushort[HIST_C1_ELEMS * HIST_C2_ELEMS]; - - m_needs_zeroed = true; /* histogram is garbage now */ - - /* Allocate storage for the completed colormap, if required. - * We do this now since it is FAR storage and may affect - * the memory manager's space calculations. - */ - if (cinfo.m_enable_2pass_quant) - { - /* Make sure color count is acceptable */ - int desired_local = cinfo.m_desired_number_of_colors; - - /* Lower bound on # of colors ... somewhat arbitrary as long as > 0 */ - if (desired_local < 8) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_FEW_COLORS, 8); - - /* Make sure colormap indexes can be represented by JSAMPLEs */ - if (desired_local > MAXNUMCOLORS) - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); - - m_sv_colormap = jpeg_common_struct.AllocJpegSamples(desired_local, 3); - m_desired = desired_local; - } - - /* Only F-S dithering or no dithering is supported. */ - /* If user asks for ordered dither, give him F-S. */ - if (cinfo.m_dither_mode != J_DITHER_MODE.JDITHER_NONE) - cinfo.m_dither_mode = J_DITHER_MODE.JDITHER_FS; - - /* Allocate Floyd-Steinberg workspace if necessary. - * This isn't really needed until pass 2, but again it is FAR storage. - * Although we will cope with a later change in dither_mode, - * we do not promise to honor max_memory_to_use if dither_mode changes. - */ - if (cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_FS) - { - m_fserrors = new short[(cinfo.m_output_width + 2) * 3]; - - /* Might as well create the error-limiting table too. */ - init_error_limit(); - } - } - - /// - /// Initialize for each processing pass. - /// - public virtual void start_pass(bool is_pre_scan) - { - /* Only F-S dithering or no dithering is supported. */ - /* If user asks for ordered dither, give him F-S. */ - if (m_cinfo.m_dither_mode != J_DITHER_MODE.JDITHER_NONE) - m_cinfo.m_dither_mode = J_DITHER_MODE.JDITHER_FS; - - if (is_pre_scan) - { - /* Set up method pointers */ - m_quantizer = QuantizerType.prescan_quantizer; - m_useFinishPass1 = true; - m_needs_zeroed = true; /* Always zero histogram */ - } - else - { - /* Set up method pointers */ - if (m_cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_FS) - m_quantizer = QuantizerType.pass2_fs_dither_quantizer; - else - m_quantizer = QuantizerType.pass2_no_dither_quantizer; - - m_useFinishPass1 = false; - - /* Make sure color count is acceptable */ - int i = m_cinfo.m_actual_number_of_colors; - if (i < 1) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_FEW_COLORS, 1); - - if (i > MAXNUMCOLORS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); - - if (m_cinfo.m_dither_mode == J_DITHER_MODE.JDITHER_FS) - { - /* Allocate Floyd-Steinberg workspace if we didn't already. */ - if (m_fserrors == null) - { - int arraysize = (m_cinfo.m_output_width + 2) * 3; - m_fserrors = new short[arraysize]; - } - else - { - /* Initialize the propagated errors to zero. */ - Array.Clear(m_fserrors, 0, m_fserrors.Length); - } - - /* Make the error-limit table if we didn't already. */ - if (m_error_limiter == null) - init_error_limit(); - - m_on_odd_row = false; - } - } - - /* Zero the histogram or inverse color map, if necessary */ - if (m_needs_zeroed) - { - for (int i = 0; i < HIST_C0_ELEMS; i++) - Array.Clear(m_histogram[i], 0, m_histogram[i].Length); - - m_needs_zeroed = false; - } - } - - public virtual void color_quantize(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - switch (m_quantizer) - { - case QuantizerType.prescan_quantizer: - prescan_quantize(input_buf, in_row, num_rows); - break; - case QuantizerType.pass2_fs_dither_quantizer: - pass2_fs_dither(input_buf, in_row, output_buf, out_row, num_rows); - break; - case QuantizerType.pass2_no_dither_quantizer: - pass2_no_dither(input_buf, in_row, output_buf, out_row, num_rows); - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - break; - } - } - - public virtual void finish_pass() - { - if (m_useFinishPass1) - finish_pass1(); - } - - /// - /// Switch to a new external colormap between output passes. - /// - public virtual void new_color_map() - { - /* Reset the inverse color map */ - m_needs_zeroed = true; - } - - /// - /// Prescan some rows of pixels. - /// In this module the prescan simply updates the histogram, which has been - /// initialized to zeroes by start_pass. - /// An output_buf parameter is required by the method signature, but no data - /// is actually output (in fact the buffer controller is probably passing a - /// null pointer). - /// - private void prescan_quantize(byte[][] input_buf, int in_row, int num_rows) - { - for (int row = 0; row < num_rows; row++) - { - int inputIndex = 0; - for (int col = m_cinfo.m_output_width; col > 0; col--) - { - int rowIndex = (int)input_buf[in_row + row][inputIndex] >> C0_SHIFT; - int columnIndex = ((int)input_buf[in_row + row][inputIndex + 1] >> C1_SHIFT) * HIST_C2_ELEMS + - ((int)input_buf[in_row + row][inputIndex + 2] >> C2_SHIFT); - - /* increment pixel value, check for overflow and undo increment if so. */ - m_histogram[rowIndex][columnIndex]++; - if (m_histogram[rowIndex][columnIndex] <= 0) - m_histogram[rowIndex][columnIndex]--; - - inputIndex += 3; - } - } - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// This version performs Floyd-Steinberg dithering - /// - private void pass2_fs_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset; - - for (int row = 0; row < num_rows; row++) - { - int inputPixelIndex = 0; - int outputPixelIndex = 0; - int errorIndex = 0; - int dir; /* +1 or -1 depending on direction */ - int dir3; /* 3*dir, for advancing inputIndex & errorIndex */ - if (m_on_odd_row) - { - /* work right to left in this row */ - inputPixelIndex += (m_cinfo.m_output_width - 1) * 3; /* so point to rightmost pixel */ - outputPixelIndex += m_cinfo.m_output_width - 1; - dir = -1; - dir3 = -3; - errorIndex = (m_cinfo.m_output_width + 1) * 3; /* => entry after last column */ - m_on_odd_row = false; /* flip for next time */ - } - else - { - /* work left to right in this row */ - dir = 1; - dir3 = 3; - errorIndex = 0; /* => entry before first real column */ - m_on_odd_row = true; /* flip for next time */ - } - - /* Preset error values: no error propagated to first pixel from left */ - /* current error or pixel value */ - int cur0 = 0; - int cur1 = 0; - int cur2 = 0; - /* and no error propagated to row below yet */ - /* error for pixel below cur */ - int belowerr0 = 0; - int belowerr1 = 0; - int belowerr2 = 0; - /* error for below/prev col */ - int bpreverr0 = 0; - int bpreverr1 = 0; - int bpreverr2 = 0; - - for (int col = m_cinfo.m_output_width; col > 0; col--) - { - /* curN holds the error propagated from the previous pixel on the - * current line. Add the error propagated from the previous line - * to form the complete error correction term for this pixel, and - * round the error term (which is expressed * 16) to an integer. - * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct - * for either sign of the error value. - * Note: errorIndex is for *previous* column's array entry. - */ - cur0 = JpegUtils.RIGHT_SHIFT(cur0 + m_fserrors[errorIndex + dir3] + 8, 4); - cur1 = JpegUtils.RIGHT_SHIFT(cur1 + m_fserrors[errorIndex + dir3 + 1] + 8, 4); - cur2 = JpegUtils.RIGHT_SHIFT(cur2 + m_fserrors[errorIndex + dir3 + 2] + 8, 4); - - /* Limit the error using transfer function set by init_error_limit. - * See comments with init_error_limit for rationale. - */ - cur0 = m_error_limiter[JpegConstants.MAXJSAMPLE + cur0]; - cur1 = m_error_limiter[JpegConstants.MAXJSAMPLE + cur1]; - cur2 = m_error_limiter[JpegConstants.MAXJSAMPLE + cur2]; - - /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. - * The maximum error is +- MAXJSAMPLE (or less with error limiting); - * this sets the required size of the range_limit array. - */ - cur0 += input_buf[in_row + row][inputPixelIndex]; - cur1 += input_buf[in_row + row][inputPixelIndex + 1]; - cur2 += input_buf[in_row + row][inputPixelIndex + 2]; - cur0 = limit[limitOffset + cur0]; - cur1 = limit[limitOffset + cur1]; - cur2 = limit[limitOffset + cur2]; - - /* Index into the cache with adjusted pixel value */ - int hRow = cur0 >> C0_SHIFT; - int hColumn = (cur1 >> C1_SHIFT) * HIST_C2_ELEMS + (cur2 >> C2_SHIFT); - - /* If we have not seen this color before, find nearest colormap */ - /* entry and update the cache */ - if (m_histogram[hRow][hColumn] == 0) - fill_inverse_cmap(cur0 >> C0_SHIFT, cur1 >> C1_SHIFT, cur2 >> C2_SHIFT); - - /* Now emit the colormap index for this cell */ - int pixcode = m_histogram[hRow][hColumn] - 1; - output_buf[out_row + row][outputPixelIndex] = (byte) pixcode; - - /* Compute representation error for this pixel */ - cur0 -= m_cinfo.m_colormap[0][pixcode]; - cur1 -= m_cinfo.m_colormap[1][pixcode]; - cur2 -= m_cinfo.m_colormap[2][pixcode]; - - /* Compute error fractions to be propagated to adjacent pixels. - * Add these into the running sums, and simultaneously shift the - * next-line error sums left by 1 column. - */ - int bnexterr = cur0; /* Process component 0 */ - int delta = cur0 * 2; - cur0 += delta; /* form error * 3 */ - m_fserrors[errorIndex] = (short) (bpreverr0 + cur0); - cur0 += delta; /* form error * 5 */ - bpreverr0 = belowerr0 + cur0; - belowerr0 = bnexterr; - cur0 += delta; /* form error * 7 */ - bnexterr = cur1; /* Process component 1 */ - delta = cur1 * 2; - cur1 += delta; /* form error * 3 */ - m_fserrors[errorIndex + 1] = (short) (bpreverr1 + cur1); - cur1 += delta; /* form error * 5 */ - bpreverr1 = belowerr1 + cur1; - belowerr1 = bnexterr; - cur1 += delta; /* form error * 7 */ - bnexterr = cur2; /* Process component 2 */ - delta = cur2 * 2; - cur2 += delta; /* form error * 3 */ - m_fserrors[errorIndex + 2] = (short) (bpreverr2 + cur2); - cur2 += delta; /* form error * 5 */ - bpreverr2 = belowerr2 + cur2; - belowerr2 = bnexterr; - cur2 += delta; /* form error * 7 */ - - /* At this point curN contains the 7/16 error value to be propagated - * to the next pixel on the current line, and all the errors for the - * next line have been shifted over. We are therefore ready to move on. - */ - inputPixelIndex += dir3; /* Advance pixel pointers to next column */ - outputPixelIndex += dir; - errorIndex += dir3; /* advance errorIndex to current column */ - } - - /* Post-loop cleanup: we must unload the final error values into the - * final fserrors[] entry. Note we need not unload belowerrN because - * it is for the dummy column before or after the actual array. - */ - m_fserrors[errorIndex] = (short) bpreverr0; /* unload prev errs into array */ - m_fserrors[errorIndex + 1] = (short) bpreverr1; - m_fserrors[errorIndex + 2] = (short) bpreverr2; - } - } - - /// - /// Map some rows of pixels to the output colormapped representation. - /// This version performs no dithering - /// - private void pass2_no_dither(byte[][] input_buf, int in_row, byte[][] output_buf, int out_row, int num_rows) - { - for (int row = 0; row < num_rows; row++) - { - int inRow = row + in_row; - int inIndex = 0; - int outIndex = 0; - int outRow = out_row + row; - for (int col = m_cinfo.m_output_width; col > 0; col--) - { - /* get pixel value and index into the cache */ - int c0 = (int)input_buf[inRow][inIndex] >> C0_SHIFT; - inIndex++; - - int c1 = (int)input_buf[inRow][inIndex] >> C1_SHIFT; - inIndex++; - - int c2 = (int)input_buf[inRow][inIndex] >> C2_SHIFT; - inIndex++; - - int hRow = c0; - int hColumn = c1 * HIST_C2_ELEMS + c2; - - /* If we have not seen this color before, find nearest colormap entry */ - /* and update the cache */ - if (m_histogram[hRow][hColumn] == 0) - fill_inverse_cmap(c0, c1, c2); - - /* Now emit the colormap index for this cell */ - output_buf[outRow][outIndex] = (byte)(m_histogram[hRow][hColumn] - 1); - outIndex++; - } - } - } - - /// - /// Finish up at the end of each pass. - /// - private void finish_pass1() - { - /* Select the representative colors and fill in cinfo.colormap */ - m_cinfo.m_colormap = m_sv_colormap; - select_colors(m_desired); - - /* Force next pass to zero the color index table */ - m_needs_zeroed = true; - } - - /// - /// Compute representative color for a box, put it in colormap[icolor] - /// - private void compute_color(box[] boxlist, int boxIndex, int icolor) - { - /* Current algorithm: mean weighted by pixels (not colors) */ - /* Note it is important to get the rounding correct! */ - long total = 0; - long c0total = 0; - long c1total = 0; - long c2total = 0; - box curBox = boxlist[boxIndex]; - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++) - { - int histogramIndex = c1 * HIST_C2_ELEMS + curBox.c2min; - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++) - { - long count = m_histogram[c0][histogramIndex]; - histogramIndex++; - - if (count != 0) - { - total += count; - c0total += ((c0 << C0_SHIFT) + ((1 << C0_SHIFT) >> 1)) * count; - c1total += ((c1 << C1_SHIFT) + ((1 << C1_SHIFT) >> 1)) * count; - c2total += ((c2 << C2_SHIFT) + ((1 << C2_SHIFT) >> 1)) * count; - } - } - } - } - - m_cinfo.m_colormap[0][icolor] = (byte)((c0total + (total >> 1)) / total); - m_cinfo.m_colormap[1][icolor] = (byte)((c1total + (total >> 1)) / total); - m_cinfo.m_colormap[2][icolor] = (byte)((c2total + (total >> 1)) / total); - } - - /// - /// Master routine for color selection - /// - private void select_colors(int desired_colors) - { - /* Allocate workspace for box list */ - box[] boxlist = new box[desired_colors]; - - /* Initialize one box containing whole space */ - int numboxes = 1; - boxlist[0].c0min = 0; - boxlist[0].c0max = JpegConstants.MAXJSAMPLE >> C0_SHIFT; - boxlist[0].c1min = 0; - boxlist[0].c1max = JpegConstants.MAXJSAMPLE >> C1_SHIFT; - boxlist[0].c2min = 0; - boxlist[0].c2max = JpegConstants.MAXJSAMPLE >> C2_SHIFT; - - /* Shrink it to actually-used volume and set its statistics */ - update_box(boxlist, 0); - - /* Perform median-cut to produce final box list */ - numboxes = median_cut(boxlist, numboxes, desired_colors); - - /* Compute the representative color for each box, fill colormap */ - for (int i = 0; i < numboxes; i++) - compute_color(boxlist, i, i); - - m_cinfo.m_actual_number_of_colors = numboxes; - m_cinfo.TRACEMS(1, J_MESSAGE_CODE.JTRC_QUANT_SELECTED, numboxes); - } - - /// - /// Repeatedly select and split the largest box until we have enough boxes - /// - private int median_cut(box[] boxlist, int numboxes, int desired_colors) - { - while (numboxes < desired_colors) - { - /* Select box to split. - * Current algorithm: by population for first half, then by volume. - */ - int foundIndex; - if (numboxes * 2 <= desired_colors) - foundIndex = find_biggest_color_pop(boxlist, numboxes); - else - foundIndex = find_biggest_volume(boxlist, numboxes); - - if (foundIndex == -1) /* no splittable boxes left! */ - break; - - /* Copy the color bounds to the new box. */ - boxlist[numboxes].c0max = boxlist[foundIndex].c0max; - boxlist[numboxes].c1max = boxlist[foundIndex].c1max; - boxlist[numboxes].c2max = boxlist[foundIndex].c2max; - boxlist[numboxes].c0min = boxlist[foundIndex].c0min; - boxlist[numboxes].c1min = boxlist[foundIndex].c1min; - boxlist[numboxes].c2min = boxlist[foundIndex].c2min; - - /* Choose which axis to split the box on. - * Current algorithm: longest scaled axis. - * See notes in update_box about scaling distances. - */ - int c0 = ((boxlist[foundIndex].c0max - boxlist[foundIndex].c0min) << C0_SHIFT) * R_SCALE; - int c1 = ((boxlist[foundIndex].c1max - boxlist[foundIndex].c1min) << C1_SHIFT) * G_SCALE; - int c2 = ((boxlist[foundIndex].c2max - boxlist[foundIndex].c2min) << C2_SHIFT) * B_SCALE; - - /* We want to break any ties in favor of green, then red, blue last. - * This code does the right thing for R,G,B or B,G,R color orders only. - */ - int cmax = c1; - int n = 1; - - if (c0 > cmax) - { - cmax = c0; - n = 0; - } - - if (c2 > cmax) - { - n = 2; - } - - /* Choose split point along selected axis, and update box bounds. - * Current algorithm: split at halfway point. - * (Since the box has been shrunk to minimum volume, - * any split will produce two nonempty subboxes.) - * Note that lb value is max for lower box, so must be < old max. - */ - int lb; - switch (n) - { - case 0: - lb = (boxlist[foundIndex].c0max + boxlist[foundIndex].c0min) / 2; - boxlist[foundIndex].c0max = lb; - boxlist[numboxes].c0min = lb + 1; - break; - case 1: - lb = (boxlist[foundIndex].c1max + boxlist[foundIndex].c1min) / 2; - boxlist[foundIndex].c1max = lb; - boxlist[numboxes].c1min = lb + 1; - break; - case 2: - lb = (boxlist[foundIndex].c2max + boxlist[foundIndex].c2min) / 2; - boxlist[foundIndex].c2max = lb; - boxlist[numboxes].c2min = lb + 1; - break; - } - - /* Update stats for boxes */ - update_box(boxlist, foundIndex); - update_box(boxlist, numboxes); - numboxes++; - } - - return numboxes; - } - - /* - * Next we have the really interesting routines: selection of a colormap - * given the completed histogram. - * These routines work with a list of "boxes", each representing a rectangular - * subset of the input color space (to histogram precision). - */ - - /// - /// Find the splittable box with the largest color population - /// Returns null if no splittable boxes remain - /// - private static int find_biggest_color_pop(box[] boxlist, int numboxes) - { - long maxc = 0; - int which = -1; - for (int i = 0; i < numboxes; i++) - { - if (boxlist[i].colorcount > maxc && boxlist[i].volume > 0) - { - which = i; - maxc = boxlist[i].colorcount; - } - } - - return which; - } - - /// - /// Find the splittable box with the largest (scaled) volume - /// Returns null if no splittable boxes remain - /// - private static int find_biggest_volume(box[] boxlist, int numboxes) - { - int maxv = 0; - int which = -1; - for (int i = 0; i < numboxes; i++) - { - if (boxlist[i].volume > maxv) - { - which = i; - maxv = boxlist[i].volume; - } - } - - return which; - } - - /// - /// Shrink the min/max bounds of a box to enclose only nonzero elements, - /// and recompute its volume and population - /// - private void update_box(box[] boxlist, int boxIndex) - { - box curBox = boxlist[boxIndex]; - bool have_c0min = false; - - if (curBox.c0max > curBox.c0min) - { - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++) - { - int histogramIndex = c1 * HIST_C2_ELEMS + curBox.c2min; - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++) - { - if (m_histogram[c0][histogramIndex++] != 0) - { - curBox.c0min = c0; - have_c0min = true; - break; - } - } - - if (have_c0min) - break; - } - - if (have_c0min) - break; - } - } - - bool have_c0max = false; - if (curBox.c0max > curBox.c0min) - { - for (int c0 = curBox.c0max; c0 >= curBox.c0min; c0--) - { - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++) - { - int histogramIndex = c1 * HIST_C2_ELEMS + curBox.c2min; - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++) - { - if (m_histogram[c0][histogramIndex++] != 0) - { - curBox.c0max = c0; - have_c0max = true; - break; - } - } - - if (have_c0max) - break; - } - - if (have_c0max) - break; - } - } - - bool have_c1min = false; - if (curBox.c1max > curBox.c1min) - { - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++) - { - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - int histogramIndex = c1 * HIST_C2_ELEMS + curBox.c2min; - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++) - { - if (m_histogram[c0][histogramIndex++] != 0) - { - curBox.c1min = c1; - have_c1min = true; - break; - } - } - - if (have_c1min) - break; - } - - if (have_c1min) - break; - } - } - - bool have_c1max = false; - if (curBox.c1max > curBox.c1min) - { - for (int c1 = curBox.c1max; c1 >= curBox.c1min; c1--) - { - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - int histogramIndex = c1 * HIST_C2_ELEMS + curBox.c2min; - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++) - { - if (m_histogram[c0][histogramIndex++] != 0) - { - curBox.c1max = c1; - have_c1max = true; - break; - } - } - - if (have_c1max) - break; - } - - if (have_c1max) - break; - } - } - - bool have_c2min = false; - if (curBox.c2max > curBox.c2min) - { - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++) - { - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - int histogramIndex = curBox.c1min * HIST_C2_ELEMS + c2; - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++, histogramIndex += HIST_C2_ELEMS) - { - if (m_histogram[c0][histogramIndex] != 0) - { - curBox.c2min = c2; - have_c2min = true; - break; - } - } - - if (have_c2min) - break; - } - - if (have_c2min) - break; - } - } - - bool have_c2max = false; - if (curBox.c2max > curBox.c2min) - { - for (int c2 = curBox.c2max; c2 >= curBox.c2min; c2--) - { - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - int histogramIndex = curBox.c1min * HIST_C2_ELEMS + c2; - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++, histogramIndex += HIST_C2_ELEMS) - { - if (m_histogram[c0][histogramIndex] != 0) - { - curBox.c2max = c2; - have_c2max = true; - break; - } - } - - if (have_c2max) - break; - } - - if (have_c2max) - break; - } - } - - /* Update box volume. - * We use 2-norm rather than real volume here; this biases the method - * against making long narrow boxes, and it has the side benefit that - * a box is splittable iff norm > 0. - * Since the differences are expressed in histogram-cell units, - * we have to shift back to byte units to get consistent distances; - * after which, we scale according to the selected distance scale factors. - */ - int dist0 = ((curBox.c0max - curBox.c0min) << C0_SHIFT) * R_SCALE; - int dist1 = ((curBox.c1max - curBox.c1min) << C1_SHIFT) * G_SCALE; - int dist2 = ((curBox.c2max - curBox.c2min) << C2_SHIFT) * B_SCALE; - curBox.volume = dist0 * dist0 + dist1 * dist1 + dist2 * dist2; - - /* Now scan remaining volume of box and compute population */ - long ccount = 0; - for (int c0 = curBox.c0min; c0 <= curBox.c0max; c0++) - { - for (int c1 = curBox.c1min; c1 <= curBox.c1max; c1++) - { - int histogramIndex = c1 * HIST_C2_ELEMS + curBox.c2min; - for (int c2 = curBox.c2min; c2 <= curBox.c2max; c2++, histogramIndex++) - { - if (m_histogram[c0][histogramIndex] != 0) - ccount++; - } - } - } - - curBox.colorcount = ccount; - boxlist[boxIndex] = curBox; - } - - /// - /// Initialize the error-limiting transfer function (lookup table). - /// The raw F-S error computation can potentially compute error values of up to - /// +- MAXJSAMPLE. But we want the maximum correction applied to a pixel to be - /// much less, otherwise obviously wrong pixels will be created. (Typical - /// effects include weird fringes at color-area boundaries, isolated bright - /// pixels in a dark area, etc.) The standard advice for avoiding this problem - /// is to ensure that the "corners" of the color cube are allocated as output - /// colors; then repeated errors in the same direction cannot cause cascading - /// error buildup. However, that only prevents the error from getting - /// completely out of hand; Aaron Giles reports that error limiting improves - /// the results even with corner colors allocated. - /// A simple clamping of the error values to about +- MAXJSAMPLE/8 works pretty - /// well, but the smoother transfer function used below is even better. Thanks - /// to Aaron Giles for this idea. - /// - private void init_error_limit() - { - m_error_limiter = new int [JpegConstants.MAXJSAMPLE * 2 + 1]; - int tableOffset = JpegConstants.MAXJSAMPLE; - - const int STEPSIZE = ((JpegConstants.MAXJSAMPLE + 1) / 16); - - /* Map errors 1:1 up to +- MAXJSAMPLE/16 */ - int output = 0; - int input = 0; - for (; input < STEPSIZE; input++, output++) - { - m_error_limiter[tableOffset + input] = output; - m_error_limiter[tableOffset - input] = -output; - } - - /* Map errors 1:2 up to +- 3*MAXJSAMPLE/16 */ - for (; input < STEPSIZE*3; input++) - { - m_error_limiter[tableOffset + input] = output; - m_error_limiter[tableOffset - input] = -output; - output += (input & 1) != 0 ? 1 : 0; - } - - /* Clamp the rest to final output value (which is (MAXJSAMPLE+1)/8) */ - for (; input <= JpegConstants.MAXJSAMPLE; input++) - { - m_error_limiter[tableOffset + input] = output; - m_error_limiter[tableOffset - input] = -output; - } - } - - /* - * These routines are concerned with the time-critical task of mapping input - * colors to the nearest color in the selected colormap. - * - * We re-use the histogram space as an "inverse color map", essentially a - * cache for the results of nearest-color searches. All colors within a - * histogram cell will be mapped to the same colormap entry, namely the one - * closest to the cell's center. This may not be quite the closest entry to - * the actual input color, but it's almost as good. A zero in the cache - * indicates we haven't found the nearest color for that cell yet; the array - * is cleared to zeroes before starting the mapping pass. When we find the - * nearest color for a cell, its colormap index plus one is recorded in the - * cache for future use. The pass2 scanning routines call fill_inverse_cmap - * when they need to use an unfilled entry in the cache. - * - * Our method of efficiently finding nearest colors is based on the "locally - * sorted search" idea described by Heckbert and on the incremental distance - * calculation described by Spencer W. Thomas in chapter III.1 of Graphics - * Gems II (James Arvo, ed. Academic Press, 1991). Thomas points out that - * the distances from a given colormap entry to each cell of the histogram can - * be computed quickly using an incremental method: the differences between - * distances to adjacent cells themselves differ by a constant. This allows a - * fairly fast implementation of the "brute force" approach of computing the - * distance from every colormap entry to every histogram cell. Unfortunately, - * it needs a work array to hold the best-distance-so-far for each histogram - * cell (because the inner loop has to be over cells, not colormap entries). - * The work array elements have to be ints, so the work array would need - * 256Kb at our recommended precision. This is not feasible in DOS machines. - * - * To get around these problems, we apply Thomas' method to compute the - * nearest colors for only the cells within a small subbox of the histogram. - * The work array need be only as big as the subbox, so the memory usage - * problem is solved. Furthermore, we need not fill subboxes that are never - * referenced in pass2; many images use only part of the color gamut, so a - * fair amount of work is saved. An additional advantage of this - * approach is that we can apply Heckbert's locality criterion to quickly - * eliminate colormap entries that are far away from the subbox; typically - * three-fourths of the colormap entries are rejected by Heckbert's criterion, - * and we need not compute their distances to individual cells in the subbox. - * The speed of this approach is heavily influenced by the subbox size: too - * small means too much overhead, too big loses because Heckbert's criterion - * can't eliminate as many colormap entries. Empirically the best subbox - * size seems to be about 1/512th of the histogram (1/8th in each direction). - * - * Thomas' article also describes a refined method which is asymptotically - * faster than the brute-force method, but it is also far more complex and - * cannot efficiently be applied to small subboxes. It is therefore not - * useful for programs intended to be portable to DOS machines. On machines - * with plenty of memory, filling the whole histogram in one shot with Thomas' - * refined method might be faster than the present code --- but then again, - * it might not be any faster, and it's certainly more complicated. - */ - - /* - * The next three routines implement inverse colormap filling. They could - * all be folded into one big routine, but splitting them up this way saves - * some stack space (the mindist[] and bestdist[] arrays need not coexist) - * and may allow some compilers to produce better code by registerizing more - * inner-loop variables. - */ - - /// - /// Locate the colormap entries close enough to an update box to be candidates - /// for the nearest entry to some cell(s) in the update box. The update box - /// is specified by the center coordinates of its first cell. The number of - /// candidate colormap entries is returned, and their colormap indexes are - /// placed in colorlist[]. - /// This routine uses Heckbert's "locally sorted search" criterion to select - /// the colors that need further consideration. - /// - private int find_nearby_colors(int minc0, int minc1, int minc2, byte[] colorlist) - { - /* Compute true coordinates of update box's upper corner and center. - * Actually we compute the coordinates of the center of the upper-corner - * histogram cell, which are the upper bounds of the volume we care about. - * Note that since ">>" rounds down, the "center" values may be closer to - * min than to max; hence comparisons to them must be "<=", not "<". - */ - int maxc0 = minc0 + ((1 << BOX_C0_SHIFT) - (1 << C0_SHIFT)); - int centerc0 = (minc0 + maxc0) >> 1; - - int maxc1 = minc1 + ((1 << BOX_C1_SHIFT) - (1 << C1_SHIFT)); - int centerc1 = (minc1 + maxc1) >> 1; - - int maxc2 = minc2 + ((1 << BOX_C2_SHIFT) - (1 << C2_SHIFT)); - int centerc2 = (minc2 + maxc2) >> 1; - - /* For each color in colormap, find: - * 1. its minimum squared-distance to any point in the update box - * (zero if color is within update box); - * 2. its maximum squared-distance to any point in the update box. - * Both of these can be found by considering only the corners of the box. - * We save the minimum distance for each color in mindist[]; - * only the smallest maximum distance is of interest. - */ - int minmaxdist = 0x7FFFFFFF; - int[] mindist = new int[MAXNUMCOLORS]; /* min distance to colormap entry i */ - - for (int i = 0; i < m_cinfo.m_actual_number_of_colors; i++) - { - /* We compute the squared-c0-distance term, then add in the other two. */ - int x = m_cinfo.m_colormap[0][i]; - int min_dist; - int max_dist; - - if (x < minc0) - { - int tdist = (x - minc0) * R_SCALE; - min_dist = tdist * tdist; - tdist = (x - maxc0) * R_SCALE; - max_dist = tdist * tdist; - } - else if (x > maxc0) - { - int tdist = (x - maxc0) * R_SCALE; - min_dist = tdist * tdist; - tdist = (x - minc0) * R_SCALE; - max_dist = tdist * tdist; - } - else - { - /* within cell range so no contribution to min_dist */ - min_dist = 0; - if (x <= centerc0) - { - int tdist = (x - maxc0) * R_SCALE; - max_dist = tdist * tdist; - } - else - { - int tdist = (x - minc0) * R_SCALE; - max_dist = tdist * tdist; - } - } - - x = m_cinfo.m_colormap[1][i]; - if (x < minc1) - { - int tdist = (x - minc1) * G_SCALE; - min_dist += tdist * tdist; - tdist = (x - maxc1) * G_SCALE; - max_dist += tdist * tdist; - } - else if (x > maxc1) - { - int tdist = (x - maxc1) * G_SCALE; - min_dist += tdist * tdist; - tdist = (x - minc1) * G_SCALE; - max_dist += tdist * tdist; - } - else - { - /* within cell range so no contribution to min_dist */ - if (x <= centerc1) - { - int tdist = (x - maxc1) * G_SCALE; - max_dist += tdist * tdist; - } - else - { - int tdist = (x - minc1) * G_SCALE; - max_dist += tdist * tdist; - } - } - - x = m_cinfo.m_colormap[2][i]; - if (x < minc2) - { - int tdist = (x - minc2) * B_SCALE; - min_dist += tdist * tdist; - tdist = (x - maxc2) * B_SCALE; - max_dist += tdist * tdist; - } - else if (x > maxc2) - { - int tdist = (x - maxc2) * B_SCALE; - min_dist += tdist * tdist; - tdist = (x - minc2) * B_SCALE; - max_dist += tdist * tdist; - } - else - { - /* within cell range so no contribution to min_dist */ - if (x <= centerc2) - { - int tdist = (x - maxc2) * B_SCALE; - max_dist += tdist * tdist; - } - else - { - int tdist = (x - minc2) * B_SCALE; - max_dist += tdist * tdist; - } - } - - mindist[i] = min_dist; /* save away the results */ - if (max_dist < minmaxdist) - minmaxdist = max_dist; - } - - /* Now we know that no cell in the update box is more than minmaxdist - * away from some colormap entry. Therefore, only colors that are - * within minmaxdist of some part of the box need be considered. - */ - int ncolors = 0; - for (int i = 0; i < m_cinfo.m_actual_number_of_colors; i++) - { - if (mindist[i] <= minmaxdist) - colorlist[ncolors++] = (byte) i; - } - - return ncolors; - } - - /// - /// Find the closest colormap entry for each cell in the update box, - /// given the list of candidate colors prepared by find_nearby_colors. - /// Return the indexes of the closest entries in the bestcolor[] array. - /// This routine uses Thomas' incremental distance calculation method to - /// find the distance from a colormap entry to successive cells in the box. - /// - private void find_best_colors(int minc0, int minc1, int minc2, int numcolors, byte[] colorlist, byte[] bestcolor) - { - /* Nominal steps between cell centers ("x" in Thomas article) */ - const int STEP_C0 = ((1 << C0_SHIFT) * R_SCALE); - const int STEP_C1 = ((1 << C1_SHIFT) * G_SCALE); - const int STEP_C2 = ((1 << C2_SHIFT) * B_SCALE); - - /* This array holds the distance to the nearest-so-far color for each cell */ - int[] bestdist = new int[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; - - /* Initialize best-distance for each cell of the update box */ - int bestIndex = 0; - for (int i = BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS - 1; i >= 0; i--) - { - bestdist[bestIndex] = 0x7FFFFFFF; - bestIndex++; - } - - /* For each color selected by find_nearby_colors, - * compute its distance to the center of each cell in the box. - * If that's less than best-so-far, update best distance and color number. - */ - for (int i = 0; i < numcolors; i++) - { - int icolor = colorlist[i]; - - /* Compute (square of) distance from minc0/c1/c2 to this color */ - int inc0 = (minc0 - m_cinfo.m_colormap[0][icolor]) * R_SCALE; - int dist0 = inc0 * inc0; - - int inc1 = (minc1 - m_cinfo.m_colormap[1][icolor]) * G_SCALE; - dist0 += inc1 * inc1; - - int inc2 = (minc2 - m_cinfo.m_colormap[2][icolor]) * B_SCALE; - dist0 += inc2 * inc2; - - /* Form the initial difference increments */ - inc0 = inc0 * (2 * STEP_C0) + STEP_C0 * STEP_C0; - inc1 = inc1 * (2 * STEP_C1) + STEP_C1 * STEP_C1; - inc2 = inc2 * (2 * STEP_C2) + STEP_C2 * STEP_C2; - - /* Now loop over all cells in box, updating distance per Thomas method */ - bestIndex = 0; - int colorIndex = 0; - int xx0 = inc0; - for (int ic0 = BOX_C0_ELEMS - 1; ic0 >= 0; ic0--) - { - int dist1 = dist0; - int xx1 = inc1; - for (int ic1 = BOX_C1_ELEMS - 1; ic1 >= 0; ic1--) - { - int dist2 = dist1; - int xx2 = inc2; - for (int ic2 = BOX_C2_ELEMS - 1; ic2 >= 0; ic2--) - { - if (dist2 < bestdist[bestIndex]) - { - bestdist[bestIndex] = dist2; - bestcolor[colorIndex] = (byte) icolor; - } - - dist2 += xx2; - xx2 += 2 * STEP_C2 * STEP_C2; - bestIndex++; - colorIndex++; - } - - dist1 += xx1; - xx1 += 2 * STEP_C1 * STEP_C1; - } - - dist0 += xx0; - xx0 += 2 * STEP_C0 * STEP_C0; - } - } - } - - /// - /// Fill the inverse-colormap entries in the update box that contains - /// histogram cell c0/c1/c2. (Only that one cell MUST be filled, but - /// we can fill as many others as we wish.) - /// - private void fill_inverse_cmap(int c0, int c1, int c2) - { - /* Convert cell coordinates to update box ID */ - c0 >>= BOX_C0_LOG; - c1 >>= BOX_C1_LOG; - c2 >>= BOX_C2_LOG; - - /* Compute true coordinates of update box's origin corner. - * Actually we compute the coordinates of the center of the corner - * histogram cell, which are the lower bounds of the volume we care about. - */ - int minc0 = (c0 << BOX_C0_SHIFT) + ((1 << C0_SHIFT) >> 1); - int minc1 = (c1 << BOX_C1_SHIFT) + ((1 << C1_SHIFT) >> 1); - int minc2 = (c2 << BOX_C2_SHIFT) + ((1 << C2_SHIFT) >> 1); - - /* Determine which colormap entries are close enough to be candidates - * for the nearest entry to some cell in the update box. - */ - /* This array lists the candidate colormap indexes. */ - byte[] colorlist = new byte[MAXNUMCOLORS]; - int numcolors = find_nearby_colors(minc0, minc1, minc2, colorlist); - - /* Determine the actually nearest colors. */ - /* This array holds the actually closest colormap index for each cell. */ - byte[] bestcolor = new byte[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; - find_best_colors(minc0, minc1, minc2, numcolors, colorlist, bestcolor); - - /* Save the best color numbers (plus 1) in the main cache array */ - c0 <<= BOX_C0_LOG; /* convert ID back to base cell indexes */ - c1 <<= BOX_C1_LOG; - c2 <<= BOX_C2_LOG; - int bestcolorIndex = 0; - for (int ic0 = 0; ic0 < BOX_C0_ELEMS; ic0++) - { - for (int ic1 = 0; ic1 < BOX_C1_ELEMS; ic1++) - { - int histogramIndex = (c1 + ic1) * HIST_C2_ELEMS + c2; - for (int ic2 = 0; ic2 < BOX_C2_ELEMS; ic2++) - { - m_histogram[c0 + ic0][histogramIndex] = (ushort) ((int)bestcolor[bestcolorIndex] + 1); - histogramIndex++; - bestcolorIndex++; - } - } - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_c_coef_controller.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_c_coef_controller.cs deleted file mode 100644 index 1a32a999..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_c_coef_controller.cs +++ /dev/null @@ -1,428 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the coefficient buffer controller for compression. - * This controller is the top level of the JPEG compressor proper. - * The coefficient buffer lies between forward-DCT and entropy encoding steps. - */ - -/* We use a full-image coefficient buffer when doing Huffman optimization, - * and also for writing multiple-scan JPEG files. In all cases, the DCT - * step is run during the first pass, and subsequent passes need only read - * the buffered coefficients. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - class my_c_coef_controller : jpeg_c_coef_controller - { - private J_BUF_MODE m_passModeSetByLastStartPass; - private jpeg_compress_struct m_cinfo; - - private int m_iMCU_row_num; /* iMCU row # within image */ - private int m_mcu_ctr; /* counts MCUs processed in current row */ - private int m_MCU_vert_offset; /* counts MCU rows within iMCU row */ - private int m_MCU_rows_per_iMCU_row; /* number of such rows needed */ - - /* For single-pass compression, it's sufficient to buffer just one MCU - * (although this may prove a bit slow in practice). We allocate a - * workspace of C_MAX_BLOCKS_IN_MCU coefficient blocks, and reuse it for each - * MCU constructed and sent. (On 80x86, the workspace is FAR even though - * it's not really very big; this is to keep the module interfaces unchanged - * when a large coefficient buffer is necessary.) - * In multi-pass modes, this array points to the current MCU's blocks - * within the virtual arrays. - */ - private JBLOCK[][] m_MCU_buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU][]; - - /* In multi-pass modes, we need a virtual block array for each component. */ - private jvirt_array[] m_whole_image = new jvirt_array[JpegConstants.MAX_COMPONENTS]; - - public my_c_coef_controller(jpeg_compress_struct cinfo, bool need_full_buffer) - { - m_cinfo = cinfo; - - /* Create the coefficient buffer. */ - if (need_full_buffer) - { - /* Allocate a full-image virtual array for each component, */ - /* padded to a multiple of samp_factor DCT blocks in each direction. */ - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - m_whole_image[ci] = jpeg_common_struct.CreateBlocksArray( - JpegUtils.jround_up(cinfo.Component_info[ci].Width_in_blocks, cinfo.Component_info[ci].H_samp_factor), - JpegUtils.jround_up(cinfo.Component_info[ci].height_in_blocks, cinfo.Component_info[ci].V_samp_factor)); - m_whole_image[ci].ErrorProcessor = cinfo; - } - } - else - { - /* We only need a single-MCU buffer. */ - JBLOCK[] buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU]; - for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) - buffer[i] = new JBLOCK(); - - for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) - { - m_MCU_buffer[i] = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU - i]; - for (int j = i; j < JpegConstants.C_MAX_BLOCKS_IN_MCU; j++) - m_MCU_buffer[i][j - i] = buffer[j]; - } - - /* flag for no virtual arrays */ - m_whole_image[0] = null; - } - } - - // Initialize for a processing pass. - public virtual void start_pass(J_BUF_MODE pass_mode) - { - m_iMCU_row_num = 0; - start_iMCU_row(); - - switch (pass_mode) - { - case J_BUF_MODE.JBUF_PASS_THRU: - if (m_whole_image[0] != null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - break; - - case J_BUF_MODE.JBUF_SAVE_AND_PASS: - if (m_whole_image[0] == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - break; - - case J_BUF_MODE.JBUF_CRANK_DEST: - if (m_whole_image[0] == null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - break; - - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - break; - } - - m_passModeSetByLastStartPass = pass_mode; - } - - public virtual bool compress_data(byte[][][] input_buf) - { - switch (m_passModeSetByLastStartPass) - { - case J_BUF_MODE.JBUF_PASS_THRU: - return compressDataImpl(input_buf); - - case J_BUF_MODE.JBUF_SAVE_AND_PASS: - return compressFirstPass(input_buf); - - case J_BUF_MODE.JBUF_CRANK_DEST: - return compressOutput(); - } - - return false; - } - - /// - /// Process some data in the single-pass case. - /// We process the equivalent of one fully interleaved MCU row ("iMCU" row) - /// per call, ie, v_samp_factor block rows for each component in the image. - /// Returns true if the iMCU row is completed, false if suspended. - /// - /// NB: input_buf contains a plane for each component in image, - /// which we index according to the component's SOF position. - /// - private bool compressDataImpl(byte[][][] input_buf) - { - int last_MCU_col = m_cinfo.m_MCUs_per_row - 1; - int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; - - /* Loop to write as much as one whole iMCU row */ - for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) - { - for (int MCU_col_num = m_mcu_ctr; MCU_col_num <= last_MCU_col; MCU_col_num++) - { - /* Determine where data comes from in input_buf and do the DCT thing. - * Each call on forward_DCT processes a horizontal row of DCT blocks - * as wide as an MCU; we rely on having allocated the MCU_buffer[] blocks - * sequentially. Dummy blocks at the right or bottom edge are filled in - * specially. The data in them does not matter for image reconstruction, - * so we fill them with values that will encode to the smallest amount of - * data, viz: all zeroes in the AC entries, DC entries equal to previous - * block's DC value. (Thanks to Thomas Kinsman for this idea.) - */ - int blkn = 0; - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - int blockcnt = (MCU_col_num < last_MCU_col) ? componentInfo.MCU_width : componentInfo.last_col_width; - int xpos = MCU_col_num * componentInfo.MCU_sample_width; - int ypos = yoffset * JpegConstants.DCTSIZE; - - for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) - { - if (m_iMCU_row_num < last_iMCU_row || yoffset + yindex < componentInfo.last_row_height) - { - m_cinfo.m_fdct.forward_DCT(componentInfo.Quant_tbl_no, input_buf[componentInfo.Component_index], - m_MCU_buffer[blkn], ypos, xpos, blockcnt); - - if (blockcnt < componentInfo.MCU_width) - { - /* Create some dummy blocks at the right edge of the image. */ - for (int i = 0; i < (componentInfo.MCU_width - blockcnt); i++) - Array.Clear(m_MCU_buffer[blkn + blockcnt][i].data, 0, m_MCU_buffer[blkn + blockcnt][i].data.Length); - - for (int bi = blockcnt; bi < componentInfo.MCU_width; bi++) - m_MCU_buffer[blkn + bi][0][0] = m_MCU_buffer[blkn + bi - 1][0][0]; - } - } - else - { - /* Create a row of dummy blocks at the bottom of the image. */ - for (int i = 0; i < componentInfo.MCU_width; i++) - Array.Clear(m_MCU_buffer[blkn][i].data, 0, m_MCU_buffer[blkn][i].data.Length); - - for (int bi = 0; bi < componentInfo.MCU_width; bi++) - m_MCU_buffer[blkn + bi][0][0] = m_MCU_buffer[blkn - 1][0][0]; - } - - blkn += componentInfo.MCU_width; - ypos += JpegConstants.DCTSIZE; - } - } - - /* Try to write the MCU. In event of a suspension failure, we will - * re-DCT the MCU on restart (a bit inefficient, could be fixed...) - */ - if (!m_cinfo.m_entropy.encode_mcu(m_MCU_buffer)) - { - /* Suspension forced; update state counters and exit */ - m_MCU_vert_offset = yoffset; - m_mcu_ctr = MCU_col_num; - return false; - } - } - - /* Completed an MCU row, but perhaps not an iMCU row */ - m_mcu_ctr = 0; - } - - /* Completed the iMCU row, advance counters for next one */ - m_iMCU_row_num++; - start_iMCU_row(); - return true; - } - - /// - /// Process some data in the first pass of a multi-pass case. - /// We process the equivalent of one fully interleaved MCU row ("iMCU" row) - /// per call, ie, v_samp_factor block rows for each component in the image. - /// This amount of data is read from the source buffer, DCT'd and quantized, - /// and saved into the virtual arrays. We also generate suitable dummy blocks - /// as needed at the right and lower edges. (The dummy blocks are constructed - /// in the virtual arrays, which have been padded appropriately.) This makes - /// it possible for subsequent passes not to worry about real vs. dummy blocks. - /// - /// We must also emit the data to the entropy encoder. This is conveniently - /// done by calling compress_output() after we've loaded the current strip - /// of the virtual arrays. - /// - /// NB: input_buf contains a plane for each component in image. All - /// components are DCT'd and loaded into the virtual arrays in this pass. - /// However, it may be that only a subset of the components are emitted to - /// the entropy encoder during this first pass; be careful about looking - /// at the scan-dependent variables (MCU dimensions, etc). - /// - private bool compressFirstPass(byte[][][] input_buf) - { - int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; - - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[ci]; - - /* Align the virtual buffer for this component. */ - JBLOCK[][] buffer = m_whole_image[ci].Access(m_iMCU_row_num * componentInfo.V_samp_factor, - componentInfo.V_samp_factor); - - /* Count non-dummy DCT block rows in this iMCU row. */ - int block_rows; - if (m_iMCU_row_num < last_iMCU_row) - { - block_rows = componentInfo.V_samp_factor; - } - else - { - /* NB: can't use last_row_height here, since may not be set! */ - block_rows = componentInfo.height_in_blocks % componentInfo.V_samp_factor; - if (block_rows == 0) - block_rows = componentInfo.V_samp_factor; - } - - int blocks_across = componentInfo.Width_in_blocks; - int h_samp_factor = componentInfo.H_samp_factor; - - /* Count number of dummy blocks to be added at the right margin. */ - int ndummy = blocks_across % h_samp_factor; - if (ndummy > 0) - ndummy = h_samp_factor - ndummy; - - /* Perform DCT for all non-dummy blocks in this iMCU row. Each call - * on forward_DCT processes a complete horizontal row of DCT blocks. - */ - for (int block_row = 0; block_row < block_rows; block_row++) - { - m_cinfo.m_fdct.forward_DCT(componentInfo.Quant_tbl_no, input_buf[ci], - buffer[block_row], block_row * JpegConstants.DCTSIZE, 0, blocks_across); - - if (ndummy > 0) - { - /* Create dummy blocks at the right edge of the image. */ - Array.Clear(buffer[block_row][blocks_across].data, 0, buffer[block_row][blocks_across].data.Length); - - short lastDC = buffer[block_row][blocks_across - 1][0]; - for (int bi = 0; bi < ndummy; bi++) - buffer[block_row][blocks_across + bi][0] = lastDC; - } - } - - /* If at end of image, create dummy block rows as needed. - * The tricky part here is that within each MCU, we want the DC values - * of the dummy blocks to match the last real block's DC value. - * This squeezes a few more bytes out of the resulting file... - */ - if (m_iMCU_row_num == last_iMCU_row) - { - blocks_across += ndummy; /* include lower right corner */ - int MCUs_across = blocks_across / h_samp_factor; - for (int block_row = block_rows; block_row < componentInfo.V_samp_factor; block_row++) - { - for (int i = 0; i < blocks_across; i++) - Array.Clear(buffer[block_row][i].data, 0, buffer[block_row][i].data.Length); - - int thisOffset = 0; - int lastOffset = 0; - for (int MCUindex = 0; MCUindex < MCUs_across; MCUindex++) - { - short lastDC = buffer[block_row - 1][lastOffset + h_samp_factor - 1][0]; - for (int bi = 0; bi < h_samp_factor; bi++) - buffer[block_row][thisOffset + bi][0] = lastDC; - - thisOffset += h_samp_factor; /* advance to next MCU in row */ - lastOffset += h_samp_factor; - } - } - } - } - - /* NB: compress_output will increment iMCU_row_num if successful. - * A suspension return will result in redoing all the work above next time. - */ - - /* Emit data to the entropy encoder, sharing code with subsequent passes */ - return compressOutput(); - } - - /// - /// Process some data in subsequent passes of a multi-pass case. - /// We process the equivalent of one fully interleaved MCU row ("iMCU" row) - /// per call, ie, v_samp_factor block rows for each component in the scan. - /// The data is obtained from the virtual arrays and fed to the entropy coder. - /// Returns true if the iMCU row is completed, false if suspended. - /// - private bool compressOutput() - { - /* Align the virtual buffers for the components used in this scan. - */ - JBLOCK[][][] buffer = new JBLOCK[JpegConstants.MAX_COMPS_IN_SCAN][][]; - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - buffer[ci] = m_whole_image[componentInfo.Component_index].Access( - m_iMCU_row_num * componentInfo.V_samp_factor, componentInfo.V_samp_factor); - } - - /* Loop to process one whole iMCU row */ - for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) - { - for (int MCU_col_num = m_mcu_ctr; MCU_col_num < m_cinfo.m_MCUs_per_row; MCU_col_num++) - { - /* Construct list of pointers to DCT blocks belonging to this MCU */ - int blkn = 0; /* index of current DCT block within MCU */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - int start_col = MCU_col_num * componentInfo.MCU_width; - for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) - { - for (int xindex = 0; xindex < componentInfo.MCU_width; xindex++) - { - int bufLength = buffer[ci][yindex + yoffset].Length; - int start = start_col + xindex; - m_MCU_buffer[blkn] = new JBLOCK[bufLength - start]; - for (int j = start; j < bufLength; j++) - m_MCU_buffer[blkn][j - start] = buffer[ci][yindex + yoffset][j]; - - blkn++; - } - } - } - - /* Try to write the MCU. */ - if (!m_cinfo.m_entropy.encode_mcu(m_MCU_buffer)) - { - /* Suspension forced; update state counters and exit */ - m_MCU_vert_offset = yoffset; - m_mcu_ctr = MCU_col_num; - return false; - } - } - - /* Completed an MCU row, but perhaps not an iMCU row */ - m_mcu_ctr = 0; - } - - /* Completed the iMCU row, advance counters for next one */ - m_iMCU_row_num++; - start_iMCU_row(); - return true; - } - - // Reset within-iMCU-row counters for a new row - private void start_iMCU_row() - { - /* In an interleaved scan, an MCU row is the same as an iMCU row. - * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. - * But at the bottom of the image, process only what's left. - */ - if (m_cinfo.m_comps_in_scan > 1) - { - m_MCU_rows_per_iMCU_row = 1; - } - else - { - if (m_iMCU_row_num < (m_cinfo.m_total_iMCU_rows - 1)) - m_MCU_rows_per_iMCU_row = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[0]].V_samp_factor; - else - m_MCU_rows_per_iMCU_row = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[0]].last_row_height; - } - - m_mcu_ctr = 0; - m_MCU_vert_offset = 0; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_destination_mgr.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_destination_mgr.cs deleted file mode 100644 index eb2eba3f..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_destination_mgr.cs +++ /dev/null @@ -1,131 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains compression data destination routines for the case of - * emitting JPEG data to a file (or any stdio stream). While these routines - * are sufficient for most applications, some will want to use a different - * destination manager. - * IMPORTANT: we assume that fwrite() will correctly transcribe an array of - * bytes into 8-bit-wide elements on external storage. If char is wider - * than 8 bits on your machine, you may need to do some tweaking. - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Expanded data destination object for output to Stream - /// - class my_destination_mgr : jpeg_destination_mgr - { - private const int OUTPUT_BUF_SIZE = 4096; /* choose an efficiently fwrite'able size */ - - private jpeg_compress_struct m_cinfo; - - private Stream m_outfile; /* target stream */ - private byte[] m_buffer; /* start of buffer */ - - public my_destination_mgr(jpeg_compress_struct cinfo, Stream alreadyOpenFile) - { - m_cinfo = cinfo; - m_outfile = alreadyOpenFile; - } - - /// - /// Initialize destination --- called by jpeg_start_compress - /// before any data is actually written. - /// - public override void init_destination() - { - /* Allocate the output buffer --- it will be released when done with image */ - m_buffer = new byte[OUTPUT_BUF_SIZE]; - initInternalBuffer(m_buffer, 0); - } - - /// - /// Empty the output buffer --- called whenever buffer fills up. - /// - /// In typical applications, this should write the entire output buffer - /// (ignoring the current state of next_output_byte and free_in_buffer), - /// reset the pointer and count to the start of the buffer, and return true - /// indicating that the buffer has been dumped. - /// - /// In applications that need to be able to suspend compression due to output - /// overrun, a false return indicates that the buffer cannot be emptied now. - /// In this situation, the compressor will return to its caller (possibly with - /// an indication that it has not accepted all the supplied scanlines). The - /// application should resume compression after it has made more room in the - /// output buffer. Note that there are substantial restrictions on the use of - /// suspension --- see the documentation. - /// - /// When suspending, the compressor will back up to a convenient restart point - /// (typically the start of the current MCU). next_output_byte and free_in_buffer - /// indicate where the restart point will be if the current call returns false. - /// Data beyond this point will be regenerated after resumption, so do not - /// write it out when emptying the buffer externally. - /// - public override bool empty_output_buffer() - { - writeBuffer(m_buffer.Length); - initInternalBuffer(m_buffer, 0); - return true; - } - - /// - /// Terminate destination --- called by jpeg_finish_compress - /// after all data has been written. Usually needs to flush buffer. - /// - /// NB: *not* called by jpeg_abort or jpeg_destroy; surrounding - /// application must deal with any cleanup that should happen even - /// for error exit. - /// - public override void term_destination() - { - int datacount = m_buffer.Length - freeInBuffer; - - /* Write any data remaining in the buffer */ - if (datacount > 0) - writeBuffer(datacount); - - m_outfile.Flush(); - } - - private void writeBuffer(int dataCount) - { - try - { - m_outfile.Write(m_buffer, 0, dataCount); - } - catch (IOException e) - { - m_cinfo.TRACEMS(0, J_MESSAGE_CODE.JERR_FILE_WRITE, e.Message); - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FILE_WRITE); - } - catch (NotSupportedException e) - { - m_cinfo.TRACEMS(0, J_MESSAGE_CODE.JERR_FILE_WRITE, e.Message); - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FILE_WRITE); - } - catch (ObjectDisposedException e) - { - m_cinfo.TRACEMS(0, J_MESSAGE_CODE.JERR_FILE_WRITE, e.Message); - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FILE_WRITE); - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_merged_upsampler.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_merged_upsampler.cs deleted file mode 100644 index 6d2dc15d..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_merged_upsampler.cs +++ /dev/null @@ -1,381 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains code for merged upsampling/color conversion. - * - * This file combines functions from my_upsampler and jpeg_color_deconverter; - * read those files first to understand what's going on. - * - * When the chroma components are to be upsampled by simple replication - * (ie, box filtering), we can save some work in color conversion by - * calculating all the output pixels corresponding to a pair of chroma - * samples at one time. In the conversion equations - * R = Y + K1 * Cr - * G = Y + K2 * Cb + K3 * Cr - * B = Y + K4 * Cb - * only the Y term varies among the group of pixels corresponding to a pair - * of chroma samples, so the rest of the terms can be calculated just once. - * At typical sampling ratios, this eliminates half or three-quarters of the - * multiplications needed for color conversion. - * - * This file currently provides implementations for the following cases: - * YCbCr => RGB color conversion only. - * Sampling ratios of 2h1v or 2h2v. - * No scaling needed at upsample time. - * Corner-aligned (non-CCIR601) sampling alignment. - * Other special cases could be added, but in most applications these are - * the only common cases. (For uncommon cases we fall back on the more - * general code in my_upsampler and jpeg_color_deconverter) - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - class my_merged_upsampler : jpeg_upsampler - { - private const int SCALEBITS = 16; /* speediest right-shift on some machines */ - private const int ONE_HALF = 1 << (SCALEBITS - 1); - - private jpeg_decompress_struct m_cinfo; - - private bool m_use_2v_upsample; - - /* Private state for YCC->RGB conversion */ - private int[] m_Cr_r_tab; /* => table for Cr to R conversion */ - private int[] m_Cb_b_tab; /* => table for Cb to B conversion */ - private int[] m_Cr_g_tab; /* => table for Cr to G conversion */ - private int[] m_Cb_g_tab; /* => table for Cb to G conversion */ - - /* For 2:1 vertical sampling, we produce two output rows at a time. - * We need a "spare" row buffer to hold the second output row if the - * application provides just a one-row buffer; we also use the spare - * to discard the dummy last row if the image height is odd. - */ - private byte[] m_spare_row; - private bool m_spare_full; /* T if spare buffer is occupied */ - - private int m_out_row_width; /* samples per output row */ - private int m_rows_to_go; /* counts rows remaining in image */ - - public my_merged_upsampler(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - m_need_context_rows = false; - - m_out_row_width = cinfo.m_output_width * cinfo.m_out_color_components; - - if (cinfo.m_max_v_samp_factor == 2) - { - m_use_2v_upsample = true; - /* Allocate a spare row buffer */ - m_spare_row = new byte[m_out_row_width]; - } - else - { - m_use_2v_upsample = false; - } - - build_ycc_rgb_table(); - } - - /// - /// Initialize for an upsampling pass. - /// - public override void start_pass() - { - /* Mark the spare buffer empty */ - m_spare_full = false; - - /* Initialize total-height counter for detecting bottom of image */ - m_rows_to_go = m_cinfo.m_output_height; - } - - public override void upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - if (m_use_2v_upsample) - merged_2v_upsample(input_buf, ref in_row_group_ctr, output_buf, ref out_row_ctr, out_rows_avail); - else - merged_1v_upsample(input_buf, ref in_row_group_ctr, output_buf, ref out_row_ctr); - } - - /// - /// Control routine to do upsampling (and color conversion). - /// The control routine just handles the row buffering considerations. - /// 1:1 vertical sampling case: much easier, never need a spare row. - /// - private void merged_1v_upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, byte[][] output_buf, ref int out_row_ctr) - { - /* Just do the upsampling. */ - h2v1_merged_upsample(input_buf, in_row_group_ctr, output_buf, out_row_ctr); - - /* Adjust counts */ - out_row_ctr++; - in_row_group_ctr++; - } - - /// - /// Control routine to do upsampling (and color conversion). - /// The control routine just handles the row buffering considerations. - /// 2:1 vertical sampling case: may need a spare row. - /// - private void merged_2v_upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - int num_rows; /* number of rows returned to caller */ - if (m_spare_full) - { - /* If we have a spare row saved from a previous cycle, just return it. */ - byte[][] temp = new byte[1][]; - temp[0] = m_spare_row; - JpegUtils.jcopy_sample_rows(temp, 0, output_buf, out_row_ctr, 1, m_out_row_width); - num_rows = 1; - m_spare_full = false; - } - else - { - /* Figure number of rows to return to caller. */ - num_rows = 2; - - /* Not more than the distance to the end of the image. */ - if (num_rows > m_rows_to_go) - num_rows = m_rows_to_go; - - /* And not more than what the client can accept: */ - out_rows_avail -= out_row_ctr; - if (num_rows > out_rows_avail) - num_rows = out_rows_avail; - - /* Create output pointer array for upsampler. */ - byte[][] work_ptrs = new byte[2][]; - work_ptrs[0] = output_buf[out_row_ctr]; - if (num_rows > 1) - { - work_ptrs[1] = output_buf[out_row_ctr + 1]; - } - else - { - work_ptrs[1] = m_spare_row; - m_spare_full = true; - } - - /* Now do the upsampling. */ - h2v2_merged_upsample(input_buf, in_row_group_ctr, work_ptrs); - } - - /* Adjust counts */ - out_row_ctr += num_rows; - m_rows_to_go -= num_rows; - - /* When the buffer is emptied, declare this input row group consumed */ - if (!m_spare_full) - in_row_group_ctr++; - } - - /* - * These are the routines invoked by the control routines to do - * the actual upsampling/conversion. One row group is processed per call. - * - * Note: since we may be writing directly into application-supplied buffers, - * we have to be honest about the output width; we can't assume the buffer - * has been rounded up to an even width. - */ - - /// - /// Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. - /// - private void h2v1_merged_upsample(ComponentBuffer[] input_buf, int in_row_group_ctr, byte[][] output_buf, int outRow) - { - int inputIndex0 = 0; - int inputIndex1 = 0; - int inputIndex2 = 0; - int outputIndex = 0; - - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset; - - /* Loop for each pair of output pixels */ - for (int col = m_cinfo.m_output_width >> 1; col > 0; col--) - { - /* Do the chroma part of the calculation */ - int cb = input_buf[1][in_row_group_ctr][inputIndex1]; - inputIndex1++; - - int cr = input_buf[2][in_row_group_ctr][inputIndex2]; - inputIndex2++; - - int cred = m_Cr_r_tab[cr]; - int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); - int cblue = m_Cb_b_tab[cb]; - - /* Fetch 2 Y values and emit 2 pixels */ - int y = input_buf[0][in_row_group_ctr][inputIndex0]; - inputIndex0++; - - output_buf[outRow][outputIndex + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[outRow][outputIndex + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[outRow][outputIndex + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - outputIndex += JpegConstants.RGB_PIXELSIZE; - - y = input_buf[0][in_row_group_ctr][inputIndex0]; - inputIndex0++; - - output_buf[outRow][outputIndex + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[outRow][outputIndex + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[outRow][outputIndex + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - outputIndex += JpegConstants.RGB_PIXELSIZE; - } - - /* If image width is odd, do the last output column separately */ - if ((m_cinfo.m_output_width & 1) != 0) - { - int cb = input_buf[1][in_row_group_ctr][inputIndex1]; - int cr = input_buf[2][in_row_group_ctr][inputIndex2]; - int cred = m_Cr_r_tab[cr]; - int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); - int cblue = m_Cb_b_tab[cb]; - - int y = input_buf[0][in_row_group_ctr][inputIndex0]; - output_buf[outRow][outputIndex + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[outRow][outputIndex + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[outRow][outputIndex + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - } - } - - /// - /// Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical. - /// - private void h2v2_merged_upsample(ComponentBuffer[] input_buf, int in_row_group_ctr, byte[][] output_buf) - { - int inputRow00 = in_row_group_ctr * 2; - int inputIndex00 = 0; - - int inputRow01 = in_row_group_ctr * 2 + 1; - int inputIndex01 = 0; - - int inputIndex1 = 0; - int inputIndex2 = 0; - - int outIndex0 = 0; - int outIndex1 = 0; - - byte[] limit = m_cinfo.m_sample_range_limit; - int limitOffset = m_cinfo.m_sampleRangeLimitOffset; - - /* Loop for each group of output pixels */ - for (int col = m_cinfo.m_output_width >> 1; col > 0; col--) - { - /* Do the chroma part of the calculation */ - int cb = input_buf[1][in_row_group_ctr][inputIndex1]; - inputIndex1++; - - int cr = input_buf[2][in_row_group_ctr][inputIndex2]; - inputIndex2++; - - int cred = m_Cr_r_tab[cr]; - int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); - int cblue = m_Cb_b_tab[cb]; - - /* Fetch 4 Y values and emit 4 pixels */ - int y = input_buf[0][inputRow00][inputIndex00]; - inputIndex00++; - - output_buf[0][outIndex0 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[0][outIndex0 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[0][outIndex0 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - outIndex0 += JpegConstants.RGB_PIXELSIZE; - - y = input_buf[0][inputRow00][inputIndex00]; - inputIndex00++; - - output_buf[0][outIndex0 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[0][outIndex0 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[0][outIndex0 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - outIndex0 += JpegConstants.RGB_PIXELSIZE; - - y = input_buf[0][inputRow01][inputIndex01]; - inputIndex01++; - - output_buf[1][outIndex1 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[1][outIndex1 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[1][outIndex1 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - outIndex1 += JpegConstants.RGB_PIXELSIZE; - - y = input_buf[0][inputRow01][inputIndex01]; - inputIndex01++; - - output_buf[1][outIndex1 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[1][outIndex1 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[1][outIndex1 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - outIndex1 += JpegConstants.RGB_PIXELSIZE; - } - - /* If image width is odd, do the last output column separately */ - if ((m_cinfo.m_output_width & 1) != 0) - { - int cb = input_buf[1][in_row_group_ctr][inputIndex1]; - int cr = input_buf[2][in_row_group_ctr][inputIndex2]; - int cred = m_Cr_r_tab[cr]; - int cgreen = JpegUtils.RIGHT_SHIFT(m_Cb_g_tab[cb] + m_Cr_g_tab[cr], SCALEBITS); - int cblue = m_Cb_b_tab[cb]; - - int y = input_buf[0][inputRow00][inputIndex00]; - output_buf[0][outIndex0 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[0][outIndex0 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[0][outIndex0 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - - y = input_buf[0][inputRow01][inputIndex01]; - output_buf[1][outIndex1 + JpegConstants.RGB_RED] = limit[limitOffset + y + cred]; - output_buf[1][outIndex1 + JpegConstants.RGB_GREEN] = limit[limitOffset + y + cgreen]; - output_buf[1][outIndex1 + JpegConstants.RGB_BLUE] = limit[limitOffset + y + cblue]; - } - } - - /// - /// Initialize tables for YCC->RGB colorspace conversion. - /// This is taken directly from jpeg_color_deconverter; see that file for more info. - /// - private void build_ycc_rgb_table() - { - m_Cr_r_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - m_Cb_b_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - m_Cr_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - m_Cb_g_tab = new int[JpegConstants.MAXJSAMPLE + 1]; - - for (int i = 0, x = -JpegConstants.CENTERJSAMPLE; i <= JpegConstants.MAXJSAMPLE; i++, x++) - { - /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ - /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ - /* Cr=>R value is nearest int to 1.40200 * x */ - m_Cr_r_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); - - /* Cb=>B value is nearest int to 1.77200 * x */ - m_Cb_b_tab[i] = JpegUtils.RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); - - /* Cr=>G value is scaled-up -0.71414 * x */ - m_Cr_g_tab[i] = (-FIX(0.71414)) * x; - - /* Cb=>G value is scaled-up -0.34414 * x */ - /* We also add in ONE_HALF so that need not do it in inner loop */ - m_Cb_g_tab[i] = (-FIX(0.34414)) * x + ONE_HALF; - } - } - - private static int FIX(double x) - { - return ((int)((x) * (1L << SCALEBITS) + 0.5)); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_source_mgr.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_source_mgr.cs deleted file mode 100644 index 84e394f2..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_source_mgr.cs +++ /dev/null @@ -1,123 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains decompression data source routines for the case of - * reading JPEG data from a file (or any stdio stream). While these routines - * are sufficient for most applications, some will want to use a different - * source manager. - * IMPORTANT: we assume that fread() will correctly transcribe an array of - * bytes from 8-bit-wide elements on external storage. If char is wider - * than 8 bits on your machine, you may need to do some tweaking. - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Expanded data source object for stdio input - /// - class my_source_mgr : jpeg_source_mgr - { - private const int INPUT_BUF_SIZE = 4096; - - private jpeg_decompress_struct m_cinfo; - - private Stream m_infile; /* source stream */ - private byte[] m_buffer; /* start of buffer */ - private bool m_start_of_file; /* have we gotten any data yet? */ - - /// - /// Initialize source - called by jpeg_read_header - /// before any data is actually read. - /// - public my_source_mgr(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - m_buffer = new byte[INPUT_BUF_SIZE]; - } - - public void Attach(Stream infile) - { - m_infile = infile; - m_infile.Seek(0, SeekOrigin.Begin); - initInternalBuffer(null, 0); - } - - public override void init_source() - { - /* We reset the empty-input-file flag for each image, - * but we don't clear the input buffer. - * This is correct behavior for reading a series of images from one source. - */ - m_start_of_file = true; - } - - /// - /// Fill the input buffer - called whenever buffer is emptied. - /// - /// In typical applications, this should read fresh data into the buffer - /// (ignoring the current state of next_input_byte and bytes_in_buffer), - /// reset the pointer and count to the start of the buffer, and return true - /// indicating that the buffer has been reloaded. It is not necessary to - /// fill the buffer entirely, only to obtain at least one more byte. - /// - /// There is no such thing as an EOF return. If the end of the file has been - /// reached, the routine has a choice of ERREXIT() or inserting fake data into - /// the buffer. In most cases, generating a warning message and inserting a - /// fake EOI marker is the best course of action --- this will allow the - /// decompressor to output however much of the image is there. However, - /// the resulting error message is misleading if the real problem is an empty - /// input file, so we handle that case specially. - /// - /// In applications that need to be able to suspend compression due to input - /// not being available yet, a false return indicates that no more data can be - /// obtained right now, but more may be forthcoming later. In this situation, - /// the decompressor will return to its caller (with an indication of the - /// number of scanlines it has read, if any). The application should resume - /// decompression after it has loaded more data into the input buffer. Note - /// that there are substantial restrictions on the use of suspension --- see - /// the documentation. - /// - /// When suspending, the decompressor will back up to a convenient restart point - /// (typically the start of the current MCU). next_input_byte and bytes_in_buffer - /// indicate where the restart point will be if the current call returns false. - /// Data beyond this point must be rescanned after resumption, so move it to - /// the front of the buffer rather than discarding it. - /// - public override bool fill_input_buffer() - { - int nbytes = m_infile.Read(m_buffer, 0, INPUT_BUF_SIZE); - if (nbytes <= 0) - { - if (m_start_of_file) /* Treat empty input file as fatal error */ - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_INPUT_EMPTY); - - m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_JPEG_EOF); - /* Insert a fake EOI marker */ - m_buffer[0] = (byte)0xFF; - m_buffer[1] = (byte)JPEG_MARKER.EOI; - nbytes = 2; - } - - initInternalBuffer(m_buffer, nbytes); - m_start_of_file = false; - - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_trans_c_coef_controller.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_trans_c_coef_controller.cs deleted file mode 100644 index 5eceee66..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_trans_c_coef_controller.cs +++ /dev/null @@ -1,204 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains library routines for transcoding compression, - * that is, writing raw DCT coefficient arrays to an output JPEG file. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// This is a special implementation of the coefficient - /// buffer controller. This is similar to jccoefct.c, but it handles only - /// output from presupplied virtual arrays. Furthermore, we generate any - /// dummy padding blocks on-the-fly rather than expecting them to be present - /// in the arrays. - /// - class my_trans_c_coef_controller : jpeg_c_coef_controller - { - private jpeg_compress_struct m_cinfo; - - private int m_iMCU_row_num; /* iMCU row # within image */ - private int m_mcu_ctr; /* counts MCUs processed in current row */ - private int m_MCU_vert_offset; /* counts MCU rows within iMCU row */ - private int m_MCU_rows_per_iMCU_row; /* number of such rows needed */ - - /* Virtual block array for each component. */ - private jvirt_array[] m_whole_image; - - /* Workspace for constructing dummy blocks at right/bottom edges. */ - private JBLOCK[][] m_dummy_buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU][]; - - /// - /// Initialize coefficient buffer controller. - /// - /// Each passed coefficient array must be the right size for that - /// coefficient: width_in_blocks wide and height_in_blocks high, - /// with unit height at least v_samp_factor. - /// - public my_trans_c_coef_controller(jpeg_compress_struct cinfo, jvirt_array[] coef_arrays) - { - m_cinfo = cinfo; - - /* Save pointer to virtual arrays */ - m_whole_image = coef_arrays; - - /* Allocate and pre-zero space for dummy DCT blocks. */ - JBLOCK[] buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU]; - for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) - buffer[i] = new JBLOCK(); - - for (int i = 0; i < JpegConstants.C_MAX_BLOCKS_IN_MCU; i++) - { - m_dummy_buffer[i] = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU - i]; - for (int j = i; j < JpegConstants.C_MAX_BLOCKS_IN_MCU; j++) - m_dummy_buffer[i][j - i] = buffer[j]; - } - } - - /// - /// Initialize for a processing pass. - /// - public virtual void start_pass(J_BUF_MODE pass_mode) - { - if (pass_mode != J_BUF_MODE.JBUF_CRANK_DEST) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - - m_iMCU_row_num = 0; - start_iMCU_row(); - } - - /// - /// Process some data. - /// We process the equivalent of one fully interleaved MCU row ("iMCU" row) - /// per call, ie, v_samp_factor block rows for each component in the scan. - /// The data is obtained from the virtual arrays and fed to the entropy coder. - /// Returns true if the iMCU row is completed, false if suspended. - /// - /// NB: input_buf is ignored; it is likely to be a null pointer. - /// - public virtual bool compress_data(byte[][][] input_buf) - { - /* Align the virtual buffers for the components used in this scan. */ - JBLOCK[][][] buffer = new JBLOCK[JpegConstants.MAX_COMPS_IN_SCAN][][]; - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - buffer[ci] = m_whole_image[componentInfo.Component_index].Access( - m_iMCU_row_num * componentInfo.V_samp_factor, componentInfo.V_samp_factor); - } - - /* Loop to process one whole iMCU row */ - int last_MCU_col = m_cinfo.m_MCUs_per_row - 1; - int last_iMCU_row = m_cinfo.m_total_iMCU_rows - 1; - JBLOCK[][] MCU_buffer = new JBLOCK[JpegConstants.C_MAX_BLOCKS_IN_MCU][]; - for (int yoffset = m_MCU_vert_offset; yoffset < m_MCU_rows_per_iMCU_row; yoffset++) - { - for (int MCU_col_num = m_mcu_ctr; MCU_col_num < m_cinfo.m_MCUs_per_row; MCU_col_num++) - { - /* Construct list of pointers to DCT blocks belonging to this MCU */ - int blkn = 0; /* index of current DCT block within MCU */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - int start_col = MCU_col_num * componentInfo.MCU_width; - int blockcnt = (MCU_col_num < last_MCU_col) ? componentInfo.MCU_width : componentInfo.last_col_width; - for (int yindex = 0; yindex < componentInfo.MCU_height; yindex++) - { - int xindex = 0; - if (m_iMCU_row_num < last_iMCU_row || yindex + yoffset < componentInfo.last_row_height) - { - /* Fill in pointers to real blocks in this row */ - for (xindex = 0; xindex < blockcnt; xindex++) - { - int bufLength = buffer[ci][yindex + yoffset].Length; - int start = start_col + xindex; - MCU_buffer[blkn] = new JBLOCK[bufLength - start]; - for (int j = start; j < bufLength; j++) - MCU_buffer[blkn][j - start] = buffer[ci][yindex + yoffset][j]; - - blkn++; - } - } - else - { - /* At bottom of image, need a whole row of dummy blocks */ - xindex = 0; - } - - /* Fill in any dummy blocks needed in this row. - * Dummy blocks are filled in the same way as in jccoefct.c: - * all zeroes in the AC entries, DC entries equal to previous - * block's DC value. The init routine has already zeroed the - * AC entries, so we need only set the DC entries correctly. - */ - for (; xindex < componentInfo.MCU_width; xindex++) - { - MCU_buffer[blkn] = m_dummy_buffer[blkn]; - MCU_buffer[blkn][0][0] = MCU_buffer[blkn - 1][0][0]; - blkn++; - } - } - } - - /* Try to write the MCU. */ - if (!m_cinfo.m_entropy.encode_mcu(MCU_buffer)) - { - /* Suspension forced; update state counters and exit */ - m_MCU_vert_offset = yoffset; - m_mcu_ctr = MCU_col_num; - return false; - } - } - - /* Completed an MCU row, but perhaps not an iMCU row */ - m_mcu_ctr = 0; - } - - /* Completed the iMCU row, advance counters for next one */ - m_iMCU_row_num++; - start_iMCU_row(); - return true; - } - - /// - /// Reset within-iMCU-row counters for a new row - /// - private void start_iMCU_row() - { - /* In an interleaved scan, an MCU row is the same as an iMCU row. - * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. - * But at the bottom of the image, process only what's left. - */ - if (m_cinfo.m_comps_in_scan > 1) - { - m_MCU_rows_per_iMCU_row = 1; - } - else - { - if (m_iMCU_row_num < (m_cinfo.m_total_iMCU_rows - 1)) - m_MCU_rows_per_iMCU_row = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[0]].V_samp_factor; - else - m_MCU_rows_per_iMCU_row = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[0]].last_row_height; - } - - m_mcu_ctr = 0; - m_MCU_vert_offset = 0; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_upsampler.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_upsampler.cs deleted file mode 100644 index 53f9a182..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/my_upsampler.cs +++ /dev/null @@ -1,525 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains upsampling routines. - * - * Upsampling input data is counted in "row groups". A row group - * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) - * sample rows of each component. Upsampling will normally produce - * max_v_samp_factor pixel rows from each row group (but this could vary - * if the upsampler is applying a scale factor of its own). - * - * An excellent reference for image resampling is - * Digital Image Warping, George Wolberg, 1990. - * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - class my_upsampler : jpeg_upsampler - { - private enum ComponentUpsampler - { - noop_upsampler, - fullsize_upsampler, - h2v1_fancy_upsampler, - h2v1_upsampler, - h2v2_fancy_upsampler, - h2v2_upsampler, - int_upsampler - } - - private jpeg_decompress_struct m_cinfo; - - /* Color conversion buffer. When using separate upsampling and color - * conversion steps, this buffer holds one upsampled row group until it - * has been color converted and output. - * Note: we do not allocate any storage for component(s) which are full-size, - * ie do not need rescaling. The corresponding entry of color_buf[] is - * simply set to point to the input data array, thereby avoiding copying. - */ - private ComponentBuffer[] m_color_buf = new ComponentBuffer[JpegConstants.MAX_COMPONENTS]; - - // used only for fullsize_upsampler mode - private int[] m_perComponentOffsets = new int[JpegConstants.MAX_COMPONENTS]; - - /* Per-component upsampling method pointers */ - private ComponentUpsampler[] m_upsampleMethods = new ComponentUpsampler[JpegConstants.MAX_COMPONENTS]; - private int m_currentComponent; // component being upsampled - private int m_upsampleRowOffset; - - private int m_next_row_out; /* counts rows emitted from color_buf */ - private int m_rows_to_go; /* counts rows remaining in image */ - - /* Height of an input row group for each component. */ - private int[] m_rowgroup_height = new int[JpegConstants.MAX_COMPONENTS]; - - /* These arrays save pixel expansion factors so that int_expand need not - * recompute them each time. They are unused for other upsampling methods. - */ - private byte[] m_h_expand = new byte[JpegConstants.MAX_COMPONENTS]; - private byte[] m_v_expand = new byte[JpegConstants.MAX_COMPONENTS]; - - public my_upsampler(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - m_need_context_rows = false; /* until we find out differently */ - - if (cinfo.m_CCIR601_sampling) /* this isn't supported */ - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL); - - /* jpeg_d_main_controller doesn't support context rows when min_DCT_scaled_size = 1, - * so don't ask for it. - */ - bool do_fancy = cinfo.m_do_fancy_upsampling && cinfo.m_min_DCT_scaled_size > 1; - - /* Verify we can handle the sampling factors, select per-component methods, - * and create storage as needed. - */ - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - jpeg_component_info componentInfo = cinfo.Comp_info[ci]; - - /* Compute size of an "input group" after IDCT scaling. This many samples - * are to be converted to max_h_samp_factor * max_v_samp_factor pixels. - */ - int h_in_group = (componentInfo.H_samp_factor * componentInfo.DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; - int v_in_group = (componentInfo.V_samp_factor * componentInfo.DCT_scaled_size) / cinfo.m_min_DCT_scaled_size; - int h_out_group = cinfo.m_max_h_samp_factor; - int v_out_group = cinfo.m_max_v_samp_factor; - - /* save for use later */ - m_rowgroup_height[ci] = v_in_group; - bool need_buffer = true; - if (!componentInfo.component_needed) - { - /* Don't bother to upsample an uninteresting component. */ - m_upsampleMethods[ci] = ComponentUpsampler.noop_upsampler; - need_buffer = false; - } - else if (h_in_group == h_out_group && v_in_group == v_out_group) - { - /* Fullsize components can be processed without any work. */ - m_upsampleMethods[ci] = ComponentUpsampler.fullsize_upsampler; - need_buffer = false; - } - else if (h_in_group * 2 == h_out_group && v_in_group == v_out_group) - { - /* Special cases for 2h1v upsampling */ - if (do_fancy && componentInfo.downsampled_width > 2) - m_upsampleMethods[ci] = ComponentUpsampler.h2v1_fancy_upsampler; - else - m_upsampleMethods[ci] = ComponentUpsampler.h2v1_upsampler; - } - else if (h_in_group * 2 == h_out_group && v_in_group * 2 == v_out_group) - { - /* Special cases for 2h2v upsampling */ - if (do_fancy && componentInfo.downsampled_width > 2) - { - m_upsampleMethods[ci] = ComponentUpsampler.h2v2_fancy_upsampler; - m_need_context_rows = true; - } - else - { - m_upsampleMethods[ci] = ComponentUpsampler.h2v2_upsampler; - } - } - else if ((h_out_group % h_in_group) == 0 && (v_out_group % v_in_group) == 0) - { - /* Generic integral-factors upsampling method */ - m_upsampleMethods[ci] = ComponentUpsampler.int_upsampler; - m_h_expand[ci] = (byte) (h_out_group / h_in_group); - m_v_expand[ci] = (byte) (v_out_group / v_in_group); - } - else - cinfo.ERREXIT(J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL); - - if (need_buffer) - { - ComponentBuffer cb = new ComponentBuffer(); - cb.SetBuffer(jpeg_common_struct.AllocJpegSamples(JpegUtils.jround_up(cinfo.m_output_width, - cinfo.m_max_h_samp_factor), cinfo.m_max_v_samp_factor), null, 0); - - m_color_buf[ci] = cb; - } - } - } - - /// - /// Initialize for an upsampling pass. - /// - public override void start_pass() - { - /* Mark the conversion buffer empty */ - m_next_row_out = m_cinfo.m_max_v_samp_factor; - - /* Initialize total-height counter for detecting bottom of image */ - m_rows_to_go = m_cinfo.m_output_height; - } - - /// - /// Control routine to do upsampling (and color conversion). - /// - /// In this version we upsample each component independently. - /// We upsample one row group into the conversion buffer, then apply - /// color conversion a row at a time. - /// - public override void upsample(ComponentBuffer[] input_buf, ref int in_row_group_ctr, int in_row_groups_avail, byte[][] output_buf, ref int out_row_ctr, int out_rows_avail) - { - /* Fill the conversion buffer, if it's empty */ - if (m_next_row_out >= m_cinfo.m_max_v_samp_factor) - { - for (int ci = 0; ci < m_cinfo.m_num_components; ci++) - { - m_perComponentOffsets[ci] = 0; - - /* Invoke per-component upsample method.*/ - m_currentComponent = ci; - m_upsampleRowOffset = in_row_group_ctr * m_rowgroup_height[ci]; - upsampleComponent(ref input_buf[ci]); - } - - m_next_row_out = 0; - } - - /* Color-convert and emit rows */ - - /* How many we have in the buffer: */ - int num_rows = m_cinfo.m_max_v_samp_factor - m_next_row_out; - - /* Not more than the distance to the end of the image. Need this test - * in case the image height is not a multiple of max_v_samp_factor: - */ - if (num_rows > m_rows_to_go) - num_rows = m_rows_to_go; - - /* And not more than what the client can accept: */ - out_rows_avail -= out_row_ctr; - if (num_rows > out_rows_avail) - num_rows = out_rows_avail; - - m_cinfo.m_cconvert.color_convert(m_color_buf, m_perComponentOffsets, m_next_row_out, output_buf, out_row_ctr, num_rows); - - /* Adjust counts */ - out_row_ctr += num_rows; - m_rows_to_go -= num_rows; - m_next_row_out += num_rows; - - /* When the buffer is emptied, declare this input row group consumed */ - if (m_next_row_out >= m_cinfo.m_max_v_samp_factor) - in_row_group_ctr++; - } - - private void upsampleComponent(ref ComponentBuffer input_data) - { - switch (m_upsampleMethods[m_currentComponent]) - { - case ComponentUpsampler.noop_upsampler: - noop_upsample(); - break; - case ComponentUpsampler.fullsize_upsampler: - fullsize_upsample(ref input_data); - break; - case ComponentUpsampler.h2v1_fancy_upsampler: - h2v1_fancy_upsample(m_cinfo.Comp_info[m_currentComponent].downsampled_width, ref input_data); - break; - case ComponentUpsampler.h2v1_upsampler: - h2v1_upsample(ref input_data); - break; - case ComponentUpsampler.h2v2_fancy_upsampler: - h2v2_fancy_upsample(m_cinfo.Comp_info[m_currentComponent].downsampled_width, ref input_data); - break; - case ComponentUpsampler.h2v2_upsampler: - h2v2_upsample(ref input_data); - break; - case ComponentUpsampler.int_upsampler: - int_upsample(ref input_data); - break; - default: - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - break; - } - } - - /* - * These are the routines invoked to upsample pixel values - * of a single component. One row group is processed per call. - */ - - /// - /// This is a no-op version used for "uninteresting" components. - /// These components will not be referenced by color conversion. - /// - private static void noop_upsample() - { - // do nothing - } - - /// - /// For full-size components, we just make color_buf[ci] point at the - /// input buffer, and thus avoid copying any data. Note that this is - /// safe only because sep_upsample doesn't declare the input row group - /// "consumed" until we are done color converting and emitting it. - /// - private void fullsize_upsample(ref ComponentBuffer input_data) - { - m_color_buf[m_currentComponent] = input_data; - m_perComponentOffsets[m_currentComponent] = m_upsampleRowOffset; - } - - /// - /// Fancy processing for the common case of 2:1 horizontal and 1:1 vertical. - /// - /// The upsampling algorithm is linear interpolation between pixel centers, - /// also known as a "triangle filter". This is a good compromise between - /// speed and visual quality. The centers of the output pixels are 1/4 and 3/4 - /// of the way between input pixel centers. - /// - /// A note about the "bias" calculations: when rounding fractional values to - /// integer, we do not want to always round 0.5 up to the next integer. - /// If we did that, we'd introduce a noticeable bias towards larger values. - /// Instead, this code is arranged so that 0.5 will be rounded up or down at - /// alternate pixel locations (a simple ordered dither pattern). - /// - private void h2v1_fancy_upsample(int downsampled_width, ref ComponentBuffer input_data) - { - ComponentBuffer output_data = m_color_buf[m_currentComponent]; - - for (int inrow = 0; inrow < m_cinfo.m_max_v_samp_factor; inrow++) - { - int row = m_upsampleRowOffset + inrow; - int inIndex = 0; - - int outIndex = 0; - - /* Special case for first column */ - int invalue = input_data[row][inIndex]; - inIndex++; - - output_data[inrow][outIndex] = (byte)invalue; - outIndex++; - output_data[inrow][outIndex] = (byte)((invalue * 3 + (int)input_data[row][inIndex] + 2) >> 2); - outIndex++; - - for (int colctr = downsampled_width - 2; colctr > 0; colctr--) - { - /* General case: 3/4 * nearer pixel + 1/4 * further pixel */ - invalue = (int)input_data[row][inIndex] * 3; - inIndex++; - - output_data[inrow][outIndex] = (byte)((invalue + (int)input_data[row][inIndex - 2] + 1) >> 2); - outIndex++; - - output_data[inrow][outIndex] = (byte)((invalue + (int)input_data[row][inIndex] + 2) >> 2); - outIndex++; - } - - /* Special case for last column */ - invalue = input_data[row][inIndex]; - output_data[inrow][outIndex] = (byte)((invalue * 3 + (int)input_data[row][inIndex - 1] + 1) >> 2); - outIndex++; - output_data[inrow][outIndex] = (byte)invalue; - outIndex++; - } - } - - /// - /// Fast processing for the common case of 2:1 horizontal and 1:1 vertical. - /// It's still a box filter. - /// - private void h2v1_upsample(ref ComponentBuffer input_data) - { - ComponentBuffer output_data = m_color_buf[m_currentComponent]; - - for (int inrow = 0; inrow < m_cinfo.m_max_v_samp_factor; inrow++) - { - int row = m_upsampleRowOffset + inrow; - int outIndex = 0; - - for (int col = 0; col < m_cinfo.m_output_width; col++) - { - byte invalue = input_data[row][col]; /* don't need GETJSAMPLE() here */ - output_data[inrow][outIndex] = invalue; - outIndex++; - output_data[inrow][outIndex] = invalue; - outIndex++; - } - } - } - - /// - /// Fancy processing for the common case of 2:1 horizontal and 2:1 vertical. - /// Again a triangle filter; see comments for h2v1 case, above. - /// - /// It is OK for us to reference the adjacent input rows because we demanded - /// context from the main buffer controller (see initialization code). - /// - private void h2v2_fancy_upsample(int downsampled_width, ref ComponentBuffer input_data) - { - ComponentBuffer output_data = m_color_buf[m_currentComponent]; - - int inrow = m_upsampleRowOffset; - int outrow = 0; - while (outrow < m_cinfo.m_max_v_samp_factor) - { - for (int v = 0; v < 2; v++) - { - // nearest input row index - int inIndex0 = 0; - - //next nearest input row index - int inIndex1 = 0; - int inRow1 = -1; - if (v == 0) - { - /* next nearest is row above */ - inRow1 = inrow - 1; - } - else - { - /* next nearest is row below */ - inRow1 = inrow + 1; - } - - int row = outrow; - int outIndex = 0; - outrow++; - - /* Special case for first column */ - int thiscolsum = (int)input_data[inrow][inIndex0] * 3 + (int)input_data[inRow1][inIndex1]; - inIndex0++; - inIndex1++; - - int nextcolsum = (int)input_data[inrow][inIndex0] * 3 + (int)input_data[inRow1][inIndex1]; - inIndex0++; - inIndex1++; - - output_data[row][outIndex] = (byte)((thiscolsum * 4 + 8) >> 4); - outIndex++; - - output_data[row][outIndex] = (byte)((thiscolsum * 3 + nextcolsum + 7) >> 4); - outIndex++; - - int lastcolsum = thiscolsum; - thiscolsum = nextcolsum; - - for (int colctr = downsampled_width - 2; colctr > 0; colctr--) - { - /* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */ - /* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */ - nextcolsum = (int)input_data[inrow][inIndex0] * 3 + (int)input_data[inRow1][inIndex1]; - inIndex0++; - inIndex1++; - - output_data[row][outIndex] = (byte)((thiscolsum * 3 + lastcolsum + 8) >> 4); - outIndex++; - - output_data[row][outIndex] = (byte)((thiscolsum * 3 + nextcolsum + 7) >> 4); - outIndex++; - - lastcolsum = thiscolsum; - thiscolsum = nextcolsum; - } - - /* Special case for last column */ - output_data[row][outIndex] = (byte)((thiscolsum * 3 + lastcolsum + 8) >> 4); - outIndex++; - output_data[row][outIndex] = (byte)((thiscolsum * 4 + 7) >> 4); - outIndex++; - } - - inrow++; - } - } - - /// - /// Fast processing for the common case of 2:1 horizontal and 2:1 vertical. - /// It's still a box filter. - /// - private void h2v2_upsample(ref ComponentBuffer input_data) - { - ComponentBuffer output_data = m_color_buf[m_currentComponent]; - - int inrow = 0; - int outrow = 0; - while (outrow < m_cinfo.m_max_v_samp_factor) - { - int row = m_upsampleRowOffset + inrow; - int outIndex = 0; - - for (int col = 0; col < m_cinfo.m_output_width; col++) - { - byte invalue = input_data[row][col]; /* don't need GETJSAMPLE() here */ - output_data[outrow][outIndex] = invalue; - outIndex++; - output_data[outrow][outIndex] = invalue; - outIndex++; - } - - JpegUtils.jcopy_sample_rows(output_data, outrow, output_data, outrow + 1, 1, m_cinfo.m_output_width); - inrow++; - outrow += 2; - } - } - - /// - /// This version handles any integral sampling ratios. - /// This is not used for typical JPEG files, so it need not be fast. - /// Nor, for that matter, is it particularly accurate: the algorithm is - /// simple replication of the input pixel onto the corresponding output - /// pixels. The hi-falutin sampling literature refers to this as a - /// "box filter". A box filter tends to introduce visible artifacts, - /// so if you are actually going to use 3:1 or 4:1 sampling ratios - /// you would be well advised to improve this code. - /// - private void int_upsample(ref ComponentBuffer input_data) - { - ComponentBuffer output_data = m_color_buf[m_currentComponent]; - int h_expand = m_h_expand[m_currentComponent]; - int v_expand = m_v_expand[m_currentComponent]; - - int inrow = 0; - int outrow = 0; - while (outrow < m_cinfo.m_max_v_samp_factor) - { - /* Generate one output row with proper horizontal expansion */ - int row = m_upsampleRowOffset + inrow; - for (int col = 0; col < m_cinfo.m_output_width; col++) - { - byte invalue = input_data[row][col]; /* don't need GETJSAMPLE() here */ - int outIndex = 0; - for (int h = h_expand; h > 0; h--) - { - output_data[outrow][outIndex] = invalue; - outIndex++; - } - } - - /* Generate any additional output rows by duplicating the first one */ - if (v_expand > 1) - { - JpegUtils.jcopy_sample_rows(output_data, outrow, output_data, - outrow + 1, v_expand - 1, m_cinfo.m_output_width); - } - - inrow++; - outrow += v_expand; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/phuff_entropy_decoder.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/phuff_entropy_decoder.cs deleted file mode 100644 index 214fdf56..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/phuff_entropy_decoder.cs +++ /dev/null @@ -1,720 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains Huffman entropy decoding routines for progressive JPEG. - * - * Much of the complexity here has to do with supporting input suspension. - * If the data source module demands suspension, we want to be able to back - * up to the start of the current MCU. To do this, we copy state variables - * into local working storage, and update them back to the permanent - * storage only upon successful completion of an MCU. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Expanded entropy decoder object for progressive Huffman decoding. - /// - /// The savable_state subrecord contains fields that change within an MCU, - /// but must not be updated permanently until we complete the MCU. - /// - class phuff_entropy_decoder : jpeg_entropy_decoder - { - private class savable_state - { - //savable_state operator=(savable_state src); - public int EOBRUN; /* remaining EOBs in EOBRUN */ - public int[] last_dc_val = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ - - public void Assign(savable_state ss) - { - EOBRUN = ss.EOBRUN; - Buffer.BlockCopy(ss.last_dc_val, 0, last_dc_val, 0, last_dc_val.Length * sizeof(int)); - } - } - - private enum MCUDecoder - { - mcu_DC_first_decoder, - mcu_AC_first_decoder, - mcu_DC_refine_decoder, - mcu_AC_refine_decoder - } - - private MCUDecoder m_decoder; - - /* These fields are loaded into local variables at start of each MCU. - * In case of suspension, we exit WITHOUT updating them. - */ - private bitread_perm_state m_bitstate; /* Bit buffer at start of MCU */ - private savable_state m_saved = new savable_state(); /* Other state at start of MCU */ - - /* These fields are NOT loaded into local working state. */ - private int m_restarts_to_go; /* MCUs left in this restart interval */ - - /* Pointers to derived tables (these workspaces have image lifespan) */ - private d_derived_tbl[] m_derived_tbls = new d_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; - - private d_derived_tbl m_ac_derived_tbl; /* active table during an AC scan */ - - public phuff_entropy_decoder(jpeg_decompress_struct cinfo) - { - m_cinfo = cinfo; - - /* Mark derived tables unallocated */ - for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) - m_derived_tbls[i] = null; - - /* Create progression status table */ - cinfo.m_coef_bits = new int[cinfo.m_num_components][]; - for (int i = 0; i < cinfo.m_num_components; i++) - cinfo.m_coef_bits[i] = new int[JpegConstants.DCTSIZE2]; - - for (int ci = 0; ci < cinfo.m_num_components; ci++) - { - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - cinfo.m_coef_bits[ci][i] = -1; - } - } - - /// - /// Initialize for a Huffman-compressed scan. - /// - public override void start_pass() - { - /* Validate scan parameters */ - bool bad = false; - bool is_DC_band = (m_cinfo.m_Ss == 0); - if (is_DC_band) - { - if (m_cinfo.m_Se != 0) - bad = true; - } - else - { - /* need not check Ss/Se < 0 since they came from unsigned bytes */ - if (m_cinfo.m_Ss > m_cinfo.m_Se || m_cinfo.m_Se >= JpegConstants.DCTSIZE2) - bad = true; - - /* AC scans may have only one component */ - if (m_cinfo.m_comps_in_scan != 1) - bad = true; - } - - if (m_cinfo.m_Ah != 0) - { - /* Successive approximation refinement scan: must have Al = Ah-1. */ - if (m_cinfo.m_Al != m_cinfo.m_Ah - 1) - bad = true; - } - - if (m_cinfo.m_Al > 13) - { - /* need not check for < 0 */ - bad = true; - } - - /* Arguably the maximum Al value should be less than 13 for 8-bit precision, - * but the spec doesn't say so, and we try to be liberal about what we - * accept. Note: large Al values could result in out-of-range DC - * coefficients during early scans, leading to bizarre displays due to - * overflows in the IDCT math. But we won't crash. - */ - if (bad) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_PROGRESSION, m_cinfo.m_Ss, m_cinfo.m_Se, m_cinfo.m_Ah, m_cinfo.m_Al); - - /* Update progression status, and verify that scan order is legal. - * Note that inter-scan inconsistencies are treated as warnings - * not fatal errors ... not clear if this is right way to behave. - */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - int cindex = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]].Component_index; - if (!is_DC_band && m_cinfo.m_coef_bits[cindex][0] < 0) /* AC without prior DC scan */ - m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_BOGUS_PROGRESSION, cindex, 0); - - for (int coefi = m_cinfo.m_Ss; coefi <= m_cinfo.m_Se; coefi++) - { - int expected = m_cinfo.m_coef_bits[cindex][coefi]; - if (expected < 0) - expected = 0; - - if (m_cinfo.m_Ah != expected) - m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_BOGUS_PROGRESSION, cindex, coefi); - - m_cinfo.m_coef_bits[cindex][coefi] = m_cinfo.m_Al; - } - } - - /* Select MCU decoding routine */ - if (m_cinfo.m_Ah == 0) - { - if (is_DC_band) - m_decoder = MCUDecoder.mcu_DC_first_decoder; - else - m_decoder = MCUDecoder.mcu_AC_first_decoder; - } - else - { - if (is_DC_band) - m_decoder = MCUDecoder.mcu_DC_refine_decoder; - else - m_decoder = MCUDecoder.mcu_AC_refine_decoder; - } - - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]]; - /* Make sure requested tables are present, and compute derived tables. - * We may build same derived table more than once, but it's not expensive. - */ - if (is_DC_band) - { - if (m_cinfo.m_Ah == 0) - { - /* DC refinement needs no table */ - jpeg_make_d_derived_tbl(true, componentInfo.Dc_tbl_no, ref m_derived_tbls[componentInfo.Dc_tbl_no]); - } - } - else - { - jpeg_make_d_derived_tbl(false, componentInfo.Ac_tbl_no, ref m_derived_tbls[componentInfo.Ac_tbl_no]); - - /* remember the single active table */ - m_ac_derived_tbl = m_derived_tbls[componentInfo.Ac_tbl_no]; - } - - /* Initialize DC predictions to 0 */ - m_saved.last_dc_val[ci] = 0; - } - - /* Initialize bitread state variables */ - m_bitstate.bits_left = 0; - m_bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ - m_insufficient_data = false; - - /* Initialize private state variables */ - m_saved.EOBRUN = 0; - - /* Initialize restart counter */ - m_restarts_to_go = m_cinfo.m_restart_interval; - } - - public override bool decode_mcu(JBLOCK[] MCU_data) - { - switch (m_decoder) - { - case MCUDecoder.mcu_DC_first_decoder: - return decode_mcu_DC_first(MCU_data); - case MCUDecoder.mcu_AC_first_decoder: - return decode_mcu_AC_first(MCU_data); - case MCUDecoder.mcu_DC_refine_decoder: - return decode_mcu_DC_refine(MCU_data); - case MCUDecoder.mcu_AC_refine_decoder: - return decode_mcu_AC_refine(MCU_data); - } - - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - return false; - } - - /* - * Huffman MCU decoding. - * Each of these routines decodes and returns one MCU's worth of - * Huffman-compressed coefficients. - * The coefficients are reordered from zigzag order into natural array order, - * but are not dequantized. - * - * The i'th block of the MCU is stored into the block pointed to by - * MCU_data[i]. WE ASSUME THIS AREA IS INITIALLY ZEROED BY THE CALLER. - * - * We return false if data source requested suspension. In that case no - * changes have been made to permanent state. (Exception: some output - * coefficients may already have been assigned. This is harmless for - * spectral selection, since we'll just re-assign them on the next call. - * Successive approximation AC refinement has to be more careful, however.) - */ - - /// - /// MCU decoding for DC initial scan (either spectral selection, - /// or first pass of successive approximation). - /// - private bool decode_mcu_DC_first(JBLOCK[] MCU_data) - { - /* Process restart marker if needed; may have to suspend */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - if (!process_restart()) - return false; - } - } - - /* If we've run out of data, just leave the MCU set to zeroes. - * This way, we return uniform gray for the remainder of the segment. - */ - if (!m_insufficient_data) - { - /* Load up working state */ - int get_buffer; - int bits_left; - bitread_working_state br_state = new bitread_working_state(); - BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); - savable_state state = new savable_state(); - state.Assign(m_saved); - - /* Outer loop handles each block in the MCU */ - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - int ci = m_cinfo.m_MCU_membership[blkn]; - - /* Decode a single block's worth of coefficients */ - - /* Section F.2.2.1: decode the DC coefficient difference */ - int s; - if (!HUFF_DECODE(out s, ref br_state, m_derived_tbls[m_cinfo.Comp_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no], ref get_buffer, ref bits_left)) - return false; - - if (s != 0) - { - if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) - return false; - - int r = GET_BITS(s, get_buffer, ref bits_left); - s = HUFF_EXTEND(r, s); - } - - /* Convert DC difference to actual value, update last_dc_val */ - s += state.last_dc_val[ci]; - state.last_dc_val[ci] = s; - - /* Scale and output the coefficient (assumes jpeg_natural_order[0]=0) */ - MCU_data[blkn][0] = (short)(s << m_cinfo.m_Al); - } - - /* Completed MCU, so update state */ - BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); - m_saved.Assign(state); - } - - /* Account for restart interval (no-op if not using restarts) */ - m_restarts_to_go--; - - return true; - } - - /// - /// MCU decoding for AC initial scan (either spectral selection, - /// or first pass of successive approximation). - /// - private bool decode_mcu_AC_first(JBLOCK[] MCU_data) - { - /* Process restart marker if needed; may have to suspend */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - if (!process_restart()) - return false; - } - } - - /* If we've run out of data, just leave the MCU set to zeroes. - * This way, we return uniform gray for the remainder of the segment. - */ - if (!m_insufficient_data) - { - /* Load up working state. - * We can avoid loading/saving bitread state if in an EOB run. - */ - int EOBRUN = m_saved.EOBRUN; /* only part of saved state we need */ - - /* There is always only one block per MCU */ - - if (EOBRUN > 0) - { - /* if it's a band of zeroes... */ - /* ...process it now (we do nothing) */ - EOBRUN--; - } - else - { - int get_buffer; - int bits_left; - bitread_working_state br_state = new bitread_working_state(); - BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); - - for (int k = m_cinfo.m_Ss; k <= m_cinfo.m_Se; k++) - { - int s; - if (!HUFF_DECODE(out s, ref br_state, m_ac_derived_tbl, ref get_buffer, ref bits_left)) - return false; - - int r = s >> 4; - s &= 15; - if (s != 0) - { - k += r; - - if (!CHECK_BIT_BUFFER(ref br_state, s, ref get_buffer, ref bits_left)) - return false; - - r = GET_BITS(s, get_buffer, ref bits_left); - s = HUFF_EXTEND(r, s); - - /* Scale and output coefficient in natural (dezigzagged) order */ - MCU_data[0][JpegUtils.jpeg_natural_order[k]] = (short) (s << m_cinfo.m_Al); - } - else - { - if (r == 15) - { - /* ZRL */ - k += 15; /* skip 15 zeroes in band */ - } - else - { - /* EOBr, run length is 2^r + appended bits */ - EOBRUN = 1 << r; - if (r != 0) - { - /* EOBr, r > 0 */ - if (!CHECK_BIT_BUFFER(ref br_state, r, ref get_buffer, ref bits_left)) - return false; - - r = GET_BITS(r, get_buffer, ref bits_left); - EOBRUN += r; - } - - EOBRUN--; /* this band is processed at this moment */ - break; /* force end-of-band */ - } - } - } - - BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); - } - - /* Completed MCU, so update state */ - m_saved.EOBRUN = EOBRUN; /* only part of saved state we need */ - } - - /* Account for restart interval (no-op if not using restarts) */ - m_restarts_to_go--; - - return true; - } - - /// - /// MCU decoding for DC successive approximation refinement scan. - /// Note: we assume such scans can be multi-component, although the spec - /// is not very clear on the point. - /// - private bool decode_mcu_DC_refine(JBLOCK[] MCU_data) - { - /* Process restart marker if needed; may have to suspend */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - if (!process_restart()) - return false; - } - } - - /* Not worth the cycles to check insufficient_data here, - * since we will not change the data anyway if we read zeroes. - */ - - /* Load up working state */ - int get_buffer; - int bits_left; - bitread_working_state br_state = new bitread_working_state(); - BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); - - /* Outer loop handles each block in the MCU */ - - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - /* Encoded data is simply the next bit of the two's-complement DC value */ - if (!CHECK_BIT_BUFFER(ref br_state, 1, ref get_buffer, ref bits_left)) - return false; - - if (GET_BITS(1, get_buffer, ref bits_left) != 0) - { - /* 1 in the bit position being coded */ - MCU_data[blkn][0] |= (short)(1 << m_cinfo.m_Al); - } - - /* Note: since we use |=, repeating the assignment later is safe */ - } - - /* Completed MCU, so update state */ - BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); - - /* Account for restart interval (no-op if not using restarts) */ - m_restarts_to_go--; - - return true; - } - - // There is always only one block per MCU - private bool decode_mcu_AC_refine(JBLOCK[] MCU_data) - { - int p1 = 1 << m_cinfo.m_Al; /* 1 in the bit position being coded */ - int m1 = -1 << m_cinfo.m_Al; /* -1 in the bit position being coded */ - - /* Process restart marker if needed; may have to suspend */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - if (!process_restart()) - return false; - } - } - - /* If we've run out of data, don't modify the MCU. - */ - if (!m_insufficient_data) - { - /* Load up working state */ - int get_buffer; - int bits_left; - bitread_working_state br_state = new bitread_working_state(); - BITREAD_LOAD_STATE(m_bitstate, out get_buffer, out bits_left, ref br_state); - int EOBRUN = m_saved.EOBRUN; /* only part of saved state we need */ - - /* If we are forced to suspend, we must undo the assignments to any newly - * nonzero coefficients in the block, because otherwise we'd get confused - * next time about which coefficients were already nonzero. - * But we need not undo addition of bits to already-nonzero coefficients; - * instead, we can test the current bit to see if we already did it. - */ - int num_newnz = 0; - int[] newnz_pos = new int[JpegConstants.DCTSIZE2]; - - /* initialize coefficient loop counter to start of band */ - int k = m_cinfo.m_Ss; - - if (EOBRUN == 0) - { - for (; k <= m_cinfo.m_Se; k++) - { - int s; - if (!HUFF_DECODE(out s, ref br_state, m_ac_derived_tbl, ref get_buffer, ref bits_left)) - { - undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); - return false; - } - - int r = s >> 4; - s &= 15; - if (s != 0) - { - if (s != 1) - { - /* size of new coef should always be 1 */ - m_cinfo.WARNMS(J_MESSAGE_CODE.JWRN_HUFF_BAD_CODE); - } - - if (!CHECK_BIT_BUFFER(ref br_state, 1, ref get_buffer, ref bits_left)) - { - undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); - return false; - } - - if (GET_BITS(1, get_buffer, ref bits_left) != 0) - { - /* newly nonzero coef is positive */ - s = p1; - } - else - { - /* newly nonzero coef is negative */ - s = m1; - } - } - else - { - if (r != 15) - { - EOBRUN = 1 << r; /* EOBr, run length is 2^r + appended bits */ - if (r != 0) - { - if (!CHECK_BIT_BUFFER(ref br_state, r, ref get_buffer, ref bits_left)) - { - undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); - return false; - } - - r = GET_BITS(r, get_buffer, ref bits_left); - EOBRUN += r; - } - break; /* rest of block is handled by EOB logic */ - } - /* note s = 0 for processing ZRL */ - } - /* Advance over already-nonzero coefs and r still-zero coefs, - * appending correction bits to the nonzeroes. A correction bit is 1 - * if the absolute value of the coefficient must be increased. - */ - do - { - int blockIndex = JpegUtils.jpeg_natural_order[k]; - short thiscoef = MCU_data[0][blockIndex]; - if (thiscoef != 0) - { - if (!CHECK_BIT_BUFFER(ref br_state, 1, ref get_buffer, ref bits_left)) - { - undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); - return false; - } - - if (GET_BITS(1, get_buffer, ref bits_left) != 0) - { - if ((thiscoef & p1) == 0) - { - /* do nothing if already set it */ - if (thiscoef >= 0) - MCU_data[0][blockIndex] += (short)p1; - else - MCU_data[0][blockIndex] += (short)m1; - } - } - } - else - { - if (--r < 0) - break; /* reached target zero coefficient */ - } - - k++; - } - while (k <= m_cinfo.m_Se); - - if (s != 0) - { - int pos = JpegUtils.jpeg_natural_order[k]; - - /* Output newly nonzero coefficient */ - MCU_data[0][pos] = (short) s; - - /* Remember its position in case we have to suspend */ - newnz_pos[num_newnz++] = pos; - } - } - } - - if (EOBRUN > 0) - { - /* Scan any remaining coefficient positions after the end-of-band - * (the last newly nonzero coefficient, if any). Append a correction - * bit to each already-nonzero coefficient. A correction bit is 1 - * if the absolute value of the coefficient must be increased. - */ - for (; k <= m_cinfo.m_Se; k++) - { - int blockIndex = JpegUtils.jpeg_natural_order[k]; - short thiscoef = MCU_data[0][blockIndex]; - if (thiscoef != 0) - { - if (!CHECK_BIT_BUFFER(ref br_state, 1, ref get_buffer, ref bits_left)) - { - //undo_decode_mcu_AC_refine(MCU_data[0], newnz_pos, num_newnz); - undo_decode_mcu_AC_refine(MCU_data, newnz_pos, num_newnz); - return false; - } - - if (GET_BITS(1, get_buffer, ref bits_left) != 0) - { - if ((thiscoef & p1) == 0) - { - /* do nothing if already changed it */ - if (thiscoef >= 0) - MCU_data[0][blockIndex] += (short)p1; - else - MCU_data[0][blockIndex] += (short)m1; - } - } - } - } - - /* Count one block completed in EOB run */ - EOBRUN--; - } - - /* Completed MCU, so update state */ - BITREAD_SAVE_STATE(ref m_bitstate, get_buffer, bits_left); - m_saved.EOBRUN = EOBRUN; /* only part of saved state we need */ - } - - /* Account for restart interval (no-op if not using restarts) */ - m_restarts_to_go--; - - return true; - } - - /// - /// Check for a restart marker and resynchronize decoder. - /// Returns false if must suspend. - /// - private bool process_restart() - { - /* Throw away any unused bits remaining in bit buffer; */ - /* include any full bytes in next_marker's count of discarded bytes */ - m_cinfo.m_marker.SkipBytes(m_bitstate.bits_left / 8); - m_bitstate.bits_left = 0; - - /* Advance past the RSTn marker */ - if (!m_cinfo.m_marker.read_restart_marker()) - return false; - - /* Re-initialize DC predictions to 0 */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - m_saved.last_dc_val[ci] = 0; - - /* Re-init EOB run count, too */ - m_saved.EOBRUN = 0; - - /* Reset restart counter */ - m_restarts_to_go = m_cinfo.m_restart_interval; - - /* Reset out-of-data flag, unless read_restart_marker left us smack up - * against a marker. In that case we will end up treating the next data - * segment as empty, and we can avoid producing bogus output pixels by - * leaving the flag set. - */ - if (m_cinfo.m_unread_marker == 0) - m_insufficient_data = false; - - return true; - } - - /// - /// MCU decoding for AC successive approximation refinement scan. - /// - private static void undo_decode_mcu_AC_refine(JBLOCK[] block, int[] newnz_pos, int num_newnz) - { - /* Re-zero any output coefficients that we made newly nonzero */ - while (num_newnz > 0) - block[0][newnz_pos[--num_newnz]] = 0; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/phuff_entropy_encoder.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/phuff_entropy_encoder.cs deleted file mode 100644 index f4c181e0..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/Internal/phuff_entropy_encoder.cs +++ /dev/null @@ -1,773 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains Huffman entropy encoding routines for progressive JPEG. - * - * We do not support output suspension in this module, since the library - * currently does not allow multiple-scan files to be written with output - * suspension. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic.Internal -{ - /// - /// Expanded entropy encoder object for progressive Huffman encoding. - /// - class phuff_entropy_encoder : jpeg_entropy_encoder - { - private enum MCUEncoder - { - mcu_DC_first_encoder, - mcu_AC_first_encoder, - mcu_DC_refine_encoder, - mcu_AC_refine_encoder - } - - /* MAX_CORR_BITS is the number of bits the AC refinement correction-bit - * buffer can hold. Larger sizes may slightly improve compression, but - * 1000 is already well into the realm of overkill. - * The minimum safe size is 64 bits. - */ - private const int MAX_CORR_BITS = 1000; /* Max # of correction bits I can buffer */ - - private MCUEncoder m_MCUEncoder; - - /* Mode flag: true for optimization, false for actual data output */ - private bool m_gather_statistics; - - // Bit-level coding status. - private int m_put_buffer; /* current bit-accumulation buffer */ - private int m_put_bits; /* # of bits now in it */ - - /* Coding status for DC components */ - private int[] m_last_dc_val = new int[JpegConstants.MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ - - /* Coding status for AC components */ - private int m_ac_tbl_no; /* the table number of the single component */ - private int m_EOBRUN; /* run length of EOBs */ - private int m_BE; /* # of buffered correction bits before MCU */ - private char[] m_bit_buffer; /* buffer for correction bits (1 per char) */ - /* packing correction bits tightly would save some space but cost time... */ - - private int m_restarts_to_go; /* MCUs left in this restart interval */ - private int m_next_restart_num; /* next restart number to write (0-7) */ - - /* Pointers to derived tables (these workspaces have image lifespan). - * Since any one scan codes only DC or only AC, we only need one set - * of tables, not one for DC and one for AC. - */ - private c_derived_tbl[] m_derived_tbls = new c_derived_tbl[JpegConstants.NUM_HUFF_TBLS]; - - /* Statistics tables for optimization; again, one set is enough */ - private long[][] m_count_ptrs = new long[JpegConstants.NUM_HUFF_TBLS][]; - - public phuff_entropy_encoder(jpeg_compress_struct cinfo) - { - m_cinfo = cinfo; - - /* Mark tables unallocated */ - for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) - { - m_derived_tbls[i] = null; - m_count_ptrs[i] = null; - } - } - - // Initialize for a Huffman-compressed scan using progressive JPEG. - public override void start_pass(bool gather_statistics) - { - m_gather_statistics = gather_statistics; - - /* We assume the scan parameters are already validated. */ - - /* Select execution routines */ - bool is_DC_band = (m_cinfo.m_Ss == 0); - if (m_cinfo.m_Ah == 0) - { - if (is_DC_band) - m_MCUEncoder = MCUEncoder.mcu_DC_first_encoder; - else - m_MCUEncoder = MCUEncoder.mcu_AC_first_encoder; - } - else - { - if (is_DC_band) - { - m_MCUEncoder = MCUEncoder.mcu_DC_refine_encoder; - } - else - { - m_MCUEncoder = MCUEncoder.mcu_AC_refine_encoder; - - /* AC refinement needs a correction bit buffer */ - if (m_bit_buffer == null) - m_bit_buffer = new char[MAX_CORR_BITS]; - } - } - - /* Only DC coefficients may be interleaved, so m_cinfo.comps_in_scan = 1 - * for AC coefficients. - */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - - /* Initialize DC predictions to 0 */ - m_last_dc_val[ci] = 0; - - /* Get table index */ - int tbl; - if (is_DC_band) - { - if (m_cinfo.m_Ah != 0) /* DC refinement needs no table */ - continue; - - tbl = componentInfo.Dc_tbl_no; - } - else - { - m_ac_tbl_no = componentInfo.Ac_tbl_no; - tbl = componentInfo.Ac_tbl_no; - } - - if (m_gather_statistics) - { - /* Check for invalid table index */ - /* (make_c_derived_tbl does this in the other path) */ - if (tbl < 0 || tbl >= JpegConstants.NUM_HUFF_TBLS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NO_HUFF_TABLE, tbl); - - /* Allocate and zero the statistics tables */ - /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ - if (m_count_ptrs[tbl] == null) - m_count_ptrs[tbl] = new long[257]; - - Array.Clear(m_count_ptrs[tbl], 0, 257); - } - else - { - /* Compute derived values for Huffman table */ - /* We may do this more than once for a table, but it's not expensive */ - jpeg_make_c_derived_tbl(is_DC_band, tbl, ref m_derived_tbls[tbl]); - } - } - - /* Initialize AC stuff */ - m_EOBRUN = 0; - m_BE = 0; - - /* Initialize bit buffer to empty */ - m_put_buffer = 0; - m_put_bits = 0; - - /* Initialize restart stuff */ - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num = 0; - } - - public override bool encode_mcu(JBLOCK[][] MCU_data) - { - switch (m_MCUEncoder) - { - case MCUEncoder.mcu_DC_first_encoder: - return encode_mcu_DC_first(MCU_data); - case MCUEncoder.mcu_AC_first_encoder: - return encode_mcu_AC_first(MCU_data); - case MCUEncoder.mcu_DC_refine_encoder: - return encode_mcu_DC_refine(MCU_data); - case MCUEncoder.mcu_AC_refine_encoder: - return encode_mcu_AC_refine(MCU_data); - } - - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_NOTIMPL); - return false; - } - - public override void finish_pass() - { - if (m_gather_statistics) - finish_pass_gather_phuff(); - else - finish_pass_phuff(); - } - - /// - /// MCU encoding for DC initial scan (either spectral selection, - /// or first pass of successive approximation). - /// - private bool encode_mcu_DC_first(JBLOCK[][] MCU_data) - { - /* Emit restart marker if needed */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - emit_restart(m_next_restart_num); - } - - /* Encode the MCU data blocks */ - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - /* Compute the DC value after the required point transform by Al. - * This is simply an arithmetic right shift. - */ - int temp2 = IRIGHT_SHIFT(MCU_data[blkn][0][0], m_cinfo.m_Al); - - /* DC differences are figured on the point-transformed values. */ - int ci = m_cinfo.m_MCU_membership[blkn]; - int temp = temp2 - m_last_dc_val[ci]; - m_last_dc_val[ci] = temp2; - - /* Encode the DC coefficient difference per section G.1.2.1 */ - temp2 = temp; - if (temp < 0) - { - /* temp is abs value of input */ - temp = -temp; - - /* For a negative input, want temp2 = bitwise complement of abs(input) */ - /* This code assumes we are on a two's complement machine */ - temp2--; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - int nbits = 0; - while (temp != 0) - { - nbits++; - temp >>= 1; - } - - /* Check for out-of-range coefficient values. - * Since we're encoding a difference, the range limit is twice as much. - */ - if (nbits > MAX_HUFFMAN_COEF_BITS + 1) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); - - /* Count/emit the Huffman-coded symbol for the number of bits */ - emit_symbol(m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]].Dc_tbl_no, nbits); - - /* Emit that number of bits of the value, if positive, */ - /* or the complement of its magnitude, if negative. */ - if (nbits != 0) - { - /* emit_bits rejects calls with size 0 */ - emit_bits(temp2, nbits); - } - } - - /* Update restart-interval state too */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num++; - m_next_restart_num &= 7; - } - - m_restarts_to_go--; - } - - return true; - } - - /// - /// MCU encoding for AC initial scan (either spectral selection, - /// or first pass of successive approximation). - /// - private bool encode_mcu_AC_first(JBLOCK[][] MCU_data) - { - /* Emit restart marker if needed */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - emit_restart(m_next_restart_num); - } - - /* Encode the AC coefficients per section G.1.2.2, fig. G.3 */ - /* r = run length of zeros */ - int r = 0; - for (int k = m_cinfo.m_Ss; k <= m_cinfo.m_Se; k++) - { - int temp = MCU_data[0][0][JpegUtils.jpeg_natural_order[k]]; - if (temp == 0) - { - r++; - continue; - } - - /* We must apply the point transform by Al. For AC coefficients this - * is an integer division with rounding towards 0. To do this portably - * in C, we shift after obtaining the absolute value; so the code is - * interwoven with finding the abs value (temp) and output bits (temp2). - */ - int temp2; - if (temp < 0) - { - temp = -temp; /* temp is abs value of input */ - temp >>= m_cinfo.m_Al; /* apply the point transform */ - /* For a negative coef, want temp2 = bitwise complement of abs(coef) */ - temp2 = ~temp; - } - else - { - temp >>= m_cinfo.m_Al; /* apply the point transform */ - temp2 = temp; - } - - /* Watch out for case that nonzero coef is zero after point transform */ - if (temp == 0) - { - r++; - continue; - } - - /* Emit any pending EOBRUN */ - if (m_EOBRUN > 0) - emit_eobrun(); - - /* if run length > 15, must emit special run-length-16 codes (0xF0) */ - while (r > 15) - { - emit_symbol(m_ac_tbl_no, 0xF0); - r -= 16; - } - - /* Find the number of bits needed for the magnitude of the coefficient */ - int nbits = 1; /* there must be at least one 1 bit */ - while ((temp >>= 1) != 0) - nbits++; - - /* Check for out-of-range coefficient values */ - if (nbits > MAX_HUFFMAN_COEF_BITS) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_DCT_COEF); - - /* Count/emit Huffman symbol for run length / number of bits */ - emit_symbol(m_ac_tbl_no, (r << 4) + nbits); - - /* Emit that number of bits of the value, if positive, */ - /* or the complement of its magnitude, if negative. */ - emit_bits(temp2, nbits); - - r = 0; /* reset zero run length */ - } - - if (r > 0) - { - /* If there are trailing zeroes, */ - m_EOBRUN++; /* count an EOB */ - if (m_EOBRUN == 0x7FFF) - emit_eobrun(); /* force it out to avoid overflow */ - } - - /* Update restart-interval state too */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num++; - m_next_restart_num &= 7; - } - m_restarts_to_go--; - } - - return true; - } - - /// - /// MCU encoding for DC successive approximation refinement scan. - /// Note: we assume such scans can be multi-component, although the spec - /// is not very clear on the point. - /// - private bool encode_mcu_DC_refine(JBLOCK[][] MCU_data) - { - /* Emit restart marker if needed */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - emit_restart(m_next_restart_num); - } - - /* Encode the MCU data blocks */ - for (int blkn = 0; blkn < m_cinfo.m_blocks_in_MCU; blkn++) - { - /* We simply emit the Al'th bit of the DC coefficient value. */ - int temp = MCU_data[blkn][0][0]; - emit_bits(temp >> m_cinfo.m_Al, 1); - } - - /* Update restart-interval state too */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num++; - m_next_restart_num &= 7; - } - m_restarts_to_go--; - } - - return true; - } - - /// - /// MCU encoding for AC successive approximation refinement scan. - /// - private bool encode_mcu_AC_refine(JBLOCK[][] MCU_data) - { - /* Emit restart marker if needed */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - emit_restart(m_next_restart_num); - } - - /* Encode the MCU data block */ - - /* It is convenient to make a pre-pass to determine the transformed - * coefficients' absolute values and the EOB position. - */ - int EOB = 0; - int[] absvalues = new int[JpegConstants.DCTSIZE2]; - for (int k = m_cinfo.m_Ss; k <= m_cinfo.m_Se; k++) - { - int temp = MCU_data[0][0][JpegUtils.jpeg_natural_order[k]]; - - /* We must apply the point transform by Al. For AC coefficients this - * is an integer division with rounding towards 0. To do this portably - * in C, we shift after obtaining the absolute value. - */ - if (temp < 0) - temp = -temp; /* temp is abs value of input */ - - temp >>= m_cinfo.m_Al; /* apply the point transform */ - absvalues[k] = temp; /* save abs value for main pass */ - - if (temp == 1) - { - /* EOB = index of last newly-nonzero coef */ - EOB = k; - } - } - - /* Encode the AC coefficients per section G.1.2.3, fig. G.7 */ - - int r = 0; /* r = run length of zeros */ - int BR = 0; /* BR = count of buffered bits added now */ - int bitBufferOffset = m_BE; /* Append bits to buffer */ - - for (int k = m_cinfo.m_Ss; k <= m_cinfo.m_Se; k++) - { - int temp = absvalues[k]; - if (temp == 0) - { - r++; - continue; - } - - /* Emit any required ZRLs, but not if they can be folded into EOB */ - while (r > 15 && k <= EOB) - { - /* emit any pending EOBRUN and the BE correction bits */ - emit_eobrun(); - - /* Emit ZRL */ - emit_symbol(m_ac_tbl_no, 0xF0); - r -= 16; - - /* Emit buffered correction bits that must be associated with ZRL */ - emit_buffered_bits(bitBufferOffset, BR); - bitBufferOffset = 0;/* BE bits are gone now */ - BR = 0; - } - - /* If the coef was previously nonzero, it only needs a correction bit. - * NOTE: a straight translation of the spec's figure G.7 would suggest - * that we also need to test r > 15. But if r > 15, we can only get here - * if k > EOB, which implies that this coefficient is not 1. - */ - if (temp > 1) - { - /* The correction bit is the next bit of the absolute value. */ - m_bit_buffer[bitBufferOffset + BR] = (char) (temp & 1); - BR++; - continue; - } - - /* Emit any pending EOBRUN and the BE correction bits */ - emit_eobrun(); - - /* Count/emit Huffman symbol for run length / number of bits */ - emit_symbol(m_ac_tbl_no, (r << 4) + 1); - - /* Emit output bit for newly-nonzero coef */ - temp = (MCU_data[0][0][JpegUtils.jpeg_natural_order[k]] < 0) ? 0 : 1; - emit_bits(temp, 1); - - /* Emit buffered correction bits that must be associated with this code */ - emit_buffered_bits(bitBufferOffset, BR); - bitBufferOffset = 0;/* BE bits are gone now */ - BR = 0; - r = 0; /* reset zero run length */ - } - - if (r > 0 || BR > 0) - { - /* If there are trailing zeroes, */ - m_EOBRUN++; /* count an EOB */ - m_BE += BR; /* concat my correction bits to older ones */ - - /* We force out the EOB if we risk either: - * 1. overflow of the EOB counter; - * 2. overflow of the correction bit buffer during the next MCU. - */ - if (m_EOBRUN == 0x7FFF || m_BE > (MAX_CORR_BITS - JpegConstants.DCTSIZE2 + 1)) - emit_eobrun(); - } - - /* Update restart-interval state too */ - if (m_cinfo.m_restart_interval != 0) - { - if (m_restarts_to_go == 0) - { - m_restarts_to_go = m_cinfo.m_restart_interval; - m_next_restart_num++; - m_next_restart_num &= 7; - } - m_restarts_to_go--; - } - - return true; - } - - /// - /// Finish up at the end of a Huffman-compressed progressive scan. - /// - private void finish_pass_phuff() - { - /* Flush out any buffered data */ - emit_eobrun(); - flush_bits(); - } - - /// - /// Finish up a statistics-gathering pass and create the new Huffman tables. - /// - private void finish_pass_gather_phuff() - { - /* Flush out buffered data (all we care about is counting the EOB symbol) */ - emit_eobrun(); - - /* It's important not to apply jpeg_gen_optimal_table more than once - * per table, because it clobbers the input frequency counts! - */ - bool[] did = new bool [JpegConstants.NUM_HUFF_TBLS]; - - bool is_DC_band = (m_cinfo.m_Ss == 0); - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - { - jpeg_component_info componentInfo = m_cinfo.Component_info[m_cinfo.m_cur_comp_info[ci]]; - int tbl = componentInfo.Ac_tbl_no; - - if (is_DC_band) - { - if (m_cinfo.m_Ah != 0) /* DC refinement needs no table */ - continue; - - tbl = componentInfo.Dc_tbl_no; - } - - if (!did[tbl]) - { - JHUFF_TBL htblptr = null; - if (is_DC_band) - { - if (m_cinfo.m_dc_huff_tbl_ptrs[tbl] == null) - m_cinfo.m_dc_huff_tbl_ptrs[tbl] = new JHUFF_TBL(); - - htblptr = m_cinfo.m_dc_huff_tbl_ptrs[tbl]; - } - else - { - if (m_cinfo.m_ac_huff_tbl_ptrs[tbl] == null) - m_cinfo.m_ac_huff_tbl_ptrs[tbl] = new JHUFF_TBL(); - - htblptr = m_cinfo.m_ac_huff_tbl_ptrs[tbl]; - } - - jpeg_gen_optimal_table(htblptr, m_count_ptrs[tbl]); - did[tbl] = true; - } - } - } - - ////////////////////////////////////////////////////////////////////////// - // Outputting bytes to the file. - // NB: these must be called only when actually outputting, - // that is, entropy.gather_statistics == false. - - // Emit a byte - private void emit_byte(int val) - { - m_cinfo.m_dest.emit_byte(val); - } - - /// - /// Outputting bits to the file - /// - /// Only the right 24 bits of put_buffer are used; the valid bits are - /// left-justified in this part. At most 16 bits can be passed to emit_bits - /// in one call, and we never retain more than 7 bits in put_buffer - /// between calls, so 24 bits are sufficient. - /// - private void emit_bits(int code, int size) - { - // Emit some bits, unless we are in gather mode - /* This routine is heavily used, so it's worth coding tightly. */ - int local_put_buffer = code; - - /* if size is 0, caller used an invalid Huffman table entry */ - if (size == 0) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_HUFF_MISSING_CODE); - - if (m_gather_statistics) - { - /* do nothing if we're only getting stats */ - return; - } - - local_put_buffer &= (1 << size) - 1; /* mask off any extra bits in code */ - - m_put_bits += size; /* new number of bits in buffer */ - - local_put_buffer <<= 24 - m_put_bits; /* align incoming bits */ - - local_put_buffer |= m_put_buffer; /* and merge with old buffer contents */ - - while (m_put_bits >= 8) - { - int c = (local_put_buffer >> 16) & 0xFF; - - emit_byte(c); - if (c == 0xFF) - { - /* need to stuff a zero byte? */ - emit_byte(0); - } - local_put_buffer <<= 8; - m_put_bits -= 8; - } - - m_put_buffer = local_put_buffer; /* update variables */ - } - - private void flush_bits() - { - emit_bits(0x7F, 7); /* fill any partial byte with ones */ - m_put_buffer = 0; /* and reset bit-buffer to empty */ - m_put_bits = 0; - } - - // Emit (or just count) a Huffman symbol. - private void emit_symbol(int tbl_no, int symbol) - { - if (m_gather_statistics) - m_count_ptrs[tbl_no][symbol]++; - else - emit_bits(m_derived_tbls[tbl_no].ehufco[symbol], m_derived_tbls[tbl_no].ehufsi[symbol]); - } - - // Emit bits from a correction bit buffer. - private void emit_buffered_bits(int offset, int nbits) - { - if (m_gather_statistics) - { - /* no real work */ - return; - } - - for (int i = 0; i < nbits; i++) - emit_bits(m_bit_buffer[offset + i], 1); - } - - // Emit any pending EOBRUN symbol. - private void emit_eobrun() - { - if (m_EOBRUN > 0) - { - /* if there is any pending EOBRUN */ - int temp = m_EOBRUN; - int nbits = 0; - while ((temp >>= 1) != 0) - nbits++; - - /* safety check: shouldn't happen given limited correction-bit buffer */ - if (nbits > 14) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_HUFF_MISSING_CODE); - - emit_symbol(m_ac_tbl_no, nbits << 4); - if (nbits != 0) - emit_bits(m_EOBRUN, nbits); - - m_EOBRUN = 0; - - /* Emit any buffered correction bits */ - emit_buffered_bits(0, m_BE); - m_BE = 0; - } - } - - // Emit a restart marker & resynchronize predictions. - private void emit_restart(int restart_num) - { - emit_eobrun(); - - if (!m_gather_statistics) - { - flush_bits(); - emit_byte(0xFF); - emit_byte((int)(JPEG_MARKER.RST0 + restart_num)); - } - - if (m_cinfo.m_Ss == 0) - { - /* Re-initialize DC predictions to 0 */ - for (int ci = 0; ci < m_cinfo.m_comps_in_scan; ci++) - m_last_dc_val[ci] = 0; - } - else - { - /* Re-initialize all AC-related fields to 0 */ - m_EOBRUN = 0; - m_BE = 0; - } - } - - /// - /// IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than int. - /// We assume that int right shift is unsigned if int right shift is, - /// which should be safe. - /// - private static int IRIGHT_SHIFT(int x, int shft) - { - return (x >> shft); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JBLOCK.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JBLOCK.cs deleted file mode 100644 index 13272fcc..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JBLOCK.cs +++ /dev/null @@ -1,47 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// One block of coefficients. - /// -#if EXPOSE_LIBJPEG - public -#endif - class JBLOCK - { - internal short[] data = new short[JpegConstants.DCTSIZE2]; - - /// - /// Gets or sets the element at the specified index. - /// - /// The index of required element. - /// The required element. - public short this[int index] - { - get - { - return data[index]; - } - set - { - data[index] = value; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JHUFF_TBL.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JHUFF_TBL.cs deleted file mode 100644 index 9f1ee099..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JHUFF_TBL.cs +++ /dev/null @@ -1,71 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Huffman coding table. - /// -#if EXPOSE_LIBJPEG - public -#endif - class JHUFF_TBL - { - /* These two fields directly represent the contents of a JPEG DHT marker */ - private readonly byte[] m_bits = new byte[17]; /* bits[k] = # of symbols with codes of */ - - /* length k bits; bits[0] is unused */ - private readonly byte[] m_huffval = new byte[256]; /* The symbols, in order of incr code length */ - - private bool m_sent_table; /* true when table has been output */ - - - internal JHUFF_TBL() - { - } - - internal byte[] Bits - { - get { return m_bits; } - } - - internal byte[] Huffval - { - get { return m_huffval; } - } - - /* This property is used only during compression. It's initialized false when - * the table is created, and set true when it's been output to the file. - * You could suppress output of a table by setting this to true. - * (See jpeg_suppress_tables for an example.) - */ - /// - /// Gets or sets a value indicating whether the table has been output to file. - /// - /// It's initialized false when the table is created, and set - /// true when it's been output to the file. You could suppress output - /// of a table by setting this to true. - /// - /// This property is used only during compression. - /// - public bool Sent_table - { - get { return m_sent_table; } - set { m_sent_table = value; } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JPEG_MARKER.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JPEG_MARKER.cs deleted file mode 100644 index d967753c..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JPEG_MARKER.cs +++ /dev/null @@ -1,242 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// JPEG marker codes. - /// - /// Special markers -#if EXPOSE_LIBJPEG - public -#endif - enum JPEG_MARKER - { - /// - /// - /// - SOF0 = 0xc0, - /// - /// - /// - SOF1 = 0xc1, - /// - /// - /// - SOF2 = 0xc2, - /// - /// - /// - SOF3 = 0xc3, - /// - /// - /// - SOF5 = 0xc5, - /// - /// - /// - SOF6 = 0xc6, - /// - /// - /// - SOF7 = 0xc7, - /// - /// - /// - JPG = 0xc8, - /// - /// - /// - SOF9 = 0xc9, - /// - /// - /// - SOF10 = 0xca, - /// - /// - /// - SOF11 = 0xcb, - /// - /// - /// - SOF13 = 0xcd, - /// - /// - /// - SOF14 = 0xce, - /// - /// - /// - SOF15 = 0xcf, - /// - /// - /// - DHT = 0xc4, - /// - /// - /// - DAC = 0xcc, - /// - /// - /// - RST0 = 0xd0, /* RST0 marker code */ - /// - /// - /// - RST1 = 0xd1, - /// - /// - /// - RST2 = 0xd2, - /// - /// - /// - RST3 = 0xd3, - /// - /// - /// - RST4 = 0xd4, - /// - /// - /// - RST5 = 0xd5, - /// - /// - /// - RST6 = 0xd6, - /// - /// - /// - RST7 = 0xd7, - /// - /// - /// - SOI = 0xd8, - /// - /// - /// - EOI = 0xd9, /* EOI marker code */ - /// - /// - /// - SOS = 0xda, - /// - /// - /// - DQT = 0xdb, - /// - /// - /// - DNL = 0xdc, - /// - /// - /// - DRI = 0xdd, - /// - /// - /// - DHP = 0xde, - /// - /// - /// - EXP = 0xdf, - /// - /// - /// - APP0 = 0xe0, /* APP0 marker code */ - /// - /// - /// - APP1 = 0xe1, - /// - /// - /// - APP2 = 0xe2, - /// - /// - /// - APP3 = 0xe3, - /// - /// - /// - APP4 = 0xe4, - /// - /// - /// - APP5 = 0xe5, - /// - /// - /// - APP6 = 0xe6, - /// - /// - /// - APP7 = 0xe7, - /// - /// - /// - APP8 = 0xe8, - /// - /// - /// - APP9 = 0xe9, - /// - /// - /// - APP10 = 0xea, - /// - /// - /// - APP11 = 0xeb, - /// - /// - /// - APP12 = 0xec, - /// - /// - /// - APP13 = 0xed, - /// - /// - /// - APP14 = 0xee, - /// - /// - /// - APP15 = 0xef, - /// - /// - /// - JPG0 = 0xf0, - /// - /// - /// - JPG13 = 0xfd, - /// - /// - /// - COM = 0xfe, /* COM marker code */ - /// - /// - /// - TEM = 0x01, - /// - /// - /// - ERROR = 0x100 - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JQUANT_TBL.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JQUANT_TBL.cs deleted file mode 100644 index 59537402..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JQUANT_TBL.cs +++ /dev/null @@ -1,59 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// DCT coefficient quantization tables. - /// -#if EXPOSE_LIBJPEG - public -#endif - class JQUANT_TBL - { - /* This field is used only during compression. It's initialized false when - * the table is created, and set true when it's been output to the file. - * You could suppress output of a table by setting this to true. - * (See jpeg_suppress_tables for an example.) - */ - private bool m_sent_table; /* true when table has been output */ - - /* This array gives the coefficient quantizers in natural array order - * (not the zigzag order in which they are stored in a JPEG DQT marker). - * CAUTION: IJG versions prior to v6a kept this array in zigzag order. - */ - internal readonly short[] quantval = new short[JpegConstants.DCTSIZE2]; /* quantization step for each coefficient */ - - internal JQUANT_TBL() - { - } - - /// - /// Gets or sets a value indicating whether the table has been output to file. - /// - /// It's initialized false when the table is created, and set - /// true when it's been output to the file. You could suppress output of a table by setting this to true. - /// - /// This property is used only during compression. - /// - public bool Sent_table - { - get { return m_sent_table; } - set { m_sent_table = value; } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/J_COLOR_SPACE.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/J_COLOR_SPACE.cs deleted file mode 100644 index f07b6693..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/J_COLOR_SPACE.cs +++ /dev/null @@ -1,54 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Known color spaces. - /// - /// Special color spaces -#if EXPOSE_LIBJPEG - public -#endif - enum J_COLOR_SPACE - { - /// - /// Unspecified color space. - /// - JCS_UNKNOWN, - /// - /// Grayscale - /// - JCS_GRAYSCALE, /* monochrome */ - /// - /// RGB - /// - JCS_RGB, - /// - /// YCbCr (also known as YUV) - /// - JCS_YCbCr, - /// - /// CMYK - /// - JCS_CMYK, - /// - /// YCbCrK - /// - JCS_YCCK - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/J_DCT_METHOD.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/J_DCT_METHOD.cs deleted file mode 100644 index a10b9e4a..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/J_DCT_METHOD.cs +++ /dev/null @@ -1,49 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Algorithm used for the DCT step. - /// - /// The FLOAT method is very slightly more accurate than the ISLOW method, - /// but may give different results on different machines due to varying roundoff behavior. - /// The integer methods should give the same results on all machines. On machines with - /// sufficiently fast hardware, the floating-point method may also be the fastest. - /// The IFAST method is considerably less accurate than the other two; its use is not recommended - /// if high quality is a concern. - /// - /// -#if EXPOSE_LIBJPEG - public -#endif - enum J_DCT_METHOD - { - /// - /// Slow but accurate integer algorithm. - /// - JDCT_ISLOW, - /// - /// Faster, less accurate integer method. - /// - JDCT_IFAST, - /// - /// Floating-point method. - /// - JDCT_FLOAT - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/J_DITHER_MODE.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/J_DITHER_MODE.cs deleted file mode 100644 index 2bd833ba..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/J_DITHER_MODE.cs +++ /dev/null @@ -1,42 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Dithering options for decompression. - /// - /// -#if EXPOSE_LIBJPEG - public -#endif - enum J_DITHER_MODE - { - /// - /// No dithering: fast, very low quality - /// - JDITHER_NONE, - /// - /// Ordered dither: moderate speed and quality - /// - JDITHER_ORDERED, - /// - /// Floyd-Steinberg dither: slow, high quality - /// - JDITHER_FS - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/J_MESSAGE_CODE.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/J_MESSAGE_CODE.cs deleted file mode 100644 index d67e4b1c..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/J_MESSAGE_CODE.cs +++ /dev/null @@ -1,431 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file defines the error and message codes for the JPEG library. - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Message codes used in code to signal errors, warning and trace messages. - /// - /// -#if EXPOSE_LIBJPEG - public -#endif - enum J_MESSAGE_CODE - { - /// - /// Must be first entry! - /// - JMSG_NOMESSAGE, - - /// - /// - /// - JERR_ARITH_NOTIMPL, - /// - /// - /// - JERR_BAD_BUFFER_MODE, - /// - /// - /// - JERR_BAD_COMPONENT_ID, - /// - /// - /// - JERR_BAD_DCT_COEF, - /// - /// - /// - JERR_BAD_DCTSIZE, - /// - /// - /// - JERR_BAD_HUFF_TABLE, - /// - /// - /// - JERR_BAD_IN_COLORSPACE, - /// - /// - /// - JERR_BAD_J_COLORSPACE, - /// - /// - /// - JERR_BAD_LENGTH, - /// - /// - /// - JERR_BAD_MCU_SIZE, - /// - /// - /// - JERR_BAD_PRECISION, - /// - /// - /// - JERR_BAD_PROGRESSION, - /// - /// - /// - JERR_BAD_PROG_SCRIPT, - /// - /// - /// - JERR_BAD_SAMPLING, - /// - /// - /// - JERR_BAD_SCAN_SCRIPT, - /// - /// - /// - JERR_BAD_STATE, - /// - /// - /// - JERR_BAD_VIRTUAL_ACCESS, - /// - /// - /// - JERR_BUFFER_SIZE, - /// - /// - /// - JERR_CANT_SUSPEND, - /// - /// - /// - JERR_CCIR601_NOTIMPL, - /// - /// - /// - JERR_COMPONENT_COUNT, - /// - /// - /// - JERR_CONVERSION_NOTIMPL, - /// - /// - /// - JERR_DHT_INDEX, - /// - /// - /// - JERR_DQT_INDEX, - /// - /// - /// - JERR_EMPTY_IMAGE, - /// - /// - /// - JERR_EOI_EXPECTED, - /// - /// - /// - JERR_FILE_WRITE, - /// - /// - /// - JERR_FRACT_SAMPLE_NOTIMPL, - /// - /// - /// - JERR_HUFF_CLEN_OVERFLOW, - /// - /// - /// - JERR_HUFF_MISSING_CODE, - /// - /// - /// - JERR_IMAGE_TOO_BIG, - /// - /// - /// - JERR_INPUT_EMPTY, - /// - /// - /// - JERR_INPUT_EOF, - /// - /// - /// - JERR_MISMATCHED_QUANT_TABLE, - /// - /// - /// - JERR_MISSING_DATA, - /// - /// - /// - JERR_MODE_CHANGE, - /// - /// - /// - JERR_NOTIMPL, - /// - /// - /// - JERR_NOT_COMPILED, - /// - /// - /// - JERR_NO_HUFF_TABLE, - /// - /// - /// - JERR_NO_IMAGE, - /// - /// - /// - JERR_NO_QUANT_TABLE, - /// - /// - /// - JERR_NO_SOI, - /// - /// - /// - JERR_OUT_OF_MEMORY, - /// - /// - /// - JERR_QUANT_COMPONENTS, - /// - /// - /// - JERR_QUANT_FEW_COLORS, - /// - /// - /// - JERR_QUANT_MANY_COLORS, - /// - /// - /// - JERR_SOF_DUPLICATE, - /// - /// - /// - JERR_SOF_NO_SOS, - /// - /// - /// - JERR_SOF_UNSUPPORTED, - /// - /// - /// - JERR_SOI_DUPLICATE, - /// - /// - /// - JERR_SOS_NO_SOF, - /// - /// - /// - JERR_TOO_LITTLE_DATA, - /// - /// - /// - JERR_UNKNOWN_MARKER, - /// - /// - /// - JERR_WIDTH_OVERFLOW, - /// - /// - /// - JTRC_16BIT_TABLES, - /// - /// - /// - JTRC_ADOBE, - /// - /// - /// - JTRC_APP0, - /// - /// - /// - JTRC_APP14, - /// - /// - /// - JTRC_DHT, - /// - /// - /// - JTRC_DQT, - /// - /// - /// - JTRC_DRI, - /// - /// - /// - JTRC_EOI, - /// - /// - /// - JTRC_HUFFBITS, - /// - /// - /// - JTRC_JFIF, - /// - /// - /// - JTRC_JFIF_BADTHUMBNAILSIZE, - /// - /// - /// - JTRC_JFIF_EXTENSION, - /// - /// - /// - JTRC_JFIF_THUMBNAIL, - /// - /// - /// - JTRC_MISC_MARKER, - /// - /// - /// - JTRC_PARMLESS_MARKER, - /// - /// - /// - JTRC_QUANTVALS, - /// - /// - /// - JTRC_QUANT_3_NCOLORS, - /// - /// - /// - JTRC_QUANT_NCOLORS, - /// - /// - /// - JTRC_QUANT_SELECTED, - /// - /// - /// - JTRC_RECOVERY_ACTION, - /// - /// - /// - JTRC_RST, - /// - /// - /// - JTRC_SMOOTH_NOTIMPL, - /// - /// - /// - JTRC_SOF, - /// - /// - /// - JTRC_SOF_COMPONENT, - /// - /// - /// - JTRC_SOI, - /// - /// - /// - JTRC_SOS, - /// - /// - /// - JTRC_SOS_COMPONENT, - /// - /// - /// - JTRC_SOS_PARAMS, - /// - /// - /// - JTRC_THUMB_JPEG, - /// - /// - /// - JTRC_THUMB_PALETTE, - /// - /// - /// - JTRC_THUMB_RGB, - /// - /// - /// - JTRC_UNKNOWN_IDS, - /// - /// - /// - JWRN_ADOBE_XFORM, - /// - /// - /// - JWRN_BOGUS_PROGRESSION, - /// - /// - /// - JWRN_EXTRANEOUS_DATA, - /// - /// - /// - JWRN_HIT_MARKER, - /// - /// - /// - JWRN_HUFF_BAD_CODE, - /// - /// - /// - JWRN_JFIF_MAJOR, - /// - /// - /// - JWRN_JPEG_EOF, - /// - /// - /// - JWRN_MUST_RESYNC, - /// - /// - /// - JWRN_NOT_SEQUENTIAL, - /// - /// - /// - JWRN_TOO_MUCH_DATA, - /// - /// - /// - JMSG_UNKNOWNMSGCODE, - /// - /// - /// - JMSG_LASTMSGCODE - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JpegConstants.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JpegConstants.cs deleted file mode 100644 index 6d09168d..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/JpegConstants.cs +++ /dev/null @@ -1,170 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Defines some JPEG constants. - /// -#if EXPOSE_LIBJPEG - public -#endif - static class JpegConstants - { - ////////////////////////////////////////////////////////////////////////// - // All of these are specified by the JPEG standard, so don't change them - // if you want to be compatible. - // - - /// - /// The basic DCT block is 8x8 samples - /// - public const int DCTSIZE = 8; - - /// - /// DCTSIZE squared; the number of elements in a block. - /// - public const int DCTSIZE2 = DCTSIZE * DCTSIZE; - - /// - /// Quantization tables are numbered 0..3 - /// - public const int NUM_QUANT_TBLS = 4; - - /// - /// Huffman tables are numbered 0..3 - /// - public const int NUM_HUFF_TBLS = 4; - - /// - /// JPEG limit on the number of components in one scan. - /// - public const int MAX_COMPS_IN_SCAN = 4; - - // compressor's limit on blocks per MCU - // - // Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; - // the PostScript DCT filter can emit files with many more than 10 blocks/MCU. - // If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU - // to handle it. We even let you do this from the jconfig.h file. However, - // we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe - // sometimes emits noncompliant files doesn't mean you should too. - - /// - /// Compressor's limit on blocks per MCU. - /// - public const int C_MAX_BLOCKS_IN_MCU = 10; - - /// - /// Decompressor's limit on blocks per MCU. - /// - public const int D_MAX_BLOCKS_IN_MCU = 10; - - /// - /// JPEG limit on sampling factors. - /// - public const int MAX_SAMP_FACTOR = 4; - - - ////////////////////////////////////////////////////////////////////////// - // implementation-specific constants - // - - // Maximum number of components (color channels) allowed in JPEG image. - // To meet the letter of the JPEG spec, set this to 255. However, darn - // few applications need more than 4 channels (maybe 5 for CMYK + alpha - // mask). We recommend 10 as a reasonable compromise; use 4 if you are - // really short on memory. (Each allowed component costs a hundred or so - // bytes of storage, whether actually used in an image or not.) - - /// - /// Maximum number of color channels allowed in JPEG image. - /// - public const int MAX_COMPONENTS = 10; - - - - /// - /// The size of sample. - /// - /// Are either: - /// 8 - for 8-bit sample values (the usual setting)
- /// 12 - for 12-bit sample values (not supported by this version)
- /// Only 8 and 12 are legal data precisions for lossy JPEG according to the JPEG standard. - /// Althought original IJG code claims it supports 12 bit images, our code does not support - /// anything except 8-bit images.
- public const int BITS_IN_JSAMPLE = 8; - - /// - /// DCT method used by default. - /// - public static J_DCT_METHOD JDCT_DEFAULT = J_DCT_METHOD.JDCT_ISLOW; - - /// - /// Fastest DCT method. - /// - public static J_DCT_METHOD JDCT_FASTEST = J_DCT_METHOD.JDCT_IFAST; - - /// - /// A tad under 64K to prevent overflows. - /// - public const int JPEG_MAX_DIMENSION = 65500; - - /// - /// The maximum sample value. - /// - public const int MAXJSAMPLE = 255; - - /// - /// The medium sample value. - /// - public const int CENTERJSAMPLE = 128; - - // Ordering of RGB data in scanlines passed to or from the application. - // RESTRICTIONS: - // 1. These macros only affect RGB<=>YCbCr color conversion, so they are not - // useful if you are using JPEG color spaces other than YCbCr or grayscale. - // 2. The color quantizer modules will not behave desirably if RGB_PIXELSIZE - // is not 3 (they don't understand about dummy color components!). So you - // can't use color quantization if you change that value. - - /// - /// Offset of Red in an RGB scanline element. - /// - public const int RGB_RED = 0; - - /// - /// Offset of Green in an RGB scanline element. - /// - public const int RGB_GREEN = 1; - - /// - /// Offset of Blue in an RGB scanline element. - /// - public const int RGB_BLUE = 2; - - /// - /// Bytes per RGB scanline element. - /// - public const int RGB_PIXELSIZE = 3; - - /// - /// The number of bits of lookahead. - /// - public const int HUFF_LOOKAHEAD = 8; - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/ReadResult.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/ReadResult.cs deleted file mode 100644 index 91a2e85f..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/ReadResult.cs +++ /dev/null @@ -1,53 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Describes a result of read operation. - /// - /// -#if EXPOSE_LIBJPEG - public -#endif - enum ReadResult - { - /// - /// Suspended due to lack of input data. Can occur only if a suspending data source is used. - /// - JPEG_SUSPENDED = 0, - /// - /// Found valid image datastream. - /// - JPEG_HEADER_OK = 1, - /// - /// Found valid table-specs-only datastream. - /// - JPEG_HEADER_TABLES_ONLY = 2, - /// - /// Reached a SOS marker (the start of a new scan) - /// - JPEG_REACHED_SOS = 3, - /// - /// Reached the EOI marker (end of image) - /// - JPEG_REACHED_EOI = 4, - /// - /// Completed reading one MCU row of compressed data. - /// - JPEG_ROW_COMPLETED = 5, - /// - /// Completed reading last MCU row of current scan. - /// - JPEG_SCAN_COMPLETED = 6 - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_common_struct.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_common_struct.cs deleted file mode 100644 index 352b95ad..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_common_struct.cs +++ /dev/null @@ -1,368 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains application interface routines that are used for both - * compression and decompression. - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.Reflection; -using System.Globalization; - -namespace BitMiracle.LibJpeg.Classic -{ - /// Base class for both JPEG compressor and decompresor. - /// - /// Routines that are to be used by both halves of the library are declared - /// to receive an instance of this class. There are no actual instances of - /// , only of - /// and - /// -#if EXPOSE_LIBJPEG - public -#endif - abstract class jpeg_common_struct - { - internal enum JpegState - { - DESTROYED = 0, - CSTATE_START = 100, /* after create_compress */ - CSTATE_SCANNING = 101, /* start_compress done, write_scanlines OK */ - CSTATE_RAW_OK = 102, /* start_compress done, write_raw_data OK */ - CSTATE_WRCOEFS = 103, /* jpeg_write_coefficients done */ - DSTATE_START = 200, /* after create_decompress */ - DSTATE_INHEADER = 201, /* reading header markers, no SOS yet */ - DSTATE_READY = 202, /* found SOS, ready for start_decompress */ - DSTATE_PRELOAD = 203, /* reading multiscan file in start_decompress*/ - DSTATE_PRESCAN = 204, /* performing dummy pass for 2-pass quant */ - DSTATE_SCANNING = 205, /* start_decompress done, read_scanlines OK */ - DSTATE_RAW_OK = 206, /* start_decompress done, read_raw_data OK */ - DSTATE_BUFIMAGE = 207, /* expecting jpeg_start_output */ - DSTATE_BUFPOST = 208, /* looking for SOS/EOI in jpeg_finish_output */ - DSTATE_RDCOEFS = 209, /* reading file in jpeg_read_coefficients */ - DSTATE_STOPPING = 210 /* looking for EOI in jpeg_finish_decompress */ - } - - // Error handler module - internal jpeg_error_mgr m_err; - - // Progress monitor, or null if none - internal jpeg_progress_mgr m_progress; - - internal JpegState m_global_state; /* For checking call sequence validity */ - - /// - /// Base constructor. - /// - /// - /// - public jpeg_common_struct() : this(new jpeg_error_mgr()) - { - } - - /// - /// Base constructor. - /// - /// The error manager. - /// - /// - public jpeg_common_struct(jpeg_error_mgr errorManager) - { - Err = errorManager; - } - - /// - /// Gets a value indicating whether this instance is Jpeg decompressor. - /// - /// - /// true if this is Jpeg decompressor; otherwise, false. - /// - public abstract bool IsDecompressor - { - get; - } - - /// - /// Progress monitor. - /// - /// The progress manager. - /// Default value: null. - public jpeg_progress_mgr Progress - { - get - { - return m_progress; - } - set - { - if (value == null) - throw new ArgumentNullException("value"); - - m_progress = value; - } - } - - /// - /// Error handler module. - /// - /// The error manager. - /// Error handling - public jpeg_error_mgr Err - { - get - { - return m_err; - } - set - { - if (value == null) - throw new ArgumentNullException("value"); - - m_err = value; - } - } - - /// - /// Gets the version of LibJpeg. - /// - /// The version of LibJpeg. - public static string Version - { - get - { - Version version = Assembly.GetExecutingAssembly().GetName().Version; - string versionString = version.Major.ToString(CultureInfo.InvariantCulture) + - "." + version.Minor.ToString(CultureInfo.InvariantCulture); - - versionString += "." + version.Build.ToString(CultureInfo.InvariantCulture); - versionString += "." + version.Revision.ToString(CultureInfo.InvariantCulture); - - return versionString; - } - } - - /// - /// Gets the LibJpeg's copyright. - /// - /// The copyright. - public static string Copyright - { - get - { - return "Copyright (C) 2008-2011, Bit Miracle"; - } - } - - /// - /// Creates the array of samples. - /// - /// The number of samples in row. - /// The number of rows. - /// The array of samples. - public static jvirt_array CreateSamplesArray(int samplesPerRow, int numberOfRows) - { - return new jvirt_array(samplesPerRow, numberOfRows, AllocJpegSamples); - } - - /// - /// Creates the array of blocks. - /// - /// The number of blocks in row. - /// The number of rows. - /// The array of blocks. - /// - public static jvirt_array CreateBlocksArray(int blocksPerRow, int numberOfRows) - { - return new jvirt_array(blocksPerRow, numberOfRows, allocJpegBlocks); - } - - /// - /// Creates 2-D sample array. - /// - /// The number of samples per row. - /// The number of rows. - /// The array of samples. - public static byte[][] AllocJpegSamples(int samplesPerRow, int numberOfRows) - { - byte[][] result = new byte[numberOfRows][]; - for (int i = 0; i < numberOfRows; ++i) - result[i] = new byte[samplesPerRow]; - - return result; - } - - // Creation of 2-D block arrays. - private static JBLOCK[][] allocJpegBlocks(int blocksPerRow, int numberOfRows) - { - JBLOCK[][] result = new JBLOCK[numberOfRows][]; - for (int i = 0; i < numberOfRows; ++i) - { - result[i] = new JBLOCK[blocksPerRow]; - for (int j = 0; j < blocksPerRow; ++j) - result[i][j] = new JBLOCK(); - } - return result; - } - - // Generic versions of jpeg_abort and jpeg_destroy that work on either - // flavor of JPEG object. These may be more convenient in some places. - - /// - /// Abort processing of a JPEG compression or decompression operation, - /// but don't destroy the object itself. - /// - /// Closing a data source or destination, if necessary, is the - /// application's responsibility. - /// - public void jpeg_abort() - { - /* Reset overall state for possible reuse of object */ - if (IsDecompressor) - { - m_global_state = JpegState.DSTATE_START; - - /* Try to keep application from accessing now-deleted marker list. - * A bit kludgy to do it here, but this is the most central place. - */ - jpeg_decompress_struct s = this as jpeg_decompress_struct; - if (s != null) - s.m_marker_list = null; - } - else - { - m_global_state = JpegState.CSTATE_START; - } - } - - /// - /// Destruction of a JPEG object. - /// - /// Closing a data source or destination, if necessary, is the - /// application's responsibility. - /// - public void jpeg_destroy() - { - // mark it destroyed - m_global_state = JpegState.DESTROYED; - } - - // Fatal errors (print message and exit) - - /// - /// Used for fatal errors (print message and exit). - /// - /// The message code. - public void ERREXIT(J_MESSAGE_CODE code) - { - ERREXIT((int)code); - } - - /// - /// Used for fatal errors (print message and exit). - /// - /// The message code. - /// The parameters of message. - public void ERREXIT(J_MESSAGE_CODE code, params object[] args) - { - ERREXIT((int)code, args); - } - - /// - /// Used for fatal errors (print message and exit). - /// - /// The message code. - /// The parameters of message. - public void ERREXIT(int code, params object[] args) - { - m_err.m_msg_code = code; - m_err.m_msg_parm = args; - m_err.error_exit(); - } - - // Nonfatal errors (we can keep going, but the data is probably corrupt) - - - /// - /// Used for non-fatal errors (we can keep going, but the data is probably corrupt). - /// - /// The message code. - public void WARNMS(J_MESSAGE_CODE code) - { - WARNMS((int)code); - } - - /// - /// Used for non-fatal errors (we can keep going, but the data is probably corrupt). - /// - /// The message code. - /// The parameters of message. - public void WARNMS(J_MESSAGE_CODE code, params object[] args) - { - WARNMS((int)code, args); - } - - /// - /// Used for non-fatal errors (we can keep going, but the data is probably corrupt). - /// - /// The message code. - /// The parameters of message. - public void WARNMS(int code, params object[] args) - { - m_err.m_msg_code = code; - m_err.m_msg_parm = args; - m_err.emit_message(-1); - } - - // Informational/debugging messages - - /// - /// Shows informational and debugging messages. - /// - /// See for description. - /// The message code. - /// - public void TRACEMS(int lvl, J_MESSAGE_CODE code) - { - TRACEMS(lvl, (int)code); - } - - /// - /// Shows informational and debugging messages. - /// - /// See for description. - /// The message code. - /// The parameters of message. - /// - public void TRACEMS(int lvl, J_MESSAGE_CODE code, params object[] args) - { - TRACEMS(lvl, (int)code, args); - } - - /// - /// Shows informational and debugging messages. - /// - /// See for description. - /// The message code. - /// The parameters of message. - /// - public void TRACEMS(int lvl, int code, params object[] args) - { - m_err.m_msg_code = code; - m_err.m_msg_parm = args; - m_err.emit_message(lvl); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_component_info.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_component_info.cs deleted file mode 100644 index bc0014f4..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_component_info.cs +++ /dev/null @@ -1,222 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -using BitMiracle.LibJpeg.Classic.Internal; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Basic info about one component (color channel). - /// -#if EXPOSE_LIBJPEG - public -#endif - class jpeg_component_info - { - /* These values are fixed over the whole image. */ - /* For compression, they must be supplied by parameter setup; */ - /* for decompression, they are read from the SOF marker. */ - - private int component_id; - private int component_index; - private int h_samp_factor; - private int v_samp_factor; - private int quant_tbl_no; - - /* These values may vary between scans. */ - /* For compression, they must be supplied by parameter setup; */ - /* for decompression, they are read from the SOS marker. */ - /* The decompressor output side may not use these variables. */ - private int dc_tbl_no; - private int ac_tbl_no; - - /* Remaining fields should be treated as private by applications. */ - - /* These values are computed during compression or decompression startup: */ - /* Component's size in DCT blocks. - * Any dummy blocks added to complete an MCU are not counted; therefore - * these values do not depend on whether a scan is interleaved or not. - */ - private int width_in_blocks; - internal int height_in_blocks; - /* Size of a DCT block in samples. Always DCTSIZE for compression. - * For decompression this is the size of the output from one DCT block, - * reflecting any scaling we choose to apply during the IDCT step. - * Values of 1,2,4,8 are likely to be supported. Note that different - * components may receive different IDCT scalings. - */ - internal int DCT_scaled_size; - /* The downsampled dimensions are the component's actual, unpadded number - * of samples at the main buffer (preprocessing/compression interface), thus - * downsampled_width = ceil(image_width * Hi/Hmax) - * and similarly for height. For decompression, IDCT scaling is included, so - * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) - */ - internal int downsampled_width; /* actual width in samples */ - - internal int downsampled_height; /* actual height in samples */ - /* This flag is used only for decompression. In cases where some of the - * components will be ignored (eg grayscale output from YCbCr image), - * we can skip most computations for the unused components. - */ - internal bool component_needed; /* do we need the value of this component? */ - - /* These values are computed before starting a scan of the component. */ - /* The decompressor output side may not use these variables. */ - internal int MCU_width; /* number of blocks per MCU, horizontally */ - internal int MCU_height; /* number of blocks per MCU, vertically */ - internal int MCU_blocks; /* MCU_width * MCU_height */ - internal int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ - internal int last_col_width; /* # of non-dummy blocks across in last MCU */ - internal int last_row_height; /* # of non-dummy blocks down in last MCU */ - - /* Saved quantization table for component; null if none yet saved. - * See jpeg_input_controller comments about the need for this information. - * This field is currently used only for decompression. - */ - internal JQUANT_TBL quant_table; - - internal jpeg_component_info() - { - } - - internal void Assign(jpeg_component_info ci) - { - component_id = ci.component_id; - component_index = ci.component_index; - h_samp_factor = ci.h_samp_factor; - v_samp_factor = ci.v_samp_factor; - quant_tbl_no = ci.quant_tbl_no; - dc_tbl_no = ci.dc_tbl_no; - ac_tbl_no = ci.ac_tbl_no; - width_in_blocks = ci.width_in_blocks; - height_in_blocks = ci.height_in_blocks; - DCT_scaled_size = ci.DCT_scaled_size; - downsampled_width = ci.downsampled_width; - downsampled_height = ci.downsampled_height; - component_needed = ci.component_needed; - MCU_width = ci.MCU_width; - MCU_height = ci.MCU_height; - MCU_blocks = ci.MCU_blocks; - MCU_sample_width = ci.MCU_sample_width; - last_col_width = ci.last_col_width; - last_row_height = ci.last_row_height; - quant_table = ci.quant_table; - } - - /// - /// Identifier for this component (0..255) - /// - /// The component ID. - public int Component_id - { - get { return component_id; } - set { component_id = value; } - } - - /// - /// Its index in SOF or . - /// - /// The component index. - public int Component_index - { - get { return component_index; } - set { component_index = value; } - } - - /// - /// Horizontal sampling factor (1..4) - /// - /// The horizontal sampling factor. - public int H_samp_factor - { - get { return h_samp_factor; } - set { h_samp_factor = value; } - } - - /// - /// Vertical sampling factor (1..4) - /// - /// The vertical sampling factor. - public int V_samp_factor - { - get { return v_samp_factor; } - set { v_samp_factor = value; } - } - - /// - /// Quantization table selector (0..3) - /// - /// The quantization table selector. - public int Quant_tbl_no - { - get { return quant_tbl_no; } - set { quant_tbl_no = value; } - } - - /// - /// DC entropy table selector (0..3) - /// - /// The DC entropy table selector. - public int Dc_tbl_no - { - get { return dc_tbl_no; } - set { dc_tbl_no = value; } - } - - /// - /// AC entropy table selector (0..3) - /// - /// The AC entropy table selector. - public int Ac_tbl_no - { - get { return ac_tbl_no; } - set { ac_tbl_no = value; } - } - - /// - /// Gets or sets the width in blocks. - /// - /// The width in blocks. - public int Width_in_blocks - { - get { return width_in_blocks; } - set { width_in_blocks = value; } - } - - /// - /// Gets the downsampled width. - /// - /// The downsampled width. - public int Downsampled_width - { - get { return downsampled_width; } - } - - internal static jpeg_component_info[] createArrayOfComponents(int length) - { - if (length < 0) - throw new ArgumentOutOfRangeException("length"); - - jpeg_component_info[] result = new jpeg_component_info[length]; - for (int i = 0; i < result.Length; ++i) - result[i] = new jpeg_component_info(); - - return result; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_compress_struct.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_compress_struct.cs deleted file mode 100644 index a19c85ad..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_compress_struct.cs +++ /dev/null @@ -1,1888 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -using BitMiracle.LibJpeg.Classic.Internal; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// JPEG compression routine. - /// - /// -#if EXPOSE_LIBJPEG - public -#endif - class jpeg_compress_struct : jpeg_common_struct - { - /* These are the sample quantization tables given in JPEG spec section K.1. - * The spec says that the values given produce "good" quality, and - * when divided by 2, "very good" quality. - */ - private static int[] std_luminance_quant_tbl = { - 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, - 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, - 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, - 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, - 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, - 100, 103, 99 }; - - private static int[] std_chrominance_quant_tbl = { - 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, - 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, - 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99 }; - - // Standard Huffman tables (cf. JPEG standard section K.3) - // - // IMPORTANT: these are only valid for 8-bit data precision! - private static byte[] bits_dc_luminance = - { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }; - - private static byte[] val_dc_luminance = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; - - private static byte[] bits_dc_chrominance = - { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; - - private static byte[] val_dc_chrominance = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; - - private static byte[] bits_ac_luminance = - { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d }; - - private static byte[] val_ac_luminance = - { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, - 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, - 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, - 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, - 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, - 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, - 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, - 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, - 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa }; - - private static byte[] bits_ac_chrominance = - { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 }; - - private static byte[] val_ac_chrominance = - { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, - 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, - 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, - 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, - 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, - 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, - 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, - 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, - 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa }; - - /* Destination for compressed data */ - internal jpeg_destination_mgr m_dest; - - internal int m_image_width; /* input image width */ - internal int m_image_height; /* input image height */ - internal int m_input_components; /* # of color components in input image */ - internal J_COLOR_SPACE m_in_color_space; /* colorspace of input image */ - - internal int m_data_precision; /* bits of precision in image data */ - internal int m_num_components; /* # of color components in JPEG image */ - internal J_COLOR_SPACE m_jpeg_color_space; /* colorspace of JPEG image */ - - /* comp_info[i] describes component that appears i'th in SOF */ - private jpeg_component_info[] m_comp_info; - - /* ptrs to coefficient quantization tables, or null if not defined */ - internal JQUANT_TBL[] m_quant_tbl_ptrs = new JQUANT_TBL[JpegConstants.NUM_QUANT_TBLS]; - - /* ptrs to Huffman coding tables, or null if not defined */ - internal JHUFF_TBL[] m_dc_huff_tbl_ptrs = new JHUFF_TBL[JpegConstants.NUM_HUFF_TBLS]; - internal JHUFF_TBL[] m_ac_huff_tbl_ptrs = new JHUFF_TBL[JpegConstants.NUM_HUFF_TBLS]; - - /* The default value of scan_info is null, which causes a single-scan - * sequential JPEG file to be emitted. To create a multi-scan file, - * set num_scans and scan_info to point to an array of scan definitions. - */ - internal int m_num_scans; /* # of entries in scan_info array */ - - /* script for multi-scan file, or null */ - internal jpeg_scan_info[] m_scan_info; - - internal bool m_raw_data_in; /* true=caller supplies downsampled data */ - internal bool m_optimize_coding; /* true=optimize entropy encoding parms */ - internal bool m_CCIR601_sampling; /* true=first samples are cosited */ - internal int m_smoothing_factor; /* 1..100, or 0 for no input smoothing */ - internal J_DCT_METHOD m_dct_method; /* DCT algorithm selector */ - - internal int m_restart_interval; /* MCUs per restart, or 0 for no restart */ - internal int m_restart_in_rows; /* if > 0, MCU rows per restart interval */ - - internal bool m_write_JFIF_header; /* should a JFIF marker be written? */ - internal byte m_JFIF_major_version; /* What to write for the JFIF version number */ - internal byte m_JFIF_minor_version; - - internal DensityUnit m_density_unit; /* JFIF code for pixel size units */ - internal short m_X_density; /* Horizontal pixel density */ - internal short m_Y_density; /* Vertical pixel density */ - internal bool m_write_Adobe_marker; /* should an Adobe marker be written? */ - - internal int m_next_scanline; /* 0 .. image_height-1 */ - - /* Remaining fields are known throughout compressor, but generally - * should not be touched by a surrounding application. - */ - - /* - * These fields are computed during compression startup - */ - internal bool m_progressive_mode; /* true if scan script uses progressive mode */ - internal int m_max_h_samp_factor; /* largest h_samp_factor */ - internal int m_max_v_samp_factor; /* largest v_samp_factor */ - - internal int m_total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ - /* The coefficient controller receives data in units of MCU rows as defined - * for fully interleaved scans (whether the JPEG file is interleaved or not). - * There are v_samp_factor * DCTSIZE sample rows of each component in an - * "iMCU" (interleaved MCU) row. - */ - - /* - * These fields are valid during any one scan. - * They describe the components and MCUs actually appearing in the scan. - */ - internal int m_comps_in_scan; /* # of JPEG components in this scan */ - internal int[] m_cur_comp_info = new int[JpegConstants.MAX_COMPS_IN_SCAN]; - /* *cur_comp_info[i] is index of m_comp_info that describes component that appears i'th in SOS */ - - internal int m_MCUs_per_row; /* # of MCUs across the image */ - internal int m_MCU_rows_in_scan; /* # of MCU rows in the image */ - - internal int m_blocks_in_MCU; /* # of DCT blocks per MCU */ - internal int[] m_MCU_membership = new int[JpegConstants.C_MAX_BLOCKS_IN_MCU]; - /* MCU_membership[i] is index in cur_comp_info of component owning */ - /* i'th block in an MCU */ - - /* progressive JPEG parameters for scan */ - internal int m_Ss; - internal int m_Se; - internal int m_Ah; - internal int m_Al; - - /* - * Links to compression subobjects (methods and private variables of modules) - */ - internal jpeg_comp_master m_master; - internal jpeg_c_main_controller m_main; - internal jpeg_c_prep_controller m_prep; - internal jpeg_c_coef_controller m_coef; - internal jpeg_marker_writer m_marker; - internal jpeg_color_converter m_cconvert; - internal jpeg_downsampler m_downsample; - internal jpeg_forward_dct m_fdct; - internal jpeg_entropy_encoder m_entropy; - internal jpeg_scan_info[] m_script_space; /* workspace for jpeg_simple_progression */ - internal int m_script_space_size; - - /// - /// Initializes a new instance of the class. - /// - public jpeg_compress_struct() - { - initialize(); - } - - /// - /// Initializes a new instance of the class. - /// - /// The error manager. - public jpeg_compress_struct(jpeg_error_mgr errorManager) - : base(errorManager) - { - initialize(); - } - - /// - /// Retrieves false because this is not decompressor. - /// - /// false - public override bool IsDecompressor - { - get { return false; } - } - - /// - /// Gets or sets the destination for compressed data - /// - /// The destination for compressed data. - public LibJpeg.Classic.jpeg_destination_mgr Dest - { - get { return m_dest; } - set { m_dest = value; } - } - - /* Description of source image --- these fields must be filled in by - * outer application before starting compression. in_color_space must - * be correct before you can even call jpeg_set_defaults(). - */ - - /// - /// Gets or sets the width of image, in pixels. - /// - /// The width of image. - /// Compression details - public int Image_width - { - get { return m_image_width; } - set { m_image_width = value; } - } - - /// - /// Gets or sets the height of image, in pixels. - /// - /// The height of image. - /// Compression details - public int Image_height - { - get { return m_image_height; } - set { m_image_height = value; } - } - - /// - /// Gets or sets the number of color channels (components per pixel) - /// - /// The number of color channels. - /// Compression details - public int Input_components - { - get { return m_input_components; } - set { m_input_components = value; } - } - - /// - /// Gets or sets the color space of source image. - /// - /// The color space. - /// Compression details - /// Special color spaces - public LibJpeg.Classic.J_COLOR_SPACE In_color_space - { - get { return m_in_color_space; } - set { m_in_color_space = value; } - } - - /* Compression parameters --- these fields must be set before calling - * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to - * initialize everything to reasonable defaults, then changing anything - * the application specifically wants to change. That way you won't get - * burnt when new parameters are added. Also note that there are several - * helper routines to simplify changing parameters. - */ - - // bits of precision in image data - - - /// - /// Gets or sets the number of bits of precision in image data. - /// - /// Default value: 8
- /// The number of bits. - ///
- /// The data precision. - public int Data_precision - { - get { return m_data_precision; } - set { m_data_precision = value; } - } - - /// - /// Gets or sets the number of color components for JPEG color space. - /// - /// The number of color components for JPEG color space. - public int Num_components - { - get { return m_num_components; } - set { m_num_components = value; } - } - - /// - /// Gets or sets the JPEG color space. - /// - /// We recommend to use if you want to change this. - /// The JPEG color space. - public J_COLOR_SPACE Jpeg_color_space - { - get { return m_jpeg_color_space; } - set { m_jpeg_color_space = value; } - } - - // true=caller supplies downsampled data - - - /// - /// Gets or sets a value indicating whether you will be supplying raw data. - /// - /// Default value: false - /// true if you will be supplying raw data; otherwise, false. - /// - public bool Raw_data_in - { - get { return m_raw_data_in; } - set { m_raw_data_in = value; } - } - - /// - /// Gets or sets a value indicating a way of using Huffman coding tables. - /// - /// When this is true, you need not supply Huffman tables at all, and any you do supply will be overwritten. - /// true causes the compressor to compute optimal Huffman coding tables - /// for the image. This requires an extra pass over the data and therefore costs a good - /// deal of space and time. The default is false, which tells the compressor to use the - /// supplied or default Huffman tables. In most cases optimal tables save only a few - /// percent of file size compared to the default tables. - /// Compression parameter selection - public bool Optimize_coding - { - get { return m_optimize_coding; } - set { m_optimize_coding = value; } - } - - /// - /// Gets or sets a value indicating whether first samples are cosited. - /// - /// true if first samples are cosited; otherwise, false. - public bool CCIR601_sampling - { - get { return m_CCIR601_sampling; } - set { m_CCIR601_sampling = value; } - } - - /// - /// Gets or sets the coefficient of image smoothing. - /// - /// Default value: 0
- /// If non-zero, the input image is smoothed; the value should be 1 for minimal smoothing - /// to 100 for maximum smoothing.
- /// The coefficient of image smoothing. - /// Compression parameter selection - public int Smoothing_factor - { - get { return m_smoothing_factor; } - set { m_smoothing_factor = value; } - } - - /// - /// Gets or sets the algorithm used for the DCT step. - /// - /// The DCT algorithm. - /// Compression parameter selection - public J_DCT_METHOD Dct_method - { - get { return m_dct_method; } - set { m_dct_method = value; } - } - - /* The restart interval can be specified in absolute MCUs by setting - * restart_interval, or in MCU rows by setting restart_in_rows - * (in which case the correct restart_interval will be figured - * for each scan). - */ - - /// - /// Gets or sets the exact interval in MCU blocks. - /// - /// Default value: 0
- /// One restart marker per MCU row is often a good choice. The overhead of restart markers - /// is higher in grayscale JPEG files than in color files, and MUCH higher in progressive JPEGs. - /// If you use restarts, you may want to use larger intervals in those cases.
- /// The restart interval. - /// - /// Compression parameter selection - public int Restart_interval - { - get { return m_restart_interval; } - set { m_restart_interval = value; } - } - - /// - /// Gets or sets the interval in MCU rows. - /// - /// Default value: 0
- /// If Restart_in_rows is not 0, then is set - /// after the image width in MCUs is computed.
- /// One restart marker per MCU row is often a good choice. - /// The overhead of restart markers is higher in grayscale JPEG files than in color files, and MUCH higher in progressive JPEGs. If you use restarts, you may want to use larger intervals in those cases. - ///
- /// The restart interval in MCU rows. - /// - /// Compression parameter selection - public int Restart_in_rows - { - get { return m_restart_in_rows; } - set { m_restart_in_rows = value; } - } - - /* Parameters controlling emission of special markers. */ - - /// - /// Gets or sets a value indicating whether the JFIF APP0 marker is emitted. - /// - /// and - /// set this true - /// if a JFIF-legal JPEG color space (i.e., YCbCr or grayscale) is selected, otherwise false. - /// true if JFIF APP0 marker is emitted; otherwise, false. - /// - /// Compression parameter selection - public bool Write_JFIF_header - { - get { return m_write_JFIF_header; } - set { m_write_JFIF_header = value; } - } - - /// - /// Gets or sets the version number to be written into the JFIF marker. - /// - /// initializes the version to - /// 1.01 (major=minor=1). You should set it to 1.02 (major=1, minor=2) if you plan to write any - /// JFIF 1.02 extension markers. - /// The version number to be written into the JFIF marker. - /// - /// - public byte JFIF_major_version - { - get { return m_JFIF_major_version; } - set { m_JFIF_major_version = value; } - } - - /// - /// Gets or sets the version number to be written into the JFIF marker. - /// - /// initializes the version to - /// 1.01 (major=minor=1). You should set it to 1.02 (major=1, minor=2) if you plan to write any - /// JFIF 1.02 extension markers. - /// The version number to be written into the JFIF marker. - /// - /// - public byte JFIF_minor_version - { - get { return m_JFIF_minor_version; } - set { m_JFIF_minor_version = value; } - } - - /* These three values are not used by the JPEG code, merely copied */ - /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ - /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ - /* ratio is defined by X_density/Y_density even when density_unit=0. */ - - /// - /// Gets or sets the resolution information to be written into the JFIF marker; not used otherwise. - /// - /// Default value:
- /// The pixel aspect ratio is defined by - /// / - /// even when Density_unit is Unknown.
- /// The density unit. - /// - /// - /// Compression parameter selection - public DensityUnit Density_unit - { - get { return m_density_unit; } - set { m_density_unit = value; } - } - - // Horizontal pixel density - - /// - /// Gets or sets the horizontal component of pixel ratio. - /// - /// Default value: 1 - /// The horizontal density. - /// - /// - public short X_density - { - get { return m_X_density; } - set { m_X_density = value; } - } - - /// - /// Gets or sets the vertical component of pixel ratio. - /// - /// Default value: 1 - /// The vertical density. - /// - /// - public short Y_density - { - get { return m_Y_density; } - set { m_Y_density = value; } - } - - /// - /// Gets or sets a value indicating whether to emit Adobe APP14 marker. - /// - /// and - /// set this true if JPEG color space RGB, CMYK, or YCCK is selected, otherwise false. - /// It is generally a bad idea to set both and - /// . - /// In fact, you probably shouldn't change the default settings at all - the default behavior ensures that the JPEG file's - /// color space can be recognized by the decoder. - /// If true an Adobe APP14 marker is emitted; false, otherwise. - /// Compression parameter selection - public bool Write_Adobe_marker - { - get { return m_write_Adobe_marker; } - set { m_write_Adobe_marker = value; } - } - - /// - /// Gets the largest vertical sample factor. - /// - /// The largest vertical sample factor. - /// Compression parameter selection - public int Max_v_samp_factor - { - get { return m_max_v_samp_factor; } - } - - /// - /// Gets the components that appears in SOF. - /// - /// The component info array. - public jpeg_component_info[] Component_info - { - get { return m_comp_info; } - } - - /* ptrs to coefficient quantization tables, or null if not defined */ - /* ptrs to coefficient quantization tables, or null if not defined */ - /// - /// Gets the coefficient quantization tables. - /// - /// The coefficient quantization tables or null if not defined. - public JQUANT_TBL[] Quant_tbl_ptrs - { - get { return m_quant_tbl_ptrs; } - } - - /// - /// Gets the Huffman coding tables. - /// - /// The Huffman coding tables or null if not defined. - public JHUFF_TBL[] Dc_huff_tbl_ptrs - { - get { return m_dc_huff_tbl_ptrs; } - } - - /// - /// Gets the Huffman coding tables. - /// - /// The Huffman coding tables or null if not defined. - public JHUFF_TBL[] Ac_huff_tbl_ptrs - { - get { return m_ac_huff_tbl_ptrs; } - } - - /// - /// Gets the index of next scanline to be written to . - /// - /// Application may use this to control its processing loop, - /// e.g., "while (Next_scanline < Image_height)" - /// Range: from 0 to (Image_height - 1) - /// - public int Next_scanline - { - get { return m_next_scanline; } - } - - /// - /// Abort processing of a JPEG compression operation. - /// - public void jpeg_abort_compress() - { - // use common routine - jpeg_abort(); - } - - /// - /// Forcibly suppress or un-suppress all quantization and Huffman tables. - /// - /// Marks all currently defined tables as already written (if suppress) - /// or not written (if !suppress). This will control whether they get - /// emitted by a subsequent call.
- /// - /// This routine is exported for use by applications that want to produce - /// abbreviated JPEG datastreams.
- /// if set to true then suppress tables; - /// otherwise unsuppress. - public void jpeg_suppress_tables(bool suppress) - { - for (int i = 0; i < JpegConstants.NUM_QUANT_TBLS; i++) - { - if (m_quant_tbl_ptrs[i] != null) - m_quant_tbl_ptrs[i].Sent_table = suppress; - } - - for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) - { - if (m_dc_huff_tbl_ptrs[i] != null) - m_dc_huff_tbl_ptrs[i].Sent_table = suppress; - - if (m_ac_huff_tbl_ptrs[i] != null) - m_ac_huff_tbl_ptrs[i].Sent_table = suppress; - } - } - - /// - /// Finishes JPEG compression. - /// - /// If a multipass operating mode was selected, this may do a great - /// deal of work including most of the actual output. - public void jpeg_finish_compress() - { - int iMCU_row; - - if (m_global_state == JpegState.CSTATE_SCANNING || m_global_state == JpegState.CSTATE_RAW_OK) - { - /* Terminate first pass */ - if (m_next_scanline < m_image_height) - ERREXIT(J_MESSAGE_CODE.JERR_TOO_LITTLE_DATA); - m_master.finish_pass(); - } - else if (m_global_state != JpegState.CSTATE_WRCOEFS) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - /* Perform any remaining passes */ - while (!m_master.IsLastPass()) - { - m_master.prepare_for_pass(); - for (iMCU_row = 0; iMCU_row < m_total_iMCU_rows; iMCU_row++) - { - if (m_progress != null) - { - m_progress.Pass_counter = iMCU_row; - m_progress.Pass_limit = m_total_iMCU_rows; - m_progress.Updated(); - } - - /* We bypass the main controller and invoke coef controller directly; - * all work is being done from the coefficient buffer. - */ - if (!m_coef.compress_data(null)) - ERREXIT(J_MESSAGE_CODE.JERR_CANT_SUSPEND); - } - - m_master.finish_pass(); - } - - /* Write EOI, do final cleanup */ - m_marker.write_file_trailer(); - m_dest.term_destination(); - - /* We can use jpeg_abort to release memory and reset global_state */ - jpeg_abort(); - } - - - /// - /// Write a special marker. - /// - /// This is only recommended for writing COM or APPn markers. - /// Must be called after and before first call to - /// or . - /// - /// Specify the marker type parameter as .COM for COM or - /// .APP0 + n for APPn. (Actually, jpeg_write_marker will let you write any marker type, - /// but we don't recommend writing any other kinds of marker) - /// The data associated with the marker. - /// Special markers - /// - public void jpeg_write_marker(int marker, byte[] data) - { - if (m_next_scanline != 0 || (m_global_state != JpegState.CSTATE_SCANNING && m_global_state != JpegState.CSTATE_RAW_OK && m_global_state != JpegState.CSTATE_WRCOEFS)) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - m_marker.write_marker_header(marker, data.Length); - - for (int i = 0; i < data.Length; i++) - m_marker.write_marker_byte(data[i]); - } - - /// - /// Writes special marker's header. - /// - /// Special marker. - /// Length of data associated with the marker. - /// After calling this method you need to call - /// exactly the number of times given in the length parameter.
- /// This method lets you empty the output buffer partway through a marker, which might be important when - /// using a suspending data destination module. In any case, if you are using a suspending destination, - /// you should flush its buffer after inserting any special markers.
- /// - /// - /// Special markers - public void jpeg_write_m_header(int marker, int datalen) - { - if (m_next_scanline != 0 || (m_global_state != JpegState.CSTATE_SCANNING && m_global_state != JpegState.CSTATE_RAW_OK && m_global_state != JpegState.CSTATE_WRCOEFS)) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - m_marker.write_marker_header(marker, datalen); - } - - /// - /// Writes a byte of special marker's data. - /// - /// The byte of data. - /// - public void jpeg_write_m_byte(byte val) - { - m_marker.write_marker_byte(val); - } - - /// - /// Alternate compression function: just write an abbreviated table file. - /// - /// Before calling this, all parameters and a data destination must be set up.
- /// - /// To produce a pair of files containing abbreviated tables and abbreviated - /// image data, one would proceed as follows:
- /// - /// Initialize JPEG object
- /// Set JPEG parameters
- /// Set destination to table file
- /// jpeg_write_tables();
- /// Set destination to image file
- /// jpeg_start_compress(false);
- /// Write data...
- /// jpeg_finish_compress();
- ///

- /// - /// jpeg_write_tables has the side effect of marking all tables written - /// (same as jpeg_suppress_tables(true)). - /// Thus a subsequent jpeg_start_compress - /// will not re-emit the tables unless it is passed write_all_tables=true. - ///
- public void jpeg_write_tables() - { - if (m_global_state != JpegState.CSTATE_START) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - /* (Re)initialize error mgr and destination modules */ - m_err.reset_error_mgr(); - m_dest.init_destination(); - - /* Initialize the marker writer ... bit of a crock to do it here. */ - m_marker = new jpeg_marker_writer(this); - - /* Write them tables! */ - m_marker.write_tables_only(); - - /* And clean up. */ - m_dest.term_destination(); - } - - /// - /// Sets output stream. - /// - /// The output stream. - /// The caller must have already opened the stream, and is responsible - /// for closing it after finishing compression. - /// Compression details - public void jpeg_stdio_dest(Stream outfile) - { - m_dest = new my_destination_mgr(this, outfile); - } - - /// - /// Jpeg_set_defaultses this instance. - /// - /// Uses only the input image's color space (property , - /// which must already be set in ). Many applications will only need - /// to use this routine and perhaps . - /// - /// Compression parameter selection - public void jpeg_set_defaults() - { - /* Safety check to ensure start_compress not called yet. */ - if (m_global_state != JpegState.CSTATE_START) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - /* Allocate comp_info array large enough for maximum component count. - * Array is made permanent in case application wants to compress - * multiple images at same param settings. - */ - if (m_comp_info == null) - { - m_comp_info = jpeg_component_info.createArrayOfComponents(JpegConstants.MAX_COMPONENTS); - } - - /* Initialize everything not dependent on the color space */ - - m_data_precision = JpegConstants.BITS_IN_JSAMPLE; - - /* Set up two quantization tables using default quality of 75 */ - jpeg_set_quality(75, true); - - /* Set up two Huffman tables */ - std_huff_tables(); - - /* Default is no multiple-scan output */ - m_scan_info = null; - m_num_scans = 0; - - /* Expect normal source image, not raw downsampled data */ - m_raw_data_in = false; - - /* By default, don't do extra passes to optimize entropy coding */ - m_optimize_coding = false; - - /* The standard Huffman tables are only valid for 8-bit data precision. - * If the precision is higher, force optimization on so that usable - * tables will be computed. This test can be removed if default tables - * are supplied that are valid for the desired precision. - */ - if (m_data_precision > 8) - m_optimize_coding = true; - - /* By default, use the simpler non-cosited sampling alignment */ - m_CCIR601_sampling = false; - - /* No input smoothing */ - m_smoothing_factor = 0; - - /* DCT algorithm preference */ - m_dct_method = JpegConstants.JDCT_DEFAULT; - - /* No restart markers */ - m_restart_interval = 0; - m_restart_in_rows = 0; - - /* Fill in default JFIF marker parameters. Note that whether the marker - * will actually be written is determined by jpeg_set_colorspace. - * - * By default, the library emits JFIF version code 1.01. - * An application that wants to emit JFIF 1.02 extension markers should set - * JFIF_minor_version to 2. We could probably get away with just defaulting - * to 1.02, but there may still be some decoders in use that will complain - * about that; saying 1.01 should minimize compatibility problems. - */ - m_JFIF_major_version = 1; /* Default JFIF version = 1.01 */ - m_JFIF_minor_version = 1; - m_density_unit = DensityUnit.Unknown; /* Pixel size is unknown by default */ - m_X_density = 1; /* Pixel aspect ratio is square by default */ - m_Y_density = 1; - - /* Choose JPEG colorspace based on input space, set defaults accordingly */ - jpeg_default_colorspace(); - } - - // Compression parameter setup aids - - /// - /// Set the JPEG colorspace (property , - /// and choose colorspace-dependent parameters appropriately. - /// - /// The required colorspace. - /// See Special color spaces, - /// below, before using this. A large number of parameters, including all per-component parameters, - /// are set by this routine; if you want to twiddle individual parameters you should call - /// jpeg_set_colorspace before rather than after. - /// Compression parameter selection - /// Special color spaces - public void jpeg_set_colorspace(J_COLOR_SPACE colorspace) - { - int ci; - - /* Safety check to ensure start_compress not called yet. */ - if (m_global_state != JpegState.CSTATE_START) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - /* For all colorspaces, we use Q and Huff tables 0 for luminance components, - * tables 1 for chrominance components. - */ - - m_jpeg_color_space = colorspace; - - m_write_JFIF_header = false; /* No marker for non-JFIF colorspaces */ - m_write_Adobe_marker = false; /* write no Adobe marker by default */ - - switch (colorspace) - { - case J_COLOR_SPACE.JCS_GRAYSCALE: - m_write_JFIF_header = true; /* Write a JFIF marker */ - m_num_components = 1; - /* JFIF specifies component ID 1 */ - jpeg_set_colorspace_SET_COMP(0, 1, 1, 1, 0, 0, 0); - break; - case J_COLOR_SPACE.JCS_RGB: - m_write_Adobe_marker = true; /* write Adobe marker to flag RGB */ - m_num_components = 3; - jpeg_set_colorspace_SET_COMP(0, 0x52 /* 'R' */, 1, 1, 0, 0, 0); - jpeg_set_colorspace_SET_COMP(1, 0x47 /* 'G' */, 1, 1, 0, 0, 0); - jpeg_set_colorspace_SET_COMP(2, 0x42 /* 'B' */, 1, 1, 0, 0, 0); - break; - case J_COLOR_SPACE.JCS_YCbCr: - m_write_JFIF_header = true; /* Write a JFIF marker */ - m_num_components = 3; - /* JFIF specifies component IDs 1,2,3 */ - /* We default to 2x2 subsamples of chrominance */ - jpeg_set_colorspace_SET_COMP(0, 1, 2, 2, 0, 0, 0); - jpeg_set_colorspace_SET_COMP(1, 2, 1, 1, 1, 1, 1); - jpeg_set_colorspace_SET_COMP(2, 3, 1, 1, 1, 1, 1); - break; - case J_COLOR_SPACE.JCS_CMYK: - m_write_Adobe_marker = true; /* write Adobe marker to flag CMYK */ - m_num_components = 4; - jpeg_set_colorspace_SET_COMP(0, 0x43 /* 'C' */, 1, 1, 0, 0, 0); - jpeg_set_colorspace_SET_COMP(1, 0x4D /* 'M' */, 1, 1, 0, 0, 0); - jpeg_set_colorspace_SET_COMP(2, 0x59 /* 'Y' */, 1, 1, 0, 0, 0); - jpeg_set_colorspace_SET_COMP(3, 0x4B /* 'K' */, 1, 1, 0, 0, 0); - break; - case J_COLOR_SPACE.JCS_YCCK: - m_write_Adobe_marker = true; /* write Adobe marker to flag YCCK */ - m_num_components = 4; - jpeg_set_colorspace_SET_COMP(0, 1, 2, 2, 0, 0, 0); - jpeg_set_colorspace_SET_COMP(1, 2, 1, 1, 1, 1, 1); - jpeg_set_colorspace_SET_COMP(2, 3, 1, 1, 1, 1, 1); - jpeg_set_colorspace_SET_COMP(3, 4, 2, 2, 0, 0, 0); - break; - case J_COLOR_SPACE.JCS_UNKNOWN: - m_num_components = m_input_components; - if (m_num_components < 1 || m_num_components > JpegConstants.MAX_COMPONENTS) - ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_num_components, JpegConstants.MAX_COMPONENTS); - for (ci = 0; ci < m_num_components; ci++) - { - jpeg_set_colorspace_SET_COMP(ci, ci, 1, 1, 0, 0, 0); - } - break; - default: - ERREXIT(J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE); - break; - } - } - - /// - /// Select an appropriate JPEG colorspace based on , - /// and calls - /// - /// This is actually a subroutine of . - /// It's broken out in case you want to change just the colorspace-dependent JPEG parameters. - /// Compression parameter selection - public void jpeg_default_colorspace() - { - switch (m_in_color_space) - { - case J_COLOR_SPACE.JCS_GRAYSCALE: - jpeg_set_colorspace(J_COLOR_SPACE.JCS_GRAYSCALE); - break; - case J_COLOR_SPACE.JCS_RGB: - jpeg_set_colorspace(J_COLOR_SPACE.JCS_YCbCr); - break; - case J_COLOR_SPACE.JCS_YCbCr: - jpeg_set_colorspace(J_COLOR_SPACE.JCS_YCbCr); - break; - case J_COLOR_SPACE.JCS_CMYK: - jpeg_set_colorspace(J_COLOR_SPACE.JCS_CMYK); /* By default, no translation */ - break; - case J_COLOR_SPACE.JCS_YCCK: - jpeg_set_colorspace(J_COLOR_SPACE.JCS_YCCK); - break; - case J_COLOR_SPACE.JCS_UNKNOWN: - jpeg_set_colorspace(J_COLOR_SPACE.JCS_UNKNOWN); - break; - default: - ERREXIT(J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE); - break; - } - } - - /// - /// Constructs JPEG quantization tables appropriate for the indicated quality setting. - /// - /// The quality value is expressed on the 0..100 scale recommended by IJG. - /// If true, then the quantization table entries are constrained - /// to the range 1..255 for full JPEG baseline compatibility. In the current implementation, - /// this only makes a difference for quality settings below 25, and it effectively prevents - /// very small/low quality files from being generated. The IJG decoder is capable of reading - /// the non-baseline files generated at low quality settings when force_baseline is false, - /// but other decoders may not be. - /// Note that the exact mapping from quality values to tables may change in future IJG releases - /// as more is learned about DCT quantization. - /// Compression parameter selection - public void jpeg_set_quality(int quality, bool force_baseline) - { - /* Convert user 0-100 rating to percentage scaling */ - quality = jpeg_quality_scaling(quality); - - /* Set up standard quality tables */ - jpeg_set_linear_quality(quality, force_baseline); - } - - /// - /// Same as except that the generated tables are the - /// sample tables given in the JPEG specification section K.1, multiplied by - /// the specified scale factor. - /// - /// The scale_factor. - /// If true, then the quantization table entries are - /// constrained to the range 1..255 for full JPEG baseline compatibility. In the current - /// implementation, this only makes a difference for quality settings below 25, and it - /// effectively prevents very small/low quality files from being generated. The IJG decoder - /// is capable of reading the non-baseline files generated at low quality settings when - /// force_baseline is false, but other decoders may not be. - /// Note that larger scale factors give lower quality. This entry point is - /// useful for conforming to the Adobe PostScript DCT conventions, but we do not - /// recommend linear scaling as a user-visible quality scale otherwise. - /// - /// Compression parameter selection - public void jpeg_set_linear_quality(int scale_factor, bool force_baseline) - { - /* Set up two quantization tables using the specified scaling */ - jpeg_add_quant_table(0, std_luminance_quant_tbl, scale_factor, force_baseline); - jpeg_add_quant_table(1, std_chrominance_quant_tbl, scale_factor, force_baseline); - } - - /// - /// Allows an arbitrary quantization table to be created. - /// - /// Indicates which table slot to fill. - /// An array of 64 unsigned integers given in normal array order. - /// These values are multiplied by scale_factor/100 and then clamped to the range 1..65535 - /// (or to 1..255 if force_baseline is true).
- /// The basic table should be given in JPEG zigzag order. - /// - /// Multiplier for values in basic_table. - /// Defines range of values in basic_table. - /// If true - 1..255, otherwise - 1..65535. - /// Compression parameter selection - public void jpeg_add_quant_table(int which_tbl, int[] basic_table, int scale_factor, bool force_baseline) - { - /* Safety check to ensure start_compress not called yet. */ - if (m_global_state != JpegState.CSTATE_START) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - if (which_tbl < 0 || which_tbl >= JpegConstants.NUM_QUANT_TBLS) - ERREXIT(J_MESSAGE_CODE.JERR_DQT_INDEX, which_tbl); - - if (m_quant_tbl_ptrs[which_tbl] == null) - m_quant_tbl_ptrs[which_tbl] = new JQUANT_TBL(); - - for (int i = 0; i < JpegConstants.DCTSIZE2; i++) - { - int temp = (basic_table[i] * scale_factor + 50) / 100; - - /* limit the values to the valid range */ - if (temp <= 0) - temp = 1; - - /* max quantizer needed for 12 bits */ - if (temp > 32767) - temp = 32767; - - /* limit to baseline range if requested */ - if (force_baseline && temp > 255) - temp = 255; - - m_quant_tbl_ptrs[which_tbl].quantval[i] = (short)temp; - } - - /* Initialize sent_table false so table will be written to JPEG file. */ - m_quant_tbl_ptrs[which_tbl].Sent_table = false; - } - - /// - /// Converts a value on the IJG-recommended quality scale to a linear scaling percentage. - /// - /// The IJG-recommended quality scale. Should be 0 (terrible) to 100 (very good). - /// The linear scaling percentage. - /// Compression parameter selection - public static int jpeg_quality_scaling(int quality) - { - /* Safety limit on quality factor. Convert 0 to 1 to avoid zero divide. */ - if (quality <= 0) - quality = 1; - - if (quality > 100) - quality = 100; - - /* The basic table is used as-is (scaling 100) for a quality of 50. - * Qualities 50..100 are converted to scaling percentage 200 - 2*Q; - * note that at Q=100 the scaling is 0, which will cause jpeg_add_quant_table - * to make all the table entries 1 (hence, minimum quantization loss). - * Qualities 1..50 are converted to scaling percentage 5000/Q. - */ - if (quality < 50) - quality = 5000 / quality; - else - quality = 200 - quality * 2; - - return quality; - } - - /// - /// Generates a default scan script for writing a progressive-JPEG file. - /// - /// This is the recommended method of creating a progressive file, unless you want - /// to make a custom scan sequence. You must ensure that the JPEG color space is - /// set correctly before calling this routine. - /// Compression parameter selection - public void jpeg_simple_progression() - { - /* Safety check to ensure start_compress not called yet. */ - if (m_global_state != JpegState.CSTATE_START) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - /* Figure space needed for script. Calculation must match code below! */ - int nscans; - if (m_num_components == 3 && m_jpeg_color_space == J_COLOR_SPACE.JCS_YCbCr) - { - /* Custom script for YCbCr color images. */ - nscans = 10; - } - else - { - /* All-purpose script for other color spaces. */ - if (m_num_components > JpegConstants.MAX_COMPS_IN_SCAN) - { - /* 2 DC + 4 AC scans per component */ - nscans = 6 * m_num_components; - } - else - { - /* 2 DC scans; 4 AC scans per component */ - nscans = 2 + 4 * m_num_components; - } - } - - /* Allocate space for script. - * We need to put it in the permanent pool in case the application performs - * multiple compressions without changing the settings. To avoid a memory - * leak if jpeg_simple_progression is called repeatedly for the same JPEG - * object, we try to re-use previously allocated space, and we allocate - * enough space to handle YCbCr even if initially asked for grayscale. - */ - if (m_script_space == null || m_script_space_size < nscans) - { - m_script_space_size = Math.Max(nscans, 10); - m_script_space = new jpeg_scan_info[m_script_space_size]; - for (int i = 0; i < m_script_space_size; i++) - m_script_space[i] = new jpeg_scan_info(); - } - - m_scan_info = m_script_space; - m_num_scans = nscans; - - int scanIndex = 0; - if (m_num_components == 3 && m_jpeg_color_space == J_COLOR_SPACE.JCS_YCbCr) - { - /* Custom script for YCbCr color images. */ - /* Initial DC scan */ - fill_dc_scans(ref scanIndex, m_num_components, 0, 1); - - /* Initial AC scan: get some luma data out in a hurry */ - fill_a_scan(ref scanIndex, 0, 1, 5, 0, 2); - - /* Chroma data is too small to be worth expending many scans on */ - fill_a_scan(ref scanIndex, 2, 1, 63, 0, 1); - fill_a_scan(ref scanIndex, 1, 1, 63, 0, 1); - - /* Complete spectral selection for luma AC */ - fill_a_scan(ref scanIndex, 0, 6, 63, 0, 2); - - /* Refine next bit of luma AC */ - fill_a_scan(ref scanIndex, 0, 1, 63, 2, 1); - - /* Finish DC successive approximation */ - fill_dc_scans(ref scanIndex, m_num_components, 1, 0); - - /* Finish AC successive approximation */ - fill_a_scan(ref scanIndex, 2, 1, 63, 1, 0); - fill_a_scan(ref scanIndex, 1, 1, 63, 1, 0); - - /* Luma bottom bit comes last since it's usually largest scan */ - fill_a_scan(ref scanIndex, 0, 1, 63, 1, 0); - } - else - { - /* All-purpose script for other color spaces. */ - /* Successive approximation first pass */ - fill_dc_scans(ref scanIndex, m_num_components, 0, 1); - fill_scans(ref scanIndex, m_num_components, 1, 5, 0, 2); - fill_scans(ref scanIndex, m_num_components, 6, 63, 0, 2); - - /* Successive approximation second pass */ - fill_scans(ref scanIndex, m_num_components, 1, 63, 2, 1); - - /* Successive approximation final pass */ - fill_dc_scans(ref scanIndex, m_num_components, 1, 0); - fill_scans(ref scanIndex, m_num_components, 1, 63, 1, 0); - } - } - - // Main entry points for compression - - /// - /// Starts JPEG compression. - /// - /// Write or not write all quantization and Huffman tables. - /// Before calling this, all parameters and a data destination must be set up. - /// - /// - /// Compression details - public void jpeg_start_compress(bool write_all_tables) - { - if (m_global_state != JpegState.CSTATE_START) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - if (write_all_tables) - jpeg_suppress_tables(false); /* mark all tables to be written */ - - /* (Re)initialize error mgr and destination modules */ - m_err.reset_error_mgr(); - m_dest.init_destination(); - - /* Perform master selection of active modules */ - jinit_compress_master(); - - /* Set up for the first pass */ - m_master.prepare_for_pass(); - - /* Ready for application to drive first pass through jpeg_write_scanlines - * or jpeg_write_raw_data. - */ - m_next_scanline = 0; - m_global_state = (m_raw_data_in ? JpegState.CSTATE_RAW_OK : JpegState.CSTATE_SCANNING); - } - - /// - /// Write some scanlines of data to the JPEG compressor. - /// - /// The array of scanlines. - /// The number of scanlines for writing. - /// The return value will be the number of lines actually written.
- /// This should be less than the supplied num_lines only in case that - /// the data destination module has requested suspension of the compressor, - /// or if more than image_height scanlines are passed in. - ///
- /// We warn about excess calls to jpeg_write_scanlines() since this likely - /// signals an application programmer error. However, excess scanlines passed in the last - /// valid call are "silently" ignored, so that the application need not adjust num_lines - /// for end-of-image when using a multiple-scanline buffer. - /// Compression details - public int jpeg_write_scanlines(byte[][] scanlines, int num_lines) - { - if (m_global_state != JpegState.CSTATE_SCANNING) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - if (m_next_scanline >= m_image_height) - WARNMS(J_MESSAGE_CODE.JWRN_TOO_MUCH_DATA); - - /* Call progress monitor hook if present */ - if (m_progress != null) - { - m_progress.Pass_counter = m_next_scanline; - m_progress.Pass_limit = m_image_height; - m_progress.Updated(); - } - - /* Give master control module another chance if this is first call to - * jpeg_write_scanlines. This lets output of the frame/scan headers be - * delayed so that application can write COM, etc, markers between - * jpeg_start_compress and jpeg_write_scanlines. - */ - if (m_master.MustCallPassStartup()) - m_master.pass_startup(); - - /* Ignore any extra scanlines at bottom of image. */ - int rows_left = m_image_height - m_next_scanline; - if (num_lines > rows_left) - num_lines = rows_left; - - int row_ctr = 0; - m_main.process_data(scanlines, ref row_ctr, num_lines); - m_next_scanline += row_ctr; - return row_ctr; - } - - /// - /// Alternate entry point to write raw data. - /// - /// The raw data. - /// The number of scanlines for writing. - /// The number of lines actually written. - /// Processes exactly one iMCU row per call, unless suspended. - /// Replaces when writing raw downsampled data. - public int jpeg_write_raw_data(byte[][][] data, int num_lines) - { - if (m_global_state != JpegState.CSTATE_RAW_OK) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - if (m_next_scanline >= m_image_height) - { - WARNMS(J_MESSAGE_CODE.JWRN_TOO_MUCH_DATA); - return 0; - } - - /* Call progress monitor hook if present */ - if (m_progress != null) - { - m_progress.Pass_counter = m_next_scanline; - m_progress.Pass_limit = m_image_height; - m_progress.Updated(); - } - - /* Give master control module another chance if this is first call to - * jpeg_write_raw_data. This lets output of the frame/scan headers be - * delayed so that application can write COM, etc, markers between - * jpeg_start_compress and jpeg_write_raw_data. - */ - if (m_master.MustCallPassStartup()) - m_master.pass_startup(); - - /* Verify that at least one iMCU row has been passed. */ - int lines_per_iMCU_row = m_max_v_samp_factor * JpegConstants.DCTSIZE; - if (num_lines < lines_per_iMCU_row) - ERREXIT(J_MESSAGE_CODE.JERR_BUFFER_SIZE); - - /* Directly compress the row. */ - if (!m_coef.compress_data(data)) - { - /* If compressor did not consume the whole row, suspend processing. */ - return 0; - } - - /* OK, we processed one iMCU row. */ - m_next_scanline += lines_per_iMCU_row; - return lines_per_iMCU_row; - } - - /// - /// Compression initialization for writing raw-coefficient data. Useful for lossless transcoding. - /// - /// The virtual arrays need not be filled or even realized at the time - /// jpeg_write_coefficients is called; indeed, the virtual arrays typically will be realized - /// during this routine and filled afterwards. - /// - /// Before calling this, all parameters and a data destination must be set up. - /// Call to actually write the data. - /// - public void jpeg_write_coefficients(jvirt_array[] coef_arrays) - { - if (m_global_state != JpegState.CSTATE_START) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - /* Mark all tables to be written */ - jpeg_suppress_tables(false); - - /* (Re)initialize error mgr and destination modules */ - m_err.reset_error_mgr(); - m_dest.init_destination(); - - /* Perform master selection of active modules */ - transencode_master_selection(coef_arrays); - - /* Wait for jpeg_finish_compress() call */ - m_next_scanline = 0; /* so jpeg_write_marker works */ - m_global_state = JpegState.CSTATE_WRCOEFS; - } - - // Compression module initialization routines - - /// - /// Initialization of a JPEG compression object - /// - private void initialize() - { - /* Zero out pointers to permanent structures. */ - m_progress = null; - m_dest = null; - m_comp_info = null; - - for (int i = 0; i < JpegConstants.NUM_QUANT_TBLS; i++) - m_quant_tbl_ptrs[i] = null; - - for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) - { - m_dc_huff_tbl_ptrs[i] = null; - m_ac_huff_tbl_ptrs[i] = null; - } - - m_script_space = null; - - /* OK, I'm ready */ - m_global_state = JpegState.CSTATE_START; - } - - /// - /// Master selection of compression modules. - /// This is done once at the start of processing an image. We determine - /// which modules will be used and give them appropriate initialization calls. - /// This routine is in charge of selecting the modules to be executed and - /// making an initialization call to each one. - /// - private void jinit_compress_master() - { - /* Initialize master control (includes parameter checking/processing) */ - jinit_c_master_control(false /* full compression */); - - /* Preprocessing */ - if (!m_raw_data_in) - { - m_cconvert = new jpeg_color_converter(this); - m_downsample = new jpeg_downsampler(this); - m_prep = new jpeg_c_prep_controller(this); - } - - /* Forward DCT */ - m_fdct = new jpeg_forward_dct(this); - - /* Entropy encoding: only Huffman coding supported. */ - if (m_progressive_mode) - m_entropy = new phuff_entropy_encoder(this); - else - m_entropy = new huff_entropy_encoder(this); - - /* Need a full-image coefficient buffer in any multi-pass mode. */ - m_coef = new my_c_coef_controller(this, (bool)(m_num_scans > 1 || m_optimize_coding)); - jinit_c_main_controller(false /* never need full buffer here */); - m_marker = new jpeg_marker_writer(this); - - /* Write the datastream header (SOI) immediately. - * Frame and scan headers are postponed till later. - * This lets application insert special markers after the SOI. - */ - m_marker.write_file_header(); - } - - /// - /// Initialize master compression control. - /// - private void jinit_c_master_control(bool transcode_only) - { - /* Validate parameters, determine derived values */ - initial_setup(); - - if (m_scan_info != null) - { - validate_script(); - } - else - { - m_progressive_mode = false; - m_num_scans = 1; - } - - if (m_progressive_mode) /* TEMPORARY HACK ??? */ - m_optimize_coding = true; /* assume default tables no good for progressive mode */ - - m_master = new jpeg_comp_master(this, transcode_only); - } - - /// - /// Initialize main buffer controller. - /// - private void jinit_c_main_controller(bool need_full_buffer) - { - /* We don't need to create a buffer in raw-data mode. */ - if (m_raw_data_in) - return; - - /* Create the buffer. It holds downsampled data, so each component - * may be of a different size. - */ - if (need_full_buffer) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE); - else - m_main = new jpeg_c_main_controller(this); - } - - /// - /// Master selection of compression modules for transcoding. - /// - private void transencode_master_selection(jvirt_array[] coef_arrays) - { - /* Although we don't actually use input_components for transcoding, - * jcmaster.c's initial_setup will complain if input_components is 0. - */ - m_input_components = 1; - - /* Initialize master control (includes parameter checking/processing) */ - jinit_c_master_control(true /* transcode only */); - - /* Entropy encoding: only Huffman coding supported. */ - if (m_progressive_mode) - m_entropy = new phuff_entropy_encoder(this); - else - m_entropy = new huff_entropy_encoder(this); - - /* We need a special coefficient buffer controller. */ - m_coef = new my_trans_c_coef_controller(this, coef_arrays); - m_marker = new jpeg_marker_writer(this); - - /* Write the datastream header (SOI, JFIF) immediately. - * Frame and scan headers are postponed till later. - * This lets application insert special markers after the SOI. - */ - m_marker.write_file_header(); - } - - /// - /// Do computations that are needed before master selection phase - /// - private void initial_setup() - { - /* Sanity check on image dimensions */ - if (m_image_height <= 0 || m_image_width <= 0 || m_num_components <= 0 || m_input_components <= 0) - ERREXIT(J_MESSAGE_CODE.JERR_EMPTY_IMAGE); - - /* Make sure image isn't bigger than I can handle */ - if (m_image_height > JpegConstants.JPEG_MAX_DIMENSION || m_image_width > JpegConstants.JPEG_MAX_DIMENSION) - ERREXIT(J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG, (int) JpegConstants.JPEG_MAX_DIMENSION); - - /* Width of an input scanline must be representable as int. */ - long samplesperrow = m_image_width * m_input_components; - int jd_samplesperrow = (int)samplesperrow; - if ((long)jd_samplesperrow != samplesperrow) - ERREXIT(J_MESSAGE_CODE.JERR_WIDTH_OVERFLOW); - - /* For now, precision must match compiled-in value... */ - if (m_data_precision != JpegConstants.BITS_IN_JSAMPLE) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_PRECISION, m_data_precision); - - /* Check that number of components won't exceed internal array sizes */ - if (m_num_components > JpegConstants.MAX_COMPONENTS) - ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, m_num_components, JpegConstants.MAX_COMPONENTS); - - /* Compute maximum sampling factors; check factor validity */ - m_max_h_samp_factor = 1; - m_max_v_samp_factor = 1; - for (int ci = 0; ci < m_num_components; ci++) - { - if (m_comp_info[ci].H_samp_factor <= 0 || m_comp_info[ci].H_samp_factor > JpegConstants.MAX_SAMP_FACTOR || - m_comp_info[ci].V_samp_factor <= 0 || m_comp_info[ci].V_samp_factor > JpegConstants.MAX_SAMP_FACTOR) - { - ERREXIT(J_MESSAGE_CODE.JERR_BAD_SAMPLING); - } - - m_max_h_samp_factor = Math.Max(m_max_h_samp_factor, m_comp_info[ci].H_samp_factor); - m_max_v_samp_factor = Math.Max(m_max_v_samp_factor, m_comp_info[ci].V_samp_factor); - } - - /* Compute dimensions of components */ - for (int ci = 0; ci < m_num_components; ci++) - { - /* Fill in the correct component_index value; don't rely on application */ - m_comp_info[ci].Component_index = ci; - - /* For compression, we never do DCT scaling. */ - m_comp_info[ci].DCT_scaled_size = JpegConstants.DCTSIZE; - - /* Size in DCT blocks */ - m_comp_info[ci].Width_in_blocks = JpegUtils.jdiv_round_up( - m_image_width * m_comp_info[ci].H_samp_factor, m_max_h_samp_factor * JpegConstants.DCTSIZE); - m_comp_info[ci].height_in_blocks = JpegUtils.jdiv_round_up( - m_image_height * m_comp_info[ci].V_samp_factor, m_max_v_samp_factor * JpegConstants.DCTSIZE); - - /* Size in samples */ - m_comp_info[ci].downsampled_width = JpegUtils.jdiv_round_up( - m_image_width * m_comp_info[ci].H_samp_factor, m_max_h_samp_factor); - m_comp_info[ci].downsampled_height = JpegUtils.jdiv_round_up( - m_image_height * m_comp_info[ci].V_samp_factor, m_max_v_samp_factor); - - /* Mark component needed (this flag isn't actually used for compression) */ - m_comp_info[ci].component_needed = true; - } - - /* Compute number of fully interleaved MCU rows (number of times that - * main controller will call coefficient controller). - */ - m_total_iMCU_rows = JpegUtils.jdiv_round_up(m_image_height, m_max_v_samp_factor * JpegConstants.DCTSIZE); - } - - /// - /// Verify that the scan script in scan_info[] is valid; - /// also determine whether it uses progressive JPEG, and set progressive_mode. - /// - private void validate_script() - { - if (m_num_scans <= 0) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_SCAN_SCRIPT, 0); - - /* For sequential JPEG, all scans must have Ss=0, Se=DCTSIZE2-1; - * for progressive JPEG, no scan can have this. - */ - int[][] last_bitpos = new int [JpegConstants.MAX_COMPONENTS][]; - for (int i = 0; i < JpegConstants.MAX_COMPONENTS; i++) - last_bitpos[i] = new int[JpegConstants.DCTSIZE2]; - - bool[] component_sent = new bool [JpegConstants.MAX_COMPONENTS]; - - /* -1 until that coefficient has been seen; then last Al for it */ - if (m_scan_info[0].Ss != 0 || m_scan_info[0].Se != JpegConstants.DCTSIZE2 - 1) - { - m_progressive_mode = true; - for (int ci = 0; ci < m_num_components; ci++) - { - for (int coefi = 0; coefi < JpegConstants.DCTSIZE2; coefi++) - last_bitpos[ci][coefi] = -1; - } - } - else - { - m_progressive_mode = false; - for (int ci = 0; ci < m_num_components; ci++) - component_sent[ci] = false; - } - - for (int scanno = 1; scanno <= m_num_scans; scanno++) - { - jpeg_scan_info scanInfo = m_scan_info[scanno - 1]; - - /* Validate component indexes */ - int ncomps = scanInfo.comps_in_scan; - if (ncomps <= 0 || ncomps > JpegConstants.MAX_COMPS_IN_SCAN) - ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, ncomps, JpegConstants.MAX_COMPS_IN_SCAN); - - for (int ci = 0; ci < ncomps; ci++) - { - int thisi = scanInfo.component_index[ci]; - if (thisi < 0 || thisi >= m_num_components) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_SCAN_SCRIPT, scanno); - - /* Components must appear in SOF order within each scan */ - if (ci > 0 && thisi <= scanInfo.component_index[ci - 1]) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_SCAN_SCRIPT, scanno); - } - - /* Validate progression parameters */ - int Ss = scanInfo.Ss; - int Se = scanInfo.Se; - int Ah = scanInfo.Ah; - int Al = scanInfo.Al; - if (m_progressive_mode) - { - /* The JPEG spec simply gives the ranges 0..13 for Ah and Al, but that - * seems wrong: the upper bound ought to depend on data precision. - * Perhaps they really meant 0..N+1 for N-bit precision. - * Here we allow 0..10 for 8-bit data; Al larger than 10 results in - * out-of-range reconstructed DC values during the first DC scan, - * which might cause problems for some decoders. - */ - const int MAX_AH_AL = 10; - if (Ss < 0 || Ss >= JpegConstants.DCTSIZE2 || Se < Ss || Se >= JpegConstants.DCTSIZE2 || - Ah < 0 || Ah > MAX_AH_AL || Al < 0 || Al > MAX_AH_AL) - { - ERREXIT(J_MESSAGE_CODE.JERR_BAD_PROG_SCRIPT, scanno); - } - - if (Ss == 0) - { - if (Se != 0) /* DC and AC together not OK */ - ERREXIT(J_MESSAGE_CODE.JERR_BAD_PROG_SCRIPT, scanno); - } - else - { - if (ncomps != 1) /* AC scans must be for only one component */ - ERREXIT(J_MESSAGE_CODE.JERR_BAD_PROG_SCRIPT, scanno); - } - - for (int ci = 0; ci < ncomps; ci++) - { - int lastBitComponentIndex = scanInfo.component_index[ci]; - if (Ss != 0 && last_bitpos[lastBitComponentIndex][0] < 0) /* AC without prior DC scan */ - ERREXIT(J_MESSAGE_CODE.JERR_BAD_PROG_SCRIPT, scanno); - - for (int coefi = Ss; coefi <= Se; coefi++) - { - if (last_bitpos[lastBitComponentIndex][coefi] < 0) - { - /* first scan of this coefficient */ - if (Ah != 0) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_PROG_SCRIPT, scanno); - } - else - { - /* not first scan */ - if (Ah != last_bitpos[lastBitComponentIndex][coefi] || Al != Ah - 1) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_PROG_SCRIPT, scanno); - } - - last_bitpos[lastBitComponentIndex][coefi] = Al; - } - } - } - else - { - /* For sequential JPEG, all progression parameters must be these: */ - if (Ss != 0 || Se != JpegConstants.DCTSIZE2 - 1 || Ah != 0 || Al != 0) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_PROG_SCRIPT, scanno); - - /* Make sure components are not sent twice */ - for (int ci = 0; ci < ncomps; ci++) - { - int thisi = scanInfo.component_index[ci]; - if (component_sent[thisi]) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_SCAN_SCRIPT, scanno); - - component_sent[thisi] = true; - } - } - } - - /* Now verify that everything got sent. */ - if (m_progressive_mode) - { - /* For progressive mode, we only check that at least some DC data - * got sent for each component; the spec does not require that all bits - * of all coefficients be transmitted. Would it be wiser to enforce - * transmission of all coefficient bits?? - */ - for (int ci = 0; ci < m_num_components; ci++) - { - if (last_bitpos[ci][0] < 0) - ERREXIT(J_MESSAGE_CODE.JERR_MISSING_DATA); - } - } - else - { - for (int ci = 0; ci < m_num_components; ci++) - { - if (!component_sent[ci]) - ERREXIT(J_MESSAGE_CODE.JERR_MISSING_DATA); - } - } - } - - // Huffman table setup routines - - /// - /// Set up the standard Huffman tables (cf. JPEG standard section K.3) - /// - /// IMPORTANT: these are only valid for 8-bit data precision! - /// - private void std_huff_tables() - { - add_huff_table(ref m_dc_huff_tbl_ptrs[0], bits_dc_luminance, val_dc_luminance); - add_huff_table(ref m_ac_huff_tbl_ptrs[0], bits_ac_luminance, val_ac_luminance); - add_huff_table(ref m_dc_huff_tbl_ptrs[1], bits_dc_chrominance, val_dc_chrominance); - add_huff_table(ref m_ac_huff_tbl_ptrs[1], bits_ac_chrominance, val_ac_chrominance); - } - - /// - /// Define a Huffman table - /// - private void add_huff_table(ref JHUFF_TBL htblptr, byte[] bits, byte[] val) - { - if (htblptr == null) - htblptr = new JHUFF_TBL(); - - /* Copy the number-of-symbols-of-each-code-length counts */ - Buffer.BlockCopy(bits, 0, htblptr.Bits, 0, htblptr.Bits.Length); - - /* Validate the counts. We do this here mainly so we can copy the right - * number of symbols from the val[] array, without risking marching off - * the end of memory. huff_entropy_encoder will do a more thorough test later. - */ - int nsymbols = 0; - for (int len = 1; len <= 16; len++) - nsymbols += bits[len]; - - if (nsymbols < 1 || nsymbols> 256) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE); - - Buffer.BlockCopy(val, 0, htblptr.Huffval, 0, nsymbols); - - /* Initialize sent_table false so table will be written to JPEG file. */ - htblptr.Sent_table = false; - } - - /// - /// Support routine: generate one scan for specified component - /// - private void fill_a_scan(ref int scanIndex, int ci, int Ss, int Se, int Ah, int Al) - { - m_script_space[scanIndex].comps_in_scan = 1; - m_script_space[scanIndex].component_index[0] = ci; - m_script_space[scanIndex].Ss = Ss; - m_script_space[scanIndex].Se = Se; - m_script_space[scanIndex].Ah = Ah; - m_script_space[scanIndex].Al = Al; - scanIndex++; - } - - /// - /// Support routine: generate interleaved DC scan if possible, else N scans - /// - private void fill_dc_scans(ref int scanIndex, int ncomps, int Ah, int Al) - { - if (ncomps <= JpegConstants.MAX_COMPS_IN_SCAN) - { - /* Single interleaved DC scan */ - m_script_space[scanIndex].comps_in_scan = ncomps; - for (int ci = 0; ci < ncomps; ci++) - m_script_space[scanIndex].component_index[ci] = ci; - - m_script_space[scanIndex].Ss = 0; - m_script_space[scanIndex].Se = 0; - m_script_space[scanIndex].Ah = Ah; - m_script_space[scanIndex].Al = Al; - scanIndex++; - } - else - { - /* Noninterleaved DC scan for each component */ - fill_scans(ref scanIndex, ncomps, 0, 0, Ah, Al); - } - } - - /// - /// Support routine: generate one scan for each component - /// - private void fill_scans(ref int scanIndex, int ncomps, int Ss, int Se, int Ah, int Al) - { - for (int ci = 0; ci < ncomps; ci++) - { - m_script_space[scanIndex].comps_in_scan = 1; - m_script_space[scanIndex].component_index[0] = ci; - m_script_space[scanIndex].Ss = Ss; - m_script_space[scanIndex].Se = Se; - m_script_space[scanIndex].Ah = Ah; - m_script_space[scanIndex].Al = Al; - scanIndex++; - } - } - - private void jpeg_set_colorspace_SET_COMP(int index, int id, int hsamp, int vsamp, int quant, int dctbl, int actbl) - { - m_comp_info[index].Component_id = id; - m_comp_info[index].H_samp_factor = hsamp; - m_comp_info[index].V_samp_factor = vsamp; - m_comp_info[index].Quant_tbl_no = quant; - m_comp_info[index].Dc_tbl_no = dctbl; - m_comp_info[index].Ac_tbl_no = actbl; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_decompress_struct.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_decompress_struct.cs deleted file mode 100644 index 699ec397..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_decompress_struct.cs +++ /dev/null @@ -1,1919 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Text; -using System.IO; - -using BitMiracle.LibJpeg.Classic.Internal; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// JPEG decompression routine. - /// - /// -#if EXPOSE_LIBJPEG - public -#endif - class jpeg_decompress_struct : jpeg_common_struct - { - /// - /// The delegate for application-supplied marker processing methods. - /// - /// Decompressor. - /// Return true to indicate success. false should be returned only - /// if you are using a suspending data source and it tells you to suspend. - /// - /// Although the marker code is not explicitly passed, the routine can find it - /// in the . At the time of call, - /// the marker proper has been read from the data source module. The processor routine - /// is responsible for reading the marker length word and the remaining parameter bytes, if any. - /// - public delegate bool jpeg_marker_parser_method(jpeg_decompress_struct cinfo); - - /* Source of compressed data */ - internal jpeg_source_mgr m_src; - - internal int m_image_width; /* nominal image width (from SOF marker) */ - internal int m_image_height; /* nominal image height */ - internal int m_num_components; /* # of color components in JPEG image */ - internal J_COLOR_SPACE m_jpeg_color_space; /* colorspace of JPEG image */ - - internal J_COLOR_SPACE m_out_color_space; /* colorspace for output */ - internal int m_scale_num; - internal int m_scale_denom; /* fraction by which to scale image */ - internal bool m_buffered_image; /* true=multiple output passes */ - internal bool m_raw_data_out; /* true=downsampled data wanted */ - internal J_DCT_METHOD m_dct_method; /* IDCT algorithm selector */ - internal bool m_do_fancy_upsampling; /* true=apply fancy upsampling */ - internal bool m_do_block_smoothing; /* true=apply interblock smoothing */ - internal bool m_quantize_colors; /* true=colormapped output wanted */ - internal J_DITHER_MODE m_dither_mode; /* type of color dithering to use */ - internal bool m_two_pass_quantize; /* true=use two-pass color quantization */ - internal int m_desired_number_of_colors; /* max # colors to use in created colormap */ - internal bool m_enable_1pass_quant; /* enable future use of 1-pass quantizer */ - internal bool m_enable_external_quant;/* enable future use of external colormap */ - internal bool m_enable_2pass_quant; /* enable future use of 2-pass quantizer */ - - internal int m_output_width; /* scaled image width */ - internal int m_output_height; /* scaled image height */ - internal int m_out_color_components; /* # of color components in out_color_space */ - /* # of color components returned - * output_components is 1 (a colormap index) when quantizing colors; - * otherwise it equals out_color_components. - */ - internal int m_output_components; - - internal int m_rec_outbuf_height; /* min recommended height of scanline buffer */ - - internal int m_actual_number_of_colors; /* number of entries in use */ - internal byte[][] m_colormap; /* The color map as a 2-D pixel array */ - - internal int m_output_scanline; /* 0 .. output_height-1 */ - - internal int m_input_scan_number; /* Number of SOS markers seen so far */ - internal int m_input_iMCU_row; /* Number of iMCU rows completed */ - - internal int m_output_scan_number; /* Nominal scan number being displayed */ - internal int m_output_iMCU_row; /* Number of iMCU rows read */ - - internal int[][] m_coef_bits; /* -1 or current Al value for each coef */ - - /* Internal JPEG parameters --- the application usually need not look at - * these fields. Note that the decompressor output side may not use - * any parameters that can change between scans. - */ - - /* Quantization and Huffman tables are carried forward across input - * datastreams when processing abbreviated JPEG datastreams. - */ - - internal JQUANT_TBL[] m_quant_tbl_ptrs = new JQUANT_TBL[JpegConstants.NUM_QUANT_TBLS]; - /* ptrs to coefficient quantization tables, or null if not defined */ - - internal JHUFF_TBL[] m_dc_huff_tbl_ptrs = new JHUFF_TBL[JpegConstants.NUM_HUFF_TBLS]; - internal JHUFF_TBL[] m_ac_huff_tbl_ptrs = new JHUFF_TBL[JpegConstants.NUM_HUFF_TBLS]; - /* ptrs to Huffman coding tables, or null if not defined */ - - /* These parameters are never carried across datastreams, since they - * are given in SOF/SOS markers or defined to be reset by SOI. - */ - - internal int m_data_precision; /* bits of precision in image data */ - - /* m_comp_info[i] describes component that appears i'th in SOF */ - private jpeg_component_info[] m_comp_info; - - internal bool m_progressive_mode; /* true if SOFn specifies progressive mode */ - - internal int m_restart_interval; /* MCUs per restart interval, or 0 for no restart */ - - /* These fields record data obtained from optional markers recognized by - * the JPEG library. - */ - internal bool m_saw_JFIF_marker; /* true iff a JFIF APP0 marker was found */ - /* Data copied from JFIF marker; only valid if saw_JFIF_marker is true: */ - internal byte m_JFIF_major_version; /* JFIF version number */ - internal byte m_JFIF_minor_version; - - internal DensityUnit m_density_unit; /* JFIF code for pixel size units */ - internal short m_X_density; /* Horizontal pixel density */ - internal short m_Y_density; /* Vertical pixel density */ - - internal bool m_saw_Adobe_marker; /* true iff an Adobe APP14 marker was found */ - internal byte m_Adobe_transform; /* Color transform code from Adobe marker */ - - internal bool m_CCIR601_sampling; /* true=first samples are cosited */ - - internal List m_marker_list; /* Head of list of saved markers */ - - /* Remaining fields are known throughout decompressor, but generally - * should not be touched by a surrounding application. - */ - - /* - * These fields are computed during decompression startup - */ - internal int m_max_h_samp_factor; /* largest h_samp_factor */ - internal int m_max_v_samp_factor; /* largest v_samp_factor */ - - internal int m_min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ - - internal int m_total_iMCU_rows; /* # of iMCU rows in image */ - /* The coefficient controller's input and output progress is measured in - * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows - * in fully interleaved JPEG scans, but are used whether the scan is - * interleaved or not. We define an iMCU row as v_samp_factor DCT block - * rows of each component. Therefore, the IDCT output contains - * v_samp_factor*DCT_scaled_size sample rows of a component per iMCU row. - */ - - internal byte[] m_sample_range_limit; /* table for fast range-limiting */ - internal int m_sampleRangeLimitOffset; - - /* - * These fields are valid during any one scan. - * They describe the components and MCUs actually appearing in the scan. - * Note that the decompressor output side must not use these fields. - */ - internal int m_comps_in_scan; /* # of JPEG components in this scan */ - internal int[] m_cur_comp_info = new int[JpegConstants.MAX_COMPS_IN_SCAN]; - /* *cur_comp_info[i] describes component that appears i'th in SOS */ - - internal int m_MCUs_per_row; /* # of MCUs across the image */ - internal int m_MCU_rows_in_scan; /* # of MCU rows in the image */ - - internal int m_blocks_in_MCU; /* # of DCT blocks per MCU */ - internal int[] m_MCU_membership = new int[JpegConstants.D_MAX_BLOCKS_IN_MCU]; - /* MCU_membership[i] is index in cur_comp_info of component owning */ - /* i'th block in an MCU */ - - /* progressive JPEG parameters for scan */ - internal int m_Ss; - internal int m_Se; - internal int m_Ah; - internal int m_Al; - - /* This field is shared between entropy decoder and marker parser. - * It is either zero or the code of a JPEG marker that has been - * read from the data source, but has not yet been processed. - */ - internal int m_unread_marker; - - /* - * Links to decompression subobjects (methods, private variables of modules) - */ - internal jpeg_decomp_master m_master; - internal jpeg_d_main_controller m_main; - internal jpeg_d_coef_controller m_coef; - internal jpeg_d_post_controller m_post; - internal jpeg_input_controller m_inputctl; - internal jpeg_marker_reader m_marker; - internal jpeg_entropy_decoder m_entropy; - internal jpeg_inverse_dct m_idct; - internal jpeg_upsampler m_upsample; - internal jpeg_color_deconverter m_cconvert; - internal jpeg_color_quantizer m_cquantize; - - /// - /// Initializes a new instance of the class. - /// - /// - public jpeg_decompress_struct() - { - initialize(); - } - - /// - /// Initializes a new instance of the class. - /// - /// The error manager. - /// - public jpeg_decompress_struct(jpeg_error_mgr errorManager) - : base(errorManager) - { - initialize(); - } - - /// - /// Retrieves true because this is a decompressor. - /// - /// true - public override bool IsDecompressor - { - get { return true; } - } - - /// - /// Gets or sets the source for decompression. - /// - /// The source for decompression. - public LibJpeg.Classic.jpeg_source_mgr Src - { - get { return m_src; } - set { m_src = value; } - } - - /* Basic description of image --- filled in by jpeg_read_header(). */ - /* Application may inspect these values to decide how to process image. */ - - /// - /// Gets the width of image, set by - /// - /// The width of image. - /// Decompression parameter selection - public int Image_width - { - get { return m_image_width; } - } - - /// - /// Gets the height of image, set by - /// - /// The height of image. - /// Decompression parameter selection - public int Image_height - { - get { return m_image_height; } - } - - /// - /// Gets the number of color components in JPEG image. - /// - /// The number of color components. - /// Decompression parameter selection - public int Num_components - { - get { return m_num_components; } - } - - /// - /// Gets or sets the colorspace of JPEG image. - /// - /// The colorspace of JPEG image. - /// Decompression parameter selection - public LibJpeg.Classic.J_COLOR_SPACE Jpeg_color_space - { - get { return m_jpeg_color_space; } - set { m_jpeg_color_space = value; } - } - - /// - /// Gets the list of loaded special markers. - /// - /// All the special markers in the file appear in this list, in order of - /// their occurrence in the file (but omitting any markers of types you didn't ask for) - /// - /// The list of loaded special markers. - /// Special markers - public ReadOnlyCollection Marker_list - { - get - { - return m_marker_list.AsReadOnly(); - } - } - - /* Decompression processing parameters --- these fields must be set before - * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes - * them to default values. - */ - - /// - /// Gets or sets the output color space. - /// - /// The output color space. - /// Decompression parameter selection - public LibJpeg.Classic.J_COLOR_SPACE Out_color_space - { - get { return m_out_color_space; } - set { m_out_color_space = value; } - } - - /// - /// Gets or sets the numerator of the fraction of image scaling. - /// - /// Scale the image by the fraction Scale_num/Scale_denom. - /// Default is 1/1, or no scaling. Currently, the only supported scaling ratios are 1/1, 1/2, 1/4, and 1/8. - /// (The library design allows for arbitrary scaling ratios but this is not likely to be implemented any time soon.) - /// - /// Smaller scaling ratios permit significantly faster decoding since fewer pixels - /// need to be processed and a simpler DCT method can be used. - /// - /// Decompression parameter selection - public int Scale_num - { - get { return m_scale_num; } - set { m_scale_num = value; } - } - - /// - /// Gets or sets the denominator of the fraction of image scaling. - /// - /// Scale the image by the fraction Scale_num/Scale_denom. - /// Default is 1/1, or no scaling. Currently, the only supported scaling ratios are 1/1, 1/2, 1/4, and 1/8. - /// (The library design allows for arbitrary scaling ratios but this is not likely to be implemented any time soon.) - /// - /// Smaller scaling ratios permit significantly faster decoding since fewer pixels - /// need to be processed and a simpler DCT method can be used. - /// - /// Decompression parameter selection - public int Scale_denom - { - get { return m_scale_denom; } - set { m_scale_denom = value; } - } - - /// - /// Gets or sets a value indicating whether to use buffered-image mode. - /// - /// true if buffered-image mode is turned on; otherwise, false. - /// Buffered-image mode - public bool Buffered_image - { - get { return m_buffered_image; } - set { m_buffered_image = value; } - } - - /// - /// Enable or disable raw data output. - /// - /// true if raw data output is enabled; otherwise, false. - /// Default value: false
- /// Set this to true before - /// if you need to obtain raw data output. - ///
- /// - public bool Raw_data_out - { - get { return m_raw_data_out; } - set { m_raw_data_out = value; } - } - - /// - /// Gets or sets the algorithm used for the DCT step. - /// - /// The algorithm used for the DCT step. - /// Decompression parameter selection - public LibJpeg.Classic.J_DCT_METHOD Dct_method - { - get { return m_dct_method; } - set { m_dct_method = value; } - } - - /// - /// Enable or disable upsampling of chroma components. - /// - /// If true, do careful upsampling of chroma components. - /// If false, a faster but sloppier method is used. - /// The visual impact of the sloppier method is often very small. - /// - /// Default value: true - /// Decompression parameter selection - public bool Do_fancy_upsampling - { - get { return m_do_fancy_upsampling; } - set { m_do_fancy_upsampling = value; } - } - - /// - /// Apply interblock smoothing in early stages of decoding progressive JPEG files. - /// - /// If true, interblock smoothing is applied in early stages of decoding progressive JPEG files; - /// if false, not. Early progression stages look "fuzzy" with smoothing, "blocky" without. - /// Default value: true
- /// In any case, block smoothing ceases to be applied after the first few AC coefficients are - /// known to full accuracy, so it is relevant only when using - /// buffered-image mode for progressive images. - ///
- /// Decompression parameter selection - public bool Do_block_smoothing - { - get { return m_do_block_smoothing; } - set { m_do_block_smoothing = value; } - } - - /// - /// Colors quantization. - /// - /// If set true, colormapped output will be delivered.
- /// Default value: false, meaning that full-color output will be delivered. - ///
- /// Decompression parameter selection - public bool Quantize_colors - { - get { return m_quantize_colors; } - set { m_quantize_colors = value; } - } - - /* the following are ignored if not quantize_colors: */ - - /// - /// Selects color dithering method. - /// - /// Default value: . - /// Ignored if is false.
- /// At present, ordered dither is implemented only in the single-pass, standard-colormap case. - /// If you ask for ordered dither when is true - /// or when you supply an external color map, you'll get F-S dithering. - ///
- /// - /// Decompression parameter selection - public LibJpeg.Classic.J_DITHER_MODE Dither_mode - { - get { return m_dither_mode; } - set { m_dither_mode = value; } - } - - /// - /// Gets or sets a value indicating whether to use two-pass color quantization. - /// - /// If true, an extra pass over the image is made to select a custom color map for the image. - /// This usually looks a lot better than the one-size-fits-all colormap that is used otherwise. - /// Ignored when the application supplies its own color map.
- /// - /// Default value: true - ///
- /// Ignored if is false.
- ///
- /// - /// Decompression parameter selection - public bool Two_pass_quantize - { - get { return m_two_pass_quantize; } - set { m_two_pass_quantize = value; } - } - - /// - /// Maximum number of colors to use in generating a library-supplied color map. - /// - /// Default value: 256. - /// Ignored if is false.
- /// The actual number of colors is returned in a . - ///
- /// - /// Decompression parameter selection - public int Desired_number_of_colors - { - get { return m_desired_number_of_colors; } - set { m_desired_number_of_colors = value; } - } - - /* these are significant only in buffered-image mode: */ - - /// - /// Enable future use of 1-pass quantizer. - /// - /// Default value: false - /// Significant only in buffered-image mode. - /// Buffered-image mode - public bool Enable_1pass_quant - { - get { return m_enable_1pass_quant; } - set { m_enable_1pass_quant = value; } - } - - /// - /// Enable future use of external colormap. - /// - /// Default value: false - /// Significant only in buffered-image mode. - /// Buffered-image mode - public bool Enable_external_quant - { - get { return m_enable_external_quant; } - set { m_enable_external_quant = value; } - } - - /// - /// Enable future use of 2-pass quantizer. - /// - /// Default value: false - /// Significant only in buffered-image mode. - /// Buffered-image mode - public bool Enable_2pass_quant - { - get { return m_enable_2pass_quant; } - set { m_enable_2pass_quant = value; } - } - - /* Description of actual output image that will be returned to application. - * These fields are computed by jpeg_start_decompress(). - * You can also use jpeg_calc_output_dimensions() to determine these values - * in advance of calling jpeg_start_decompress(). - */ - - /// - /// Gets the actual width of output image. - /// - /// The width of output image. - /// Computed by . - /// You can also use to determine this value - /// in advance of calling . - /// - public int Output_width - { - get { return m_output_width; } - } - - /// - /// Gets the actual height of output image. - /// - /// The height of output image. - /// Computed by . - /// You can also use to determine this value - /// in advance of calling . - /// - public int Output_height - { - get { return m_output_height; } - } - - /// - /// Gets the number of color components in . - /// - /// Computed by . - /// You can also use to determine this value - /// in advance of calling . - /// The number of color components. - /// - /// Decompression parameter selection - public int Out_color_components - { - get { return m_out_color_components; } - } - - /// - /// Gets the number of color components returned. - /// - /// Computed by . - /// You can also use to determine this value - /// in advance of calling . - /// When quantizing colors, - /// Output_components is 1, indicating a single color map index per pixel. - /// Otherwise it equals to . - /// - /// - /// Decompression parameter selection - public int Output_components - { - get { return m_output_components; } - } - - /// - /// Gets the recommended height of scanline buffer. - /// - /// In high-quality modes, Rec_outbuf_height is always 1, but some faster, - /// lower-quality modes set it to larger values (typically 2 to 4). - /// Computed by . - /// You can also use to determine this value - /// in advance of calling .
- /// - /// Rec_outbuf_height is the recommended minimum height (in scanlines) - /// of the buffer passed to . - /// If the buffer is smaller, the library will still work, but time will be wasted due - /// to unnecessary data copying. If you are going to ask for a high-speed processing mode, - /// you may as well go to the trouble of honoring Rec_outbuf_height so as to avoid data copying. - /// (An output buffer larger than Rec_outbuf_height lines is OK, but won't provide - /// any material speed improvement over that height.) - ///
- /// Decompression parameter selection - public int Rec_outbuf_height - { - get { return m_rec_outbuf_height; } - } - - /* When quantizing colors, the output colormap is described by these fields. - * The application can supply a colormap by setting colormap non-null before - * calling jpeg_start_decompress; otherwise a colormap is created during - * jpeg_start_decompress or jpeg_start_output. - * The map has out_color_components rows and actual_number_of_colors columns. - */ - - /// - /// The number of colors in the color map. - /// - /// The number of colors in the color map. - /// - /// Decompression parameter selection - public int Actual_number_of_colors - { - get { return m_actual_number_of_colors; } - set { m_actual_number_of_colors = value; } - } - - /// - /// The color map, represented as a 2-D pixel array of rows - /// and columns. - /// - /// Colormap is set to null by . - /// The application can supply a color map by setting Colormap non-null and setting - /// to the map size. - /// - /// Ignored if not quantizing.
- /// Implementation restriction: at present, an externally supplied Colormap - /// is only accepted for 3-component output color spaces. - ///
- /// - /// - /// Decompression parameter selection - public byte[][] Colormap - { - get { return m_colormap; } - set { m_colormap = value; } - } - - /* State variables: these variables indicate the progress of decompression. - * The application may examine these but must not modify them. - */ - - /* Row index of next scanline to be read from jpeg_read_scanlines(). - * Application may use this to control its processing loop, e.g., - * "while (output_scanline < output_height)". - */ - - /// - /// Gets the number of scanlines returned so far. - /// - /// The output_scanline. - /// Usually you can just use this variable as the loop counter, - /// so that the loop test looks like - /// while (cinfo.Output_scanline < cinfo.Output_height) - /// Decompression details - public int Output_scanline - { - get { return m_output_scanline; } - } - - /* Current input scan number and number of iMCU rows completed in scan. - * These indicate the progress of the decompressor input side. - */ - - /// - /// Gets the number of SOS markers seen so far. - /// - /// The number of SOS markers seen so far. - /// Indicates the progress of the decompressor input side. - public int Input_scan_number - { - get { return m_input_scan_number; } - } - - /// - /// Gets the number of iMCU rows completed. - /// - /// The number of iMCU rows completed. - /// Indicates the progress of the decompressor input side. - public int Input_iMCU_row - { - get { return m_input_iMCU_row; } - } - - /* The "output scan number" is the notional scan being displayed by the - * output side. The decompressor will not allow output scan/row number - * to get ahead of input scan/row, but it can fall arbitrarily far behind. - */ - - /// - /// Gets the nominal scan number being displayed. - /// - /// The nominal scan number being displayed. - public int Output_scan_number - { - get { return m_output_scan_number; } - } - - /// - /// Gets the number of iMCU rows read. - /// - /// The number of iMCU rows read. - public int Output_iMCU_row - { - get { return m_output_iMCU_row; } - } - - /* Current progression status. coef_bits[c][i] indicates the precision - * with which component c's DCT coefficient i (in zigzag order) is known. - * It is -1 when no data has yet been received, otherwise it is the point - * transform (shift) value for the most recent scan of the coefficient - * (thus, 0 at completion of the progression). - * This is null when reading a non-progressive file. - */ - - /// - /// Gets the current progression status.. - /// - /// Coef_bits[c][i] indicates the precision with - /// which component c's DCT coefficient i (in zigzag order) is known. - /// It is -1 when no data has yet been received, otherwise - /// it is the point transform (shift) value for the most recent scan of the coefficient - /// (thus, 0 at completion of the progression). This is null when reading a non-progressive file. - /// - /// Progressive JPEG support - public int[][] Coef_bits - { - get { return m_coef_bits; } - } - - // These fields record data obtained from optional markers - // recognized by the JPEG library. - - /// - /// Gets the resolution information from JFIF marker. - /// - /// The information from JFIF marker. - /// - /// - /// Decompression parameter selection - public DensityUnit Density_unit - { - get { return m_density_unit; } - } - - /// - /// Gets the horizontal component of pixel ratio. - /// - /// The horizontal component of pixel ratio. - /// - /// - public short X_density - { - get { return m_X_density; } - } - - /// - /// Gets the vertical component of pixel ratio. - /// - /// The vertical component of pixel ratio. - /// - /// - public short Y_density - { - get { return m_Y_density; } - } - - /// - /// Gets the data precision. - /// - /// The data precision. - public int Data_precision - { - get { return m_data_precision; } - //set { m_data_precision = value; } - } - - /// - /// Gets the largest vertical sample factor. - /// - /// The largest vertical sample factor. - public int Max_v_samp_factor - { - get { return m_max_v_samp_factor; } - //set { m_max_v_samp_factor = value; } - } - - /// - /// Gets the last read and unprocessed JPEG marker. - /// - /// It is either zero or the code of a JPEG marker that has been - /// read from the data source, but has not yet been processed. - /// - /// - /// Special markers - public int Unread_marker - { - get { return m_unread_marker; } - } - - /// - /// Comp_info[i] describes component that appears i'th in SOF - /// - /// The components in SOF. - /// - public jpeg_component_info[] Comp_info - { - get { return m_comp_info; } - internal set { m_comp_info = value; } - } - - /// - /// Sets input stream. - /// - /// The input stream. - /// - /// The caller must have already opened the stream, and is responsible - /// for closing it after finishing decompression. - /// - /// Decompression details - public void jpeg_stdio_src(Stream infile) - { - /* The source object and input buffer are made permanent so that a series - * of JPEG images can be read from the same file by calling jpeg_stdio_src - * only before the first one. (If we discarded the buffer at the end of - * one image, we'd likely lose the start of the next one.) - * This makes it unsafe to use this manager and a different source - * manager serially with the same JPEG object. Caveat programmer. - */ - if (m_src == null) - { - /* first time for this JPEG object? */ - m_src = new my_source_mgr(this); - } - - my_source_mgr m = m_src as my_source_mgr; - if (m != null) - m.Attach(infile); - } - - /// - /// Decompression startup: this will read the source datastream header markers, up to the beginning of the compressed data proper. - /// - /// Read a description of Return Value. - /// - /// If you pass require_image=true (normal case), you need not check for a - /// return code; an abbreviated file will cause - /// an error exit. is only possible if you use a data source - /// module that can give a suspension return.

- /// - /// This method will read as far as the first SOS marker (ie, actual start of compressed data), - /// and will save all tables and parameters in the JPEG object. It will also initialize the - /// decompression parameters to default values, and finally return . - /// On return, the application may adjust the decompression parameters and then call - /// . (Or, if the application only wanted to - /// determine the image parameters, the data need not be decompressed. In that case, call - /// to release any temporary space.)

- /// - /// If an abbreviated (tables only) datastream is presented, the routine will return - /// upon reaching EOI. The application may then re-use - /// the JPEG object to read the abbreviated image datastream(s). It is unnecessary (but OK) to call - /// jpeg_abort in this case. - /// The return code only occurs if the data source module - /// requests suspension of the decompressor. In this case the application should load more source - /// data and then re-call jpeg_read_header to resume processing.

- /// - /// If a non-suspending data source is used and require_image is true, - /// then the return code need not be inspected since only is possible. - ///
- /// Need only initialize JPEG object and supply a data source before calling.
- /// On return, the image dimensions and other info have been stored in the JPEG object. - /// The application may wish to consult this information before selecting decompression parameters.
- /// This routine is now just a front end to , with some extra error checking. - ///
- /// Decompression details - /// Decompression parameter selection - public ReadResult jpeg_read_header(bool require_image) - { - if (m_global_state != JpegState.DSTATE_START && m_global_state != JpegState.DSTATE_INHEADER) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - ReadResult retcode = jpeg_consume_input(); - - switch (retcode) - { - case ReadResult.JPEG_REACHED_SOS: - return ReadResult.JPEG_HEADER_OK; - case ReadResult.JPEG_REACHED_EOI: - if (require_image) /* Complain if application wanted an image */ - ERREXIT(J_MESSAGE_CODE.JERR_NO_IMAGE); - /* Reset to start state; it would be safer to require the application to - * call jpeg_abort, but we can't change it now for compatibility reasons. - * A side effect is to free any temporary memory (there shouldn't be any). - */ - jpeg_abort(); /* sets state = DSTATE_START */ - return ReadResult.JPEG_HEADER_TABLES_ONLY; - - case ReadResult.JPEG_SUSPENDED: - /* no work */ - break; - } - - return ReadResult.JPEG_SUSPENDED; - } - - ////////////////////////////////////////////////////////////////////////// - // Main entry points for decompression - - /// - /// Decompression initialization. - /// - /// Returns false if suspended. The return value need be inspected - /// only if a suspending data source is used. - /// - /// jpeg_read_header must be completed before calling this.
- /// - /// If a multipass operating mode was selected, this will do all but the last pass, and thus may take a great deal of time. - ///
- /// - /// Decompression details - public bool jpeg_start_decompress() - { - if (m_global_state == JpegState.DSTATE_READY) - { - /* First call: initialize master control, select active modules */ - m_master = new jpeg_decomp_master(this); - if (m_buffered_image) - { - /* No more work here; expecting jpeg_start_output next */ - m_global_state = JpegState.DSTATE_BUFIMAGE; - return true; - } - m_global_state = JpegState.DSTATE_PRELOAD; - } - - if (m_global_state == JpegState.DSTATE_PRELOAD) - { - /* If file has multiple scans, absorb them all into the coef buffer */ - if (m_inputctl.HasMultipleScans()) - { - for ( ; ; ) - { - ReadResult retcode; - /* Call progress monitor hook if present */ - if (m_progress != null) - m_progress.Updated(); - - /* Absorb some more input */ - retcode = m_inputctl.consume_input(); - if (retcode == ReadResult.JPEG_SUSPENDED) - return false; - - if (retcode == ReadResult.JPEG_REACHED_EOI) - break; - - /* Advance progress counter if appropriate */ - if (m_progress != null && (retcode == ReadResult.JPEG_ROW_COMPLETED || retcode == ReadResult.JPEG_REACHED_SOS)) - { - m_progress.Pass_counter++; - if (m_progress.Pass_counter >= m_progress.Pass_limit) - { - /* underestimated number of scans; ratchet up one scan */ - m_progress.Pass_limit += m_total_iMCU_rows; - } - } - } - } - - m_output_scan_number = m_input_scan_number; - } - else if (m_global_state != JpegState.DSTATE_PRESCAN) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - /* Perform any dummy output passes, and set up for the final pass */ - return output_pass_setup(); - } - - /// - /// Read some scanlines of data from the JPEG decompressor. - /// - /// Buffer for filling. - /// Required number of lines. - /// The return value will be the number of lines actually read. - /// This may be less than the number requested in several cases, including - /// bottom of image, data source suspension, and operating modes that emit multiple scanlines at a time. - /// - /// We warn about excess calls to jpeg_read_scanlines since this likely signals an - /// application programmer error. However, an oversize buffer (max_lines > scanlines remaining) - /// is not an error. - /// - /// Decompression details - public int jpeg_read_scanlines(byte[][] scanlines, int max_lines) - { - if (m_global_state != JpegState.DSTATE_SCANNING) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - if (m_output_scanline >= m_output_height) - { - WARNMS(J_MESSAGE_CODE.JWRN_TOO_MUCH_DATA); - return 0; - } - - /* Call progress monitor hook if present */ - if (m_progress != null) - { - m_progress.Pass_counter = m_output_scanline; - m_progress.Pass_limit = m_output_height; - m_progress.Updated(); - } - - /* Process some data */ - int row_ctr = 0; - m_main.process_data(scanlines, ref row_ctr, max_lines); - m_output_scanline += row_ctr; - return row_ctr; - } - - /// - /// Finish JPEG decompression. - /// - /// Returns false if suspended. The return value need be inspected - /// only if a suspending data source is used. - /// - /// This will normally just verify the file trailer and release temp storage. - /// - /// Decompression details - public bool jpeg_finish_decompress() - { - if ((m_global_state == JpegState.DSTATE_SCANNING || m_global_state == JpegState.DSTATE_RAW_OK) && !m_buffered_image) - { - /* Terminate final pass of non-buffered mode */ - if (m_output_scanline < m_output_height) - ERREXIT(J_MESSAGE_CODE.JERR_TOO_LITTLE_DATA); - - m_master.finish_output_pass(); - m_global_state = JpegState.DSTATE_STOPPING; - } - else if (m_global_state == JpegState.DSTATE_BUFIMAGE) - { - /* Finishing after a buffered-image operation */ - m_global_state = JpegState.DSTATE_STOPPING; - } - else if (m_global_state != JpegState.DSTATE_STOPPING) - { - /* STOPPING = repeat call after a suspension, anything else is error */ - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - } - - /* Read until EOI */ - while (!m_inputctl.EOIReached()) - { - if (m_inputctl.consume_input() == ReadResult.JPEG_SUSPENDED) - { - /* Suspend, come back later */ - return false; - } - } - - /* Do final cleanup */ - m_src.term_source(); - - /* We can use jpeg_abort to release memory and reset global_state */ - jpeg_abort(); - return true; - } - - /// - /// Alternate entry point to read raw data. - /// - /// The raw data. - /// The number of scanlines for reading. - /// The number of lines actually read. - /// Replaces jpeg_read_scanlines - /// when reading raw downsampled data. Processes exactly one iMCU row per call, unless suspended. - /// - public int jpeg_read_raw_data(byte[][][] data, int max_lines) - { - if (m_global_state != JpegState.DSTATE_RAW_OK) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - if (m_output_scanline >= m_output_height) - { - WARNMS(J_MESSAGE_CODE.JWRN_TOO_MUCH_DATA); - return 0; - } - - /* Call progress monitor hook if present */ - if (m_progress != null) - { - m_progress.Pass_counter = m_output_scanline; - m_progress.Pass_limit = m_output_height; - m_progress.Updated(); - } - - /* Verify that at least one iMCU row can be returned. */ - int lines_per_iMCU_row = m_max_v_samp_factor * m_min_DCT_scaled_size; - if (max_lines < lines_per_iMCU_row) - ERREXIT(J_MESSAGE_CODE.JERR_BUFFER_SIZE); - - int componentCount = data.Length; // maybe we should use max_lines here - ComponentBuffer[] cb = new ComponentBuffer[componentCount]; - for (int i = 0; i < componentCount; i++) - { - cb[i] = new ComponentBuffer(); - cb[i].SetBuffer(data[i], null, 0); - } - - /* Decompress directly into user's buffer. */ - if (m_coef.decompress_data(cb) == ReadResult.JPEG_SUSPENDED) - { - /* suspension forced, can do nothing more */ - return 0; - } - - /* OK, we processed one iMCU row. */ - m_output_scanline += lines_per_iMCU_row; - return lines_per_iMCU_row; - } - - ////////////////////////////////////////////////////////////////////////// - // Additional entry points for buffered-image mode. - - /// - /// Is there more than one scan? - /// - /// true if image has more than one scan; otherwise, false - /// If you are concerned about maximum performance on baseline JPEG files, - /// you should use buffered-image mode only - /// when the incoming file actually has multiple scans. This can be tested by calling this method. - /// - public bool jpeg_has_multiple_scans() - { - /* Only valid after jpeg_read_header completes */ - if (m_global_state < JpegState.DSTATE_READY || m_global_state > JpegState.DSTATE_STOPPING) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - return m_inputctl.HasMultipleScans(); - } - - /// - /// Initialize for an output pass in buffered-image mode. - /// - /// Indicates which scan of the input file is to be displayed; - /// the scans are numbered starting at 1 for this purpose. - /// true if done; false if suspended - /// - /// Buffered-image mode - public bool jpeg_start_output(int scan_number) - { - if (m_global_state != JpegState.DSTATE_BUFIMAGE && m_global_state != JpegState.DSTATE_PRESCAN) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - /* Limit scan number to valid range */ - if (scan_number <= 0) - scan_number = 1; - - if (m_inputctl.EOIReached() && scan_number > m_input_scan_number) - scan_number = m_input_scan_number; - - m_output_scan_number = scan_number; - /* Perform any dummy output passes, and set up for the real pass */ - return output_pass_setup(); - } - - /// - /// Finish up after an output pass in buffered-image mode. - /// - /// Returns false if suspended. The return value need be inspected only if a suspending data source is used. - /// - /// Buffered-image mode - public bool jpeg_finish_output() - { - if ((m_global_state == JpegState.DSTATE_SCANNING || m_global_state == JpegState.DSTATE_RAW_OK) && m_buffered_image) - { - /* Terminate this pass. */ - /* We do not require the whole pass to have been completed. */ - m_master.finish_output_pass(); - m_global_state = JpegState.DSTATE_BUFPOST; - } - else if (m_global_state != JpegState.DSTATE_BUFPOST) - { - /* BUFPOST = repeat call after a suspension, anything else is error */ - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - } - - /* Read markers looking for SOS or EOI */ - while (m_input_scan_number <= m_output_scan_number && !m_inputctl.EOIReached()) - { - if (m_inputctl.consume_input() == ReadResult.JPEG_SUSPENDED) - { - /* Suspend, come back later */ - return false; - } - } - - m_global_state = JpegState.DSTATE_BUFIMAGE; - return true; - } - - /// - /// Indicates if we have finished reading the input file. - /// - /// true if we have finished reading the input file. - /// Buffered-image mode - public bool jpeg_input_complete() - { - /* Check for valid jpeg object */ - if (m_global_state < JpegState.DSTATE_START || m_global_state > JpegState.DSTATE_STOPPING) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - return m_inputctl.EOIReached(); - } - - /// - /// Consume data in advance of what the decompressor requires. - /// - /// The result of data consumption. - /// This routine can be called at any time after initializing the JPEG object. - /// It reads some additional data and returns when one of the indicated significant events - /// occurs. If called after the EOI marker is reached, it will immediately return - /// without attempting to read more data. - public ReadResult jpeg_consume_input() - { - ReadResult retcode = ReadResult.JPEG_SUSPENDED; - - /* NB: every possible DSTATE value should be listed in this switch */ - switch (m_global_state) - { - case JpegState.DSTATE_START: - jpeg_consume_input_start(); - retcode = jpeg_consume_input_inHeader(); - break; - case JpegState.DSTATE_INHEADER: - retcode = jpeg_consume_input_inHeader(); - break; - case JpegState.DSTATE_READY: - /* Can't advance past first SOS until start_decompress is called */ - retcode = ReadResult.JPEG_REACHED_SOS; - break; - case JpegState.DSTATE_PRELOAD: - case JpegState.DSTATE_PRESCAN: - case JpegState.DSTATE_SCANNING: - case JpegState.DSTATE_RAW_OK: - case JpegState.DSTATE_BUFIMAGE: - case JpegState.DSTATE_BUFPOST: - case JpegState.DSTATE_STOPPING: - retcode = m_inputctl.consume_input(); - break; - default: - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - break; - } - return retcode; - } - - /// - /// Pre-calculate output image dimensions and related values for current decompression parameters. - /// - /// This is allowed for possible use by application. Hence it mustn't do anything - /// that can't be done twice. Also note that it may be called before the master module is initialized! - /// - public void jpeg_calc_output_dimensions() - { - // Do computations that are needed before master selection phase - /* Prevent application from calling me at wrong times */ - if (m_global_state != JpegState.DSTATE_READY) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - - /* Compute actual output image dimensions and DCT scaling choices. */ - if (m_scale_num * 8 <= m_scale_denom) - { - /* Provide 1/8 scaling */ - m_output_width = JpegUtils.jdiv_round_up(m_image_width, 8); - m_output_height = JpegUtils.jdiv_round_up(m_image_height, 8); - m_min_DCT_scaled_size = 1; - } - else if (m_scale_num * 4 <= m_scale_denom) - { - /* Provide 1/4 scaling */ - m_output_width = JpegUtils.jdiv_round_up(m_image_width, 4); - m_output_height = JpegUtils.jdiv_round_up(m_image_height, 4); - m_min_DCT_scaled_size = 2; - } - else if (m_scale_num * 2 <= m_scale_denom) - { - /* Provide 1/2 scaling */ - m_output_width = JpegUtils.jdiv_round_up(m_image_width, 2); - m_output_height = JpegUtils.jdiv_round_up(m_image_height, 2); - m_min_DCT_scaled_size = 4; - } - else - { - /* Provide 1/1 scaling */ - m_output_width = m_image_width; - m_output_height = m_image_height; - m_min_DCT_scaled_size = JpegConstants.DCTSIZE; - } - - /* In selecting the actual DCT scaling for each component, we try to - * scale up the chroma components via IDCT scaling rather than upsampling. - * This saves time if the upsampler gets to use 1:1 scaling. - * Note this code assumes that the supported DCT scalings are powers of 2. - */ - for (int ci = 0; ci < m_num_components; ci++) - { - int ssize = m_min_DCT_scaled_size; - while (ssize < JpegConstants.DCTSIZE && - (m_comp_info[ci].H_samp_factor * ssize * 2 <= m_max_h_samp_factor * m_min_DCT_scaled_size) && - (m_comp_info[ci].V_samp_factor * ssize * 2 <= m_max_v_samp_factor * m_min_DCT_scaled_size)) - { - ssize = ssize * 2; - } - - m_comp_info[ci].DCT_scaled_size = ssize; - } - - /* Recompute downsampled dimensions of components; - * application needs to know these if using raw downsampled data. - */ - for (int ci = 0; ci < m_num_components; ci++) - { - /* Size in samples, after IDCT scaling */ - m_comp_info[ci].downsampled_width = JpegUtils.jdiv_round_up( - m_image_width * m_comp_info[ci].H_samp_factor * m_comp_info[ci].DCT_scaled_size, - m_max_h_samp_factor * JpegConstants.DCTSIZE); - - m_comp_info[ci].downsampled_height = JpegUtils.jdiv_round_up( - m_image_height * m_comp_info[ci].V_samp_factor * m_comp_info[ci].DCT_scaled_size, - m_max_v_samp_factor * JpegConstants.DCTSIZE); - } - - /* Report number of components in selected colorspace. */ - /* Probably this should be in the color conversion module... */ - switch (m_out_color_space) - { - case J_COLOR_SPACE.JCS_GRAYSCALE: - m_out_color_components = 1; - break; - case J_COLOR_SPACE.JCS_RGB: - case J_COLOR_SPACE.JCS_YCbCr: - m_out_color_components = 3; - break; - case J_COLOR_SPACE.JCS_CMYK: - case J_COLOR_SPACE.JCS_YCCK: - m_out_color_components = 4; - break; - default: - /* else must be same colorspace as in file */ - m_out_color_components = m_num_components; - break; - } - - m_output_components = (m_quantize_colors ? 1 : m_out_color_components); - - /* See if upsampler will want to emit more than one row at a time */ - if (use_merged_upsample()) - m_rec_outbuf_height = m_max_v_samp_factor; - else - m_rec_outbuf_height = 1; - } - - /// - /// Read or write the raw DCT coefficient arrays from a JPEG file (useful for lossless transcoding). - /// - /// Returns null if suspended. This case need be checked only - /// if a suspending data source is used. - /// - /// - /// jpeg_read_header must be completed before calling this.
- /// - /// The entire image is read into a set of virtual coefficient-block arrays, one per component. - /// The return value is an array of virtual-array descriptors.
- /// - /// An alternative usage is to simply obtain access to the coefficient arrays during a - /// buffered-image mode decompression operation. This is allowed after any - /// jpeg_finish_output call. The arrays can be accessed - /// until jpeg_finish_decompress is called. - /// Note that any call to the library may reposition the arrays, - /// so don't rely on results to stay valid across library calls. - ///
- public jvirt_array[] jpeg_read_coefficients() - { - if (m_global_state == JpegState.DSTATE_READY) - { - /* First call: initialize active modules */ - transdecode_master_selection(); - m_global_state = JpegState.DSTATE_RDCOEFS; - } - - if (m_global_state == JpegState.DSTATE_RDCOEFS) - { - /* Absorb whole file into the coef buffer */ - for ( ; ; ) - { - ReadResult retcode; - /* Call progress monitor hook if present */ - if (m_progress != null) - m_progress.Updated(); - - /* Absorb some more input */ - retcode = m_inputctl.consume_input(); - if (retcode == ReadResult.JPEG_SUSPENDED) - return null; - - if (retcode == ReadResult.JPEG_REACHED_EOI) - break; - - /* Advance progress counter if appropriate */ - if (m_progress != null && (retcode == ReadResult.JPEG_ROW_COMPLETED || retcode == ReadResult.JPEG_REACHED_SOS)) - { - m_progress.Pass_counter++; - if (m_progress.Pass_counter >= m_progress.Pass_limit) - { - /* startup underestimated number of scans; ratchet up one scan */ - m_progress.Pass_limit += m_total_iMCU_rows; - } - } - } - - /* Set state so that jpeg_finish_decompress does the right thing */ - m_global_state = JpegState.DSTATE_STOPPING; - } - - /* At this point we should be in state DSTATE_STOPPING if being used - * standalone, or in state DSTATE_BUFIMAGE if being invoked to get access - * to the coefficients during a full buffered-image-mode decompression. - */ - if ((m_global_state == JpegState.DSTATE_STOPPING || m_global_state == JpegState.DSTATE_BUFIMAGE) && m_buffered_image) - return m_coef.GetCoefArrays(); - - /* Oops, improper usage */ - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)m_global_state); - /* keep compiler happy */ - return null; - } - - /// - /// Initializes the compression object with default parameters, then copy from the source object - /// all parameters needed for lossless transcoding. - /// - /// Target JPEG compression object. - /// Parameters that can be varied without loss (such as scan script and - /// Huffman optimization) are left in their default states. - public void jpeg_copy_critical_parameters(jpeg_compress_struct dstinfo) - { - /* Safety check to ensure start_compress not called yet. */ - if (dstinfo.m_global_state != JpegState.CSTATE_START) - ERREXIT(J_MESSAGE_CODE.JERR_BAD_STATE, (int)dstinfo.m_global_state); - - /* Copy fundamental image dimensions */ - dstinfo.m_image_width = m_image_width; - dstinfo.m_image_height = m_image_height; - dstinfo.m_input_components = m_num_components; - dstinfo.m_in_color_space = m_jpeg_color_space; - - /* Initialize all parameters to default values */ - dstinfo.jpeg_set_defaults(); - - /* jpeg_set_defaults may choose wrong colorspace, eg YCbCr if input is RGB. - * Fix it to get the right header markers for the image colorspace. - */ - dstinfo.jpeg_set_colorspace(m_jpeg_color_space); - dstinfo.m_data_precision = m_data_precision; - dstinfo.m_CCIR601_sampling = m_CCIR601_sampling; - - /* Copy the source's quantization tables. */ - for (int tblno = 0; tblno < JpegConstants.NUM_QUANT_TBLS; tblno++) - { - if (m_quant_tbl_ptrs[tblno] != null) - { - if (dstinfo.m_quant_tbl_ptrs[tblno] == null) - dstinfo.m_quant_tbl_ptrs[tblno] = new JQUANT_TBL(); - - Buffer.BlockCopy(m_quant_tbl_ptrs[tblno].quantval, 0, - dstinfo.m_quant_tbl_ptrs[tblno].quantval, 0, - dstinfo.m_quant_tbl_ptrs[tblno].quantval.Length * sizeof(short)); - - dstinfo.m_quant_tbl_ptrs[tblno].Sent_table = false; - } - } - - /* Copy the source's per-component info. - * Note we assume jpeg_set_defaults has allocated the dest comp_info array. - */ - dstinfo.m_num_components = m_num_components; - if (dstinfo.m_num_components < 1 || dstinfo.m_num_components> JpegConstants.MAX_COMPONENTS) - ERREXIT(J_MESSAGE_CODE.JERR_COMPONENT_COUNT, dstinfo.m_num_components, JpegConstants.MAX_COMPONENTS); - - for (int ci = 0; ci < dstinfo.m_num_components; ci++) - { - dstinfo.Component_info[ci].Component_id = m_comp_info[ci].Component_id; - dstinfo.Component_info[ci].H_samp_factor = m_comp_info[ci].H_samp_factor; - dstinfo.Component_info[ci].V_samp_factor = m_comp_info[ci].V_samp_factor; - dstinfo.Component_info[ci].Quant_tbl_no = m_comp_info[ci].Quant_tbl_no; - - /* Make sure saved quantization table for component matches the qtable - * slot. If not, the input file re-used this qtable slot. - * IJG encoder currently cannot duplicate this. - */ - int tblno = dstinfo.Component_info[ci].Quant_tbl_no; - if (tblno < 0 || tblno >= JpegConstants.NUM_QUANT_TBLS || m_quant_tbl_ptrs[tblno] == null) - ERREXIT(J_MESSAGE_CODE.JERR_NO_QUANT_TABLE, tblno); - - JQUANT_TBL c_quant = m_comp_info[ci].quant_table; - if (c_quant != null) - { - JQUANT_TBL slot_quant = m_quant_tbl_ptrs[tblno]; - for (int coefi = 0; coefi < JpegConstants.DCTSIZE2; coefi++) - { - if (c_quant.quantval[coefi] != slot_quant.quantval[coefi]) - ERREXIT(J_MESSAGE_CODE.JERR_MISMATCHED_QUANT_TABLE, tblno); - } - } - /* Note: we do not copy the source's Huffman table assignments; - * instead we rely on jpeg_set_colorspace to have made a suitable choice. - */ - } - - /* Also copy JFIF version and resolution information, if available. - * Strictly speaking this isn't "critical" info, but it's nearly - * always appropriate to copy it if available. In particular, - * if the application chooses to copy JFIF 1.02 extension markers from - * the source file, we need to copy the version to make sure we don't - * emit a file that has 1.02 extensions but a claimed version of 1.01. - * We will *not*, however, copy version info from mislabeled "2.01" files. - */ - if (m_saw_JFIF_marker) - { - if (m_JFIF_major_version == 1) - { - dstinfo.m_JFIF_major_version = m_JFIF_major_version; - dstinfo.m_JFIF_minor_version = m_JFIF_minor_version; - } - - dstinfo.m_density_unit = m_density_unit; - dstinfo.m_X_density = (short)m_X_density; - dstinfo.m_Y_density = (short)m_Y_density; - } - } - - /// - /// Aborts processing of a JPEG decompression operation. - /// - /// - public void jpeg_abort_decompress() - { - jpeg_abort(); - } - - /// - /// Sets processor for special marker. - /// - /// The marker code. - /// The processor. - /// Allows you to supply your own routine to process - /// COM and/or APPn markers on-the-fly as they are read. - /// - /// Special markers - public void jpeg_set_marker_processor(int marker_code, jpeg_marker_parser_method routine) - { - m_marker.jpeg_set_marker_processor(marker_code, routine); - } - - /// - /// Control saving of COM and APPn markers into Marker_list. - /// - /// The marker type to save (see JPEG_MARKER enumeration).
- /// To arrange to save all the special marker types, you need to call this - /// routine 17 times, for COM and APP0-APP15 markers. - /// If the incoming marker is longer than length_limit data bytes, - /// only length_limit bytes will be saved; this parameter allows you to avoid chewing up memory - /// when you only need to see the first few bytes of a potentially large marker. If you want to save - /// all the data, set length_limit to 0xFFFF; that is enough since marker lengths are only 16 bits. - /// As a special case, setting length_limit to 0 prevents that marker type from being saved at all. - /// (That is the default behavior, in fact.) - /// - /// - /// Special markers - public void jpeg_save_markers(int marker_code, int length_limit) - { - m_marker.jpeg_save_markers(marker_code, length_limit); - } - - /// - /// Determine whether merged upsample/color conversion should be used. - /// CRUCIAL: this must match the actual capabilities of merged upsampler! - /// - internal bool use_merged_upsample() - { - /* Merging is the equivalent of plain box-filter upsampling */ - if (m_do_fancy_upsampling || m_CCIR601_sampling) - return false; - - /* my_upsampler only supports YCC=>RGB color conversion */ - if (m_jpeg_color_space != J_COLOR_SPACE.JCS_YCbCr || m_num_components != 3 || - m_out_color_space != J_COLOR_SPACE.JCS_RGB || m_out_color_components != JpegConstants.RGB_PIXELSIZE) - { - return false; - } - - /* and it only handles 2h1v or 2h2v sampling ratios */ - if (m_comp_info[0].H_samp_factor != 2 || m_comp_info[1].H_samp_factor != 1 || - m_comp_info[2].H_samp_factor != 1 || m_comp_info[0].V_samp_factor > 2 || - m_comp_info[1].V_samp_factor != 1 || m_comp_info[2].V_samp_factor != 1) - { - return false; - } - - /* furthermore, it doesn't work if we've scaled the IDCTs differently */ - if (m_comp_info[0].DCT_scaled_size != m_min_DCT_scaled_size || - m_comp_info[1].DCT_scaled_size != m_min_DCT_scaled_size || - m_comp_info[2].DCT_scaled_size != m_min_DCT_scaled_size) - { - return false; - } - - /* ??? also need to test for upsample-time rescaling, when & if supported */ - /* by golly, it'll work... */ - return true; - } - - /// - /// Initialization of JPEG compression objects. - /// The error manager must already be set up (in case memory manager fails). - /// - private void initialize() - { - /* Zero out pointers to permanent structures. */ - m_progress = null; - m_src = null; - - for (int i = 0; i < JpegConstants.NUM_QUANT_TBLS; i++) - m_quant_tbl_ptrs[i] = null; - - for (int i = 0; i < JpegConstants.NUM_HUFF_TBLS; i++) - { - m_dc_huff_tbl_ptrs[i] = null; - m_ac_huff_tbl_ptrs[i] = null; - } - - /* Initialize marker processor so application can override methods - * for COM, APPn markers before calling jpeg_read_header. - */ - m_marker_list = new List(); - m_marker = new jpeg_marker_reader(this); - - /* And initialize the overall input controller. */ - m_inputctl = new jpeg_input_controller(this); - - /* OK, I'm ready */ - m_global_state = JpegState.DSTATE_START; - } - - /// - /// Master selection of decompression modules for transcoding (that is, reading - /// raw DCT coefficient arrays from an input JPEG file.) - /// This substitutes for initialization of the full decompressor. - /// - private void transdecode_master_selection() - { - /* This is effectively a buffered-image operation. */ - m_buffered_image = true; - - if (m_progressive_mode) - m_entropy = new phuff_entropy_decoder(this); - else - m_entropy = new huff_entropy_decoder(this); - - /* Always get a full-image coefficient buffer. */ - m_coef = new jpeg_d_coef_controller(this, true); - - /* Initialize input side of decompressor to consume first scan. */ - m_inputctl.start_input_pass(); - - /* Initialize progress monitoring. */ - if (m_progress != null) - { - int nscans = 1; - /* Estimate number of scans to set pass_limit. */ - if (m_progressive_mode) - { - /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ - nscans = 2 + 3 * m_num_components; - } - else if (m_inputctl.HasMultipleScans()) - { - /* For a nonprogressive multiscan file, estimate 1 scan per component. */ - nscans = m_num_components; - } - - m_progress.Pass_counter = 0; - m_progress.Pass_limit = m_total_iMCU_rows * nscans; - m_progress.Completed_passes = 0; - m_progress.Total_passes = 1; - } - } - - /// - /// Set up for an output pass, and perform any dummy pass(es) needed. - /// Common subroutine for jpeg_start_decompress and jpeg_start_output. - /// Entry: global_state = DSTATE_PRESCAN only if previously suspended. - /// Exit: If done, returns true and sets global_state for proper output mode. - /// If suspended, returns false and sets global_state = DSTATE_PRESCAN. - /// - private bool output_pass_setup() - { - if (m_global_state != JpegState.DSTATE_PRESCAN) - { - /* First call: do pass setup */ - m_master.prepare_for_output_pass(); - m_output_scanline = 0; - m_global_state = JpegState.DSTATE_PRESCAN; - } - - /* Loop over any required dummy passes */ - while (m_master.IsDummyPass()) - { - /* Crank through the dummy pass */ - while (m_output_scanline < m_output_height) - { - int last_scanline; - /* Call progress monitor hook if present */ - if (m_progress != null) - { - m_progress.Pass_counter = m_output_scanline; - m_progress.Pass_limit = m_output_height; - m_progress.Updated(); - } - - /* Process some data */ - last_scanline = m_output_scanline; - m_main.process_data(null, ref m_output_scanline, 0); - if (m_output_scanline == last_scanline) - { - /* No progress made, must suspend */ - return false; - } - } - - /* Finish up dummy pass, and set up for another one */ - m_master.finish_output_pass(); - m_master.prepare_for_output_pass(); - m_output_scanline = 0; - } - - /* Ready for application to drive output pass through - * jpeg_read_scanlines or jpeg_read_raw_data. - */ - m_global_state = m_raw_data_out ? JpegState.DSTATE_RAW_OK : JpegState.DSTATE_SCANNING; - return true; - } - - /// - /// Set default decompression parameters. - /// - private void default_decompress_parms() - { - /* Guess the input colorspace, and set output colorspace accordingly. */ - /* (Wish JPEG committee had provided a real way to specify this...) */ - /* Note application may override our guesses. */ - switch (m_num_components) - { - case 1: - m_jpeg_color_space = J_COLOR_SPACE.JCS_GRAYSCALE; - m_out_color_space = J_COLOR_SPACE.JCS_GRAYSCALE; - break; - - case 3: - if (m_saw_JFIF_marker) - { - /* JFIF implies YCbCr */ - m_jpeg_color_space = J_COLOR_SPACE.JCS_YCbCr; - } - else if (m_saw_Adobe_marker) - { - switch (m_Adobe_transform) - { - case 0: - m_jpeg_color_space = J_COLOR_SPACE.JCS_RGB; - break; - case 1: - m_jpeg_color_space = J_COLOR_SPACE.JCS_YCbCr; - break; - default: - WARNMS(J_MESSAGE_CODE.JWRN_ADOBE_XFORM, m_Adobe_transform); - m_jpeg_color_space = J_COLOR_SPACE.JCS_YCbCr; /* assume it's YCbCr */ - break; - } - } - else - { - /* Saw no special markers, try to guess from the component IDs */ - int cid0 = m_comp_info[0].Component_id; - int cid1 = m_comp_info[1].Component_id; - int cid2 = m_comp_info[2].Component_id; - - if (cid0 == 1 && cid1 == 2 && cid2 == 3) - { - /* assume JFIF w/out marker */ - m_jpeg_color_space = J_COLOR_SPACE.JCS_YCbCr; - } - else if (cid0 == 82 && cid1 == 71 && cid2 == 66) - { - /* ASCII 'R', 'G', 'B' */ - m_jpeg_color_space = J_COLOR_SPACE.JCS_RGB; - } - else - { - TRACEMS(1, J_MESSAGE_CODE.JTRC_UNKNOWN_IDS, cid0, cid1, cid2); - /* assume it's YCbCr */ - m_jpeg_color_space = J_COLOR_SPACE.JCS_YCbCr; - } - } - /* Always guess RGB is proper output colorspace. */ - m_out_color_space = J_COLOR_SPACE.JCS_RGB; - break; - - case 4: - if (m_saw_Adobe_marker) - { - switch (m_Adobe_transform) - { - case 0: - m_jpeg_color_space = J_COLOR_SPACE.JCS_CMYK; - break; - case 2: - m_jpeg_color_space = J_COLOR_SPACE.JCS_YCCK; - break; - default: - WARNMS(J_MESSAGE_CODE.JWRN_ADOBE_XFORM, m_Adobe_transform); - /* assume it's YCCK */ - m_jpeg_color_space = J_COLOR_SPACE.JCS_YCCK; - break; - } - } - else - { - /* No special markers, assume straight CMYK. */ - m_jpeg_color_space = J_COLOR_SPACE.JCS_CMYK; - } - - m_out_color_space = J_COLOR_SPACE.JCS_CMYK; - break; - - default: - m_jpeg_color_space = J_COLOR_SPACE.JCS_UNKNOWN; - m_out_color_space = J_COLOR_SPACE.JCS_UNKNOWN; - break; - } - - /* Set defaults for other decompression parameters. */ - m_scale_num = 1; /* 1:1 scaling */ - m_scale_denom = 1; - m_buffered_image = false; - m_raw_data_out = false; - m_dct_method = JpegConstants.JDCT_DEFAULT; - m_do_fancy_upsampling = true; - m_do_block_smoothing = true; - m_quantize_colors = false; - - /* We set these in case application only sets quantize_colors. */ - m_dither_mode = J_DITHER_MODE.JDITHER_FS; - m_two_pass_quantize = true; - m_desired_number_of_colors = 256; - m_colormap = null; - - /* Initialize for no mode change in buffered-image mode. */ - m_enable_1pass_quant = false; - m_enable_external_quant = false; - m_enable_2pass_quant = false; - } - - private void jpeg_consume_input_start() - { - /* Start-of-datastream actions: reset appropriate modules */ - m_inputctl.reset_input_controller(); - - /* Initialize application's data source module */ - m_src.init_source(); - m_global_state = JpegState.DSTATE_INHEADER; - } - - private ReadResult jpeg_consume_input_inHeader() - { - ReadResult retcode = m_inputctl.consume_input(); - if (retcode == ReadResult.JPEG_REACHED_SOS) - { - /* Found SOS, prepare to decompress */ - /* Set up default parameters based on header data */ - default_decompress_parms(); - - /* Set global state: ready for start_decompress */ - m_global_state = JpegState.DSTATE_READY; - } - - return retcode; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_destination_mgr.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_destination_mgr.cs deleted file mode 100644 index f3e100c1..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_destination_mgr.cs +++ /dev/null @@ -1,91 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Data destination object for compression. - /// -#if EXPOSE_LIBJPEG - public -#endif - abstract class jpeg_destination_mgr - { - private byte[] m_buffer; - private int m_position; - private int m_free_in_buffer; /* # of byte spaces remaining in buffer */ - - /// - /// Initializes this instance. - /// - public abstract void init_destination(); - - /// - /// Empties output buffer. - /// - /// true if operation succeed; otherwise, false - public abstract bool empty_output_buffer(); - - /// - /// Term_destinations this instance. - /// - public abstract void term_destination(); - - /// - /// Emits a byte. - /// - /// The byte value. - /// true if operation succeed; otherwise, false - public virtual bool emit_byte(int val) - { - m_buffer[m_position] = (byte)val; - m_position++; - - if (--m_free_in_buffer == 0) - { - if (!empty_output_buffer()) - return false; - } - - return true; - } - - /// - /// Initializes the internal buffer. - /// - /// The buffer. - /// The offset. - protected void initInternalBuffer(byte[] buffer, int offset) - { - m_buffer = buffer; - m_free_in_buffer = buffer.Length - offset; - m_position = offset; - } - - /// - /// Gets the number of free bytes in buffer. - /// - /// The number of free bytes in buffer. - protected int freeInBuffer - { - get - { - return m_free_in_buffer; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_error_mgr.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_error_mgr.cs deleted file mode 100644 index 508ab3d5..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_error_mgr.cs +++ /dev/null @@ -1,408 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains simple error-reporting and trace-message routines. - * Many applications will want to override some or all of these routines. - * - * These routines are used by both the compression and decompression code. - */ - -using System; -using System.Collections.Generic; -using System.Text; -using System.Globalization; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Contains simple error-reporting and trace-message routines. - /// - /// This class is used by both the compression and decompression code. - /// Error handling -#if EXPOSE_LIBJPEG - public -#endif - class jpeg_error_mgr - { - // The message ID code and any parameters are saved in fields below. - internal int m_msg_code; - internal object[] m_msg_parm; - - internal int m_trace_level; - internal int m_num_warnings; - - /// - /// Initializes a new instance of the class. - /// - public jpeg_error_mgr() - { - } - - /// - /// Gets or sets the maximum message level that will be displayed. - /// - /// Values are: - /// -1: recoverable corrupt-data warning, may want to abort.
- /// 0: important advisory messages (always display to user).
- /// 1: first level of tracing detail.
- /// 2, 3, ...: successively more detailed tracing messages. - ///
- /// - public int Trace_level - { - get { return m_trace_level; } - set { m_trace_level = value; } - } - - /// - /// Gets the number of corrupt-data warnings. - /// - /// The num_warnings. - /// For recoverable corrupt-data errors, we emit a warning message, but keep going - /// unless emit_message chooses to abort. - /// emit_message should count warnings in Num_warnings. The surrounding application - /// can check for bad data by seeing if Num_warnings is nonzero at the end of processing. - public int Num_warnings - { - get { return m_num_warnings; } - } - - /// - /// Receives control for a fatal error. - /// - /// This method calls output_message - /// and then throws an exception. - /// Error handling - public virtual void error_exit() - { - // Always display the message - output_message(); - - string buffer = format_message(); - throw new Exception(buffer); - } - - /// - /// Conditionally emit a trace or warning message. - /// - /// The message severity level.
- /// Values are:
- /// -1: recoverable corrupt-data warning, may want to abort.
- /// 0: important advisory messages (always display to user).
- /// 1: first level of tracing detail.
- /// 2, 3, ...: successively more detailed tracing messages. - /// - /// The main reason for overriding this method would be to abort on warnings. - /// This method calls output_message for message showing.
- /// - /// An application might override this method if it wanted to abort on - /// warnings or change the policy about which messages to display. - ///
- /// Error handling - public virtual void emit_message(int msg_level) - { - if (msg_level < 0) - { - /* It's a warning message. Since corrupt files may generate many warnings, - * the policy implemented here is to show only the first warning, - * unless trace_level >= 3. - */ - if (m_num_warnings == 0 || m_trace_level >= 3) - output_message(); - - /* Always count warnings in num_warnings. */ - m_num_warnings++; - } - else - { - /* It's a trace message. Show it if trace_level >= msg_level. */ - if (m_trace_level >= msg_level) - output_message(); - } - } - - /// - /// Actual output of any JPEG message. - /// - /// Override this to send messages somewhere other than Console. - /// Note that this method does not know how to generate a message, only where to send it. - /// For extending a generation of messages see format_message. - /// - /// Error handling - public virtual void output_message() - { - // Create the message - string buffer = format_message(); - - // Send it to console, adding a newline */ - Console.WriteLine(buffer); - } - - /// - /// Constructs a readable error message string. - /// - /// This method is called by output_message. - /// Few applications should need to override this method. One possible reason for doing so is to - /// implement dynamic switching of error message language. - /// The formatted message - /// Error handling - public virtual string format_message() - { - string msgtext = GetMessageText(m_msg_code); - if (msgtext == null) - { - m_msg_parm = new object[] { m_msg_code }; - msgtext = GetMessageText(0); - } - - /* Format the message into the passed buffer */ - return string.Format(CultureInfo.CurrentCulture, msgtext, m_msg_parm); - } - - /// - /// Resets error manager to initial state. - /// - /// This is called during compression startup to reset trace/error - /// processing to default state. An application might possibly want to - /// override this method if it has additional error processing state. - /// - public virtual void reset_error_mgr() - { - m_num_warnings = 0; - - /* trace_level is not reset since it is an application-supplied parameter */ - - // may be useful as a flag for "no error" - m_msg_code = 0; - } - - /// - /// Gets the actual message texts. - /// - /// The message code. See for details. - /// The message text associated with code. - /// It may be useful for an application to add its own message texts that are handled - /// by the same mechanism. You can override GetMessageText for this purpose. If you number - /// the addon messages beginning at 1000 or so, you won't have to worry about conflicts - /// with the library's built-in messages. - /// - /// - /// Error handling - protected virtual string GetMessageText(int code) - { - switch ((J_MESSAGE_CODE)code) - { - default: - case J_MESSAGE_CODE.JMSG_NOMESSAGE: - return "Bogus message code {0}"; - - /* For maintenance convenience, list is alphabetical by message code name */ - case J_MESSAGE_CODE.JERR_ARITH_NOTIMPL: - return "Sorry, there are legal restrictions on arithmetic coding"; - case J_MESSAGE_CODE.JERR_BAD_BUFFER_MODE: - return "Bogus buffer control mode"; - case J_MESSAGE_CODE.JERR_BAD_COMPONENT_ID: - return "Invalid component ID {0} in SOS"; - case J_MESSAGE_CODE.JERR_BAD_DCT_COEF: - return "DCT coefficient out of range"; - case J_MESSAGE_CODE.JERR_BAD_DCTSIZE: - return "IDCT output block size {0} not supported"; - case J_MESSAGE_CODE.JERR_BAD_HUFF_TABLE: - return "Bogus Huffman table definition"; - case J_MESSAGE_CODE.JERR_BAD_IN_COLORSPACE: - return "Bogus input colorspace"; - case J_MESSAGE_CODE.JERR_BAD_J_COLORSPACE: - return "Bogus JPEG colorspace"; - case J_MESSAGE_CODE.JERR_BAD_LENGTH: - return "Bogus marker length"; - case J_MESSAGE_CODE.JERR_BAD_MCU_SIZE: - return "Sampling factors too large for interleaved scan"; - case J_MESSAGE_CODE.JERR_BAD_PRECISION: - return "Unsupported JPEG data precision {0}"; - case J_MESSAGE_CODE.JERR_BAD_PROGRESSION: - return "Invalid progressive parameters Ss={0} Se={1} Ah={2} Al={3}"; - case J_MESSAGE_CODE.JERR_BAD_PROG_SCRIPT: - return "Invalid progressive parameters at scan script entry {0}"; - case J_MESSAGE_CODE.JERR_BAD_SAMPLING: - return "Bogus sampling factors"; - case J_MESSAGE_CODE.JERR_BAD_SCAN_SCRIPT: - return "Invalid scan script at entry {0}"; - case J_MESSAGE_CODE.JERR_BAD_STATE: - return "Improper call to JPEG library in state {0}"; - case J_MESSAGE_CODE.JERR_BAD_VIRTUAL_ACCESS: - return "Bogus virtual array access"; - case J_MESSAGE_CODE.JERR_BUFFER_SIZE: - return "Buffer passed to JPEG library is too small"; - case J_MESSAGE_CODE.JERR_CANT_SUSPEND: - return "Suspension not allowed here"; - case J_MESSAGE_CODE.JERR_CCIR601_NOTIMPL: - return "CCIR601 sampling not implemented yet"; - case J_MESSAGE_CODE.JERR_COMPONENT_COUNT: - return "Too many color components: {0}, max {1}"; - case J_MESSAGE_CODE.JERR_CONVERSION_NOTIMPL: - return "Unsupported color conversion request"; - case J_MESSAGE_CODE.JERR_DHT_INDEX: - return "Bogus DHT index {0}"; - case J_MESSAGE_CODE.JERR_DQT_INDEX: - return "Bogus DQT index {0}"; - case J_MESSAGE_CODE.JERR_EMPTY_IMAGE: - return "Empty JPEG image (DNL not supported)"; - case J_MESSAGE_CODE.JERR_EOI_EXPECTED: - return "Didn't expect more than one scan"; - case J_MESSAGE_CODE.JERR_FILE_WRITE: - return "Output file write error --- out of disk space?"; - case J_MESSAGE_CODE.JERR_FRACT_SAMPLE_NOTIMPL: - return "Fractional sampling not implemented yet"; - case J_MESSAGE_CODE.JERR_HUFF_CLEN_OVERFLOW: - return "Huffman code size table overflow"; - case J_MESSAGE_CODE.JERR_HUFF_MISSING_CODE: - return "Missing Huffman code table entry"; - case J_MESSAGE_CODE.JERR_IMAGE_TOO_BIG: - return "Maximum supported image dimension is {0} pixels"; - case J_MESSAGE_CODE.JERR_INPUT_EMPTY: - return "Empty input file"; - case J_MESSAGE_CODE.JERR_INPUT_EOF: - return "Premature end of input file"; - case J_MESSAGE_CODE.JERR_MISMATCHED_QUANT_TABLE: - return "Cannot transcode due to multiple use of quantization table {0}"; - case J_MESSAGE_CODE.JERR_MISSING_DATA: - return "Scan script does not transmit all data"; - case J_MESSAGE_CODE.JERR_MODE_CHANGE: - return "Invalid color quantization mode change"; - case J_MESSAGE_CODE.JERR_NOTIMPL: - return "Not implemented yet"; - case J_MESSAGE_CODE.JERR_NOT_COMPILED: - return "Requested feature was omitted at compile time"; - case J_MESSAGE_CODE.JERR_NO_HUFF_TABLE: - return "Huffman table 0x{0:X2} was not defined"; - case J_MESSAGE_CODE.JERR_NO_IMAGE: - return "JPEG datastream contains no image"; - case J_MESSAGE_CODE.JERR_NO_QUANT_TABLE: - return "Quantization table 0x{0:X2} was not defined"; - case J_MESSAGE_CODE.JERR_NO_SOI: - return "Not a JPEG file: starts with 0x{0:X2} 0x{1:X2}"; - case J_MESSAGE_CODE.JERR_OUT_OF_MEMORY: - return "Insufficient memory (case {0})"; - case J_MESSAGE_CODE.JERR_QUANT_COMPONENTS: - return "Cannot quantize more than {0} color components"; - case J_MESSAGE_CODE.JERR_QUANT_FEW_COLORS: - return "Cannot quantize to fewer than {0} colors"; - case J_MESSAGE_CODE.JERR_QUANT_MANY_COLORS: - return "Cannot quantize to more than {0} colors"; - case J_MESSAGE_CODE.JERR_SOF_DUPLICATE: - return "Invalid JPEG file structure: two SOF markers"; - case J_MESSAGE_CODE.JERR_SOF_NO_SOS: - return "Invalid JPEG file structure: missing SOS marker"; - case J_MESSAGE_CODE.JERR_SOF_UNSUPPORTED: - return "Unsupported JPEG process: SOF type 0x{0:X2}"; - case J_MESSAGE_CODE.JERR_SOI_DUPLICATE: - return "Invalid JPEG file structure: two SOI markers"; - case J_MESSAGE_CODE.JERR_SOS_NO_SOF: - return "Invalid JPEG file structure: SOS before SOF"; - case J_MESSAGE_CODE.JERR_TOO_LITTLE_DATA: - return "Application transferred too few scanlines"; - case J_MESSAGE_CODE.JERR_UNKNOWN_MARKER: - return "Unsupported marker type 0x{0:X2}"; - case J_MESSAGE_CODE.JERR_WIDTH_OVERFLOW: - return "Image too wide for this implementation"; - case J_MESSAGE_CODE.JTRC_16BIT_TABLES: - return "Caution: quantization tables are too coarse for baseline JPEG"; - case J_MESSAGE_CODE.JTRC_ADOBE: - return "Adobe APP14 marker: version {0}, flags 0x{1:X4} 0x{2:X4}, transform {3}"; - case J_MESSAGE_CODE.JTRC_APP0: - return "Unknown APP0 marker (not JFIF), length {0}"; - case J_MESSAGE_CODE.JTRC_APP14: - return "Unknown APP14 marker (not Adobe), length {0}"; - case J_MESSAGE_CODE.JTRC_DHT: - return "Define Huffman Table 0x{0:X2}"; - case J_MESSAGE_CODE.JTRC_DQT: - return "Define Quantization Table {0} precision {1}"; - case J_MESSAGE_CODE.JTRC_DRI: - return "Define Restart Interval {0}"; - case J_MESSAGE_CODE.JTRC_EOI: - return "End Of Image"; - case J_MESSAGE_CODE.JTRC_HUFFBITS: - return " {0:D3} {1:D3} {2:D3} {3:D3} {4:D3} {5:D3} {6:D3} {7:D3}"; - case J_MESSAGE_CODE.JTRC_JFIF: - return "JFIF APP0 marker: version {0}.{1:D2}, density {2}x{3} {4}"; - case J_MESSAGE_CODE.JTRC_JFIF_BADTHUMBNAILSIZE: - return "Warning: thumbnail image size does not match data length {0}"; - case J_MESSAGE_CODE.JTRC_JFIF_EXTENSION: - return "JFIF extension marker: type 0x{0:X2}, length {1}"; - case J_MESSAGE_CODE.JTRC_JFIF_THUMBNAIL: - return " with {0} x {1} thumbnail image"; - case J_MESSAGE_CODE.JTRC_MISC_MARKER: - return "Miscellaneous marker 0x{0:X2}, length {1}"; - case J_MESSAGE_CODE.JTRC_PARMLESS_MARKER: - return "Unexpected marker 0x{0:X2}"; - case J_MESSAGE_CODE.JTRC_QUANTVALS: - return " {0:D4} {1:D4} {2:D4} {3:D4} {4:D4} {5:D4} {6:D4} {7:D4}"; - case J_MESSAGE_CODE.JTRC_QUANT_3_NCOLORS: - return "Quantizing to {0} = {1}*{2}*{3} colors"; - case J_MESSAGE_CODE.JTRC_QUANT_NCOLORS: - return "Quantizing to {0} colors"; - case J_MESSAGE_CODE.JTRC_QUANT_SELECTED: - return "Selected {0} colors for quantization"; - case J_MESSAGE_CODE.JTRC_RECOVERY_ACTION: - return "At marker 0x{0:X2}, recovery action {1}"; - case J_MESSAGE_CODE.JTRC_RST: - return "RST{0}"; - case J_MESSAGE_CODE.JTRC_SMOOTH_NOTIMPL: - return "Smoothing not supported with nonstandard sampling ratios"; - case J_MESSAGE_CODE.JTRC_SOF: - return "Start Of Frame 0x{0:X2}: width={1}, height={2}, components={3}"; - case J_MESSAGE_CODE.JTRC_SOF_COMPONENT: - return " Component {0}: {1}hx{2}v q={3}"; - case J_MESSAGE_CODE.JTRC_SOI: - return "Start of Image"; - case J_MESSAGE_CODE.JTRC_SOS: - return "Start Of Scan: {0} components"; - case J_MESSAGE_CODE.JTRC_SOS_COMPONENT: - return " Component {0}: dc={1} ac={2}"; - case J_MESSAGE_CODE.JTRC_SOS_PARAMS: - return " Ss={0}, Se={1}, Ah={2}, Al={3}"; - case J_MESSAGE_CODE.JTRC_THUMB_JPEG: - return "JFIF extension marker: JPEG-compressed thumbnail image, length {0}"; - case J_MESSAGE_CODE.JTRC_THUMB_PALETTE: - return "JFIF extension marker: palette thumbnail image, length {0}"; - case J_MESSAGE_CODE.JTRC_THUMB_RGB: - return "JFIF extension marker: RGB thumbnail image, length {0}"; - case J_MESSAGE_CODE.JTRC_UNKNOWN_IDS: - return "Unrecognized component IDs {0} {1} {2}, assuming YCbCr"; - case J_MESSAGE_CODE.JWRN_ADOBE_XFORM: - return "Unknown Adobe color transform code {0}"; - case J_MESSAGE_CODE.JWRN_BOGUS_PROGRESSION: - return "Inconsistent progression sequence for component {0} coefficient {1}"; - case J_MESSAGE_CODE.JWRN_EXTRANEOUS_DATA: - return "Corrupt JPEG data: {0} extraneous bytes before marker 0x{1:X2}"; - case J_MESSAGE_CODE.JWRN_HIT_MARKER: - return "Corrupt JPEG data: premature end of data segment"; - case J_MESSAGE_CODE.JWRN_HUFF_BAD_CODE: - return "Corrupt JPEG data: bad Huffman code"; - case J_MESSAGE_CODE.JWRN_JFIF_MAJOR: - return "Warning: unknown JFIF revision number {0}.{1:D2}"; - case J_MESSAGE_CODE.JWRN_JPEG_EOF: - return "Premature end of JPEG file"; - case J_MESSAGE_CODE.JWRN_MUST_RESYNC: - return "Corrupt JPEG data: found marker 0x{0:X2} instead of RST{1}"; - case J_MESSAGE_CODE.JWRN_NOT_SEQUENTIAL: - return "Invalid SOS parameters for sequential JPEG"; - case J_MESSAGE_CODE.JWRN_TOO_MUCH_DATA: - return "Application transferred too many scanlines"; - case J_MESSAGE_CODE.JMSG_UNKNOWNMSGCODE: - return "Unknown message code (possibly it is an error from application)"; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_marker_struct.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_marker_struct.cs deleted file mode 100644 index 3064d36f..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_marker_struct.cs +++ /dev/null @@ -1,88 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Representation of special JPEG marker. - /// - /// You can't create instance of this class manually. - /// Concrete objects are instantiated by library and you can get them - /// through Marker_list property. - /// - /// - /// Special markers -#if EXPOSE_LIBJPEG - public -#endif - class jpeg_marker_struct - { - private byte m_marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ - private int m_originalLength; /* # bytes of data in the file */ - private byte[] m_data; /* the data contained in the marker */ - - internal jpeg_marker_struct(byte marker, int originalDataLength, int lengthLimit) - { - m_marker = marker; - m_originalLength = originalDataLength; - m_data = new byte[lengthLimit]; - } - - /// - /// Gets the special marker. - /// - /// The marker value. - public byte Marker - { - get - { - return m_marker; - } - } - - /// - /// Gets the full length of original data associated with the marker. - /// - /// The length of original data associated with the marker. - /// This length excludes the marker length word, whereas the stored representation - /// within the JPEG file includes it. (Hence the maximum data length is really only 65533.) - /// - public int OriginalLength - { - get - { - return m_originalLength; - } - } - - /// - /// Gets the data associated with the marker. - /// - /// The data associated with the marker. - /// The length of this array doesn't exceed length_limit for the particular marker type. - /// Note that this length excludes the marker length word, whereas the stored representation - /// within the JPEG file includes it. (Hence the maximum data length is really only 65533.) - /// - public byte[] Data - { - get - { - return m_data; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_progress_mgr.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_progress_mgr.cs deleted file mode 100644 index 43f7015e..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_progress_mgr.cs +++ /dev/null @@ -1,95 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// The progress monitor object. - /// - /// Progress monitoring -#if EXPOSE_LIBJPEG - public -#endif - class jpeg_progress_mgr - { - private int m_passCounter; - private int m_passLimit; - private int m_completedPasses; - private int m_totalPasses; - - /// - /// Occurs when progress is changed. - /// - /// Progress monitoring - public event EventHandler OnProgress; - - /// - /// Gets or sets the number of work units completed in this pass. - /// - /// The number of work units completed in this pass. - /// Progress monitoring - public int Pass_counter - { - get { return m_passCounter; } - set { m_passCounter = value; } - } - - /// - /// Gets or sets the total number of work units in this pass. - /// - /// The total number of work units in this pass. - /// Progress monitoring - public int Pass_limit - { - get { return m_passLimit; } - set { m_passLimit = value; } - } - - /// - /// Gets or sets the number of passes completed so far. - /// - /// The number of passes completed so far. - /// Progress monitoring - public int Completed_passes - { - get { return m_completedPasses; } - set { m_completedPasses = value; } - } - - /// - /// Gets or sets the total number of passes expected. - /// - /// The total number of passes expected. - /// Progress monitoring - public int Total_passes - { - get { return m_totalPasses; } - set { m_totalPasses = value; } - } - - /// - /// Indicates that progress was changed. - /// - /// Call this method if you change some progress parameters manually. - /// This method ensures happening of the OnProgress event. - public void Updated() - { - if (OnProgress != null) - OnProgress(this, new EventArgs()); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_source_mgr.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_source_mgr.cs deleted file mode 100644 index cb6f9388..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jpeg_source_mgr.cs +++ /dev/null @@ -1,300 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -using System; -using System.Collections.Generic; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// Data source object for decompression. - /// -#if EXPOSE_LIBJPEG - public -#endif - abstract class jpeg_source_mgr - { - private byte[] m_next_input_byte; - private int m_bytes_in_buffer; /* # of bytes remaining (unread) in buffer */ - private int m_position; - - /// - /// Initializes this instance. - /// - public abstract void init_source(); - - /// - /// Fills input buffer - /// - /// true if operation succeed; otherwise, false - public abstract bool fill_input_buffer(); - - /// - /// Initializes the internal buffer. - /// - /// The buffer. - /// The size. - protected void initInternalBuffer(byte[] buffer, int size) - { - m_bytes_in_buffer = size; - m_next_input_byte = buffer; - m_position = 0; - } - - /// - /// Skip data - used to skip over a potentially large amount of - /// uninteresting data (such as an APPn marker). - /// - /// The number of bytes to skip. - /// Writers of suspendable-input applications must note that skip_input_data - /// is not granted the right to give a suspension return. If the skip extends - /// beyond the data currently in the buffer, the buffer can be marked empty so - /// that the next read will cause a fill_input_buffer call that can suspend. - /// Arranging for additional bytes to be discarded before reloading the input - /// buffer is the application writer's problem. - public virtual void skip_input_data(int num_bytes) - { - /* Just a dumb implementation for now. Could use fseek() except - * it doesn't work on pipes. Not clear that being smart is worth - * any trouble anyway --- large skips are infrequent. - */ - if (num_bytes > 0) - { - while (num_bytes > m_bytes_in_buffer) - { - num_bytes -= m_bytes_in_buffer; - fill_input_buffer(); - /* note we assume that fill_input_buffer will never return false, - * so suspension need not be handled. - */ - } - - m_position += num_bytes; - m_bytes_in_buffer -= num_bytes; - } - } - - /// - /// This is the default resync_to_restart method for data source - /// managers to use if they don't have any better approach. - /// - /// An instance of - /// The desired - /// false if suspension is required. - /// That method assumes that no backtracking is possible. - /// Some data source managers may be able to back up, or may have - /// additional knowledge about the data which permits a more - /// intelligent recovery strategy; such managers would - /// presumably supply their own resync method.

- /// - /// read_restart_marker calls resync_to_restart if it finds a marker other than - /// the restart marker it was expecting. (This code is *not* used unless - /// a nonzero restart interval has been declared.) cinfo.unread_marker is - /// the marker code actually found (might be anything, except 0 or FF). - /// The desired restart marker number (0..7) is passed as a parameter.

- /// - /// This routine is supposed to apply whatever error recovery strategy seems - /// appropriate in order to position the input stream to the next data segment. - /// Note that cinfo.unread_marker is treated as a marker appearing before - /// the current data-source input point; usually it should be reset to zero - /// before returning.

- /// - /// This implementation is substantially constrained by wanting to treat the - /// input as a data stream; this means we can't back up. Therefore, we have - /// only the following actions to work with:
- /// 1. Simply discard the marker and let the entropy decoder resume at next - /// byte of file.
- /// 2. Read forward until we find another marker, discarding intervening - /// data. (In theory we could look ahead within the current bufferload, - /// without having to discard data if we don't find the desired marker. - /// This idea is not implemented here, in part because it makes behavior - /// dependent on buffer size and chance buffer-boundary positions.)
- /// 3. Leave the marker unread (by failing to zero cinfo.unread_marker). - /// This will cause the entropy decoder to process an empty data segment, - /// inserting dummy zeroes, and then we will reprocess the marker.
- /// - /// #2 is appropriate if we think the desired marker lies ahead, while #3 is - /// appropriate if the found marker is a future restart marker (indicating - /// that we have missed the desired restart marker, probably because it got - /// corrupted).
- /// We apply #2 or #3 if the found marker is a restart marker no more than - /// two counts behind or ahead of the expected one. We also apply #2 if the - /// found marker is not a legal JPEG marker code (it's certainly bogus data). - /// If the found marker is a restart marker more than 2 counts away, we do #1 - /// (too much risk that the marker is erroneous; with luck we will be able to - /// resync at some future point).
- /// For any valid non-restart JPEG marker, we apply #3. This keeps us from - /// overrunning the end of a scan. An implementation limited to single-scan - /// files might find it better to apply #2 for markers other than EOI, since - /// any other marker would have to be bogus data in that case.
- public virtual bool resync_to_restart(jpeg_decompress_struct cinfo, int desired) - { - /* Always put up a warning. */ - cinfo.WARNMS(J_MESSAGE_CODE.JWRN_MUST_RESYNC, cinfo.m_unread_marker, desired); - - /* Outer loop handles repeated decision after scanning forward. */ - int action = 1; - for ( ; ; ) - { - if (cinfo.m_unread_marker < (int)JPEG_MARKER.SOF0) - { - /* invalid marker */ - action = 2; - } - else if (cinfo.m_unread_marker < (int)JPEG_MARKER.RST0 || - cinfo.m_unread_marker > (int)JPEG_MARKER.RST7) - { - /* valid non-restart marker */ - action = 3; - } - else - { - if (cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired + 1) & 7)) - || cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired + 2) & 7))) - { - /* one of the next two expected restarts */ - action = 3; - } - else if (cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired - 1) & 7)) || - cinfo.m_unread_marker == ((int)JPEG_MARKER.RST0 + ((desired - 2) & 7))) - { - /* a prior restart, so advance */ - action = 2; - } - else - { - /* desired restart or too far away */ - action = 1; - } - } - - cinfo.TRACEMS(4, J_MESSAGE_CODE.JTRC_RECOVERY_ACTION, cinfo.m_unread_marker, action); - - switch (action) - { - case 1: - /* Discard marker and let entropy decoder resume processing. */ - cinfo.m_unread_marker = 0; - return true; - case 2: - /* Scan to the next marker, and repeat the decision loop. */ - if (!cinfo.m_marker.next_marker()) - return false; - break; - case 3: - /* Return without advancing past this marker. */ - /* Entropy decoder will be forced to process an empty segment. */ - return true; - } - } - } - - /// - /// Terminate source - called by jpeg_finish_decompress - /// after all data has been read. Often a no-op. - /// - /// NB: not called by jpeg_abort or jpeg_destroy; surrounding - /// application must deal with any cleanup that should happen even - /// for error exit. - public virtual void term_source() - { - } - - /// - /// Reads two bytes interpreted as an unsigned 16-bit integer. - /// - /// The result. - /// true if operation succeed; otherwise, false - public virtual bool GetTwoBytes(out int V) - { - if (!MakeByteAvailable()) - { - V = 0; - return false; - } - - m_bytes_in_buffer--; - V = m_next_input_byte[m_position] << 8; - m_position++; - - if (!MakeByteAvailable()) - return false; - - m_bytes_in_buffer--; - V += m_next_input_byte[m_position]; - m_position++; - return true; - } - - /// - /// Read a byte into variable V. - /// If must suspend, take the specified action (typically "return false"). - /// - /// The result. - /// true if operation succeed; otherwise, false - public virtual bool GetByte(out int V) - { - if (!MakeByteAvailable()) - { - V = 0; - return false; - } - - m_bytes_in_buffer--; - V = m_next_input_byte[m_position]; - m_position++; - return true; - } - - /// - /// Gets the bytes. - /// - /// The destination. - /// The amount. - /// The number of available bytes. - public virtual int GetBytes(byte[] dest, int amount) - { - int avail = amount; - if (avail > m_bytes_in_buffer) - avail = m_bytes_in_buffer; - - for (int i = 0; i < avail; i++) - { - dest[i] = m_next_input_byte[m_position]; - m_position++; - m_bytes_in_buffer--; - } - - return avail; - } - - /// - /// Functions for fetching data from the data source module. - /// - /// true if operation succeed; otherwise, false - /// At all times, cinfo.src.next_input_byte and .bytes_in_buffer reflect - /// the current restart point; we update them only when we have reached a - /// suitable place to restart if a suspension occurs. - public virtual bool MakeByteAvailable() - { - if (m_bytes_in_buffer == 0) - { - if (!fill_input_buffer()) - return false; - } - - return true; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jvirt_array.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jvirt_array.cs deleted file mode 100644 index 8ab12e7c..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/LibJpeg/jvirt_array.cs +++ /dev/null @@ -1,109 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * Copyright (C) 1994-1996, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - */ - -/* - * This file contains the JPEG system-independent memory management - * routines. - */ - -/* - * About virtual array management: - * - * Full-image-sized buffers are handled as "virtual" arrays. The array is still accessed a strip at a - * time, but the memory manager must save the whole array for repeated - * accesses. - * - * The Access method is responsible for making a specific strip area accessible. - */ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Text; - -namespace BitMiracle.LibJpeg.Classic -{ - /// - /// JPEG virtual array. - /// - /// The type of array's elements. - /// You can't create virtual array manually. For creation use methods - /// and - /// . - /// -#if EXPOSE_LIBJPEG - public -#endif - class jvirt_array - { - internal delegate T[][] Allocator(int width, int height); - - private jpeg_common_struct m_cinfo; - - private T[][] m_buffer; /* => the in-memory buffer */ - - /// - /// Request a virtual 2-D array - /// - /// Width of array - /// Total virtual array height - /// The allocator. - internal jvirt_array(int width, int height, Allocator allocator) - { - m_cinfo = null; - m_buffer = allocator(width, height); - - Debug.Assert(m_buffer != null); - } - - /// - /// Gets or sets the error processor. - /// - /// The error processor.
- /// Default value: null - ///
- /// Uses only for calling - /// jpeg_common_struct.ERREXIT - /// on error. - public jpeg_common_struct ErrorProcessor - { - get { return m_cinfo; } - set { m_cinfo = value; } - } - - /// - /// Access the part of a virtual array. - /// - /// The first row in required block. - /// The number of required rows. - /// The required part of virtual array. - public T[][] Access(int startRow, int numberOfRows) - { - /* debugging check */ - if (startRow + numberOfRows > m_buffer.Length) - { - if (m_cinfo != null) - m_cinfo.ERREXIT(J_MESSAGE_CODE.JERR_BAD_VIRTUAL_ACCESS); - else - throw new InvalidOperationException("Bogus virtual array access"); - } - - /* Return proper part of the buffer */ - T[][] ret = new T[numberOfRows][]; - for (int i = 0; i < numberOfRows; i++) - ret[i] = m_buffer[startRow + i]; - - return ret; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/Tiff.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/Tiff.cs deleted file mode 100644 index f88f923a..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/Tiff.cs +++ /dev/null @@ -1,5976 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -/* - * Tile-oriented Read Support - * Contributed by Nancy Cam (Silicon Graphics). - */ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Reflection; - -using BitMiracle.LibTiff.Classic.Internal; - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Tag Image File Format (TIFF) - /// - /// - /// Based on Rev 6.0 from - /// - /// -#if EXPOSE_LIBTIFF - public -#endif - partial class Tiff : IDisposable - { - /// - /// Delegate for LibTiff.Net extender method - /// - /// An instance of the class. - /// - /// Extender method is usually used for registering custom tags. - /// To setup extender method that will be called upon creation of - /// each instance of object please use - /// method. - /// - public delegate void TiffExtendProc(Tiff tif); - - /// - /// Delegate for a method used to image decoded spans. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The array of black and white run lengths (white then black). - /// The zero-based offset in array at - /// which current row's run begins. - /// The zero-based offset in array at - /// which next row's run begins. - /// The width in pixels of the row. - /// - /// To override the default method used to image decoded spans please set - /// tag with an instance of this delegate. - /// - /// Fill methods can assume the array has room for at least - /// runs and can overwrite data in the - /// array as needed (e.g. to append zero runs to bring the count up to a nice multiple). - /// - public delegate void FaxFillFunc( - byte[] buffer, int offset, int[] runs, int thisRunOffset, int nextRunOffset, int width); - - /// - /// Gets the library version string. - /// - /// The library version string. - public static string GetVersion() - { - return string.Format(CultureInfo.InvariantCulture, - "LibTiff.Net, Version {0}\nCopyright (C) 2008-2011, Bit Miracle.", AssemblyVersion); - } - - /// - /// Gets the version of the library's assembly. - /// - /// The version of the library's assembly. - public static string AssemblyVersion - { - get - { - Assembly assembly = Assembly.GetExecutingAssembly(); - string assemblyVersion = assembly.FullName.Split(',')[1]; - return assemblyVersion.Split('=')[1]; - } - } - - /// - /// Gets the R component from ABGR value returned by - /// ReadRGBAImage. - /// - /// The ABGR value. - /// The R component from ABGR value. - public static int GetR(int abgr) - { - return (abgr & 0xff); - } - - /// - /// Gets the G component from ABGR value returned by - /// ReadRGBAImage. - /// - /// The ABGR value. - /// The G component from ABGR value. - public static int GetG(int abgr) - { - return ((abgr >> 8) & 0xff); - } - - /// - /// Gets the B component from ABGR value returned by - /// ReadRGBAImage. - /// - /// The ABGR value. - /// The B component from ABGR value. - public static int GetB(int abgr) - { - return ((abgr >> 16) & 0xff); - } - - /// - /// Gets the A component from ABGR value returned by - /// ReadRGBAImage. - /// - /// The ABGR value. - /// The A component from ABGR value. - public static int GetA(int abgr) - { - return ((abgr >> 24) & 0xff); - } - - /// - /// Retrieves the codec registered for the specified compression scheme. - /// - /// The compression scheme. - /// The codec registered for the specified compression scheme or null - /// if there is no codec registered for the given scheme. - /// - /// - /// LibTiff.Net supports a variety of compression schemes implemented by software codecs. - /// Each codec adheres to a modular interface that provides for the decoding and encoding - /// of image data; as well as some other methods for initialization, setup, cleanup, and - /// the control of default strip and tile sizes. Codecs are identified by the associated - /// value of the .COMPRESSION tag. - /// - /// - /// Other compression schemes may be registered. Registered schemes can also override the - /// built-in versions provided by the library. - /// - /// - public TiffCodec FindCodec(Compression scheme) - { - for (codecList list = m_registeredCodecs; list != null; list = list.next) - { - if (list.codec.m_scheme == scheme) - return list.codec; - } - - for (int i = 0; m_builtInCodecs[i] != null; i++) - { - TiffCodec codec = m_builtInCodecs[i]; - if (codec.m_scheme == scheme) - return codec; - } - - return null; - } - - /// - /// Adds specified codec to a list of registered codec. - /// - /// The codec to register. - /// - /// This method can be used to augment or override the set of codecs available to an - /// application. If the is for a scheme that already has a - /// registered codec then it is overridden and any images with data encoded with this - /// compression scheme will be decoded using the supplied codec. - /// - public void RegisterCodec(TiffCodec codec) - { - if (codec == null) - throw new ArgumentNullException("codec"); - - codecList list = new codecList(); - list.codec = codec; - list.next = m_registeredCodecs; - m_registeredCodecs = list; - } - - /// - /// Removes specified codec from a list of registered codecs. - /// - /// The codec to remove from a list of registered codecs. - public void UnRegisterCodec(TiffCodec codec) - { - if (m_registeredCodecs == null) - return; - - codecList temp; - if (m_registeredCodecs.codec == codec) - { - temp = m_registeredCodecs.next; - m_registeredCodecs = temp; - return; - } - - for (codecList list = m_registeredCodecs; list != null; list = list.next) - { - if (list.next != null) - { - if (list.next.codec == codec) - { - temp = list.next.next; - list.next = temp; - return; - } - } - } - - ErrorExt(this, 0, "UnRegisterCodec", - "Cannot remove compression scheme {0}; not registered", codec.m_name); - } - - /// - /// Checks whether library has working codec for the specific compression scheme. - /// - /// The scheme to check. - /// - /// true if the codec is configured and working; otherwise, false. - /// - public bool IsCodecConfigured(Compression scheme) - { - TiffCodec codec = FindCodec(scheme); - - if (codec == null) - return false; - - if (codec.CanEncode != false || codec.CanDecode != false) - return true; - - return false; - } - - /// - /// Retrieves an array of configured codecs, both built-in and registered by user. - /// - /// An array of configured codecs. - public TiffCodec[] GetConfiguredCodecs() - { - int totalCodecs = 0; - for (int i = 0; m_builtInCodecs[i] != null; i++) - { - if (m_builtInCodecs[i] != null && IsCodecConfigured(m_builtInCodecs[i].m_scheme)) - totalCodecs++; - } - - for (codecList cd = m_registeredCodecs; cd != null; cd = cd.next) - totalCodecs++; - - TiffCodec[] codecs = new TiffCodec[totalCodecs]; - - int codecPos = 0; - for (codecList cd = m_registeredCodecs; cd != null; cd = cd.next) - codecs[codecPos++] = cd.codec; - - for (int i = 0; m_builtInCodecs[i] != null; i++) - { - if (m_builtInCodecs[i] != null && IsCodecConfigured(m_builtInCodecs[i].m_scheme)) - codecs[codecPos++] = m_builtInCodecs[i]; - } - - return codecs; - } - - /// - /// Allocates new byte array of specified size and copies data from the existing to - /// the new array. - /// - /// The existing array. - /// The number of elements in new array. - /// - /// The new byte array of specified size with data from the existing array. - /// - /// Allocates new array of specified size and copies data from the existing to - /// the new array. - public static byte[] Realloc(byte[] array, int size) - { - byte[] newArray = new byte[size]; - if (array != null) - { - int copyLength = Math.Min(array.Length, size); - Buffer.BlockCopy(array, 0, newArray, 0, copyLength); - } - - return newArray; - } - - /// - /// Allocates new integer array of specified size and copies data from the existing to - /// the new array. - /// - /// The existing array. - /// The number of elements in new array. - /// - /// The new integer array of specified size with data from the existing array. - /// - /// Size of the array is in elements, not bytes. - public static int[] Realloc(int[] array, int size) - { - int[] newArray = new int[size]; - if (array != null) - { - int copyLength = Math.Min(array.Length, size); - Buffer.BlockCopy(array, 0, newArray, 0, copyLength * sizeof(int)); - } - - return newArray; - } - - /// - /// Compares specified number of elements in two arrays. - /// - /// The first array to compare. - /// The second array to compare. - /// The number of elements to compare. - /// - /// The difference between compared elements or 0 if all elements are equal. - /// - public static int Compare(short[] first, short[] second, int elementCount) - { - for (int i = 0; i < elementCount; i++) - { - if (first[i] != second[i]) - return first[i] - second[i]; - } - - return 0; - } - - /// - /// Initializes new instance of class and opens a TIFF file for - /// reading or writing. - /// - /// The name of the file to open. - /// The open mode. Specifies if the file is to be opened for - /// reading ("r"), writing ("w"), or appending ("a") and, optionally, whether to override - /// certain default aspects of library operation (see remarks). - /// The new instance of class if specified file is - /// successfully opened; otherwise, null. - /// - /// - /// opens a TIFF file whose name is . When - /// a file is opened for appending, existing data will not be touched; instead new data - /// will be written as additional subfiles. If an existing file is opened for writing, - /// all previous data is overwritten. - /// - /// - /// If a file is opened for reading, the first TIFF directory in the file is automatically - /// read (see for reading directories other than the first). If - /// a file is opened for writing or appending, a default directory is automatically - /// created for writing subsequent data. This directory has all the default values - /// specified in TIFF Revision 6.0: BitsPerSample = 1, ThreshHolding = Threshold.BILEVEL - /// (bilevel art scan), FillOrder = MSB2LSB (most significant bit of each data byte is - /// filled first), Orientation = TOPLEFT (the 0th row represents the visual top of the - /// image, and the 0th column represents the visual left hand side), SamplesPerPixel = 1, - /// RowsPerStrip = infinity, ResolutionUnit = INCH, and Compression = NONE. To alter - /// these values, or to define values for additional fields, must - /// be used. - /// - /// - /// The parameter can include the following flags in addition to - /// the "r", "w", and "a" flags. Note however that option flags must follow the - /// read-write-append specification. - /// - /// - /// FlagDescription - /// l - /// When creating a new file force information be written with Little-Endian - /// byte order (but see below). - /// b - /// When creating a new file force information be written with Big-Endian - /// byte order (but see below). - /// L - /// Force image data that is read or written to be treated with bits filled - /// from Least Significant Bit (LSB) to Most Significant Bit (MSB). Note that this is the - /// opposite to the way the library has worked from its inception. - /// B - /// Force image data that is read or written to be treated with bits filled - /// from Most Significant Bit (MSB) to Least Significant Bit (LSB); this is the - /// default. - /// H - /// Force image data that is read or written to be treated with bits filled - /// in the same order as the native CPU. - /// C - /// Enable the use of "strip chopping" when reading images that are comprised - /// of a single strip or tile of uncompressed data. Strip chopping is a mechanism by which - /// the library will automatically convert the single-strip image to multiple strips, each - /// of which has about 8 Kilobytes of data. This facility can be useful in reducing the - /// amount of memory used to read an image because the library normally reads each strip - /// in its entirety. Strip chopping does however alter the apparent contents of the image - /// because when an image is divided into multiple strips it looks as though the - /// underlying file contains multiple separate strips. The default behaviour is to enable - /// strip chopping. - /// c - /// Disable the use of strip chopping when reading images. - /// h - /// Read TIFF header only, do not load the first image directory. That could - /// be useful in case of the broken first directory. We can open the file and proceed to - /// the other directories. - /// - /// By default the library will create new files with the native byte-order of the CPU on - /// which the application is run. This ensures optimal performance and is portable to any - /// application that conforms to the TIFF specification. To force the library to use a - /// specific byte-order when creating a new file the "b" and "l" option flags may be - /// included in the parameter; for example, "wb" or "wl". - /// The use of the "l" and "b" flags is strongly discouraged. These flags are - /// provided solely because numerous vendors do not correctly support TIFF; they only - /// support one of the two byte orders. It is strongly recommended that you not use this - /// feature except to deal with busted apps that write invalid TIFF. - /// The "L", "B", and "H" flags are intended for applications that can optimize - /// operations on data by using a particular bit order. By default the library returns - /// data in MSB2LSB bit order. Returning data in the bit order of the native CPU makes the - /// most sense but also requires applications to check the value of the - /// tag; something they probably do not do right now. - /// The "c" option permits applications that only want to look at the tags, for - /// example, to get the unadulterated TIFF tag information. - /// - public static Tiff Open(string fileName, string mode) - { - return Tiff.Open(fileName, mode, null, null); - } - - /// - /// Initializes new instance of class and opens a stream with TIFF data - /// for reading or writing. - /// - /// The name for the new instance of class. - /// The open mode. Specifies if the file is to be opened for - /// reading ("r"), writing ("w"), or appending ("a") and, optionally, whether to override - /// certain default aspects of library operation (see remarks for - /// method for the list of the mode flags). - /// Some client data. This data is passed as parameter to every - /// method of the object specified by the - /// parameter. - /// An instance of the class to use for - /// reading, writing and seeking of TIFF data. - /// The new instance of class if stream is successfully - /// opened; otherwise, null. - /// - /// - /// This method can be used to read TIFF data from sources other than file. When custom - /// stream class derived from is used it is possible to read (or - /// write) TIFF data that reside in memory, database, etc. - /// - /// Please note, that is an arbitrary string used as - /// ID for the created . It's not required to be a file name or anything - /// meaningful at all. - /// - /// Please read remarks for method for the list of option flags that - /// can be specified in parameter. - /// - /// - public static Tiff ClientOpen(string name, string mode, object clientData, TiffStream stream) - { - return ClientOpen(name, mode, clientData, stream, null, null); - } - - /// - /// Closes a previously opened TIFF file. - /// - /// - /// This method closes a file or stream that was previously opened with - /// or . - /// Any buffered data are flushed to the file/stream, - /// including the contents of the current directory (if modified); and all resources - /// are reclaimed. - /// - public void Close() - { - Flush(); - - m_stream.Close(m_clientdata); - - if (m_fileStream != null) - m_fileStream.Close(); - } - - /// - /// Frees and releases all resources allocated by this . - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Gets the number of elements in the custom tag list. - /// - /// The number of elements in the custom tag list. - public int GetTagListCount() - { - return m_dir.td_customValueCount; - } - - /// - /// Retrieves the custom tag with specified index. - /// - /// The zero-based index of a custom tag to retrieve. - /// The custom tag with specified index. - public int GetTagListEntry(int index) - { - if (index < 0 || index >= m_dir.td_customValueCount) - return -1; - else - return (int)m_dir.td_customValues[index].info.Tag; - } - - /// - /// Merges given field information to existing one. - /// - /// The array of objects. - /// The number of items to use from the array. - public void MergeFieldInfo(TiffFieldInfo[] info, int count) - { - m_foundfield = null; - - if (m_nfields > 0) - m_fieldinfo = Realloc(m_fieldinfo, m_nfields, m_nfields + count); - else - m_fieldinfo = new TiffFieldInfo[count]; - - for (int i = 0; i < count; i++) - { - TiffFieldInfo fip = FindFieldInfo(info[i].Tag, info[i].Type); - - // only add definitions that aren't already present - if (fip == null) - { - m_fieldinfo[m_nfields] = info[i]; - m_nfields++; - } - } - - // Sort the field info by tag number - IComparer myComparer = new TagCompare(); - Array.Sort(m_fieldinfo, 0, m_nfields, myComparer); - } - - /// - /// Retrieves field information for the specified tag. - /// - /// The tag to retrieve field information for. - /// The tiff data type to use us additional filter. - /// The field information for specified tag with specified type or null if - /// the field information wasn't found. - public TiffFieldInfo FindFieldInfo(TiffTag tag, TiffType type) - { - if (m_foundfield != null && m_foundfield.Tag == tag && - (type == TiffType.ANY || type == m_foundfield.Type)) - { - return m_foundfield; - } - - // If we are invoked with no field information, then just return. - if (m_fieldinfo == null) - return null; - - m_foundfield = null; - - foreach (TiffFieldInfo info in m_fieldinfo) - { - if (info != null && info.Tag == tag && (type == TiffType.ANY || type == info.Type)) - { - m_foundfield = info; - break; - } - } - - return m_foundfield; - } - - /// - /// Retrieves field information for the tag with specified name. - /// - /// The name of the tag to retrieve field information for. - /// The tiff data type to use us additional filter. - /// The field information for specified tag with specified type or null if - /// the field information wasn't found. - public TiffFieldInfo FindFieldInfoByName(string name, TiffType type) - { - if (m_foundfield != null && m_foundfield.Name == name && - (type == TiffType.ANY || type == m_foundfield.Type)) - { - return m_foundfield; - } - - // If we are invoked with no field information, then just return. - if (m_fieldinfo == null) - return null; - - m_foundfield = null; - - foreach (TiffFieldInfo info in m_fieldinfo) - { - if (info != null && info.Name == name && - (type == TiffType.ANY || type == info.Type)) - { - m_foundfield = info; - break; - } - } - - return m_foundfield; - } - - /// - /// Retrieves field information for the specified tag. - /// - /// The tag to retrieve field information for. - /// The field information for specified tag or null if - /// the field information wasn't found. - public TiffFieldInfo FieldWithTag(TiffTag tag) - { - TiffFieldInfo fip = FindFieldInfo(tag, TiffType.ANY); - if (fip != null) - return fip; - - ErrorExt(this, m_clientdata, "FieldWithTag", "Internal error, unknown tag 0x{0:x}", tag); - Debug.Assert(false); - return null; - } - - /// - /// Retrieves field information for the tag with specified name. - /// - /// The name of the tag to retrieve field information for. - /// The field information for specified tag or null if - /// the field information wasn't found. - public TiffFieldInfo FieldWithName(string name) - { - TiffFieldInfo fip = FindFieldInfoByName(name, TiffType.ANY); - if (fip != null) - return fip; - - ErrorExt(this, m_clientdata, "FieldWithName", "Internal error, unknown tag {0}", name); - Debug.Assert(false); - return null; - } - - /// - /// Gets the currently used tag methods. - /// - /// The currently used tag methods. - public TiffTagMethods GetTagMethods() - { - return m_tagmethods; - } - - /// - /// Sets the new tag methods to use. - /// - /// Tag methods. - /// The previously used tag methods. - public TiffTagMethods SetTagMethods(TiffTagMethods methods) - { - TiffTagMethods prevTagMethods = m_tagmethods; - - if (methods != null) - m_tagmethods = methods; - - return prevTagMethods; - } - - /// - /// Gets the extra information with specified name associated with this . - /// - /// Name of the extra information to retrieve. - /// The extra information with specified name associated with - /// this or null if extra information with specified - /// name was not found. - public object GetClientInfo(string name) - { - // should get copy - clientInfoLink link = m_clientinfo; - - while (link != null && link.name != name) - link = link.next; - - if (link != null) - return link.data; - - return null; - } - - /// - /// Associates extra information with this . - /// - /// The information to associate with this . - /// The name (label) of the information. - /// If there is already an extra information with the name specified by - /// it will be replaced by the information specified by - /// . - public void SetClientInfo(object data, string name) - { - clientInfoLink link = m_clientinfo; - - // Do we have an existing link with this name? If so, just set it. - while (link != null && link.name != name) - link = link.next; - - if (link != null) - { - link.data = data; - return; - } - - // Create a new link. - link = new clientInfoLink(); - link.next = m_clientinfo; - link.name = name; - link.data = data; - - m_clientinfo = link; - } - - /// - /// Flushes pending writes to an open TIFF file. - /// - /// true if succeeded; otherwise, false - /// causes any pending writes for the specified file - /// (including writes for the current directory) to be done. In normal operation this call - /// is never needed − the library automatically does any flushing required. - /// - /// - public bool Flush() - { - if (m_mode != O_RDONLY) - { - if (!FlushData()) - return false; - - if ((m_flags & TiffFlags.DIRTYDIRECT) == TiffFlags.DIRTYDIRECT && !WriteDirectory()) - return false; - } - - return true; - } - - /// - /// Flushes any pending image data for the specified file to be written out. - /// - /// true if succeeded; otherwise, false - /// flushes any pending image data for the specified file - /// to be written out; directory-related data are not flushed. In normal operation this - /// call is never needed − the library automatically does any flushing required. - /// - /// - public bool FlushData() - { - if ((m_flags & TiffFlags.BEENWRITING) != TiffFlags.BEENWRITING) - return false; - - if ((m_flags & TiffFlags.POSTENCODE) == TiffFlags.POSTENCODE) - { - m_flags &= ~TiffFlags.POSTENCODE; - if (!m_currentCodec.PostEncode()) - return false; - } - - return flushData1(); - } - - /// - /// Gets the value(s) of a tag in an open TIFF file. - /// - /// The tag. - /// The value(s) of a tag in an open TIFF file as array of - /// objects or null if there is no such tag set. - /// - /// - /// returns the value(s) of a tag or pseudo-tag associated with the - /// current directory of the opened TIFF file. The tag is identified by - /// . The type and number of values returned is dependent on the - /// tag being requested. You may want to consult - /// "Well-known tags and their - /// value(s) data types" to become familiar with exact data types and calling - /// conventions required for each tag supported by the library. - /// - /// - /// A pseudo-tag is a parameter that is used to control the operation of the library but - /// whose value is not read or written to the underlying file. - /// - /// - /// - public FieldValue[] GetField(TiffTag tag) - { - TiffFieldInfo fip = FindFieldInfo(tag, TiffType.ANY); - if (fip != null && (isPseudoTag(tag) || fieldSet(fip.Bit))) - return m_tagmethods.GetField(this, tag); - - return null; - } - - /// - /// Gets the value(s) of a tag in an open TIFF file or default value(s) of a tag if a tag - /// is not defined in the current directory and it has a default value(s). - /// - /// The tag. - /// - /// The value(s) of a tag in an open TIFF file as array of - /// objects or null if there is no such tag set and - /// tag has no default value. - /// - /// - /// - /// returns the value(s) of a tag or pseudo-tag associated - /// with the current directory of the opened TIFF file or default value(s) of a tag if a - /// tag is not defined in the current directory and it has a default value(s). The tag is - /// identified by . The type and number of values returned is - /// dependent on the tag being requested. You may want to consult - /// "Well-known tags and their - /// value(s) data types" to become familiar with exact data types and calling - /// conventions required for each tag supported by the library. - /// - /// - /// A pseudo-tag is a parameter that is used to control the operation of the library but - /// whose value is not read or written to the underlying file. - /// - /// - /// - public FieldValue[] GetFieldDefaulted(TiffTag tag) - { - TiffDirectory td = m_dir; - - FieldValue[] result = GetField(tag); - if (result != null) - return result; - - switch (tag) - { - case TiffTag.SUBFILETYPE: - result = new FieldValue[1]; - result[0].Set(td.td_subfiletype); - break; - case TiffTag.BITSPERSAMPLE: - result = new FieldValue[1]; - result[0].Set(td.td_bitspersample); - break; - case TiffTag.THRESHHOLDING: - result = new FieldValue[1]; - result[0].Set(td.td_threshholding); - break; - case TiffTag.FILLORDER: - result = new FieldValue[1]; - result[0].Set(td.td_fillorder); - break; - case TiffTag.ORIENTATION: - result = new FieldValue[1]; - result[0].Set(td.td_orientation); - break; - case TiffTag.SAMPLESPERPIXEL: - result = new FieldValue[1]; - result[0].Set(td.td_samplesperpixel); - break; - case TiffTag.ROWSPERSTRIP: - result = new FieldValue[1]; - result[0].Set(td.td_rowsperstrip); - break; - case TiffTag.MINSAMPLEVALUE: - result = new FieldValue[1]; - result[0].Set(td.td_minsamplevalue); - break; - case TiffTag.MAXSAMPLEVALUE: - result = new FieldValue[1]; - result[0].Set(td.td_maxsamplevalue); - break; - case TiffTag.PLANARCONFIG: - result = new FieldValue[1]; - result[0].Set(td.td_planarconfig); - break; - case TiffTag.RESOLUTIONUNIT: - result = new FieldValue[1]; - result[0].Set(td.td_resolutionunit); - break; - case TiffTag.PREDICTOR: - CodecWithPredictor sp = m_currentCodec as CodecWithPredictor; - if (sp != null) - { - result = new FieldValue[1]; - result[0].Set(sp.GetPredictorValue()); - } - break; - case TiffTag.DOTRANGE: - result = new FieldValue[2]; - result[0].Set(0); - result[1].Set((1 << td.td_bitspersample) - 1); - break; - case TiffTag.INKSET: - result = new FieldValue[1]; - result[0].Set(InkSet.CMYK); - break; - case TiffTag.NUMBEROFINKS: - result = new FieldValue[1]; - result[0].Set(4); - break; - case TiffTag.EXTRASAMPLES: - result = new FieldValue[2]; - result[0].Set(td.td_extrasamples); - result[1].Set(td.td_sampleinfo); - break; - case TiffTag.MATTEING: - result = new FieldValue[1]; - result[0].Set((td.td_extrasamples == 1 && td.td_sampleinfo[0] == ExtraSample.ASSOCALPHA)); - break; - case TiffTag.TILEDEPTH: - result = new FieldValue[1]; - result[0].Set(td.td_tiledepth); - break; - case TiffTag.DATATYPE: - result = new FieldValue[1]; - result[0].Set(td.td_sampleformat - 1); - break; - case TiffTag.SAMPLEFORMAT: - result = new FieldValue[1]; - result[0].Set(td.td_sampleformat); - break; - case TiffTag.IMAGEDEPTH: - result = new FieldValue[1]; - result[0].Set(td.td_imagedepth); - break; - case TiffTag.YCBCRCOEFFICIENTS: - { - // defaults are from CCIR Recommendation 601-1 - float[] ycbcrcoeffs = new float[3]; - ycbcrcoeffs[0] = 0.299f; - ycbcrcoeffs[1] = 0.587f; - ycbcrcoeffs[2] = 0.114f; - - result = new FieldValue[1]; - result[0].Set(ycbcrcoeffs); - break; - } - case TiffTag.YCBCRSUBSAMPLING: - result = new FieldValue[2]; - result[0].Set(td.td_ycbcrsubsampling[0]); - result[1].Set(td.td_ycbcrsubsampling[1]); - break; - case TiffTag.YCBCRPOSITIONING: - result = new FieldValue[1]; - result[0].Set(td.td_ycbcrpositioning); - break; - case TiffTag.WHITEPOINT: - { - // TIFF 6.0 specification tells that it is no default value for the - // WhitePoint, but AdobePhotoshop TIFF Technical Note tells that it - // should be CIE D50. - float[] whitepoint = new float[2]; - whitepoint[0] = D50_X0 / (D50_X0 + D50_Y0 + D50_Z0); - whitepoint[1] = D50_Y0 / (D50_X0 + D50_Y0 + D50_Z0); - - result = new FieldValue[1]; - result[0].Set(whitepoint); - break; - } - case TiffTag.TRANSFERFUNCTION: - if (td.td_transferfunction[0] == null && !defaultTransferFunction(td)) - { - ErrorExt(this, m_clientdata, m_name, "No space for \"TransferFunction\" tag"); - return null; - } - - result = new FieldValue[3]; - result[0].Set(td.td_transferfunction[0]); - if (td.td_samplesperpixel - td.td_extrasamples > 1) - { - result[1].Set(td.td_transferfunction[1]); - result[2].Set(td.td_transferfunction[2]); - } - break; - case TiffTag.REFERENCEBLACKWHITE: - if (td.td_refblackwhite == null) - defaultRefBlackWhite(td); - - result = new FieldValue[1]; - result[0].Set(td.td_refblackwhite); - break; - } - - return result; - } - - /// - /// Reads the contents of the next TIFF directory in an open TIFF file/stream and makes - /// it the current directory. - /// - /// true if directory was successfully read; otherwise, false if an - /// error was encountered, or if there are no more directories to be read. - /// Directories are read sequentially. - /// Applications only need to call to read multiple - /// subfiles in a single TIFF file/stream - the first directory in a file/stream is - /// automatically read when or - /// is called. - /// - /// The images that have a single uncompressed strip or tile of data are automatically - /// treated as if they were made up of multiple strips or tiles of approximately 8 - /// kilobytes each. This operation is done only in-memory; it does not alter the contents - /// of the file/stream. However, the construction of the "chopped strips" is visible to - /// the application through the number of strips returned by - /// or the number of tiles returned by . - /// - public bool ReadDirectory() - { - const string module = "ReadDirectory"; - - m_diroff = m_nextdiroff; - if (m_diroff == 0) - { - // no more directories - return false; - } - - // Check whether we have the last offset or bad offset (IFD looping). - if (!checkDirOffset(m_nextdiroff)) - return false; - - // Cleanup any previous compression state. - m_currentCodec.Cleanup(); - m_curdir++; - TiffDirEntry[] dir; - short dircount = fetchDirectory(m_nextdiroff, out dir, out m_nextdiroff); - if (dircount == 0) - { - ErrorExt(this, m_clientdata, module, "{0}: Failed to read directory at offset {1}", m_name, m_nextdiroff); - return false; - } - - // reset before new dir - m_flags &= ~TiffFlags.BEENWRITING; - - // Setup default value and then make a pass over the fields to check type and tag - // information, and to extract info required to size data structures. A second pass is - // made afterwards to read in everthing not taken in the first pass. - - // free any old stuff and reinit - FreeDirectory(); - setupDefaultDirectory(); - - // Electronic Arts writes gray-scale TIFF files without a PlanarConfiguration - // directory entry. Thus we setup a default value here, even though the TIFF spec says - // there is no default value. - SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG); - - // Sigh, we must make a separate pass through the directory for the following reason: - // - // We must process the Compression tag in the first pass in order to merge in - // codec-private tag definitions (otherwise we may get complaints about unknown tags). - // However, the Compression tag may be dependent on the SamplesPerPixel tag value - // because older TIFF specs permited Compression to be written as a - // SamplesPerPixel-count tag entry. Thus if we don't first figure out the correct - // SamplesPerPixel tag value then we may end up ignoring the Compression tag value - // because it has an incorrect count value (if the true value of SamplesPerPixel is not 1). - // - // It sure would have been nice if Aldus had really thought this stuff through carefully. - - for (int i = 0; i < dircount; i++) - { - TiffDirEntry dp = dir[i]; - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - { - short temp = (short)dp.tdir_tag; - SwabShort(ref temp); - dp.tdir_tag = (TiffTag)(ushort)temp; - - temp = (short)dp.tdir_type; - SwabShort(ref temp); - dp.tdir_type = (TiffType)temp; - - SwabLong(ref dp.tdir_count); - SwabUInt(ref dp.tdir_offset); - } - - if (dp.tdir_tag == TiffTag.SAMPLESPERPIXEL) - { - if (!fetchNormalTag(dir[i])) - return false; - - dp.tdir_tag = TiffTag.IGNORE; - } - } - - // First real pass over the directory. - int fix = 0; - bool diroutoforderwarning = false; - bool haveunknowntags = false; - for (int i = 0; i < dircount; i++) - { - if (dir[i].tdir_tag == TiffTag.IGNORE) - continue; - - if (fix >= m_nfields) - fix = 0; - - // Silicon Beach (at least) writes unordered directory tags (violating the spec). - // Handle it here, but be obnoxious (maybe they'll fix it?). - if (dir[i].tdir_tag < m_fieldinfo[fix].Tag) - { - if (!diroutoforderwarning) - { - WarningExt(this, m_clientdata, module, - "{0}: invalid TIFF directory; tags are not sorted in ascending order", m_name); - diroutoforderwarning = true; - } - - fix = 0; // O(n^2) - } - - while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) - fix++; - - if (fix >= m_nfields || m_fieldinfo[fix].Tag != dir[i].tdir_tag) - { - // Unknown tag ... we'll deal with it below - haveunknowntags = true; - continue; - } - - // null out old tags that we ignore. - if (m_fieldinfo[fix].Bit == FieldBit.Ignore) - { - dir[i].tdir_tag = TiffTag.IGNORE; - continue; - } - - // Check data type. - TiffFieldInfo fip = m_fieldinfo[fix]; - bool tagIgnored = false; - while (dir[i].tdir_type != fip.Type && fix < m_nfields) - { - if (fip.Type == TiffType.ANY) - { - // wildcard - break; - } - - fip = m_fieldinfo[++fix]; - if (fix >= m_nfields || fip.Tag != dir[i].tdir_tag) - { - WarningExt(this, m_clientdata, module, - "{0}: wrong data type {1} for \"{2}\"; tag ignored", - m_name, dir[i].tdir_type, m_fieldinfo[fix - 1].Name); - - dir[i].tdir_tag = TiffTag.IGNORE; - tagIgnored = true; - break; - } - } - - if (tagIgnored) - continue; - - // Check count if known in advance. - if (fip.ReadCount != TiffFieldInfo.Variable && - fip.ReadCount != TiffFieldInfo.Variable2) - { - int expected = fip.ReadCount; - if (fip.ReadCount == TiffFieldInfo.Spp) - expected = m_dir.td_samplesperpixel; - - if (!checkDirCount(dir[i], expected)) - { - dir[i].tdir_tag = TiffTag.IGNORE; - continue; - } - } - - switch (dir[i].tdir_tag) - { - case TiffTag.COMPRESSION: - // The 5.0 spec says the Compression tag has one value, - // while earlier specs say it has one value per sample. - // Because of this, we accept the tag if one value is supplied. - if (dir[i].tdir_count == 1) - { - int v = extractData(dir[i]); - if (!SetField(dir[i].tdir_tag, v)) - return false; - - break; - // XXX: workaround for broken TIFFs - } - else if (dir[i].tdir_type == TiffType.LONG) - { - int v; - if (!fetchPerSampleLongs(dir[i], out v) || !SetField(dir[i].tdir_tag, v)) - return false; - } - else - { - short iv; - if (!fetchPerSampleShorts(dir[i], out iv) || !SetField(dir[i].tdir_tag, iv)) - return false; - } - dir[i].tdir_tag = TiffTag.IGNORE; - break; - case TiffTag.STRIPOFFSETS: - case TiffTag.STRIPBYTECOUNTS: - case TiffTag.TILEOFFSETS: - case TiffTag.TILEBYTECOUNTS: - setFieldBit(fip.Bit); - break; - case TiffTag.IMAGEWIDTH: - case TiffTag.IMAGELENGTH: - case TiffTag.IMAGEDEPTH: - case TiffTag.TILELENGTH: - case TiffTag.TILEWIDTH: - case TiffTag.TILEDEPTH: - case TiffTag.PLANARCONFIG: - case TiffTag.ROWSPERSTRIP: - case TiffTag.EXTRASAMPLES: - if (!fetchNormalTag(dir[i])) - return false; - dir[i].tdir_tag = TiffTag.IGNORE; - break; - } - } - - // If we saw any unknown tags, make an extra pass over the directory to deal with - // them. This must be done separately because the tags could have become known when we - // registered a codec after finding the Compression tag. In a correctly-sorted - // directory there's no problem because Compression will come before any codec-private - // tags, but if the sorting is wrong that might not hold. - if (haveunknowntags) - { - fix = 0; - for (int i = 0; i < dircount; i++) - { - if (dir[i].tdir_tag == TiffTag.IGNORE) - continue; - - if (fix >= m_nfields || dir[i].tdir_tag < m_fieldinfo[fix].Tag) - { - // O(n^2) - fix = 0; - } - - while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) - fix++; - - if (fix >= m_nfields || m_fieldinfo[fix].Tag != dir[i].tdir_tag) - { - Tiff.WarningExt(this, m_clientdata, module, - "{0}: unknown field with tag {1} (0x{2:x}) encountered", - m_name, (ushort)dir[i].tdir_tag, (ushort)dir[i].tdir_tag); - - TiffFieldInfo[] arr = new TiffFieldInfo[1]; - arr[0] = createAnonFieldInfo(dir[i].tdir_tag, dir[i].tdir_type); - MergeFieldInfo(arr, 1); - - fix = 0; - while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) - fix++; - } - - // Check data type. - TiffFieldInfo fip = m_fieldinfo[fix]; - while (dir[i].tdir_type != fip.Type && fix < m_nfields) - { - if (fip.Type == TiffType.ANY) - { - // wildcard - break; - } - - fip = m_fieldinfo[++fix]; - if (fix >= m_nfields || fip.Tag != dir[i].tdir_tag) - { - Tiff.WarningExt(this, m_clientdata, module, - "{0}: wrong data type {1} for \"{2}\"; tag ignored", - m_name, dir[i].tdir_type, m_fieldinfo[fix - 1].Name); - - dir[i].tdir_tag = TiffTag.IGNORE; - break; - } - } - } - } - - // XXX: OJPEG hack. - // If a) compression is OJPEG, b) planarconfig tag says it's separate, c) strip - // offsets/bytecounts tag are both present and d) both contain exactly one value, then - // we consistently find that the buggy implementation of the buggy compression scheme - // matches contig planarconfig best. So we 'fix-up' the tag here - if ((m_dir.td_compression == Compression.OJPEG) && (m_dir.td_planarconfig == PlanarConfig.SEPARATE)) - { - int dpIndex = readDirectoryFind(dir, dircount, TiffTag.STRIPOFFSETS); - if (dpIndex != -1 && dir[dpIndex].tdir_count == 1) - { - dpIndex = readDirectoryFind(dir, dircount, TiffTag.STRIPBYTECOUNTS); - if (dpIndex != -1 && dir[dpIndex].tdir_count == 1) - { - m_dir.td_planarconfig = PlanarConfig.CONTIG; - WarningExt(this, m_clientdata, "ReadDirectory", - "Planarconfig tag value assumed incorrect, assuming data is contig instead of chunky"); - } - } - } - - // Allocate directory structure and setup defaults. - if (!fieldSet(FieldBit.ImageDimensions)) - { - missingRequired("ImageLength"); - return false; - } - - // Setup appropriate structures (by strip or by tile) - if (!fieldSet(FieldBit.TileDimensions)) - { - m_dir.td_nstrips = NumberOfStrips(); - m_dir.td_tilewidth = m_dir.td_imagewidth; - m_dir.td_tilelength = m_dir.td_rowsperstrip; - m_dir.td_tiledepth = m_dir.td_imagedepth; - m_flags &= ~TiffFlags.ISTILED; - } - else - { - m_dir.td_nstrips = NumberOfTiles(); - m_flags |= TiffFlags.ISTILED; - } - - if (m_dir.td_nstrips == 0) - { - ErrorExt(this, m_clientdata, module, - "{0}: cannot handle zero number of {1}", m_name, IsTiled() ? "tiles" : "strips"); - return false; - } - - m_dir.td_stripsperimage = m_dir.td_nstrips; - if (m_dir.td_planarconfig == PlanarConfig.SEPARATE) - m_dir.td_stripsperimage /= m_dir.td_samplesperpixel; - - if (!fieldSet(FieldBit.StripOffsets)) - { - if ((m_dir.td_compression == Compression.OJPEG) && !IsTiled() && (m_dir.td_nstrips == 1)) - { - // XXX: OJPEG hack. - // If a) compression is OJPEG, b) it's not a tiled TIFF, and c) the number of - // strips is 1, then we tolerate the absence of stripoffsets tag, because, - // presumably, all required data is in the JpegInterchangeFormat stream. - setFieldBit(FieldBit.StripOffsets); - } - else - { - missingRequired(IsTiled() ? "TileOffsets" : "StripOffsets"); - return false; - } - } - - // Second pass: extract other information. - for (int i = 0; i < dircount; i++) - { - if (dir[i].tdir_tag == TiffTag.IGNORE) - continue; - - switch (dir[i].tdir_tag) - { - case TiffTag.MINSAMPLEVALUE: - case TiffTag.MAXSAMPLEVALUE: - case TiffTag.BITSPERSAMPLE: - case TiffTag.DATATYPE: - case TiffTag.SAMPLEFORMAT: - // The 5.0 spec says the Compression tag has one value, while earlier - // specs say it has one value per sample. Because of this, we accept the - // tag if one value is supplied. - // - // The MinSampleValue, MaxSampleValue, BitsPerSample DataType and - // SampleFormat tags are supposed to be written as one value/sample, but - // some vendors incorrectly write one value only - so we accept that as - // well (yech). Other vendors write correct value for NumberOfSamples, but - // incorrect one for BitsPerSample and friends, and we will read this too. - if (dir[i].tdir_count == 1) - { - int v = extractData(dir[i]); - if (!SetField(dir[i].tdir_tag, v)) - return false; - // XXX: workaround for broken TIFFs - } - else if (dir[i].tdir_tag == TiffTag.BITSPERSAMPLE && dir[i].tdir_type == TiffType.LONG) - { - int v; - if (!fetchPerSampleLongs(dir[i], out v) || !SetField(dir[i].tdir_tag, v)) - return false; - } - else - { - short iv; - if (!fetchPerSampleShorts(dir[i], out iv) || !SetField(dir[i].tdir_tag, iv)) - return false; - } - break; - case TiffTag.SMINSAMPLEVALUE: - case TiffTag.SMAXSAMPLEVALUE: - double dv; - if (!fetchPerSampleAnys(dir[i], out dv) || !SetField(dir[i].tdir_tag, dv)) - return false; - break; - case TiffTag.STRIPOFFSETS: - case TiffTag.TILEOFFSETS: - if (!fetchStripThing(dir[i], m_dir.td_nstrips, ref m_dir.td_stripoffset)) - return false; - break; - case TiffTag.STRIPBYTECOUNTS: - case TiffTag.TILEBYTECOUNTS: - if (!fetchStripThing(dir[i], m_dir.td_nstrips, ref m_dir.td_stripbytecount)) - return false; - break; - case TiffTag.COLORMAP: - case TiffTag.TRANSFERFUNCTION: - { - // TransferFunction can have either 1x or 3x data values; - // Colormap can have only 3x items. - int v = 1 << m_dir.td_bitspersample; - if (dir[i].tdir_tag == TiffTag.COLORMAP || dir[i].tdir_count != v) - { - if (!checkDirCount(dir[i], 3 * v)) - break; - } - - byte[] cp = new byte[dir[i].tdir_count * sizeof(short)]; - if (fetchData(dir[i], cp) != 0) - { - int c = 1 << m_dir.td_bitspersample; - if (dir[i].tdir_count == c) - { - // This deals with there being only one array to apply to all samples. - short[] u = ByteArrayToShorts(cp, 0, dir[i].tdir_count * sizeof(short)); - SetField(dir[i].tdir_tag, u, u, u); - } - else - { - v *= sizeof(short); - short[] u0 = ByteArrayToShorts(cp, 0, v); - short[] u1 = ByteArrayToShorts(cp, v, v); - short[] u2 = ByteArrayToShorts(cp, 2 * v, v); - SetField(dir[i].tdir_tag, u0, u1, u2); - } - } - break; - } - case TiffTag.PAGENUMBER: - case TiffTag.HALFTONEHINTS: - case TiffTag.YCBCRSUBSAMPLING: - case TiffTag.DOTRANGE: - fetchShortPair(dir[i]); - break; - case TiffTag.REFERENCEBLACKWHITE: - fetchRefBlackWhite(dir[i]); - break; - // BEGIN REV 4.0 COMPATIBILITY - case TiffTag.OSUBFILETYPE: - FileType ft = 0; - switch ((OFileType)extractData(dir[i])) - { - case OFileType.REDUCEDIMAGE: - ft = FileType.REDUCEDIMAGE; - break; - case OFileType.PAGE: - ft = FileType.PAGE; - break; - } - - if (ft != 0) - SetField(TiffTag.SUBFILETYPE, ft); - - break; - // END REV 4.0 COMPATIBILITY - default: - fetchNormalTag(dir[i]); - break; - } - } - - // OJPEG hack: - // - If a) compression is OJPEG, and b) photometric tag is missing, then we - // consistently find that photometric should be YCbCr - // - If a) compression is OJPEG, and b) photometric tag says it's RGB, then we - // consistently find that the buggy implementation of the buggy compression scheme - // matches photometric YCbCr instead. - // - If a) compression is OJPEG, and b) bitspersample tag is missing, then we - // consistently find bitspersample should be 8. - // - If a) compression is OJPEG, b) samplesperpixel tag is missing, and c) photometric - // is RGB or YCbCr, then we consistently find samplesperpixel should be 3 - // - If a) compression is OJPEG, b) samplesperpixel tag is missing, and c) photometric - // is MINISWHITE or MINISBLACK, then we consistently find samplesperpixel should be 3 - if (m_dir.td_compression == Compression.OJPEG) - { - if (!fieldSet(FieldBit.Photometric)) - { - WarningExt(this, m_clientdata, - "ReadDirectory", "Photometric tag is missing, assuming data is YCbCr"); - - if (!SetField(TiffTag.PHOTOMETRIC, Photometric.YCBCR)) - return false; - } - else if (m_dir.td_photometric == Photometric.RGB) - { - m_dir.td_photometric = Photometric.YCBCR; - WarningExt(this, m_clientdata, "ReadDirectory", - "Photometric tag value assumed incorrect, assuming data is YCbCr instead of RGB"); - } - - if (!fieldSet(FieldBit.BitsPerSample)) - { - WarningExt(this, m_clientdata, "ReadDirectory", - "BitsPerSample tag is missing, assuming 8 bits per sample"); - - if (!SetField(TiffTag.BITSPERSAMPLE, 8)) - return false; - } - - if (!fieldSet(FieldBit.SamplesPerPixel)) - { - if ((m_dir.td_photometric == Photometric.RGB) || - (m_dir.td_photometric == Photometric.YCBCR)) - { - WarningExt(this, m_clientdata, "ReadDirectory", - "SamplesPerPixel tag is missing, assuming correct SamplesPerPixel value is 3"); - - if (!SetField(TiffTag.SAMPLESPERPIXEL, 3)) - return false; - } - else if ((m_dir.td_photometric == Photometric.MINISWHITE) || - (m_dir.td_photometric == Photometric.MINISBLACK)) - { - WarningExt(this, m_clientdata, "ReadDirectory", - "SamplesPerPixel tag is missing, assuming correct SamplesPerPixel value is 1"); - if (!SetField(TiffTag.SAMPLESPERPIXEL, 1)) - return false; - } - } - } - - // Verify Palette image has a Colormap. - if (m_dir.td_photometric == Photometric.PALETTE && !fieldSet(FieldBit.ColorMap)) - { - missingRequired("Colormap"); - return false; - } - - // OJPEG hack: - // We do no further messing with strip/tile offsets/bytecounts in OJPEG TIFFs - if (m_dir.td_compression != Compression.OJPEG) - { - // Attempt to deal with a missing StripByteCounts tag. - if (!fieldSet(FieldBit.StripByteCounts)) - { - // Some manufacturers violate the spec by not giving the size of the strips. - // In this case, assume there is one uncompressed strip of data. - if ((m_dir.td_planarconfig == PlanarConfig.CONTIG && m_dir.td_nstrips > 1) || - (m_dir.td_planarconfig == PlanarConfig.SEPARATE && m_dir.td_nstrips != m_dir.td_samplesperpixel)) - { - missingRequired("StripByteCounts"); - return false; - } - - WarningExt(this, m_clientdata, module, - "{0}: TIFF directory is missing required \"{1}\" field, calculating from imagelength", - m_name, FieldWithTag(TiffTag.STRIPBYTECOUNTS).Name); - - if (!estimateStripByteCounts(dir, dircount)) - return false; - } - else if (m_dir.td_nstrips == 1 && m_dir.td_stripoffset[0] != 0 && byteCountLooksBad(m_dir)) - { - // XXX: Plexus (and others) sometimes give a value of zero for a tag when - // they don't know what the correct value is! Try and handle the simple case - // of estimating the size of a one strip image. - WarningExt(this, m_clientdata, module, - "{0}: Bogus \"{1}\" field, ignoring and calculating from imagelength", - m_name, FieldWithTag(TiffTag.STRIPBYTECOUNTS).Name); - - if (!estimateStripByteCounts(dir, dircount)) - return false; - } - else if (m_dir.td_planarconfig == PlanarConfig.CONTIG && m_dir.td_nstrips > 2 && - m_dir.td_compression == Compression.NONE && - m_dir.td_stripbytecount[0] != m_dir.td_stripbytecount[1]) - { - // XXX: Some vendors fill StripByteCount array with absolutely wrong values - // (it can be equal to StripOffset array, for example). Catch this case here. - WarningExt(this, m_clientdata, module, - "{0}: Wrong \"{1}\" field, ignoring and calculating from imagelength", - m_name, FieldWithTag(TiffTag.STRIPBYTECOUNTS).Name); - - if (!estimateStripByteCounts(dir, dircount)) - return false; - } - } - - dir = null; - - if (!fieldSet(FieldBit.MaxSampleValue)) - m_dir.td_maxsamplevalue = (short)((1 << m_dir.td_bitspersample) - 1); - - // Setup default compression scheme. - - // XXX: We can optimize checking for the strip bounds using the sorted bytecounts - // array. See also comments for appendToStrip() function. - if (m_dir.td_nstrips > 1) - { - m_dir.td_stripbytecountsorted = true; - for (int strip = 1; strip < m_dir.td_nstrips; strip++) - { - if (m_dir.td_stripoffset[strip - 1] > m_dir.td_stripoffset[strip]) - { - m_dir.td_stripbytecountsorted = false; - break; - } - } - } - - if (!fieldSet(FieldBit.Compression)) - SetField(TiffTag.COMPRESSION, Compression.NONE); - - // Some manufacturers make life difficult by writing large amounts of uncompressed - // data as a single strip. This is contrary to the recommendations of the spec. The - // following makes an attempt at breaking such images into strips closer to the - // recommended 8k bytes. A side effect, however, is that the RowsPerStrip tag value - // may be changed. - if (m_dir.td_nstrips == 1 && m_dir.td_compression == Compression.NONE && - (m_flags & TiffFlags.STRIPCHOP) == TiffFlags.STRIPCHOP && - (m_flags & TiffFlags.ISTILED) != TiffFlags.ISTILED) - { - chopUpSingleUncompressedStrip(); - } - - // Reinitialize i/o since we are starting on a new directory. - m_row = -1; - m_curstrip = -1; - m_col = -1; - m_curtile = -1; - m_tilesize = -1; - - m_scanlinesize = ScanlineSize(); - if (m_scanlinesize == 0) - { - ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero scanline size", m_name); - return false; - } - - if (IsTiled()) - { - m_tilesize = TileSize(); - if (m_tilesize == 0) - { - ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero tile size", m_name); - return false; - } - } - else - { - if (StripSize() == 0) - { - ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero strip size", m_name); - return false; - } - } - - return true; - } - - /// - /// Reads a custom directory from the arbitrary offset within file/stream. - /// - /// The directory offset. - /// The array of objects to merge to - /// existing field information. - /// The number of items to use from - /// the array. - /// true if a custom directory was read successfully; - /// otherwise, false - public bool ReadCustomDirectory(long offset, TiffFieldInfo[] info, int count) - { - const string module = "ReadCustomDirectory"; - - setupFieldInfo(info, count); - - uint dummyNextDirOff; - TiffDirEntry[] dir; - short dircount = fetchDirectory((uint)offset, out dir, out dummyNextDirOff); - if (dircount == 0) - { - ErrorExt(this, m_clientdata, module, - "{0}: Failed to read custom directory at offset {1}", m_name, offset); - return false; - } - - FreeDirectory(); - m_dir = new TiffDirectory(); - - int fix = 0; - for (short i = 0; i < dircount; i++) - { - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - { - short temp = (short)dir[i].tdir_tag; - SwabShort(ref temp); - dir[i].tdir_tag = (TiffTag)(ushort)temp; - - temp = (short)dir[i].tdir_type; - SwabShort(ref temp); - dir[i].tdir_type = (TiffType)temp; - - SwabLong(ref dir[i].tdir_count); - SwabUInt(ref dir[i].tdir_offset); - } - - if (fix >= m_nfields || dir[i].tdir_tag == TiffTag.IGNORE) - continue; - - while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) - fix++; - - if (fix >= m_nfields || m_fieldinfo[fix].Tag != dir[i].tdir_tag) - { - WarningExt(this, m_clientdata, module, - "{0}: unknown field with tag {1} (0x{2:x}) encountered", - m_name, (ushort)dir[i].tdir_tag, (ushort)dir[i].tdir_tag); - - TiffFieldInfo[] arr = new TiffFieldInfo[1]; - arr[0] = createAnonFieldInfo(dir[i].tdir_tag, dir[i].tdir_type); - MergeFieldInfo(arr, 1); - - fix = 0; - while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) - fix++; - } - - // null out old tags that we ignore. - if (m_fieldinfo[fix].Bit == FieldBit.Ignore) - { - dir[i].tdir_tag = TiffTag.IGNORE; - continue; - } - - // Check data type. - TiffFieldInfo fip = m_fieldinfo[fix]; - while (dir[i].tdir_type != fip.Type && fix < m_nfields) - { - if (fip.Type == TiffType.ANY) - { - // wildcard - break; - } - - fip = m_fieldinfo[++fix]; - if (fix >= m_nfields || fip.Tag != dir[i].tdir_tag) - { - WarningExt(this, m_clientdata, module, - "{0}: wrong data type {1} for \"{2}\"; tag ignored", - m_name, dir[i].tdir_type, m_fieldinfo[fix - 1].Name); - - dir[i].tdir_tag = TiffTag.IGNORE; - continue; - } - } - - // Check count if known in advance. - if (fip.ReadCount != TiffFieldInfo.Variable && - fip.ReadCount != TiffFieldInfo.Variable2) - { - int expected = fip.ReadCount; - if (fip.ReadCount == TiffFieldInfo.Spp) - expected = m_dir.td_samplesperpixel; - - if (!checkDirCount(dir[i], expected)) - { - dir[i].tdir_tag = TiffTag.IGNORE; - continue; - } - } - - // EXIF tags which need to be specifically processed. - switch (dir[i].tdir_tag) - { - case TiffTag.EXIF_SUBJECTDISTANCE: - fetchSubjectDistance(dir[i]); - break; - default: - fetchNormalTag(dir[i]); - break; - } - } - - return true; - } - - /// - /// Reads an EXIF directory from the given offset within file/stream. - /// - /// The directory offset. - /// true if an EXIF directory was read successfully; - /// otherwise, false - public bool ReadEXIFDirectory(long offset) - { - int exifFieldInfoCount; - TiffFieldInfo[] exifFieldInfo = getExifFieldInfo(out exifFieldInfoCount); - return ReadCustomDirectory(offset, exifFieldInfo, exifFieldInfoCount); - } - - /// - /// Calculates the size in bytes of a row of data as it would be returned in a call to - /// , or as it would be - /// expected in a call to . - /// - /// The size in bytes of a row of data. - /// ScanlineSize calculates size for one sample plane only. Please use - /// if you want to get size in bytes of a complete - /// decoded and packed raster scanline. - /// - public int ScanlineSize() - { - int scanline; - if (m_dir.td_planarconfig == PlanarConfig.CONTIG) - { - if (m_dir.td_photometric == Photometric.YCBCR && !IsUpSampled()) - { - FieldValue[] result = GetFieldDefaulted(TiffTag.YCBCRSUBSAMPLING); - short ycbcrsubsampling0 = result[0].ToShort(); - - if (ycbcrsubsampling0 == 0) - { - ErrorExt(this, m_clientdata, m_name, "Invalid YCbCr subsampling"); - return 0; - } - - scanline = roundUp(m_dir.td_imagewidth, ycbcrsubsampling0); - scanline = howMany8(multiply(scanline, m_dir.td_bitspersample, "ScanlineSize")); - return summarize(scanline, multiply(2, scanline / ycbcrsubsampling0, "VStripSize"), "VStripSize"); - } - else - { - scanline = multiply(m_dir.td_imagewidth, m_dir.td_samplesperpixel, "ScanlineSize"); - } - } - else - { - scanline = m_dir.td_imagewidth; - } - - return howMany8(multiply(scanline, m_dir.td_bitspersample, "ScanlineSize")); - } - - /// - /// Calculates the size in bytes of a complete decoded and packed raster scanline. - /// - /// The size in bytes of a complete decoded and packed raster scanline. - /// The value returned by RasterScanlineSize may be different from the - /// value returned by if data is stored as separate - /// planes ( = .SEPARATE). - /// - public int RasterScanlineSize() - { - int scanline = multiply(m_dir.td_bitspersample, m_dir.td_imagewidth, "RasterScanlineSize"); - if (m_dir.td_planarconfig == PlanarConfig.CONTIG) - { - scanline = multiply(scanline, m_dir.td_samplesperpixel, "RasterScanlineSize"); - return howMany8(scanline); - } - - return multiply(howMany8(scanline), m_dir.td_samplesperpixel, "RasterScanlineSize"); - } - - /// - /// Computes the number of rows for a reasonable-sized strip according to the current - /// settings of the , - /// and tags and any compression-specific requirements. - /// - /// The esimated value (may be zero). - /// The number of rows for a reasonable-sized strip according to the current - /// tag settings and compression-specific requirements. - /// If the parameter is non-zero, then it is taken - /// as an estimate of the desired strip size and adjusted according to any - /// compression-specific requirements. The value returned by DefaultStripSize is - /// typically used to define the tag. If there is no - /// any unusual requirements DefaultStripSize tries to create strips that have - /// approximately 8 kilobytes of uncompressed data. - public int DefaultStripSize(int estimate) - { - return m_currentCodec.DefStripSize(estimate); - } - - /// - /// Computes the number of bytes in a row-aligned strip. - /// - /// The number of bytes in a row-aligned strip - /// - /// - /// StripSize returns the equivalent size for a strip of data as it would be - /// returned in a call to or as it would be expected in a - /// call to . - /// - /// If the value of the field corresponding to is - /// larger than the recorded , then the strip size is - /// truncated to reflect the actual space required to hold the strip. - /// - public int StripSize() - { - int rps = m_dir.td_rowsperstrip; - if (rps > m_dir.td_imagelength) - rps = m_dir.td_imagelength; - - return VStripSize(rps); - } - - /// - /// Computes the number of bytes in a row-aligned strip with specified number of rows. - /// - /// The number of rows in a strip. - /// - /// The number of bytes in a row-aligned strip with specified number of rows. - public int VStripSize(int rowCount) - { - if (rowCount == -1) - rowCount = m_dir.td_imagelength; - - if (m_dir.td_planarconfig == PlanarConfig.CONTIG && - m_dir.td_photometric == Photometric.YCBCR && !IsUpSampled()) - { - // Packed YCbCr data contain one Cb+Cr for every - // HorizontalSampling * VerticalSampling Y values. - // Must also roundup width and height when calculating since images that are not - // a multiple of the horizontal/vertical subsampling area include YCbCr data for - // the extended image. - FieldValue[] result = GetFieldDefaulted(TiffTag.YCBCRSUBSAMPLING); - short ycbcrsubsampling0 = result[0].ToShort(); - short ycbcrsubsampling1 = result[1].ToShort(); - - int samplingarea = ycbcrsubsampling0 * ycbcrsubsampling1; - if (samplingarea == 0) - { - ErrorExt(this, m_clientdata, m_name, "Invalid YCbCr subsampling"); - return 0; - } - - int w = roundUp(m_dir.td_imagewidth, ycbcrsubsampling0); - int scanline = howMany8(multiply(w, m_dir.td_bitspersample, "VStripSize")); - rowCount = roundUp(rowCount, ycbcrsubsampling1); - // NB: don't need howMany here 'cuz everything is rounded - scanline = multiply(rowCount, scanline, "VStripSize"); - return summarize(scanline, multiply(2, scanline / samplingarea, "VStripSize"), "VStripSize"); - } - - return multiply(rowCount, ScanlineSize(), "VStripSize"); - } - - /// - /// Computes the number of bytes in a raw (i.e. not decoded) strip. - /// - /// The zero-based index of a strip. - /// The number of bytes in a raw strip. - public long RawStripSize(int strip) - { - long bytecount = m_dir.td_stripbytecount[strip]; - if (bytecount <= 0) - { - ErrorExt(this, m_clientdata, m_name, - "{0}: Invalid strip byte count, strip {1}", bytecount, strip); - bytecount = -1; - } - - return bytecount; - } - - /// - /// Computes which strip contains the specified coordinates (row, plane). - /// - /// The row. - /// The sample plane. - /// The number of the strip that contains the specified coordinates. - /// - /// A valid strip number is always returned; out-of-range coordinate values are clamped to - /// the bounds of the image. The parameter is always used in - /// calculating a strip. The parameter is used only if data are - /// organized in separate planes - /// ( = .SEPARATE). - /// - public int ComputeStrip(int row, short plane) - { - int strip = 0; - if (m_dir.td_rowsperstrip != -1) - strip = row / m_dir.td_rowsperstrip; - - if (m_dir.td_planarconfig == PlanarConfig.SEPARATE) - { - if (plane >= m_dir.td_samplesperpixel) - { - ErrorExt(this, m_clientdata, m_name, "{0}: Sample out of range, max {1}", plane, m_dir.td_samplesperpixel); - return 0; - } - - strip += plane * m_dir.td_stripsperimage; - } - - return strip; - } - - /// - /// Retrives the number of strips in the image. - /// - /// The number of strips in the image. - public int NumberOfStrips() - { - int nstrips = (m_dir.td_rowsperstrip == -1 ? 1 : howMany(m_dir.td_imagelength, m_dir.td_rowsperstrip)); - if (m_dir.td_planarconfig == PlanarConfig.SEPARATE) - nstrips = multiply(nstrips, m_dir.td_samplesperpixel, "NumberOfStrips"); - - return nstrips; - } - - /// - /// Computes the pixel width and height of a reasonable-sized tile suitable for setting - /// up the and tags. - /// - /// The proposed tile width upon the call / tile width to use - /// after the call. - /// The proposed tile height upon the call / tile height to use - /// after the call. - /// If the and values passed - /// in are non-zero, then they are adjusted to reflect any compression-specific - /// requirements. The returned width and height are constrained to be a multiple of - /// 16 pixels to conform with the TIFF specification. - public void DefaultTileSize(ref int width, ref int height) - { - m_currentCodec.DefTileSize(ref width, ref height); - } - - /// - /// Compute the number of bytes in a row-aligned tile. - /// - /// The number of bytes in a row-aligned tile. - /// TileSize returns the equivalent size for a tile of data as it would be - /// returned in a call to or as it would be expected in a - /// call to . - /// - public int TileSize() - { - return VTileSize(m_dir.td_tilelength); - } - - /// - /// Computes the number of bytes in a row-aligned tile with specified number of rows. - /// - /// The number of rows in a tile. - /// - /// The number of bytes in a row-aligned tile with specified number of rows. - public int VTileSize(int rowCount) - { - if (m_dir.td_tilelength == 0 || m_dir.td_tilewidth == 0 || m_dir.td_tiledepth == 0) - return 0; - - int tilesize; - if (m_dir.td_planarconfig == PlanarConfig.CONTIG && - m_dir.td_photometric == Photometric.YCBCR && !IsUpSampled()) - { - // Packed YCbCr data contain one Cb+Cr for every - // HorizontalSampling * VerticalSampling Y values. - // Must also roundup width and height when calculating since images that are not a - // multiple of the horizontal/vertical subsampling area include YCbCr data for - // the extended image. - int w = roundUp(m_dir.td_tilewidth, m_dir.td_ycbcrsubsampling[0]); - int rowsize = howMany8(multiply(w, m_dir.td_bitspersample, "VTileSize")); - int samplingarea = m_dir.td_ycbcrsubsampling[0] * m_dir.td_ycbcrsubsampling[1]; - if (samplingarea == 0) - { - ErrorExt(this, m_clientdata, m_name, "Invalid YCbCr subsampling"); - return 0; - } - - rowCount = roundUp(rowCount, m_dir.td_ycbcrsubsampling[1]); - // NB: don't need howMany here 'cuz everything is rounded - tilesize = multiply(rowCount, rowsize, "VTileSize"); - tilesize = summarize(tilesize, multiply(2, tilesize / samplingarea, "VTileSize"), "VTileSize"); - } - else - { - tilesize = multiply(rowCount, TileRowSize(), "VTileSize"); - } - - return multiply(tilesize, m_dir.td_tiledepth, "VTileSize"); - } - - /// - /// Computes the number of bytes in a raw (i.e. not decoded) tile. - /// - /// The zero-based index of a tile. - /// The number of bytes in a raw tile. - public long RawTileSize(int tile) - { - // yes, one method for raw tile and strip sizes - return RawStripSize(tile); - } - - /// - /// Compute the number of bytes in each row of a tile. - /// - /// The number of bytes in each row of a tile. - public int TileRowSize() - { - if (m_dir.td_tilelength == 0 || m_dir.td_tilewidth == 0) - return 0; - - int rowsize = multiply(m_dir.td_bitspersample, m_dir.td_tilewidth, "TileRowSize"); - if (m_dir.td_planarconfig == PlanarConfig.CONTIG) - rowsize = multiply(rowsize, m_dir.td_samplesperpixel, "TileRowSize"); - - return howMany8(rowsize); - } - - /// - /// Computes which tile contains the specified coordinates (x, y, z, plane). - /// - /// The x-coordinate. - /// The y-coordinate. - /// The z-coordinate. - /// The sample plane. - /// The number of the tile that contains the specified coordinates. - /// - /// A valid tile number is always returned; out-of-range coordinate values are - /// clamped to the bounds of the image. The and - /// parameters are always used in calculating a tile. The parameter - /// is used if the image is deeper than 1 slice ( > 1). - /// The parameter is used only if data are organized in separate - /// planes ( = .SEPARATE). - /// - public int ComputeTile(int x, int y, int z, short plane) - { - if (m_dir.td_imagedepth == 1) - z = 0; - - int dx = m_dir.td_tilewidth; - if (dx == -1) - dx = m_dir.td_imagewidth; - - int dy = m_dir.td_tilelength; - if (dy == -1) - dy = m_dir.td_imagelength; - - int dz = m_dir.td_tiledepth; - if (dz == -1) - dz = m_dir.td_imagedepth; - - int tile = 1; - if (dx != 0 && dy != 0 && dz != 0) - { - int xpt = howMany(m_dir.td_imagewidth, dx); - int ypt = howMany(m_dir.td_imagelength, dy); - int zpt = howMany(m_dir.td_imagedepth, dz); - - if (m_dir.td_planarconfig == PlanarConfig.SEPARATE) - tile = (xpt * ypt * zpt) * plane + (xpt * ypt) * (z / dz) + xpt * (y / dy) + x / dx; - else - tile = (xpt * ypt) * (z / dz) + xpt * (y / dy) + x / dx; - } - - return tile; - } - - /// - /// Checks whether the specified (x, y, z, plane) coordinates are within the bounds of - /// the image. - /// - /// The x-coordinate. - /// The y-coordinate. - /// The z-coordinate. - /// The sample plane. - /// true if the specified coordinates are within the bounds of the image; - /// otherwise, false. - /// The parameter is checked against the value of the - /// tag. The parameter is checked - /// against the value of the tag. The - /// parameter is checked against the value of the tag - /// (if defined). The parameter is checked against the value of - /// the tag if the data are organized in separate - /// planes. - public bool CheckTile(int x, int y, int z, short plane) - { - if (x >= m_dir.td_imagewidth) - { - ErrorExt(this, m_clientdata, m_name, "{0}: Col out of range, max {1}", x, m_dir.td_imagewidth - 1); - return false; - } - - if (y >= m_dir.td_imagelength) - { - ErrorExt(this, m_clientdata, m_name, "{0}: Row out of range, max {1}", y, m_dir.td_imagelength - 1); - return false; - } - - if (z >= m_dir.td_imagedepth) - { - ErrorExt(this, m_clientdata, m_name, "{0}: Depth out of range, max {1}", z, m_dir.td_imagedepth - 1); - return false; - } - - if (m_dir.td_planarconfig == PlanarConfig.SEPARATE && plane >= m_dir.td_samplesperpixel) - { - ErrorExt(this, m_clientdata, m_name, "{0}: Sample out of range, max {1}", plane, m_dir.td_samplesperpixel - 1); - return false; - } - - return true; - } - - /// - /// Retrives the number of tiles in the image. - /// - /// The number of tiles in the image. - public int NumberOfTiles() - { - int dx = m_dir.td_tilewidth; - if (dx == -1) - dx = m_dir.td_imagewidth; - - int dy = m_dir.td_tilelength; - if (dy == -1) - dy = m_dir.td_imagelength; - - int dz = m_dir.td_tiledepth; - if (dz == -1) - dz = m_dir.td_imagedepth; - - int ntiles; - if (dx == 0 || dy == 0 || dz == 0) - { - ntiles = 0; - } - else - { - ntiles = multiply( - multiply(howMany(m_dir.td_imagewidth, dx), howMany(m_dir.td_imagelength, dy), "NumberOfTiles"), - howMany(m_dir.td_imagedepth, dz), "NumberOfTiles"); - } - - if (m_dir.td_planarconfig == PlanarConfig.SEPARATE) - ntiles = multiply(ntiles, m_dir.td_samplesperpixel, "NumberOfTiles"); - - return ntiles; - } - - /// - /// Returns the custom client data associated with this . - /// - /// The custom client data associated with this . - public object Clientdata() - { - return m_clientdata; - } - - /// - /// Asscociates a custom data with this . - /// - /// The data to associate. - /// The previously associated data. - public object SetClientdata(object data) - { - object prev = m_clientdata; - m_clientdata = data; - return prev; - } - - /// - /// Gets the mode with which the underlying file or stream was opened. - /// - /// The mode with which the underlying file or stream was opened. - public int GetMode() - { - return m_mode; - } - - /// - /// Sets the new mode for the underlying file or stream. - /// - /// The new mode for the underlying file or stream. - /// The previous mode with which the underlying file or stream was opened. - public int SetMode(int mode) - { - int prevMode = m_mode; - m_mode = mode; - return prevMode; - } - - /// - /// Gets the value indicating whether the image data of this has a - /// tiled organization. - /// - /// - /// true if the image data of this has a tiled organization or - /// false if the image data of this is organized in strips. - /// - public bool IsTiled() - { - return ((m_flags & TiffFlags.ISTILED) == TiffFlags.ISTILED); - } - - /// - /// Gets the value indicating whether the image data was in a different byte-order than - /// the host computer. - /// - /// true if the image data was in a different byte-order than the host - /// computer or false if the TIFF file/stream and local host byte-orders are the - /// same. - /// - /// Note that , , - /// and - /// methods already - /// normally perform byte swapping to local host order if needed. - /// - /// Also note that and do not - /// perform byte swapping to local host order. - /// - public bool IsByteSwapped() - { - return ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB); - } - - /// - /// Gets the value indicating whether the image data returned through the read interface - /// methods is being up-sampled. - /// - /// - /// true if the data is returned up-sampled; otherwise, false. - /// - /// The value returned by this method can be useful to applications that want to - /// calculate I/O buffer sizes to reflect this usage (though the usual strip and tile size - /// routines already do this). - public bool IsUpSampled() - { - return ((m_flags & TiffFlags.UPSAMPLED) == TiffFlags.UPSAMPLED); - } - - /// - /// Gets the value indicating whether the image data is being returned in MSB-to-LSB - /// bit order. - /// - /// - /// true if the data is being returned in MSB-to-LSB bit order (i.e with bit 0 as - /// the most significant bit); otherwise, false. - /// - public bool IsMSB2LSB() - { - return isFillOrder(FillOrder.MSB2LSB); - } - - /// - /// Gets the value indicating whether given image data was written in big-endian order. - /// - /// - /// true if given image data was written in big-endian order; otherwise, false. - /// - public bool IsBigEndian() - { - return (m_header.tiff_magic == TIFF_BIGENDIAN); - } - - /// - /// Gets the tiff stream. - /// - /// The tiff stream. - public TiffStream GetStream() - { - return m_stream; - } - - /// - /// Gets the current row that is being read or written. - /// - /// The current row that is being read or written. - /// The current row is updated each time a read or write is done. - public int CurrentRow() - { - return m_row; - } - - /// - /// Gets the zero-based index of the current directory. - /// - /// The zero-based index of the current directory. - /// The zero-based index returned by this method is suitable for use with - /// the method. - /// - public short CurrentDirectory() - { - return m_curdir; - } - - /// - /// Gets the number of directories in a file. - /// - /// The number of directories in a file. - public short NumberOfDirectories() - { - uint nextdir = m_header.tiff_diroff; - short n = 0; - long dummyOff; - while (nextdir != 0 && advanceDirectory(ref nextdir, out dummyOff)) - n++; - - return n; - } - - /// - /// Retrieves the file/stream offset of the current directory. - /// - /// The file/stream offset of the current directory. - public long CurrentDirOffset() - { - return m_diroff; - } - - /// - /// Gets the current strip that is being read or written. - /// - /// The current strip that is being read or written. - /// The current strip is updated each time a read or write is done. - public int CurrentStrip() - { - return m_curstrip; - } - - /// - /// Gets the current tile that is being read or written. - /// - /// The current tile that is being read or written. - /// The current tile is updated each time a read or write is done. - public int CurrentTile() - { - return m_curtile; - } - - /// - /// Sets up the data buffer used to read raw (encoded) data from a file. - /// - /// The data buffer. - /// The buffer size. - /// - /// - /// This method is provided for client-control of the I/O buffers used by the library. - /// Applications need never use this method; it's provided only for "intelligent clients" - /// that wish to optimize memory usage and/or eliminate potential copy operations that can - /// occur when working with images that have data stored without compression. - /// - /// - /// If the is null, then a buffer of appropriate size is - /// allocated by the library. Otherwise, the caller must guarantee that the buffer is - /// large enough to hold any individual strip of raw data. - /// - /// - public void ReadBufferSetup(byte[] buffer, int size) - { - Debug.Assert((m_flags & TiffFlags.NOREADRAW) != TiffFlags.NOREADRAW); - m_rawdata = null; - - if (buffer != null) - { - m_rawdatasize = size; - m_rawdata = buffer; - m_flags &= ~TiffFlags.MYBUFFER; - } - else - { - m_rawdatasize = roundUp(size, 1024); - if (m_rawdatasize > 0) - { - m_rawdata = new byte[m_rawdatasize]; - } - else - { - Tiff.ErrorExt(this, m_clientdata, - "ReadBufferSetup", "{0}: No space for data buffer at scanline {1}", m_name, m_row); - m_rawdatasize = 0; - } - - m_flags |= TiffFlags.MYBUFFER; - } - } - - /// - /// Sets up the data buffer used to write raw (encoded) data to a file. - /// - /// The data buffer. - /// The buffer size. - /// - /// - /// This method is provided for client-control of the I/O buffers used by the library. - /// Applications need never use this method; it's provided only for "intelligent clients" - /// that wish to optimize memory usage and/or eliminate potential copy operations that can - /// occur when working with images that have data stored without compression. - /// - /// - /// If the is -1 then the buffer size is selected to hold a - /// complete tile or strip, or at least 8 kilobytes, whichever is greater. If the - /// is null, then a buffer of appropriate size is - /// allocated by the library. - /// - /// - public void WriteBufferSetup(byte[] buffer, int size) - { - if (m_rawdata != null) - { - if ((m_flags & TiffFlags.MYBUFFER) == TiffFlags.MYBUFFER) - m_flags &= ~TiffFlags.MYBUFFER; - - m_rawdata = null; - } - - if (size == -1) - { - size = (IsTiled() ? m_tilesize : StripSize()); - - // Make raw data buffer at least 8K - if (size < 8 * 1024) - size = 8 * 1024; - - // force allocation - buffer = null; - } - - if (buffer == null) - { - buffer = new byte[size]; - m_flags |= TiffFlags.MYBUFFER; - } - else - { - m_flags &= ~TiffFlags.MYBUFFER; - } - - m_rawdata = buffer; - m_rawdatasize = size; - m_rawcc = 0; - m_rawcp = 0; - m_flags |= TiffFlags.BUFFERSETUP; - } - - /// - /// Setups the strips. - /// - /// true if setup successfully; otherwise, false - public bool SetupStrips() - { - if (IsTiled()) - m_dir.td_stripsperimage = isUnspecified(FieldBit.TileDimensions) ? m_dir.td_samplesperpixel : NumberOfTiles(); - else - m_dir.td_stripsperimage = isUnspecified(FieldBit.RowsPerStrip) ? m_dir.td_samplesperpixel : NumberOfStrips(); - - m_dir.td_nstrips = m_dir.td_stripsperimage; - - if (m_dir.td_planarconfig == PlanarConfig.SEPARATE) - m_dir.td_stripsperimage /= m_dir.td_samplesperpixel; - - m_dir.td_stripoffset = new uint[m_dir.td_nstrips]; - m_dir.td_stripbytecount = new uint[m_dir.td_nstrips]; - - setFieldBit(FieldBit.StripOffsets); - setFieldBit(FieldBit.StripByteCounts); - return true; - } - - /// - /// Verifies that file/stream is writable and that the directory information is - /// setup properly. - /// - /// If set to true then ability to write tiles will be verified; - /// otherwise, ability to write strips will be verified. - /// The name of the calling method. - /// true if file/stream is writeable and the directory information is - /// setup properly; otherwise, false - public bool WriteCheck(bool tiles, string method) - { - if (m_mode == O_RDONLY) - { - ErrorExt(this, m_clientdata, method, "{0}: File not open for writing", m_name); - return false; - } - - if (tiles ^ IsTiled()) - { - ErrorExt(this, m_clientdata, m_name, - tiles ? "Can not write tiles to a stripped image" : "Can not write scanlines to a tiled image"); - - return false; - } - - // On the first write verify all the required information has been setup and - // initialize any data structures that had to wait until directory information was set. - // Note that a lot of our work is assumed to remain valid because we disallow any of - // the important parameters from changing after we start writing (i.e. once - // BEENWRITING is set, SetField will only allow the image's length to be changed). - if (!fieldSet(FieldBit.ImageDimensions)) - { - ErrorExt(this, m_clientdata, method, "{0}: Must set \"ImageWidth\" before writing data", m_name); - return false; - } - - if (m_dir.td_samplesperpixel == 1) - { - // PlanarConfiguration is irrelevant in case of single band images and need not - // be included. We will set it anyway, because this field is used in other parts - // of library even in the single band case. - if (!fieldSet(FieldBit.PlanarConfig)) - m_dir.td_planarconfig = PlanarConfig.CONTIG; - } - else - { - if (!fieldSet(FieldBit.PlanarConfig)) - { - ErrorExt(this, m_clientdata, method, - "{0}: Must set \"PlanarConfiguration\" before writing data", m_name); - - return false; - } - } - - if (m_dir.td_stripoffset == null && !SetupStrips()) - { - m_dir.td_nstrips = 0; - ErrorExt(this, m_clientdata, method, - "{0}: No space for {1} arrays", m_name, IsTiled() ? "tile" : "strip"); - - return false; - } - - m_tilesize = IsTiled() ? TileSize() : -1; - m_scanlinesize = ScanlineSize(); - m_flags |= TiffFlags.BEENWRITING; - return true; - } - - /// - /// Releases storage associated with current directory. - /// - public void FreeDirectory() - { - if (m_dir != null) - { - clearFieldBit(FieldBit.YCbCrSubsampling); - clearFieldBit(FieldBit.YCbCrPositioning); - - m_dir = null; - } - } - - /// - /// Creates a new directory within file/stream. - /// - /// The newly created directory will not exist on the file/stream till - /// , , - /// or is called. - public void CreateDirectory() - { - // Should we automatically call WriteDirectory() - // if the current one is dirty? - - setupDefaultDirectory(); - m_diroff = 0; - m_nextdiroff = 0; - m_curoff = 0; - m_row = -1; - m_curstrip = -1; - } - - /// - /// Returns an indication of whether the current directory is the last directory - /// in the file. - /// - /// true if current directory is the last directory in the file; - /// otherwise, false. - public bool LastDirectory() - { - return (m_nextdiroff == 0); - } - - /// - /// Sets the directory with specified number as the current directory. - /// - /// The zero-based number of the directory to set as the - /// current directory. - /// true if the specified directory was set as current successfully; - /// otherwise, false - /// SetDirectory changes the current directory and reads its contents with - /// . - public bool SetDirectory(short number) - { - uint nextdir = m_header.tiff_diroff; - short n; - for (n = number; n > 0 && nextdir != 0; n--) - { - long dummyOff; - if (!advanceDirectory(ref nextdir, out dummyOff)) - return false; - } - - m_nextdiroff = nextdir; - - // Set curdir to the actual directory index. The -1 is because - // ReadDirectory will increment m_curdir after successfully reading - // the directory. - m_curdir = (short)(number - n - 1); - - // Reset m_dirnumber counter and start new list of seen directories. - // We need this to prevent IFD loops. - m_dirnumber = 0; - return ReadDirectory(); - } - - /// - /// Sets the directory at specified file/stream offset as the current directory. - /// - /// The offset from the beginnig of the file/stream to the directory - /// to set as the current directory. - /// true if the directory at specified file offset was set as current - /// successfully; otherwise, false - /// SetSubDirectory acts like , except the - /// directory is specified as a file offset instead of an index; this is required for - /// accessing subdirectories linked through a SubIFD tag (e.g. thumbnail images). - public bool SetSubDirectory(long offset) - { - m_nextdiroff = (uint)offset; - - // Reset m_dirnumber counter and start new list of seen directories. - // We need this to prevent IFD loops. - m_dirnumber = 0; - return ReadDirectory(); - } - - /// - /// Unlinks the specified directory from the directory chain. - /// - /// The zero-based number of the directory to unlink. - /// true if directory was unlinked successfully; otherwise, false. - /// UnlinkDirectory does not removes directory bytes from the file/stream. - /// It only makes them unused. - public bool UnlinkDirectory(short number) - { - const string module = "UnlinkDirectory"; - - if (m_mode == O_RDONLY) - { - ErrorExt(this, m_clientdata, module, "Can not unlink directory in read-only file"); - return false; - } - - // Go to the directory before the one we want - // to unlink and nab the offset of the link - // field we'll need to patch. - uint nextdir = m_header.tiff_diroff; - long off = sizeof(short) + sizeof(short); - for (int n = number - 1; n > 0; n--) - { - if (nextdir == 0) - { - ErrorExt(this, m_clientdata, module, - "Directory {0} does not exist", number); - return false; - } - - if (!advanceDirectory(ref nextdir, out off)) - return false; - } - - // Advance to the directory to be unlinked and fetch the offset of the directory - // that follows. - long dummyOff; - if (!advanceDirectory(ref nextdir, out dummyOff)) - return false; - - // Go back and patch the link field of the preceding directory to point to the - // offset of the directory that follows. - seekFile(off, SeekOrigin.Begin); - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabUInt(ref nextdir); - - if (!writeIntOK((int)nextdir)) - { - ErrorExt(this, m_clientdata, module, "Error writing directory link"); - return false; - } - - // Leave directory state setup safely. We don't have facilities for doing inserting - // and removing directories, so it's safest to just invalidate everything. This means - // that the caller can only append to the directory chain. - m_currentCodec.Cleanup(); - if ((m_flags & TiffFlags.MYBUFFER) == TiffFlags.MYBUFFER && m_rawdata != null) - { - m_rawdata = null; - m_rawcc = 0; - } - - m_flags &= ~(TiffFlags.BEENWRITING | TiffFlags.BUFFERSETUP | TiffFlags.POSTENCODE); - FreeDirectory(); - setupDefaultDirectory(); - m_diroff = 0; // force link on next write - m_nextdiroff = 0; // next write must be at end - m_curoff = 0; - m_row = -1; - m_curstrip = -1; - return true; - } - - /// - /// Sets the value(s) of a tag in a TIFF file/stream open for writing. - /// - /// The tag. - /// The tag value(s). - /// true if tag value(s) were set successfully; otherwise, false. - /// - /// SetField sets the value of a tag or pseudo-tag in the current directory - /// associated with the open TIFF file/stream. To set the value of a field the file/stream - /// must have been previously opened for writing with or - /// ; - /// pseudo-tags can be set whether the file was opened for - /// reading or writing. The tag is identified by . - /// The type and number of values in is dependent on the tag - /// being set. You may want to consult - /// "Well-known tags and their - /// value(s) data types" to become familiar with exact data types and calling - /// conventions required for each tag supported by the library. - /// - /// A pseudo-tag is a parameter that is used to control the operation of the library but - /// whose value is not read or written to the underlying file. - /// - /// The field will be written to the file when/if the directory structure is updated. - /// - public bool SetField(TiffTag tag, params object[] value) - { - if (okToChangeTag(tag)) - return m_tagmethods.SetField(this, tag, FieldValue.FromParams(value)); - - return false; - } - - /// - /// Writes the contents of the current directory to the file and setup to create a new - /// subfile (page) in the same file. - /// - /// true if the current directory was written successfully; - /// otherwise, false - /// Applications only need to call WriteDirectory when writing multiple - /// subfiles (pages) to a single TIFF file. WriteDirectory is automatically called - /// by and to write a modified directory if the - /// file is open for writing. - public bool WriteDirectory() - { - return writeDirectory(true); - } - - /// - /// Writes the current state of the TIFF directory into the file to make what is currently - /// in the file/stream readable. - /// - /// true if the current directory was rewritten successfully; - /// otherwise, false - /// Unlike , CheckpointDirectory does not free - /// up the directory data structures in memory, so they can be updated (as strips/tiles - /// are written) and written again. Reading such a partial file you will at worst get a - /// TIFF read error for the first strip/tile encountered that is incomplete, but you will - /// at least get all the valid data in the file before that. When the file is complete, - /// just use as usual to finish it off cleanly. - public bool CheckpointDirectory() - { - // Setup the strips arrays, if they haven't already been. - if (m_dir.td_stripoffset == null) - SetupStrips(); - - bool rc = writeDirectory(false); - SetWriteOffset(seekFile(0, SeekOrigin.End)); - return rc; - } - - /// - /// Rewrites the contents of the current directory to the file and setup to create a new - /// subfile (page) in the same file. - /// - /// true if the current directory was rewritten successfully; - /// otherwise, false - /// The RewriteDirectory operates similarly to , - /// but can be called with directories previously read or written that already have an - /// established location in the file. It will rewrite the directory, but instead of place - /// it at it's old location (as would) it will place them at - /// the end of the file, correcting the pointer from the preceeding directory or file - /// header to point to it's new location. This is particularly important in cases where - /// the size of the directory and pointed to data has grown, so it won’t fit in the space - /// available at the old location. Note that this will result in the loss of the - /// previously used directory space. - public bool RewriteDirectory() - { - const string module = "RewriteDirectory"; - - // We don't need to do anything special if it hasn't been written. - if (m_diroff == 0) - return WriteDirectory(); - - // Find and zero the pointer to this directory, so that linkDirectory will cause it to - // be added after this directories current pre-link. - - // Is it the first directory in the file? - if (m_header.tiff_diroff == m_diroff) - { - m_header.tiff_diroff = 0; - m_diroff = 0; - - seekFile(TiffHeader.TIFF_MAGIC_SIZE + TiffHeader.TIFF_VERSION_SIZE, SeekOrigin.Begin); - if (!writeIntOK((int)m_header.tiff_diroff)) - { - ErrorExt(this, m_clientdata, m_name, "Error updating TIFF header"); - return false; - } - } - else - { - uint nextdir = m_header.tiff_diroff; - do - { - short dircount; - if (!seekOK(nextdir) || !readShortOK(out dircount)) - { - ErrorExt(this, m_clientdata, module, "Error fetching directory count"); - return false; - } - - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabShort(ref dircount); - - seekFile(dircount * TiffDirEntry.SizeInBytes, SeekOrigin.Current); - - if (!readUIntOK(out nextdir)) - { - ErrorExt(this, m_clientdata, module, "Error fetching directory link"); - return false; - } - - if ((m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - SwabUInt(ref nextdir); - } - while (nextdir != m_diroff && nextdir != 0); - - // get current offset - long off = seekFile(0, SeekOrigin.Current); - seekFile(off - sizeof(int), SeekOrigin.Begin); - m_diroff = 0; - - if (!writeIntOK((int)m_diroff)) - { - ErrorExt(this, m_clientdata, module, "Error writing directory link"); - return false; - } - } - - // Now use WriteDirectory() normally. - return WriteDirectory(); - } - - /// - /// Prints formatted description of the contents of the current directory to the - /// specified stream. - /// - /// - /// Prints formatted description of the contents of the current directory to the - /// specified stream possibly using specified print options. - /// - /// The stream. - public void PrintDirectory(Stream stream) - { - PrintDirectory(stream, TiffPrintFlags.NONE); - } - - /// - /// Prints formatted description of the contents of the current directory to the - /// specified stream using specified print (formatting) options. - /// - /// The stream. - /// The print (formatting) options. - public void PrintDirectory(Stream stream, TiffPrintFlags flags) - { - const string EndOfLine = "\r\n"; - - fprintf(stream, "TIFF Directory at offset 0x{0:x} ({1})" + EndOfLine, m_diroff, m_diroff); - - if (fieldSet(FieldBit.SubFileType)) - { - fprintf(stream, " Subfile Type:"); - string sep = " "; - if ((m_dir.td_subfiletype & FileType.REDUCEDIMAGE) != 0) - { - fprintf(stream, "{0}reduced-resolution image", sep); - sep = "/"; - } - - if ((m_dir.td_subfiletype & FileType.PAGE) != 0) - { - fprintf(stream, "{0}multi-page document", sep); - sep = "/"; - } - - if ((m_dir.td_subfiletype & FileType.MASK) != 0) - fprintf(stream, "{0}transparency mask", sep); - - fprintf(stream, " ({0} = 0x{1:x})" + EndOfLine, m_dir.td_subfiletype, m_dir.td_subfiletype); - } - - if (fieldSet(FieldBit.ImageDimensions)) - { - fprintf(stream, " Image Width: {0} Image Length: {1}", m_dir.td_imagewidth, m_dir.td_imagelength); - if (fieldSet(FieldBit.ImageDepth)) - fprintf(stream, " Image Depth: {0}", m_dir.td_imagedepth); - fprintf(stream, EndOfLine); - } - - if (fieldSet(FieldBit.TileDimensions)) - { - fprintf(stream, " Tile Width: {0} Tile Length: {1}", m_dir.td_tilewidth, m_dir.td_tilelength); - if (fieldSet(FieldBit.TileDepth)) - fprintf(stream, " Tile Depth: {0}", m_dir.td_tiledepth); - fprintf(stream, EndOfLine); - } - - if (fieldSet(FieldBit.Resolution)) - { - fprintf(stream, " Resolution: {0:G}, {1:G}", m_dir.td_xresolution, m_dir.td_yresolution); - if (fieldSet(FieldBit.ResolutionUnit)) - { - switch (m_dir.td_resolutionunit) - { - case ResUnit.NONE: - fprintf(stream, " (unitless)"); - break; - case ResUnit.INCH: - fprintf(stream, " pixels/inch"); - break; - case ResUnit.CENTIMETER: - fprintf(stream, " pixels/cm"); - break; - default: - fprintf(stream, " (unit {0} = 0x{1:x})", m_dir.td_resolutionunit, m_dir.td_resolutionunit); - break; - } - } - fprintf(stream, EndOfLine); - } - - if (fieldSet(FieldBit.Position)) - fprintf(stream, " Position: {0:G}, {1:G}" + EndOfLine, m_dir.td_xposition, m_dir.td_yposition); - - if (fieldSet(FieldBit.BitsPerSample)) - fprintf(stream, " Bits/Sample: {0}" + EndOfLine, m_dir.td_bitspersample); - - if (fieldSet(FieldBit.SampleFormat)) - { - fprintf(stream, " Sample Format: "); - switch (m_dir.td_sampleformat) - { - case SampleFormat.VOID: - fprintf(stream, "void" + EndOfLine); - break; - case SampleFormat.INT: - fprintf(stream, "signed integer" + EndOfLine); - break; - case SampleFormat.UINT: - fprintf(stream, "unsigned integer" + EndOfLine); - break; - case SampleFormat.IEEEFP: - fprintf(stream, "IEEE floating point" + EndOfLine); - break; - case SampleFormat.COMPLEXINT: - fprintf(stream, "complex signed integer" + EndOfLine); - break; - case SampleFormat.COMPLEXIEEEFP: - fprintf(stream, "complex IEEE floating point" + EndOfLine); - break; - default: - fprintf(stream, "{0} (0x{1:x})" + EndOfLine, m_dir.td_sampleformat, m_dir.td_sampleformat); - break; - } - } - - if (fieldSet(FieldBit.Compression)) - { - TiffCodec c = FindCodec(m_dir.td_compression); - fprintf(stream, " Compression Scheme: "); - if (c != null) - fprintf(stream, "{0}" + EndOfLine, c.m_name); - else - fprintf(stream, "{0} (0x{1:x})" + EndOfLine, m_dir.td_compression, m_dir.td_compression); - } - - if (fieldSet(FieldBit.Photometric)) - { - fprintf(stream, " Photometric Interpretation: "); - if ((int)m_dir.td_photometric < photoNames.Length) - fprintf(stream, "{0}" + EndOfLine, photoNames[(int)m_dir.td_photometric]); - else - { - switch (m_dir.td_photometric) - { - case Photometric.LOGL: - fprintf(stream, "CIE Log2(L)" + EndOfLine); - break; - case Photometric.LOGLUV: - fprintf(stream, "CIE Log2(L) (u',v')" + EndOfLine); - break; - default: - fprintf(stream, "{0} (0x{1:x})" + EndOfLine, m_dir.td_photometric, m_dir.td_photometric); - break; - } - } - } - - if (fieldSet(FieldBit.ExtraSamples) && m_dir.td_extrasamples != 0) - { - fprintf(stream, " Extra Samples: {0}<", m_dir.td_extrasamples); - string sep = ""; - for (short i = 0; i < m_dir.td_extrasamples; i++) - { - switch (m_dir.td_sampleinfo[i]) - { - case ExtraSample.UNSPECIFIED: - fprintf(stream, "{0}unspecified", sep); - break; - case ExtraSample.ASSOCALPHA: - fprintf(stream, "{0}assoc-alpha", sep); - break; - case ExtraSample.UNASSALPHA: - fprintf(stream, "{0}unassoc-alpha", sep); - break; - default: - fprintf(stream, "{0}{1} (0x{2:x})", sep, m_dir.td_sampleinfo[i], m_dir.td_sampleinfo[i]); - break; - } - sep = ", "; - } - fprintf(stream, ">" + EndOfLine); - } - - if (fieldSet(FieldBit.InkNames)) - { - fprintf(stream, " Ink Names: "); - - string[] names = m_dir.td_inknames.Split(new char[] { '\0' }); - for (int i = 0; i < names.Length; i++) - { - printAscii(stream, names[i]); - fprintf(stream, ", "); - } - - fprintf(stream, EndOfLine); - } - - if (fieldSet(FieldBit.Thresholding)) - { - fprintf(stream, " Thresholding: "); - switch (m_dir.td_threshholding) - { - case Threshold.BILEVEL: - fprintf(stream, "bilevel art scan" + EndOfLine); - break; - case Threshold.HALFTONE: - fprintf(stream, "halftone or dithered scan" + EndOfLine); - break; - case Threshold.ERRORDIFFUSE: - fprintf(stream, "error diffused" + EndOfLine); - break; - default: - fprintf(stream, "{0} (0x{1:x})" + EndOfLine, m_dir.td_threshholding, m_dir.td_threshholding); - break; - } - } - - if (fieldSet(FieldBit.FillOrder)) - { - fprintf(stream, " FillOrder: "); - switch (m_dir.td_fillorder) - { - case FillOrder.MSB2LSB: - fprintf(stream, "msb-to-lsb" + EndOfLine); - break; - case FillOrder.LSB2MSB: - fprintf(stream, "lsb-to-msb" + EndOfLine); - break; - default: - fprintf(stream, "{0} (0x{1:x})" + EndOfLine, m_dir.td_fillorder, m_dir.td_fillorder); - break; - } - } - - if (fieldSet(FieldBit.YCbCrSubsampling)) - { - // For hacky reasons (see JpegCodecTagMethods.JPEGFixupTestSubsampling method), - // we need to fetch this rather than trust what is in our structures. - FieldValue[] result = GetField(TiffTag.YCBCRSUBSAMPLING); - short subsampling0 = result[0].ToShort(); - short subsampling1 = result[1].ToShort(); - fprintf(stream, " YCbCr Subsampling: {0}, {1}" + EndOfLine, subsampling0, subsampling1); - } - - if (fieldSet(FieldBit.YCbCrPositioning)) - { - fprintf(stream, " YCbCr Positioning: "); - switch (m_dir.td_ycbcrpositioning) - { - case YCbCrPosition.CENTERED: - fprintf(stream, "centered" + EndOfLine); - break; - case YCbCrPosition.COSITED: - fprintf(stream, "cosited" + EndOfLine); - break; - default: - fprintf(stream, "{0} (0x{1:x})" + EndOfLine, m_dir.td_ycbcrpositioning, m_dir.td_ycbcrpositioning); - break; - } - } - - if (fieldSet(FieldBit.HalftoneHints)) - fprintf(stream, " Halftone Hints: light {0} dark {1}" + EndOfLine, m_dir.td_halftonehints[0], m_dir.td_halftonehints[1]); - - if (fieldSet(FieldBit.Orientation)) - { - fprintf(stream, " Orientation: "); - if ((int)m_dir.td_orientation < orientNames.Length) - fprintf(stream, "{0}" + EndOfLine, orientNames[(int)m_dir.td_orientation]); - else - fprintf(stream, "{0} (0x{1:x})" + EndOfLine, m_dir.td_orientation, m_dir.td_orientation); - } - - if (fieldSet(FieldBit.SamplesPerPixel)) - fprintf(stream, " Samples/Pixel: {0}" + EndOfLine, m_dir.td_samplesperpixel); - - if (fieldSet(FieldBit.RowsPerStrip)) - { - fprintf(stream, " Rows/Strip: "); - if (m_dir.td_rowsperstrip == -1) - fprintf(stream, "(infinite)" + EndOfLine); - else - fprintf(stream, "{0}" + EndOfLine, m_dir.td_rowsperstrip); - } - - if (fieldSet(FieldBit.MinSampleValue)) - fprintf(stream, " Min Sample Value: {0}" + EndOfLine, m_dir.td_minsamplevalue); - - if (fieldSet(FieldBit.MaxSampleValue)) - fprintf(stream, " Max Sample Value: {0}" + EndOfLine, m_dir.td_maxsamplevalue); - - if (fieldSet(FieldBit.SMinSampleValue)) - fprintf(stream, " SMin Sample Value: {0:G}" + EndOfLine, m_dir.td_sminsamplevalue); - - if (fieldSet(FieldBit.SMaxSampleValue)) - fprintf(stream, " SMax Sample Value: {0:G}" + EndOfLine, m_dir.td_smaxsamplevalue); - - if (fieldSet(FieldBit.PlanarConfig)) - { - fprintf(stream, " Planar Configuration: "); - switch (m_dir.td_planarconfig) - { - case PlanarConfig.CONTIG: - fprintf(stream, "single image plane" + EndOfLine); - break; - case PlanarConfig.SEPARATE: - fprintf(stream, "separate image planes" + EndOfLine); - break; - default: - fprintf(stream, "{0} (0x{1:x})" + EndOfLine, m_dir.td_planarconfig, m_dir.td_planarconfig); - break; - } - } - - if (fieldSet(FieldBit.PageNumber)) - fprintf(stream, " Page Number: {0}-{1}" + EndOfLine, m_dir.td_pagenumber[0], m_dir.td_pagenumber[1]); - - if (fieldSet(FieldBit.ColorMap)) - { - fprintf(stream, " Color Map: "); - if ((flags & TiffPrintFlags.COLORMAP) != 0) - { - fprintf(stream, "" + EndOfLine); - int n = 1 << m_dir.td_bitspersample; - for (int l = 0; l < n; l++) - fprintf(stream, " {0,5}: {1,5} {2,5} {3,5}" + EndOfLine, l, m_dir.td_colormap[0][l], m_dir.td_colormap[1][l], m_dir.td_colormap[2][l]); - } - else - fprintf(stream, "(present)" + EndOfLine); - } - - if (fieldSet(FieldBit.TransferFunction)) - { - fprintf(stream, " Transfer Function: "); - if ((flags & TiffPrintFlags.CURVES) != 0) - { - fprintf(stream, "" + EndOfLine); - int n = 1 << m_dir.td_bitspersample; - for (int l = 0; l < n; l++) - { - fprintf(stream, " {0,2}: {0,5}", l, m_dir.td_transferfunction[0][l]); - for (short i = 1; i < m_dir.td_samplesperpixel; i++) - fprintf(stream, " {0,5}", m_dir.td_transferfunction[i][l]); - fprintf(stream, "" + EndOfLine); - } - } - else - fprintf(stream, "(present)" + EndOfLine); - } - - if (fieldSet(FieldBit.SubIFD) && m_dir.td_subifd != null) - { - fprintf(stream, " SubIFD Offsets:"); - for (short i = 0; i < m_dir.td_nsubifd; i++) - fprintf(stream, " {0,5}", m_dir.td_subifd[i]); - fprintf(stream, "" + EndOfLine); - } - - // Custom tag support. - - int count = GetTagListCount(); - for (int i = 0; i < count; i++) - { - TiffTag tag = (TiffTag)GetTagListEntry(i); - TiffFieldInfo fip = FieldWithTag(tag); - if (fip == null) - continue; - - byte[] raw_data = null; - int value_count; - if (fip.PassCount) - { - FieldValue[] result = GetField(tag); - if (result == null) - continue; - - value_count = result[0].ToInt(); - raw_data = result[1].ToByteArray(); - } - else - { - if (fip.ReadCount == TiffFieldInfo.Variable || - fip.ReadCount == TiffFieldInfo.Variable2) - { - value_count = 1; - } - else if (fip.ReadCount == TiffFieldInfo.Spp) - { - value_count = m_dir.td_samplesperpixel; - } - else - { - value_count = fip.ReadCount; - } - - if ((fip.Type == TiffType.ASCII || - fip.ReadCount == TiffFieldInfo.Variable || - fip.ReadCount == TiffFieldInfo.Variable2 || - fip.ReadCount == TiffFieldInfo.Spp || - value_count > 1) && - fip.Tag != TiffTag.PAGENUMBER && - fip.Tag != TiffTag.HALFTONEHINTS && - fip.Tag != TiffTag.YCBCRSUBSAMPLING && - fip.Tag != TiffTag.DOTRANGE) - { - FieldValue[] result = GetField(tag); - if (result == null) - continue; - - raw_data = result[0].ToByteArray(); - } - else if (fip.Tag != TiffTag.PAGENUMBER && - fip.Tag != TiffTag.HALFTONEHINTS && - fip.Tag != TiffTag.YCBCRSUBSAMPLING && - fip.Tag != TiffTag.DOTRANGE) - { - raw_data = new byte[dataSize(fip.Type) * value_count]; - - FieldValue[] result = GetField(tag); - if (result == null) - continue; - - raw_data = result[0].ToByteArray(); - } - else - { - // XXX: Should be fixed and removed, see the notes - // related to PAGENUMBER, HALFTONEHINTS, - // YCBCRSUBSAMPLING and DOTRANGE tags - raw_data = new byte[dataSize(fip.Type) * value_count]; - - FieldValue[] result = GetField(tag); - if (result == null) - continue; - - byte[] first = result[0].ToByteArray(); - byte[] second = result[1].ToByteArray(); - - Buffer.BlockCopy(first, 0, raw_data, 0, first.Length); - Buffer.BlockCopy(second, 0, raw_data, dataSize(fip.Type), second.Length); - } - } - - // Catch the tags which needs to be specially handled and - // pretty print them. If tag not handled in prettyPrintField() - // fall down and print it as any other tag. - if (prettyPrintField(stream, tag, value_count, raw_data)) - continue; - else - printField(stream, fip, value_count, raw_data); - } - - m_tagmethods.PrintDir(this, stream, flags); - - if ((flags & TiffPrintFlags.STRIPS) != 0 && fieldSet(FieldBit.StripOffsets)) - { - fprintf(stream, " {0} {1}:" + EndOfLine, m_dir.td_nstrips, IsTiled() ? "Tiles" : "Strips"); - for (int s = 0; s < m_dir.td_nstrips; s++) - fprintf(stream, " {0,3}: [{0,8}, {0,8}]" + EndOfLine, s, m_dir.td_stripoffset[s], m_dir.td_stripbytecount[s]); - } - } - - /// - /// Reads and decodes a scanline of data from an open TIFF file/stream. - /// - /// - /// Reads and decodes a scanline of data from an open TIFF file/stream. - /// - /// The buffer to place read and decoded image data to. - /// The zero-based index of scanline (row) to read. - /// - /// true if image data were read and decoded successfully; otherwise, false - /// - /// - /// - /// ReadScanline reads the data for the specified into the - /// user supplied data buffer . The data are returned - /// decompressed and, in the native byte- and bit-ordering, but are otherwise packed - /// (see further below). The must be large enough to hold an - /// entire scanline of data. Applications should call the - /// to find out the size (in bytes) of a scanline buffer. Applications should use - /// or - /// and specify correct sample plane if - /// image data are organized in separate planes - /// ( = .SEPARATE). - /// - /// - /// The library attempts to hide bit- and byte-ordering differences between the image and - /// the native machine by converting data to the native machine order. Bit reversal is - /// done if the value of tag is opposite to the native - /// machine bit order. 16- and 32-bit samples are automatically byte-swapped if the file - /// was written with a byte order opposite to the native machine byte order. - /// - /// - public bool ReadScanline(byte[] buffer, int row) - { - return ReadScanline(buffer, 0, row, 0); - } - - /// - /// Reads and decodes a scanline of data from an open TIFF file/stream. - /// - /// The buffer to place read and decoded image data to. - /// The zero-based index of scanline (row) to read. - /// The zero-based index of the sample plane. - /// - /// true if image data were read and decoded successfully; otherwise, false - /// - /// - /// - /// ReadScanline reads the data for the specified and - /// specified sample plane into the user supplied data buffer - /// . The data are returned decompressed and, in the native - /// byte- and bit-ordering, but are otherwise packed (see further below). The - /// must be large enough to hold an entire scanline of data. - /// Applications should call the to find out the size (in - /// bytes) of a scanline buffer. Applications may use - /// or specify 0 for - /// parameter if image data is contiguous (i.e not organized in separate planes, - /// = .CONTIG). - /// - /// - /// The library attempts to hide bit- and byte-ordering differences between the image and - /// the native machine by converting data to the native machine order. Bit reversal is - /// done if the value of tag is opposite to the native - /// machine bit order. 16- and 32-bit samples are automatically byte-swapped if the file - /// was written with a byte order opposite to the native machine byte order. - /// - /// - public bool ReadScanline(byte[] buffer, int row, short plane) - { - return ReadScanline(buffer, 0, row, plane); - } - - /// - /// Reads and decodes a scanline of data from an open TIFF file/stream. - /// - /// The buffer to place read and decoded image data to. - /// The zero-based byte offset in at which - /// to begin storing read and decoded bytes. - /// The zero-based index of scanline (row) to read. - /// The zero-based index of the sample plane. - /// - /// true if image data were read and decoded successfully; otherwise, false - /// - /// - /// - /// ReadScanline reads the data for the specified and - /// specified sample plane into the user supplied data buffer - /// . The data are returned decompressed and, in the native - /// byte- and bit-ordering, but are otherwise packed (see further below). The - /// must be large enough to hold an entire scanline of data. - /// Applications should call the to find out the size (in - /// bytes) of a scanline buffer. Applications may use - /// or specify 0 for - /// parameter if image data is contiguous (i.e not organized in separate planes, - /// = .CONTIG). - /// - /// - /// The library attempts to hide bit- and byte-ordering differences between the image and - /// the native machine by converting data to the native machine order. Bit reversal is - /// done if the value of tag is opposite to the native - /// machine bit order. 16- and 32-bit samples are automatically byte-swapped if the file - /// was written with a byte order opposite to the native machine byte order. - /// - /// - public bool ReadScanline(byte[] buffer, int offset, int row, short plane) - { - if (!checkRead(false)) - return false; - - bool e = seek(row, plane); - if (e) - { - // Decompress desired row into user buffer. - e = m_currentCodec.DecodeRow(buffer, offset, m_scanlinesize, plane); - - // we are now poised at the beginning of the next row - m_row = row + 1; - - if (e) - postDecode(buffer, offset, m_scanlinesize); - } - - return e; - } - - /// - /// Encodes and writes a scanline of data to an open TIFF file/stream. - /// - /// Encodes and writes a scanline of data to an open TIFF file/stream. - /// The buffer with image data to be encoded and written. - /// The zero-based index of scanline (row) to place encoded data at. - /// - /// true if image data were encoded and written successfully; otherwise, false - /// - /// - /// - /// WriteScanline encodes and writes to a file at the specified - /// . Applications should use - /// or - /// and specify correct sample plane - /// parameter if image data in a file/stream is organized in separate planes (i.e - /// = .SEPARATE). - /// - /// The data are assumed to be uncompressed and in the native bit- and byte-order of the - /// host machine. The data written to the file is compressed according to the compression - /// scheme of the current TIFF directory (see further below). If the current scanline is - /// past the end of the current subfile, the value of - /// tag is automatically increased to include the scanline (except for - /// = .SEPARATE, where the - /// tag cannot be changed once the first data are - /// written). If the is increased, the values of - /// and tags are - /// similarly enlarged to reflect data written past the previous end of image. - /// - /// The library writes encoded data using the native machine byte order. Correctly - /// implemented TIFF readers are expected to do any necessary byte-swapping to correctly - /// process image data with value of tag greater - /// than 8. The library attempts to hide bit-ordering differences between the image and - /// the native machine by converting data from the native machine order. - /// - /// Once data are written to a file/stream for the current directory, the values of - /// certain tags may not be altered; see - /// "Well-known tags and their - /// value(s) data types" for more information. - /// - /// It is not possible to write scanlines to a file/stream that uses a tiled organization. - /// The can be used to determine if the file/stream is organized as - /// tiles or strips. - /// - public bool WriteScanline(byte[] buffer, int row) - { - return WriteScanline(buffer, 0, row, 0); - } - - /// - /// Encodes and writes a scanline of data to an open TIFF file/stream. - /// - /// The buffer with image data to be encoded and written. - /// The zero-based index of scanline (row) to place encoded data at. - /// The zero-based index of the sample plane. - /// - /// true if image data were encoded and written successfully; otherwise, false - /// - /// - /// - /// WriteScanline encodes and writes to a file at the specified - /// and specified sample plane . - /// Applications may use or specify 0 for - /// parameter if image data in a file/stream is contiguous (i.e - /// not organized in separate planes, - /// = .CONTIG). - /// - /// The data are assumed to be uncompressed and in the native bit- and byte-order of the - /// host machine. The data written to the file is compressed according to the compression - /// scheme of the current TIFF directory (see further below). If the current scanline is - /// past the end of the current subfile, the value of - /// tag is automatically increased to include the scanline (except for - /// = .SEPARATE, where the - /// tag cannot be changed once the first data are - /// written). If the is increased, the values of - /// and tags are - /// similarly enlarged to reflect data written past the previous end of image. - /// - /// The library writes encoded data using the native machine byte order. Correctly - /// implemented TIFF readers are expected to do any necessary byte-swapping to correctly - /// process image data with value of tag greater - /// than 8. The library attempts to hide bit-ordering differences between the image and - /// the native machine by converting data from the native machine order. - /// - /// Once data are written to a file/stream for the current directory, the values of - /// certain tags may not be altered; see - /// "Well-known tags and their - /// value(s) data types" for more information. - /// - /// It is not possible to write scanlines to a file/stream that uses a tiled organization. - /// The can be used to determine if the file/stream is organized as - /// tiles or strips. - /// - public bool WriteScanline(byte[] buffer, int row, short plane) - { - return WriteScanline(buffer, 0, row, plane); - } - - /// - /// Encodes and writes a scanline of data to an open TIFF file/stream. - /// - /// The buffer with image data to be encoded and written. - /// The zero-based byte offset in at which - /// to begin reading bytes. - /// The zero-based index of scanline (row) to place encoded data at. - /// The zero-based index of the sample plane. - /// - /// true if image data were encoded and written successfully; otherwise, false - /// - /// - /// - /// WriteScanline encodes and writes to a file at the specified - /// and specified sample plane . - /// Applications may use or specify 0 for - /// parameter if image data in a file/stream is contiguous (i.e - /// not organized in separate planes, - /// = .CONTIG). - /// - /// The data are assumed to be uncompressed and in the native bit- and byte-order of the - /// host machine. The data written to the file is compressed according to the compression - /// scheme of the current TIFF directory (see further below). If the current scanline is - /// past the end of the current subfile, the value of - /// tag is automatically increased to include the scanline (except for - /// = .CONTIG, where the - /// tag cannot be changed once the first data are - /// written). If the is increased, the values of - /// and tags are - /// similarly enlarged to reflect data written past the previous end of image. - /// - /// The library writes encoded data using the native machine byte order. Correctly - /// implemented TIFF readers are expected to do any necessary byte-swapping to correctly - /// process image data with value of tag greater - /// than 8. The library attempts to hide bit-ordering differences between the image and - /// the native machine by converting data from the native machine order. - /// - /// Once data are written to a file/stream for the current directory, the values of - /// certain tags may not be altered; see - /// "Well-known tags and their - /// value(s) data types" for more information. - /// - /// It is not possible to write scanlines to a file/stream that uses a tiled organization. - /// The can be used to determine if the file/stream is organized as - /// tiles or strips. - /// - public bool WriteScanline(byte[] buffer, int offset, int row, short plane) - { - const string module = "WriteScanline"; - - if (!writeCheckStrips(module)) - return false; - - // Handle delayed allocation of data buffer. This permits it to be - // sized more intelligently (using directory information). - bufferCheck(); - - // Extend image length if needed (but only for PlanarConfig.CONTIG). - bool imagegrew = false; - if (row >= m_dir.td_imagelength) - { - // extend image - if (m_dir.td_planarconfig == PlanarConfig.SEPARATE) - { - ErrorExt(this, m_clientdata, m_name, "Can not change \"ImageLength\" when using separate planes"); - return false; - } - - m_dir.td_imagelength = row + 1; - imagegrew = true; - } - - // Calculate strip and check for crossings. - int strip; - if (m_dir.td_planarconfig == PlanarConfig.SEPARATE) - { - if (plane >= m_dir.td_samplesperpixel) - { - ErrorExt(this, m_clientdata, m_name, - "{0}: Sample out of range, max {1}", plane, m_dir.td_samplesperpixel); - return false; - } - - if (m_dir.td_rowsperstrip != -1) - strip = plane * m_dir.td_stripsperimage + row / m_dir.td_rowsperstrip; - else - strip = 0; - } - else - { - if (m_dir.td_rowsperstrip != -1) - strip = row / m_dir.td_rowsperstrip; - else - strip = 0; - } - - // Check strip array to make sure there's space. We don't support - // dynamically growing files that have data organized in separate - // bitplanes because it's too painful. In that case we require that - // the imagelength be set properly before the first write (so that - // the strips array will be fully allocated above). - if (strip >= m_dir.td_nstrips && !growStrips(1)) - return false; - - if (strip != m_curstrip) - { - // Changing strips - flush any data present. - if (!FlushData()) - return false; - - m_curstrip = (int)strip; - - // Watch out for a growing image. The value of strips/image - // will initially be 1 (since it can't be deduced until the - // imagelength is known). - if (strip >= m_dir.td_stripsperimage && imagegrew) - m_dir.td_stripsperimage = howMany(m_dir.td_imagelength, m_dir.td_rowsperstrip); - - m_row = (strip % m_dir.td_stripsperimage) * m_dir.td_rowsperstrip; - if ((m_flags & TiffFlags.CODERSETUP) != TiffFlags.CODERSETUP) - { - if (!m_currentCodec.SetupEncode()) - return false; - - m_flags |= TiffFlags.CODERSETUP; - } - - m_rawcc = 0; - m_rawcp = 0; - - if (m_dir.td_stripbytecount[strip] > 0) - { - // if we are writing over existing tiles, zero length - m_dir.td_stripbytecount[strip] = 0; - - // this forces appendToStrip() to do a seek - m_curoff = 0; - } - - if (!m_currentCodec.PreEncode(plane)) - return false; - - m_flags |= TiffFlags.POSTENCODE; - } - - // Ensure the write is either sequential or at the beginning of a - // strip (or that we can randomly access the data - i.e. no encoding). - if (row != m_row) - { - if (row < m_row) - { - // Moving backwards within the same strip: - // backup to the start and then decode forward (below). - m_row = (strip % m_dir.td_stripsperimage) * m_dir.td_rowsperstrip; - m_rawcp = 0; - } - - // Seek forward to the desired row. - if (!m_currentCodec.Seek(row - m_row)) - return false; - - m_row = row; - } - - // swab if needed - note that source buffer will be altered - postDecode(buffer, offset, m_scanlinesize); - - bool status = m_currentCodec.EncodeRow(buffer, offset, m_scanlinesize, plane); - - // we are now poised at the beginning of the next row - m_row = row + 1; - return status; - } - - /// - /// Reads the image and decodes it into RGBA format raster. - /// - /// - /// Reads the image and decodes it into RGBA format raster. - /// - /// The raster width. - /// The raster height. - /// The raster (the buffer to place decoded image data to). - /// true if the image was successfully read and converted; otherwise, - /// false is returned if an error was encountered. - /// - /// ReadRGBAImage reads a strip- or tile-based image into memory, storing the - /// result in the user supplied RGBA . The raster is assumed to - /// be an array of times 32-bit entries, - /// where must be less than or equal to the width of the image - /// ( may be any non-zero size). If the raster dimensions are - /// smaller than the image, the image data is cropped to the raster bounds. If the raster - /// height is greater than that of the image, then the image data are placed in the lower - /// part of the raster. Note that the raster is assumed to be organized such that the - /// pixel at location (x, y) is [y * width + x]; with the raster - /// origin in the lower-left hand corner. Please use - /// if you - /// want to specify another raster origin. - /// - /// Raster pixels are 8-bit packed red, green, blue, alpha samples. The - /// , , , and - /// should be used to access individual samples. Images without - /// Associated Alpha matting information have a constant Alpha of 1.0 (255). - /// - /// ReadRGBAImage converts non-8-bit images by scaling sample values. Palette, - /// grayscale, bilevel, CMYK, and YCbCr images are converted to RGB transparently. Raster - /// pixels are returned uncorrected by any colorimetry information present in the directory. - /// - /// Samples must be either 1, 2, 4, 8, or 16 bits. Colorimetric samples/pixel must be - /// either 1, 3, or 4 (i.e. SamplesPerPixel minus ExtraSamples). - /// - /// Palette image colormaps that appear to be incorrectly written as 8-bit values are - /// automatically scaled to 16-bits. - /// - /// ReadRGBAImage is just a wrapper around the more general - /// facilities. - /// - /// All error messages are directed to the current error handler. - /// - /// - /// - /// - /// - public bool ReadRGBAImage(int width, int height, int[] raster) - { - return ReadRGBAImage(width, height, raster, false); - } - - /// - /// Reads the image and decodes it into RGBA format raster. - /// - /// The raster width. - /// The raster height. - /// The raster (the buffer to place decoded image data to). - /// if set to true then an error will terminate the - /// operation; otherwise method will continue processing data until all the possible data - /// in the image have been requested. - /// - /// true if the image was successfully read and converted; otherwise, false - /// is returned if an error was encountered and stopOnError is false. - /// - /// - /// ReadRGBAImage reads a strip- or tile-based image into memory, storing the - /// result in the user supplied RGBA . The raster is assumed to - /// be an array of times 32-bit entries, - /// where must be less than or equal to the width of the image - /// ( may be any non-zero size). If the raster dimensions are - /// smaller than the image, the image data is cropped to the raster bounds. If the raster - /// height is greater than that of the image, then the image data are placed in the lower - /// part of the raster. Note that the raster is assumed to be organized such that the - /// pixel at location (x, y) is [y * width + x]; with the raster - /// origin in the lower-left hand corner. Please use - /// if you - /// want to specify another raster origin. - /// - /// Raster pixels are 8-bit packed red, green, blue, alpha samples. The - /// , , , and - /// should be used to access individual samples. Images without - /// Associated Alpha matting information have a constant Alpha of 1.0 (255). - /// - /// ReadRGBAImage converts non-8-bit images by scaling sample values. Palette, - /// grayscale, bilevel, CMYK, and YCbCr images are converted to RGB transparently. Raster - /// pixels are returned uncorrected by any colorimetry information present in the directory. - /// - /// Samples must be either 1, 2, 4, 8, or 16 bits. Colorimetric samples/pixel must be - /// either 1, 3, or 4 (i.e. SamplesPerPixel minus ExtraSamples). - /// - /// Palette image colormaps that appear to be incorrectly written as 8-bit values are - /// automatically scaled to 16-bits. - /// - /// ReadRGBAImage is just a wrapper around the more general - /// facilities. - /// - /// All error messages are directed to the current error handler. - /// - /// - /// - /// - /// - public bool ReadRGBAImage(int width, int height, int[] raster, bool stopOnError) - { - return ReadRGBAImageOriented(width, height, raster, Orientation.BOTLEFT, stopOnError); - } - - /// - /// Reads the image and decodes it into RGBA format raster using specified raster origin. - /// - /// - /// Reads the image and decodes it into RGBA format raster using specified raster origin. - /// - /// The raster width. - /// The raster height. - /// The raster (the buffer to place decoded image data to). - /// The raster origin position. - /// - /// true if the image was successfully read and converted; otherwise, false - /// is returned if an error was encountered. - /// - /// - /// ReadRGBAImageOriented reads a strip- or tile-based image into memory, storing the - /// result in the user supplied RGBA . The raster is assumed to - /// be an array of times 32-bit entries, - /// where must be less than or equal to the width of the image - /// ( may be any non-zero size). If the raster dimensions are - /// smaller than the image, the image data is cropped to the raster bounds. If the raster - /// height is greater than that of the image, then the image data placement depends on - /// . Note that the raster is assumed to be organized such - /// that the pixel at location (x, y) is [y * width + x]; with - /// the raster origin specified by parameter. - /// - /// When ReadRGBAImageOriented is used with .BOTLEFT for - /// the the produced result is the same as retuned by - /// . - /// - /// Raster pixels are 8-bit packed red, green, blue, alpha samples. The - /// , , , and - /// should be used to access individual samples. Images without - /// Associated Alpha matting information have a constant Alpha of 1.0 (255). - /// - /// ReadRGBAImageOriented converts non-8-bit images by scaling sample values. - /// Palette, grayscale, bilevel, CMYK, and YCbCr images are converted to RGB transparently. - /// Raster pixels are returned uncorrected by any colorimetry information present in - /// the directory. - /// - /// Samples must be either 1, 2, 4, 8, or 16 bits. Colorimetric samples/pixel must be - /// either 1, 3, or 4 (i.e. SamplesPerPixel minus ExtraSamples). - /// - /// Palette image colormaps that appear to be incorrectly written as 8-bit values are - /// automatically scaled to 16-bits. - /// - /// ReadRGBAImageOriented is just a wrapper around the more general - /// facilities. - /// - /// All error messages are directed to the current error handler. - /// - /// - /// - /// - /// - public bool ReadRGBAImageOriented(int width, int height, int[] raster, Orientation orientation) - { - return ReadRGBAImageOriented(width, height, raster, orientation, false); - } - - /// - /// Reads the image and decodes it into RGBA format raster using specified raster origin. - /// - /// The raster width. - /// The raster height. - /// The raster (the buffer to place decoded image data to). - /// The raster origin position. - /// if set to true then an error will terminate the - /// operation; otherwise method will continue processing data until all the possible data - /// in the image have been requested. - /// - /// true if the image was successfully read and converted; otherwise, false - /// is returned if an error was encountered and stopOnError is false. - /// - /// - /// ReadRGBAImageOriented reads a strip- or tile-based image into memory, storing the - /// result in the user supplied RGBA . The raster is assumed to - /// be an array of times 32-bit entries, - /// where must be less than or equal to the width of the image - /// ( may be any non-zero size). If the raster dimensions are - /// smaller than the image, the image data is cropped to the raster bounds. If the raster - /// height is greater than that of the image, then the image data placement depends on - /// . Note that the raster is assumed to be organized such - /// that the pixel at location (x, y) is [y * width + x]; with - /// the raster origin specified by parameter. - /// - /// When ReadRGBAImageOriented is used with .BOTLEFT for - /// the the produced result is the same as retuned by - /// . - /// - /// Raster pixels are 8-bit packed red, green, blue, alpha samples. The - /// , , , and - /// should be used to access individual samples. Images without - /// Associated Alpha matting information have a constant Alpha of 1.0 (255). - /// - /// ReadRGBAImageOriented converts non-8-bit images by scaling sample values. - /// Palette, grayscale, bilevel, CMYK, and YCbCr images are converted to RGB transparently. - /// Raster pixels are returned uncorrected by any colorimetry information present in - /// the directory. - /// - /// Samples must be either 1, 2, 4, 8, or 16 bits. Colorimetric samples/pixel must be - /// either 1, 3, or 4 (i.e. SamplesPerPixel minus ExtraSamples). - /// - /// Palette image colormaps that appear to be incorrectly written as 8-bit values are - /// automatically scaled to 16-bits. - /// - /// ReadRGBAImageOriented is just a wrapper around the more general - /// facilities. - /// - /// All error messages are directed to the current error handler. - /// - /// - /// - /// - /// - public bool ReadRGBAImageOriented(int width, int height, int[] raster, Orientation orientation, bool stopOnError) - { - bool ok = false; - string emsg; - if (RGBAImageOK(out emsg)) - { - TiffRgbaImage img = TiffRgbaImage.Create(this, stopOnError, out emsg); - if (img != null) - { - img.ReqOrientation = orientation; - // XXX verify rwidth and rheight against image width and height - ok = img.GetRaster(raster, (height - img.Height) * width, width, img.Height); - } - } - else - { - ErrorExt(this, m_clientdata, FileName(), "{0}", emsg); - ok = false; - } - - return ok; - } - - /// - /// Reads a whole strip of a strip-based image, decodes it and converts it to RGBA format. - /// - /// The row. - /// The RGBA raster. - /// true if the strip was successfully read and converted; otherwise, - /// false - /// - /// - /// ReadRGBAStrip reads a single strip of a strip-based image into memory, storing - /// the result in the user supplied RGBA . If specified strip is - /// the last strip, then it will only contain the portion of the strip that is actually - /// within the image space. The raster is assumed to be an array of width times - /// rowsperstrip 32-bit entries, where width is the width of the image - /// () and rowsperstrip is the maximum lines in a strip - /// (). - /// - /// The value should be the row of the first row in the strip - /// (strip * rowsperstrip, zero based). - /// - /// Note that the raster is assumed to be organized such that the pixel at location (x, y) - /// is [y * width + x]; with the raster origin in the lower-left - /// hand corner of the strip. That is bottom to top organization. When reading a partial - /// last strip in the file the last line of the image will begin at the beginning of - /// the buffer. - /// - /// Raster pixels are 8-bit packed red, green, blue, alpha samples. The - /// , , , and - /// should be used to access individual samples. Images without - /// Associated Alpha matting information have a constant Alpha of 1.0 (255). - /// - /// See for more details on how various image types are - /// converted to RGBA values. - /// - /// Samples must be either 1, 2, 4, 8, or 16 bits. Colorimetric samples/pixel must be - /// either 1, 3, or 4 (i.e. SamplesPerPixel minus ExtraSamples). - /// - /// Palette image colormaps that appear to be incorrectly written as 8-bit values are - /// automatically scaled to 16-bits. - /// - /// ReadRGBAStrip's main advantage over the similar - /// function is that for - /// large images a single buffer capable of holding the whole image doesn't need to be - /// allocated, only enough for one strip. The function does a - /// similar operation for tiled images. - /// - /// ReadRGBAStrip is just a wrapper around the more general - /// facilities. - /// - /// All error messages are directed to the current error handler. - /// - /// - /// - /// - /// - public bool ReadRGBAStrip(int row, int[] raster) - { - if (IsTiled()) - { - ErrorExt(this, m_clientdata, FileName(), "Can't use ReadRGBAStrip() with tiled file."); - return false; - } - - FieldValue[] result = GetFieldDefaulted(TiffTag.ROWSPERSTRIP); - int rowsperstrip = result[0].ToInt(); - if ((row % rowsperstrip) != 0) - { - ErrorExt(this, m_clientdata, FileName(), "Row passed to ReadRGBAStrip() must be first in a strip."); - return false; - } - - bool ok; - string emsg; - if (RGBAImageOK(out emsg)) - { - TiffRgbaImage img = TiffRgbaImage.Create(this, false, out emsg); - if (img != null) - { - img.row_offset = row; - img.col_offset = 0; - - int rows_to_read = rowsperstrip; - if (row + rowsperstrip > img.Height) - rows_to_read = img.Height - row; - - ok = img.GetRaster(raster, 0, img.Width, rows_to_read); - return ok; - } - - return true; - } - - ErrorExt(this, m_clientdata, FileName(), "{0}", emsg); - return false; - } - - - /// - /// Reads a whole tile of a tile-based image, decodes it and converts it to RGBA format. - /// - /// The column. - /// The row. - /// The RGBA raster. - /// true if the strip was successfully read and converted; otherwise, - /// false - /// - /// ReadRGBATile reads a single tile of a tile-based image into memory, - /// storing the result in the user supplied RGBA . The raster is - /// assumed to be an array of width times length 32-bit entries, where width is the width - /// of the tile () and length is the height of a tile - /// (). - /// - /// The and values are the offsets from the - /// top left corner of the image to the top left corner of the tile to be read. They must - /// be an exact multiple of the tile width and length. - /// - /// Note that the raster is assumed to be organized such that the pixel at location (x, y) - /// is [y * width + x]; with the raster origin in the lower-left - /// hand corner of the tile. That is bottom to top organization. Edge tiles which partly - /// fall off the image will be filled out with appropriate zeroed areas. - /// - /// Raster pixels are 8-bit packed red, green, blue, alpha samples. The - /// , , , and - /// should be used to access individual samples. Images without - /// Associated Alpha matting information have a constant Alpha of 1.0 (255). - /// - /// See for more details on how various image types are - /// converted to RGBA values. - /// - /// Samples must be either 1, 2, 4, 8, or 16 bits. Colorimetric samples/pixel must be - /// either 1, 3, or 4 (i.e. SamplesPerPixel minus ExtraSamples). - /// - /// Palette image colormaps that appear to be incorrectly written as 8-bit values are - /// automatically scaled to 16-bits. - /// - /// ReadRGBATile's main advantage over the similar - /// function is that for - /// large images a single buffer capable of holding the whole image doesn't need to be - /// allocated, only enough for one tile. The function does a - /// similar operation for stripped images. - /// - /// ReadRGBATile is just a wrapper around the more general - /// facilities. - /// - /// All error messages are directed to the current error handler. - /// - /// - /// - /// - /// - public bool ReadRGBATile(int col, int row, int[] raster) - { - // Verify that our request is legal - on a tile file, and on a tile boundary. - if (!IsTiled()) - { - ErrorExt(this, m_clientdata, FileName(), "Can't use ReadRGBATile() with stripped file."); - return false; - } - - FieldValue[] result = GetFieldDefaulted(TiffTag.TILEWIDTH); - int tile_xsize = result[0].ToInt(); - result = GetFieldDefaulted(TiffTag.TILELENGTH); - int tile_ysize = result[0].ToInt(); - - if ((col % tile_xsize) != 0 || (row % tile_ysize) != 0) - { - ErrorExt(this, m_clientdata, FileName(), "Row/col passed to ReadRGBATile() must be topleft corner of a tile."); - return false; - } - - // Setup the RGBA reader. - string emsg; - TiffRgbaImage img = TiffRgbaImage.Create(this, false, out emsg); - if (!RGBAImageOK(out emsg) || img == null) - { - ErrorExt(this, m_clientdata, FileName(), "{0}", emsg); - return false; - } - - // The TiffRgbaImage.Get() function doesn't allow us to get off the edge of the - // image, even to fill an otherwise valid tile. So we figure out how much we can read, - // and fix up the tile buffer to a full tile configuration afterwards. - int read_ysize; - if (row + tile_ysize > img.Height) - read_ysize = img.Height - row; - else - read_ysize = tile_ysize; - - int read_xsize; - if (col + tile_xsize > img.Width) - read_xsize = img.Width - col; - else - read_xsize = tile_xsize; - - // Read the chunk of imagery. - img.row_offset = row; - img.col_offset = col; - - bool ok = img.GetRaster(raster, 0, read_xsize, read_ysize); - - // If our read was incomplete we will need to fix up the tile by shifting the data - // around as if a full tile of data is being returned. This is all the more - // complicated because the image is organized in bottom to top format. - if (read_xsize == tile_xsize && read_ysize == tile_ysize) - return ok; - - for (int i_row = 0; i_row < read_ysize; i_row++) - { - Buffer.BlockCopy(raster, (read_ysize - i_row - 1) * read_xsize * sizeof(int), - raster, (tile_ysize - i_row - 1) * tile_xsize * sizeof(int), read_xsize * sizeof(int)); - - Array.Clear(raster, (tile_ysize - i_row - 1) * tile_xsize + read_xsize, tile_xsize - read_xsize); - } - - for (int i_row = read_ysize; i_row < tile_ysize; i_row++) - Array.Clear(raster, (tile_ysize - i_row - 1) * tile_xsize, tile_xsize); - - return ok; - } - - /// - /// Check the image to see if it can be converted to RGBA format. - /// - /// The error message (if any) gets placed here. - /// true if the image can be converted to RGBA format; otherwise, - /// false is returned and contains the reason why it - /// is being rejected. - /// - /// To convert the image to RGBA format please use - /// , - /// , - /// or - /// - /// Convertible images should follow this rules: samples must be either 1, 2, 4, 8, or - /// 16 bits; colorimetric samples/pixel must be either 1, 3, or 4 (i.e. SamplesPerPixel - /// minus ExtraSamples). - /// - public bool RGBAImageOK(out string errorMsg) - { - errorMsg = null; - - if (!m_decodestatus) - { - errorMsg = "Sorry, requested compression method is not configured"; - return false; - } - - switch (m_dir.td_bitspersample) - { - case 1: - case 2: - case 4: - case 8: - case 16: - break; - default: - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle images with {0}-bit samples", m_dir.td_bitspersample); - return false; - } - - int colorchannels = m_dir.td_samplesperpixel - m_dir.td_extrasamples; - Photometric photometric = Photometric.RGB; - FieldValue[] result = GetField(TiffTag.PHOTOMETRIC); - if (result == null) - { - switch (colorchannels) - { - case 1: - photometric = Photometric.MINISBLACK; - break; - case 3: - photometric = Photometric.RGB; - break; - default: - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Missing needed {0} tag", TiffRgbaImage.photoTag); - return false; - } - } - else - { - // San Chen - photometric = (Photometric)result[0].Value; - } - - switch (photometric) - { - case Photometric.MINISWHITE: - case Photometric.MINISBLACK: - case Photometric.PALETTE: - if (m_dir.td_planarconfig == PlanarConfig.CONTIG && - m_dir.td_samplesperpixel != 1 && m_dir.td_bitspersample < 8) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle contiguous data with {0}={1}, and {2}={3} and Bits/Sample={4}", - TiffRgbaImage.photoTag, photometric, "Samples/pixel", m_dir.td_samplesperpixel, - m_dir.td_bitspersample); - - return false; - } - // We should likely validate that any extra samples are either to be ignored, - // or are alpha, and if alpha we should try to use them. But for now we won't - // bother with this. - break; - case Photometric.YCBCR: - // TODO: if at all meaningful and useful, make more complete support check - // here, or better still, refactor to let supporting code decide whether there - // is support and what meaningfull error to return - break; - case Photometric.RGB: - if (colorchannels < 3) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle RGB image with {0}={1}", - "Color channels", colorchannels); - - return false; - } - break; - case Photometric.SEPARATED: - result = GetFieldDefaulted(TiffTag.INKSET); - InkSet inkset = (InkSet)result[0].ToByte(); - if (inkset != InkSet.CMYK) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle separated image with {0}={1}", "InkSet", inkset); - return false; - } - if (m_dir.td_samplesperpixel < 4) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle separated image with {0}={1}", - "Samples/pixel", m_dir.td_samplesperpixel); - return false; - } - break; - case Photometric.LOGL: - if (m_dir.td_compression != Compression.SGILOG) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, LogL data must have {0}={1}", - "Compression", Compression.SGILOG); - return false; - } - break; - case Photometric.LOGLUV: - if (m_dir.td_compression != Compression.SGILOG && - m_dir.td_compression != Compression.SGILOG24) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, LogLuv data must have {0}={1} or {2}", - "Compression", Compression.SGILOG, Compression.SGILOG24); - return false; - } - - if (m_dir.td_planarconfig != PlanarConfig.CONTIG) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle LogLuv images with {0}={1}", - "Planarconfiguration", m_dir.td_planarconfig); - return false; - } - break; - case Photometric.CIELAB: - break; - default: - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle image with {0}={1}", - TiffRgbaImage.photoTag, photometric); - return false; - } - - return true; - } - - /// - /// Gets the name of the file or ID string for this . - /// - /// The name of the file or ID string for this . - /// If this was created using method then - /// value of fileName parameter of method is returned. If this - /// was created using - /// - /// then value of name parameter of - /// - /// method is returned. - public string FileName() - { - return m_name; - } - - /// - /// Sets the new ID string for this . - /// - /// The ID string for this . - /// The previous file name or ID string for this . - /// Please note, that is an arbitrary string used as - /// ID for this . It's not required to be a file name or anything - /// meaningful at all. - public string SetFileName(string name) - { - string old_name = m_name; - m_name = name; - return old_name; - } - - /// - /// Invokes the library-wide error handling methods to (normally) write an error message - /// to the . - /// - /// An instance of the class. Can be null. - /// The method where an error is detected. - /// A composite format string (see Remarks). - /// An object array that contains zero or more objects to format. - /// - /// - /// The is a composite format string that uses the same format as - /// method. The parameter, if - /// not null, is printed before the message; it typically is used to identify the - /// method in which an error is detected. - /// - /// Applications that desire to capture control in the event of an error should use - /// to override the default error and warning handler. - /// - /// - /// - /// Invokes the library-wide error handling methods to (normally) write an error message - /// to the . - /// - public static void Error(Tiff tif, string method, string format, params object[] args) - { - TiffErrorHandler errorHandler = getErrorHandler(tif); - if (errorHandler == null) - return; - - errorHandler.ErrorHandler(tif, method, format, args); - errorHandler.ErrorHandlerExt(tif, null, method, format, args); - } - - /// - /// Invokes the library-wide error handling methods to (normally) write an error message - /// to the . - /// - /// The method where an error is detected. - /// A composite format string (see Remarks). - /// An object array that contains zero or more objects to format. - /// - /// - /// The is a composite format string that uses the same format as - /// method. The parameter, if - /// not null, is printed before the message; it typically is used to identify the - /// method in which an error is detected. - /// - /// Applications that desire to capture control in the event of an error should use - /// to override the default error and warning handler. - /// - /// - public static void Error(string method, string format, params object[] args) - { - Error(null, method, format, args); - } - - /// - /// Invokes the library-wide error handling methods to (normally) write an error message - /// to the . - /// - /// An instance of the class. Can be null. - /// The client data to be passed to error handler. - /// The method where an error is detected. - /// A composite format string (see Remarks). - /// An object array that contains zero or more objects to format. - /// - /// - /// The is a composite format string that uses the same format as - /// method. The parameter, if - /// not null, is printed before the message; it typically is used to identify the - /// method in which an error is detected. - /// - /// - /// The parameter can be anything you want. It will be passed - /// unchanged to the error handler. Default error handler does not use it. Only custom - /// error handlers may make use of it. - /// - /// Applications that desire to capture control in the event of an error should use - /// to override the default error and warning handler. - /// - /// - /// - /// Invokes the library-wide error handling methods to (normally) write an error message - /// to the and passes client data to the error handler. - /// - public static void ErrorExt(Tiff tif, object clientData, string method, string format, params object[] args) - { - TiffErrorHandler errorHandler = getErrorHandler(tif); - if (errorHandler == null) - return; - - errorHandler.ErrorHandler(tif, method, format, args); - errorHandler.ErrorHandlerExt(tif, clientData, method, format, args); - } - - /// - /// Invokes the library-wide error handling methods to (normally) write an error message - /// to the . - /// - /// The client data to be passed to error handler. - /// The method where an error is detected. - /// A composite format string (see Remarks). - /// An object array that contains zero or more objects to format. - /// - /// - /// The is a composite format string that uses the same format as - /// method. The parameter, if - /// not null, is printed before the message; it typically is used to identify the - /// method in which an error is detected. - /// - /// - /// The parameter can be anything you want. It will be passed - /// unchanged to the error handler. Default error handler does not use it. Only custom - /// error handlers may make use of it. - /// - /// Applications that desire to capture control in the event of an error should use - /// to override the default error and warning handler. - /// - /// - public static void ErrorExt(object clientData, string method, string format, params object[] args) - { - ErrorExt(null, clientData, method, format, args); - } - - /// - /// Invokes the library-wide warning handling methods to (normally) write a warning message - /// to the . - /// - /// An instance of the class. Can be null. - /// The method in which a warning is detected. - /// A composite format string (see Remarks). - /// An object array that contains zero or more objects to format. - /// - /// - /// The is a composite format string that uses the same format as - /// method. The parameter, - /// if not null, is printed before the message; it typically is used to identify the - /// method in which a warning is detected. - /// - /// Applications that desire to capture control in the event of a warning should use - /// to override the default error and warning handler. - /// - /// - /// - /// Invokes the library-wide warning handling methods to (normally) write a warning message - /// to the . - /// - public static void Warning(Tiff tif, string method, string format, params object[] args) - { - TiffErrorHandler errorHandler = getErrorHandler(tif); - if (errorHandler == null) - return; - - errorHandler.WarningHandler(tif, method, format, args); - errorHandler.WarningHandlerExt(tif, null, method, format, args); - } - - /// - /// Invokes the library-wide warning handling methods to (normally) write a warning message - /// to the . - /// - /// The method in which a warning is detected. - /// A composite format string (see Remarks). - /// An object array that contains zero or more objects to format. - /// - /// The is a composite format string that uses the same format as - /// method. The parameter, - /// if not null, is printed before the message; it typically is used to identify the - /// method in which a warning is detected. - /// - /// Applications that desire to capture control in the event of a warning should use - /// to override the default error and warning handler. - /// - /// - public static void Warning(string method, string format, params object[] args) - { - Warning(null, method, format, args); - } - - /// - /// Invokes the library-wide warning handling methods to (normally) write a warning message - /// to the and passes client data to the warning handler. - /// - /// An instance of the class. Can be null. - /// The client data to be passed to warning handler. - /// The method in which a warning is detected. - /// A composite format string (see Remarks). - /// An object array that contains zero or more objects to format. - /// - /// - /// The is a composite format string that uses the same format as - /// method. The parameter, if - /// not null, is printed before the message; it typically is used to identify the - /// method in which a warning is detected. - /// - /// - /// The parameter can be anything you want. It will be passed - /// unchanged to the warning handler. Default warning handler does not use it. Only custom - /// warning handlers may make use of it. - /// - /// Applications that desire to capture control in the event of a warning should use - /// to override the default error and warning handler. - /// - /// - /// - /// Invokes the library-wide warning handling methods to (normally) write a warning message - /// to the and passes client data to the warning handler. - /// - public static void WarningExt(Tiff tif, object clientData, string method, string format, params object[] args) - { - TiffErrorHandler errorHandler = getErrorHandler(tif); - if (errorHandler == null) - return; - - errorHandler.WarningHandler(tif, method, format, args); - errorHandler.WarningHandlerExt(tif, clientData, method, format, args); - } - - /// - /// Invokes the library-wide warning handling methods to (normally) write a warning message - /// to the and passes client data to the warning handler. - /// - /// The client data to be passed to warning handler. - /// The method in which a warning is detected. - /// A composite format string (see Remarks). - /// An object array that contains zero or more objects to format. - /// - /// The is a composite format string that uses the same format as - /// method. The parameter, if - /// not null, is printed before the message; it typically is used to identify the - /// method in which a warning is detected. - /// - /// The parameter can be anything you want. It will be passed - /// unchanged to the warning handler. Default warning handler does not use it. Only custom - /// warning handlers may make use of it. - /// - /// Applications that desire to capture control in the event of a warning should use - /// to override the default error and warning handler. - /// - /// - public static void WarningExt(object clientData, string method, string format, params object[] args) - { - WarningExt(null, clientData, method, format, args); - } - - /// - /// Sets an instance of the class as custom library-wide - /// error and warning handler. - /// - /// An instance of the class - /// to set as custom library-wide error and warning handler. - /// - /// Previous error handler or null if there was no error handler set. - /// - public static TiffErrorHandler SetErrorHandler(TiffErrorHandler errorHandler) - { - return setErrorHandlerImpl(errorHandler); - } - - /// - /// Sets the tag extender method. - /// - /// The tag extender method. - /// Previous tag extender method. - /// - /// Extender method is called upon creation of each instance of object. - /// - public static TiffExtendProc SetTagExtender(TiffExtendProc extender) - { - return setTagExtenderImpl(extender); - } - - /// - /// Reads and decodes a tile of data from an open TIFF file/stream. - /// - /// The buffer to place read and decoded image data to. - /// The zero-based byte offset in at which - /// to begin storing read and decoded bytes. - /// The x-coordinate of the pixel within a tile to be read and decoded. - /// The y-coordinate of the pixel within a tile to be read and decoded. - /// The z-coordinate of the pixel within a tile to be read and decoded. - /// The zero-based index of the sample plane. - /// The number of bytes in the decoded tile or -1 if an error occurred. - /// - /// - /// The tile to read and decode is selected by the (x, y, z, plane) coordinates (i.e. - /// ReadTile returns the data for the tile containing the specified coordinates. - /// The data placed in are returned decompressed and, typically, - /// in the native byte- and bit-ordering, but are otherwise packed (see further below). - /// The buffer must be large enough to hold an entire tile of data. Applications should - /// call the to find out the size (in bytes) of a tile buffer. - /// The and parameters are always used by - /// ReadTile. The parameter is used if the image is deeper - /// than 1 slice (a value of > 1). In other cases the - /// value of is ignored. The parameter is - /// used only if data are organized in separate planes - /// ( = .SEPARATE). In other - /// cases the value of is ignored. - /// - /// The library attempts to hide bit- and byte-ordering differences between the image and - /// the native machine by converting data to the native machine order. Bit reversal is - /// done if the value of tag is opposite to the native - /// machine bit order. 16- and 32-bit samples are automatically byte-swapped if the file - /// was written with a byte order opposite to the native machine byte order. - /// - public int ReadTile(byte[] buffer, int offset, int x, int y, int z, short plane) - { - if (!checkRead(true) || !CheckTile(x, y, z, plane)) - return -1; - - return ReadEncodedTile(ComputeTile(x, y, z, plane), buffer, offset, -1); - } - - /// - /// Reads a tile of data from an open TIFF file/stream, decompresses it and places - /// specified amount of decompressed bytes into the user supplied buffer. - /// - /// The zero-based index of the tile to read. - /// The buffer to place decompressed tile bytes to. - /// The zero-based byte offset in buffer at which to begin storing - /// decompressed tile bytes. - /// The maximum number of decompressed tile bytes to be stored - /// to buffer. - /// The actual number of bytes of data that were placed in buffer or -1 if an - /// error was encountered. - /// - /// - /// The value of is a "raw tile number". That is, the caller - /// must take into account whether or not the data are organized in separate planes - /// ( = .SEPARATE). - /// automatically does this when converting an (x, y, z, plane) - /// coordinate quadruple to a tile number. - /// To read a full tile of data the data buffer should typically be at least as - /// large as the number returned by . If the -1 passed in - /// parameter, the whole tile will be read. You should be sure - /// you have enough space allocated for the buffer. - /// The library attempts to hide bit- and byte-ordering differences between the - /// image and the native machine by converting data to the native machine order. Bit - /// reversal is done if the tag is opposite to the native - /// machine bit order. 16- and 32-bit samples are automatically byte-swapped if the file - /// was written with a byte order opposite to the native machine byte order. - /// - public int ReadEncodedTile(int tile, byte[] buffer, int offset, int count) - { - if (!checkRead(true)) - return -1; - - if (tile >= m_dir.td_nstrips) - { - ErrorExt(this, m_clientdata, m_name, "{0}: Tile out of range, max {1}", tile, m_dir.td_nstrips); - return -1; - } - - if (count == -1) - count = m_tilesize; - else if (count > m_tilesize) - count = m_tilesize; - - if (fillTile(tile) && m_currentCodec.DecodeTile(buffer, offset, count, (short)(tile / m_dir.td_stripsperimage))) - { - postDecode(buffer, offset, count); - return count; - } - - return -1; - } - - /// - /// Reads the undecoded contents of a tile of data from an open TIFF file/stream and places - /// specified amount of read bytes into the user supplied buffer. - /// - /// The zero-based index of the tile to read. - /// The buffer to place read tile bytes to. - /// The zero-based byte offset in buffer at which to begin storing - /// read tile bytes. - /// The maximum number of read tile bytes to be stored to buffer. - /// The actual number of bytes of data that were placed in buffer or -1 if an - /// error was encountered. - /// - /// - /// The value of is a "raw tile number". That is, the caller - /// must take into account whether or not the data are organized in separate planes - /// ( = .SEPARATE). - /// automatically does this when converting an (x, y, z, plane) - /// coordinate quadruple to a tile number. - /// To read a full tile of data the data buffer should typically be at least as - /// large as the number returned by . If the -1 passed in - /// parameter, the whole tile will be read. You should be sure - /// you have enough space allocated for the buffer. - public int ReadRawTile(int tile, byte[] buffer, int offset, int count) - { - const string module = "ReadRawTile"; - - if (!checkRead(true)) - return -1; - - if (tile >= m_dir.td_nstrips) - { - ErrorExt(this, m_clientdata, m_name, "{0}: Tile out of range, max {1}", tile, m_dir.td_nstrips); - return -1; - } - - if ((m_flags & TiffFlags.NOREADRAW) == TiffFlags.NOREADRAW) - { - ErrorExt(m_clientdata, m_name, "Compression scheme does not support access to raw uncompressed data"); - return -1; - } - - uint bytecount = m_dir.td_stripbytecount[tile]; - if (count != -1 && (uint)count < bytecount) - bytecount = (uint)count; - - return readRawTile1(tile, buffer, offset, (int)bytecount, module); - } - - /// - /// Encodes and writes a tile of data to an open TIFF file/stream. - /// - /// Encodes and writes a tile of data to an open TIFF file/stream. - /// The buffer with image data to be encoded and written. - /// The x-coordinate of the pixel within a tile to be encoded and written. - /// The y-coordinate of the pixel within a tile to be encoded and written. - /// The z-coordinate of the pixel within a tile to be encoded and written. - /// The zero-based index of the sample plane. - /// - /// The number of encoded and written bytes or -1 if an error occurred. - /// - /// - /// - /// The tile to place encoded data is selected by the (x, y, z, plane) coordinates (i.e. - /// WriteTile writes data to the tile containing the specified coordinates. - /// WriteTile (potentially) encodes the data and writes - /// it to open file/stream. The buffer must contain an entire tile of data. Applications - /// should call the to find out the size (in bytes) of a tile buffer. - /// The and parameters are always used by - /// WriteTile. The parameter is used if the image is deeper - /// than 1 slice (a value of > 1). In other cases the - /// value of is ignored. The parameter is - /// used only if data are organized in separate planes - /// ( = .SEPARATE). In other - /// cases the value of is ignored. - /// - /// A correct value for the tag must be setup before - /// writing; WriteTile does not support automatically growing the image on - /// each write (as does). - /// - public int WriteTile(byte[] buffer, int x, int y, int z, short plane) - { - return WriteTile(buffer, 0, x, y, z, plane); - } - - /// - /// Encodes and writes a tile of data to an open TIFF file/stream. - /// - /// The buffer with image data to be encoded and written. - /// The zero-based byte offset in at which - /// to begin reading bytes to be encoded and written. - /// The x-coordinate of the pixel within a tile to be encoded and written. - /// The y-coordinate of the pixel within a tile to be encoded and written. - /// The z-coordinate of the pixel within a tile to be encoded and written. - /// The zero-based index of the sample plane. - /// The number of encoded and written bytes or -1 if an error occurred. - /// - /// - /// The tile to place encoded data is selected by the (x, y, z, plane) coordinates (i.e. - /// WriteTile writes data to the tile containing the specified coordinates. - /// WriteTile (potentially) encodes the data and writes - /// it to open file/stream. The buffer must contain an entire tile of data. Applications - /// should call the to find out the size (in bytes) of a tile buffer. - /// The and parameters are always used by - /// WriteTile. The parameter is used if the image is deeper - /// than 1 slice (a value of > 1). In other cases the - /// value of is ignored. The parameter is - /// used only if data are organized in separate planes - /// ( = .SEPARATE). In other - /// cases the value of is ignored. - /// - /// A correct value for the tag must be setup before - /// writing; WriteTile does not support automatically growing the image on - /// each write (as does). - /// - public int WriteTile(byte[] buffer, int offset, int x, int y, int z, short plane) - { - if (!CheckTile(x, y, z, plane)) - return -1; - - // NB: A tile size of -1 is used instead of m_tilesize knowing that WriteEncodedTile - // will clamp this to the tile size. This is done because the tile size may not be - // defined until after the output buffer is setup in WriteBufferSetup. - return WriteEncodedTile(ComputeTile(x, y, z, plane), buffer, offset, -1); - } - - /// - /// Reads a strip of data from an open TIFF file/stream, decompresses it and places - /// specified amount of decompressed bytes into the user supplied buffer. - /// - /// The zero-based index of the strip to read. - /// The buffer to place decompressed strip bytes to. - /// The zero-based byte offset in buffer at which to begin storing - /// decompressed strip bytes. - /// The maximum number of decompressed strip bytes to be stored - /// to buffer. - /// The actual number of bytes of data that were placed in buffer or -1 if an - /// error was encountered. - /// - /// - /// The value of is a "raw strip number". That is, the caller - /// must take into account whether or not the data are organized in separate planes - /// ( = .SEPARATE). - /// automatically does this when converting an (row, plane) to a - /// strip index. - /// To read a full strip of data the data buffer should typically be at least - /// as large as the number returned by . If the -1 passed in - /// parameter, the whole strip will be read. You should be sure - /// you have enough space allocated for the buffer. - /// The library attempts to hide bit- and byte-ordering differences between the - /// image and the native machine by converting data to the native machine order. Bit - /// reversal is done if the tag is opposite to the native - /// machine bit order. 16- and 32-bit samples are automatically byte-swapped if the file - /// was written with a byte order opposite to the native machine byte order. - /// - public int ReadEncodedStrip(int strip, byte[] buffer, int offset, int count) - { - if (!checkRead(false)) - return -1; - - if (strip >= m_dir.td_nstrips) - { - ErrorExt(this, m_clientdata, m_name, "{0}: Strip out of range, max {1}", strip, m_dir.td_nstrips); - return -1; - } - - // Calculate the strip size according to the number of rows in the strip (check for - // truncated last strip on any of the separations). - int strips_per_sep; - if (m_dir.td_rowsperstrip >= m_dir.td_imagelength) - strips_per_sep = 1; - else - strips_per_sep = (m_dir.td_imagelength + m_dir.td_rowsperstrip - 1) / m_dir.td_rowsperstrip; - - int sep_strip = strip % strips_per_sep; - - int nrows = m_dir.td_imagelength % m_dir.td_rowsperstrip; - if (sep_strip != strips_per_sep - 1 || nrows == 0) - nrows = m_dir.td_rowsperstrip; - - int stripsize = VStripSize(nrows); - if (count == -1) - count = stripsize; - else if (count > stripsize) - count = stripsize; - - if (fillStrip(strip) && m_currentCodec.DecodeStrip(buffer, offset, count, (short)(strip / m_dir.td_stripsperimage))) - { - postDecode(buffer, offset, count); - return count; - } - - return -1; - } - - /// - /// Reads the undecoded contents of a strip of data from an open TIFF file/stream and - /// places specified amount of read bytes into the user supplied buffer. - /// - /// The zero-based index of the strip to read. - /// The buffer to place read bytes to. - /// The zero-based byte offset in buffer at which to begin storing - /// read bytes. - /// The maximum number of read bytes to be stored to buffer. - /// The actual number of bytes of data that were placed in buffer or -1 if an - /// error was encountered. - /// - /// - /// The value of is a "raw strip number". That is, the caller - /// must take into account whether or not the data are organized in separate planes - /// ( = .SEPARATE). - /// automatically does this when converting an (row, plane) to a - /// strip index. - /// To read a full strip of data the data buffer should typically be at least - /// as large as the number returned by . If the -1 passed in - /// parameter, the whole strip will be read. You should be sure - /// you have enough space allocated for the buffer. - public int ReadRawStrip(int strip, byte[] buffer, int offset, int count) - { - const string module = "ReadRawStrip"; - - if (!checkRead(false)) - return -1; - - if (strip >= m_dir.td_nstrips) - { - ErrorExt(this, m_clientdata, m_name, "{0}: Strip out of range, max {1}", strip, m_dir.td_nstrips); - return -1; - } - - if ((m_flags & TiffFlags.NOREADRAW) == TiffFlags.NOREADRAW) - { - ErrorExt(this, m_clientdata, m_name, "Compression scheme does not support access to raw uncompressed data"); - return -1; - } - - uint bytecount = m_dir.td_stripbytecount[strip]; - if (bytecount <= 0) - { - ErrorExt(this, m_clientdata, m_name, "{0}: Invalid strip byte count, strip {1}", bytecount, strip); - return -1; - } - - if (count != -1 && (uint)count < bytecount) - bytecount = (uint)count; - - return readRawStrip1(strip, buffer, offset, (int)bytecount, module); - } - - /// - /// Encodes and writes a strip of data to an open TIFF file/stream. - /// - /// The zero-based index of the strip to write. - /// The buffer with image data to be encoded and written. - /// The maximum number of strip bytes to be read from - /// . - /// - /// The number of encoded and written bytes or -1 if an error occurred. - /// - /// Encodes and writes a strip of data to an open TIFF file/stream. - /// - /// - /// WriteEncodedStrip encodes bytes of raw data from - /// and append the result to the specified strip; replacing any - /// previously written data. Note that the value of is a "raw - /// strip number". That is, the caller must take into account whether or not the data are - /// organized in separate planes - /// ( = .SEPARATE). - /// automatically does this when converting an (row, plane) to - /// a strip index. - /// - /// If there is no space for the strip, the value of - /// tag is automatically increased to include the strip (except for - /// = .SEPARATE, where the - /// tag cannot be changed once the first data are - /// written). If the is increased, the values of - /// and tags are - /// similarly enlarged to reflect data written past the previous end of image. - /// - /// The library writes encoded data using the native machine byte order. Correctly - /// implemented TIFF readers are expected to do any necessary byte-swapping to correctly - /// process image data with value of tag greater - /// than 8. - /// - public int WriteEncodedStrip(int strip, byte[] buffer, int count) - { - return WriteEncodedStrip(strip, buffer, 0, count); - } - - /// - /// Encodes and writes a strip of data to an open TIFF file/stream. - /// - /// The zero-based index of the strip to write. - /// The buffer with image data to be encoded and written. - /// The zero-based byte offset in at which - /// to begin reading bytes to be encoded and written. - /// The maximum number of strip bytes to be read from - /// . - /// The number of encoded and written bytes or -1 if an error occurred. - /// - /// - /// WriteEncodedStrip encodes bytes of raw data from - /// and append the result to the specified strip; replacing any - /// previously written data. Note that the value of is a "raw - /// strip number". That is, the caller must take into account whether or not the data are - /// organized in separate planes - /// ( = .SEPARATE). - /// automatically does this when converting an (row, plane) to - /// a strip index. - /// - /// If there is no space for the strip, the value of - /// tag is automatically increased to include the strip (except for - /// = .SEPARATE, where the - /// tag cannot be changed once the first data are - /// written). If the is increased, the values of - /// and tags are - /// similarly enlarged to reflect data written past the previous end of image. - /// - /// The library writes encoded data using the native machine byte order. Correctly - /// implemented TIFF readers are expected to do any necessary byte-swapping to correctly - /// process image data with value of tag greater - /// than 8. - /// - public int WriteEncodedStrip(int strip, byte[] buffer, int offset, int count) - { - const string module = "WriteEncodedStrip"; - - if (!writeCheckStrips(module)) - return -1; - - // Check strip array to make sure there's space. We don't support dynamically growing - // files that have data organized in separate bitplanes because it's too painful. - // In that case we require that the imagelength be set properly before the first write - // (so that the strips array will be fully allocated above). - if (strip >= m_dir.td_nstrips) - { - if (m_dir.td_planarconfig == PlanarConfig.SEPARATE) - { - ErrorExt(this, m_clientdata, m_name, "Can not grow image by strips when using separate planes"); - return -1; - } - - if (!growStrips(1)) - return -1; - - m_dir.td_stripsperimage = howMany(m_dir.td_imagelength, m_dir.td_rowsperstrip); - } - - // Handle delayed allocation of data buffer. This permits it to be sized according to - // the directory info. - bufferCheck(); - - m_curstrip = strip; - m_row = (strip % m_dir.td_stripsperimage) * m_dir.td_rowsperstrip; - if ((m_flags & TiffFlags.CODERSETUP) != TiffFlags.CODERSETUP) - { - if (!m_currentCodec.SetupEncode()) - return -1; - - m_flags |= TiffFlags.CODERSETUP; - } - - m_rawcc = 0; - m_rawcp = 0; - - if (m_dir.td_stripbytecount[strip] > 0) - { - // this forces appendToStrip() to do a seek - m_curoff = 0; - } - - m_flags &= ~TiffFlags.POSTENCODE; - short sample = (short)(strip / m_dir.td_stripsperimage); - if (!m_currentCodec.PreEncode(sample)) - return -1; - - // swab if needed - note that source buffer will be altered - postDecode(buffer, offset, count); - - if (!m_currentCodec.EncodeStrip(buffer, offset, count, sample)) - return 0; - - if (!m_currentCodec.PostEncode()) - return -1; - - if (!isFillOrder(m_dir.td_fillorder) && (m_flags & TiffFlags.NOBITREV) != TiffFlags.NOBITREV) - ReverseBits(m_rawdata, m_rawcc); - - if (m_rawcc > 0 && !appendToStrip(strip, m_rawdata, 0, m_rawcc)) - return -1; - - m_rawcc = 0; - m_rawcp = 0; - return count; - } - - /// - /// Writes a strip of raw data to an open TIFF file/stream. - /// - /// Writes a strip of raw data to an open TIFF file/stream. - /// The zero-based index of the strip to write. - /// The buffer with raw image data to be written. - /// The maximum number of strip bytes to be read from - /// . - /// - /// The number of written bytes or -1 if an error occurred. - /// - /// - /// - /// WriteRawStrip appends bytes of raw data from - /// to the specified strip; replacing any - /// previously written data. Note that the value of is a "raw - /// strip number". That is, the caller must take into account whether or not the data are - /// organized in separate planes - /// ( = .SEPARATE). - /// automatically does this when converting an (row, plane) to - /// a strip index. - /// - /// If there is no space for the strip, the value of - /// tag is automatically increased to include the strip (except for - /// = .SEPARATE, where the - /// tag cannot be changed once the first data are - /// written). If the is increased, the values of - /// and tags are - /// similarly enlarged to reflect data written past the previous end of image. - /// - public int WriteRawStrip(int strip, byte[] buffer, int count) - { - return WriteRawStrip(strip, buffer, 0, count); - } - - /// - /// Writes a strip of raw data to an open TIFF file/stream. - /// - /// The zero-based index of the strip to write. - /// The buffer with raw image data to be written. - /// The zero-based byte offset in at which - /// to begin reading bytes to be written. - /// The maximum number of strip bytes to be read from - /// . - /// The number of written bytes or -1 if an error occurred. - /// - /// - /// WriteRawStrip appends bytes of raw data from - /// to the specified strip; replacing any - /// previously written data. Note that the value of is a "raw - /// strip number". That is, the caller must take into account whether or not the data are - /// organized in separate planes - /// ( = .SEPARATE). - /// automatically does this when converting an (row, plane) to - /// a strip index. - /// - /// If there is no space for the strip, the value of - /// tag is automatically increased to include the strip (except for - /// = .SEPARATE, where the - /// tag cannot be changed once the first data are - /// written). If the is increased, the values of - /// and tags are - /// similarly enlarged to reflect data written past the previous end of image. - /// - public int WriteRawStrip(int strip, byte[] buffer, int offset, int count) - { - const string module = "WriteRawStrip"; - - if (!writeCheckStrips(module)) - return -1; - - // Check strip array to make sure there's space. We don't support dynamically growing - // files that have data organized in separate bitplanes because it's too painful. - // In that case we require that the imagelength be set properly before the first write - // (so that the strips array will be fully allocated above). - if (strip >= m_dir.td_nstrips) - { - if (m_dir.td_planarconfig == PlanarConfig.SEPARATE) - { - ErrorExt(this, m_clientdata, m_name, "Can not grow image by strips when using separate planes"); - return -1; - } - - // Watch out for a growing image. The value of strips/image will initially be 1 - // (since it can't be deduced until the imagelength is known). - if (strip >= m_dir.td_stripsperimage) - m_dir.td_stripsperimage = howMany(m_dir.td_imagelength, m_dir.td_rowsperstrip); - - if (!growStrips(1)) - return -1; - } - - m_curstrip = strip; - m_row = (strip % m_dir.td_stripsperimage) * m_dir.td_rowsperstrip; - return (appendToStrip(strip, buffer, offset, count) ? count : -1); - } - - /// - /// Encodes and writes a tile of data to an open TIFF file/stream. - /// - /// Encodes and writes a tile of data to an open TIFF file/stream. - /// The zero-based index of the tile to write. - /// The buffer with image data to be encoded and written. - /// The maximum number of tile bytes to be read from - /// . - /// - /// The number of encoded and written bytes or -1 if an error occurred. - /// - /// - /// WriteEncodedTile encodes bytes of raw data from - /// and append the result to the end of the specified tile. Note - /// that the value of is a "raw tile number". That is, the caller - /// must take into account whether or not the data are organized in separate planes - /// ( = .SEPARATE). - /// automatically does this when converting an (x, y, z, plane) - /// coordinate quadruple to a tile number. - /// - /// There must be space for the data. The function clamps individual writes to a tile to - /// the tile size, but does not (and can not) check that multiple writes to the same tile - /// were performed. - /// - /// A correct value for the tag must be setup before - /// writing; WriteEncodedTile does not support automatically growing the image on - /// each write (as does). - /// - /// The library writes encoded data using the native machine byte order. Correctly - /// implemented TIFF readers are expected to do any necessary byte-swapping to correctly - /// process image data with value of tag greater - /// than 8. - /// - public int WriteEncodedTile(int tile, byte[] buffer, int count) - { - return WriteEncodedTile(tile, buffer, 0, count); - } - - /// - /// Encodes and writes a tile of data to an open TIFF file/stream. - /// - /// The zero-based index of the tile to write. - /// The buffer with image data to be encoded and written. - /// The zero-based byte offset in at which - /// to begin reading bytes to be encoded and written. - /// The maximum number of tile bytes to be read from - /// . - /// The number of encoded and written bytes or -1 if an error occurred. - /// - /// - /// WriteEncodedTile encodes bytes of raw data from - /// and append the result to the end of the specified tile. Note - /// that the value of is a "raw tile number". That is, the caller - /// must take into account whether or not the data are organized in separate planes - /// ( = .SEPARATE). - /// automatically does this when converting an (x, y, z, plane) - /// coordinate quadruple to a tile number. - /// - /// There must be space for the data. The function clamps individual writes to a tile to - /// the tile size, but does not (and can not) check that multiple writes to the same tile - /// were performed. - /// - /// A correct value for the tag must be setup before - /// writing; WriteEncodedTile does not support automatically growing the image on - /// each write (as does). - /// - /// The library writes encoded data using the native machine byte order. Correctly - /// implemented TIFF readers are expected to do any necessary byte-swapping to correctly - /// process image data with value of tag greater - /// than 8. - /// - public int WriteEncodedTile(int tile, byte[] buffer, int offset, int count) - { - const string module = "WriteEncodedTile"; - - if (!writeCheckTiles(module)) - return -1; - - if (tile >= m_dir.td_nstrips) - { - ErrorExt(this, m_clientdata, module, "{0}: Tile {1} out of range, max {2}", m_name, tile, m_dir.td_nstrips); - return -1; - } - - // Handle delayed allocation of data buffer. This permits it to be sized more - // intelligently (using directory information). - bufferCheck(); - - m_curtile = tile; - - m_rawcc = 0; - m_rawcp = 0; - - if (m_dir.td_stripbytecount[tile] > 0) - { - // this forces appendToStrip() to do a seek - m_curoff = 0; - } - - // Compute tiles per row & per column to compute current row and column - m_row = (tile % howMany(m_dir.td_imagelength, m_dir.td_tilelength)) * m_dir.td_tilelength; - m_col = (tile % howMany(m_dir.td_imagewidth, m_dir.td_tilewidth)) * m_dir.td_tilewidth; - - if ((m_flags & TiffFlags.CODERSETUP) != TiffFlags.CODERSETUP) - { - if (!m_currentCodec.SetupEncode()) - return -1; - - m_flags |= TiffFlags.CODERSETUP; - } - - m_flags &= ~TiffFlags.POSTENCODE; - short sample = (short)(tile / m_dir.td_stripsperimage); - if (!m_currentCodec.PreEncode(sample)) - return -1; - - // Clamp write amount to the tile size. This is mostly done so that callers can pass - // in some large number (e.g. -1) and have the tile size used instead. - if (count < 1 || count > m_tilesize) - count = m_tilesize; - - // swab if needed - note that source buffer will be altered - postDecode(buffer, offset, count); - - if (!m_currentCodec.EncodeTile(buffer, offset, count, sample)) - return 0; - - if (!m_currentCodec.PostEncode()) - return -1; - - if (!isFillOrder(m_dir.td_fillorder) && (m_flags & TiffFlags.NOBITREV) != TiffFlags.NOBITREV) - ReverseBits(m_rawdata, m_rawcc); - - if (m_rawcc > 0 && !appendToStrip(tile, m_rawdata, 0, m_rawcc)) - return -1; - - m_rawcc = 0; - m_rawcp = 0; - return count; - } - - /// - /// Writes a tile of raw data to an open TIFF file/stream. - /// - /// Writes a tile of raw data to an open TIFF file/stream. - /// The zero-based index of the tile to write. - /// The buffer with raw image data to be written. - /// The maximum number of tile bytes to be read from - /// . - /// - /// The number of written bytes or -1 if an error occurred. - /// - /// - /// - /// WriteRawTile appends bytes of raw data to the end of - /// the specified tile. Note that the value of is a "raw tile - /// number". That is, the caller must take into account whether or not the data are - /// organized in separate planes - /// ( = .SEPARATE). - /// automatically does this when converting an (x, y, z, plane) - /// coordinate quadruple to a tile number. - /// - /// There must be space for the data. The function clamps individual writes to a tile to - /// the tile size, but does not (and can not) check that multiple writes to the same tile - /// were performed. - /// - /// A correct value for the tag must be setup before - /// writing; WriteRawTile does not support automatically growing the image on - /// each write (as does). - /// - public int WriteRawTile(int tile, byte[] buffer, int count) - { - return WriteRawTile(tile, buffer, 0, count); - } - - /// - /// Writes a tile of raw data to an open TIFF file/stream. - /// - /// The zero-based index of the tile to write. - /// The buffer with raw image data to be written. - /// The zero-based byte offset in at which - /// to begin reading bytes to be written. - /// The maximum number of tile bytes to be read from - /// . - /// The number of written bytes or -1 if an error occurred. - /// - /// - /// WriteRawTile appends bytes of raw data to the end of - /// the specified tile. Note that the value of is a "raw tile - /// number". That is, the caller must take into account whether or not the data are - /// organized in separate planes - /// ( = .SEPARATE). - /// automatically does this when converting an (x, y, z, plane) - /// coordinate quadruple to a tile number. - /// - /// There must be space for the data. The function clamps individual writes to a tile to - /// the tile size, but does not (and can not) check that multiple writes to the same tile - /// were performed. - /// - /// A correct value for the tag must be setup before - /// writing; WriteRawTile does not support automatically growing the image on - /// each write (as does). - /// - public int WriteRawTile(int tile, byte[] buffer, int offset, int count) - { - const string module = "WriteRawTile"; - - if (!writeCheckTiles(module)) - return -1; - - if (tile >= m_dir.td_nstrips) - { - ErrorExt(this, m_clientdata, module, - "{0}: Tile {1} out of range, max {2}", m_name, tile, m_dir.td_nstrips); - return -1; - } - - return (appendToStrip(tile, buffer, offset, count) ? count : -1); - } - - /// - /// Sets the current write offset. - /// - /// The write offset. - /// This should only be used to set the offset to a known previous location - /// (very carefully), or to 0 so that the next write gets appended to the end of the file. - /// - public void SetWriteOffset(long offset) - { - m_curoff = (uint)offset; - } - - /// - /// Gets the number of bytes occupied by the item of given type. - /// - /// The type. - /// The number of bytes occupied by the or 0 if unknown - /// data type is supplied. - public static int DataWidth(TiffType type) - { - switch (type) - { - case TiffType.NOTYPE: - case TiffType.BYTE: - case TiffType.ASCII: - case TiffType.SBYTE: - case TiffType.UNDEFINED: - return 1; - - case TiffType.SHORT: - case TiffType.SSHORT: - return 2; - - case TiffType.LONG: - case TiffType.SLONG: - case TiffType.FLOAT: - case TiffType.IFD: - return 4; - - case TiffType.RATIONAL: - case TiffType.SRATIONAL: - case TiffType.DOUBLE: - return 8; - - default: - // will return 0 for unknown types - return 0; - } - } - - /// - /// Swaps the bytes in a single 16-bit item. - /// - /// The value to swap bytes in. - public static void SwabShort(ref short value) - { - byte[] bytes = new byte[2]; - bytes[0] = (byte)value; - bytes[1] = (byte)(value >> 8); - - byte temp = bytes[1]; - bytes[1] = bytes[0]; - bytes[0] = temp; - - value = (short)(bytes[0] & 0xFF); - value += (short)((bytes[1] & 0xFF) << 8); - } - - /// - /// Swaps the bytes in a single 32-bit item. - /// - /// The value to swap bytes in. - public static void SwabLong(ref int value) - { - byte[] bytes = new byte[4]; - bytes[0] = (byte)value; - bytes[1] = (byte)(value >> 8); - bytes[2] = (byte)(value >> 16); - bytes[3] = (byte)(value >> 24); - - byte temp = bytes[3]; - bytes[3] = bytes[0]; - bytes[0] = temp; - - temp = bytes[2]; - bytes[2] = bytes[1]; - bytes[1] = temp; - - value = bytes[0] & 0xFF; - value += (bytes[1] & 0xFF) << 8; - value += (bytes[2] & 0xFF) << 16; - value += bytes[3] << 24; - } - - /// - /// Swaps the bytes in a single double-precision floating-point number. - /// - /// The value to swap bytes in. - public static void SwabDouble(ref double value) - { - byte[] bytes = BitConverter.GetBytes(value); - int[] ints = new int[2]; - ints[0] = BitConverter.ToInt32(bytes, 0); - ints[0] = BitConverter.ToInt32(bytes, sizeof(int)); - - SwabArrayOfLong(ints, 2); - - int temp = ints[0]; - ints[0] = ints[1]; - ints[1] = temp; - - Buffer.BlockCopy(BitConverter.GetBytes(ints[0]), 0, bytes, 0, sizeof(int)); - Buffer.BlockCopy(BitConverter.GetBytes(ints[1]), 0, bytes, sizeof(int), sizeof(int)); - value = BitConverter.ToDouble(bytes, 0); - } - - /// - /// Swaps the bytes in specified number of values in the array of 16-bit items. - /// - /// - /// Swaps the bytes in specified number of values in the array of 16-bit items. - /// - /// The array to swap bytes in. - /// The number of items to swap bytes in. - public static void SwabArrayOfShort(short[] array, int count) - { - SwabArrayOfShort(array, 0, count); - } - - /// - /// Swaps the bytes in specified number of values in the array of 16-bit items starting at - /// specified offset. - /// - /// The array to swap bytes in. - /// The zero-based offset in at - /// which to begin swapping bytes. - /// The number of items to swap bytes in. - public static void SwabArrayOfShort(short[] array, int offset, int count) - { - byte[] bytes = new byte[2]; - for (int i = 0; i < count; i++, offset++) - { - bytes[0] = (byte)array[offset]; - bytes[1] = (byte)(array[offset] >> 8); - - byte temp = bytes[1]; - bytes[1] = bytes[0]; - bytes[0] = temp; - - array[offset] = (short)(bytes[0] & 0xFF); - array[offset] += (short)((bytes[1] & 0xFF) << 8); - } - } - - /// - /// Swaps the bytes in specified number of values in the array of triples (24-bit items). - /// - /// - /// Swaps the bytes in specified number of values in the array of triples (24-bit items). - /// - /// The array to swap bytes in. - /// The number of items to swap bytes in. - public static void SwabArrayOfTriples(byte[] array, int count) - { - SwabArrayOfTriples(array, 0, count); - } - - /// - /// Swaps the bytes in specified number of values in the array of triples (24-bit items) - /// starting at specified offset. - /// - /// The array to swap bytes in. - /// The zero-based offset in at - /// which to begin swapping bytes. - /// The number of items to swap bytes in. - public static void SwabArrayOfTriples(byte[] array, int offset, int count) - { - // XXX unroll loop some - while (count-- > 0) - { - byte t = array[offset + 2]; - array[offset + 2] = array[offset]; - array[offset] = t; - offset += 3; - } - } - - /// - /// Swaps the bytes in specified number of values in the array of 32-bit items. - /// - /// - /// Swaps the bytes in specified number of values in the array of 32-bit items. - /// - /// The array to swap bytes in. - /// The number of items to swap bytes in. - public static void SwabArrayOfLong(int[] array, int count) - { - SwabArrayOfLong(array, 0, count); - } - - /// - /// Swaps the bytes in specified number of values in the array of 32-bit items - /// starting at specified offset. - /// - /// The array to swap bytes in. - /// The zero-based offset in at - /// which to begin swapping bytes. - /// The number of items to swap bytes in. - public static void SwabArrayOfLong(int[] array, int offset, int count) - { - byte[] bytes = new byte[4]; - - for (int i = 0; i < count; i++, offset++) - { - bytes[0] = (byte)array[offset]; - bytes[1] = (byte)(array[offset] >> 8); - bytes[2] = (byte)(array[offset] >> 16); - bytes[3] = (byte)(array[offset] >> 24); - - byte temp = bytes[3]; - bytes[3] = bytes[0]; - bytes[0] = temp; - - temp = bytes[2]; - bytes[2] = bytes[1]; - bytes[1] = temp; - - array[offset] = bytes[0] & 0xFF; - array[offset] += (bytes[1] & 0xFF) << 8; - array[offset] += (bytes[2] & 0xFF) << 16; - array[offset] += bytes[3] << 24; - } - } - - /// - /// Swaps the bytes in specified number of values in the array of double-precision - /// floating-point numbers. - /// - /// - /// Swaps the bytes in specified number of values in the array of double-precision - /// floating-point numbers. - /// - /// The array to swap bytes in. - /// The number of items to swap bytes in. - public static void SwabArrayOfDouble(double[] array, int count) - { - SwabArrayOfDouble(array, 0, count); - } - - /// - /// Swaps the bytes in specified number of values in the array of double-precision - /// floating-point numbers starting at specified offset. - /// - /// The array to swap bytes in. - /// The zero-based offset in at - /// which to begin swapping bytes. - /// The number of items to swap bytes in. - public static void SwabArrayOfDouble(double[] array, int offset, int count) - { - int[] ints = new int[count * sizeof(double) / sizeof(int)]; - Buffer.BlockCopy(array, offset * sizeof(double), ints, 0, ints.Length * sizeof(int)); - - SwabArrayOfLong(ints, ints.Length); - - int pos = 0; - while (count-- > 0) - { - int temp = ints[pos]; - ints[pos] = ints[pos + 1]; - ints[pos + 1] = temp; - pos += 2; - } - - Buffer.BlockCopy(ints, 0, array, offset * sizeof(double), ints.Length * sizeof(int)); - } - - /// - /// Replaces specified number of bytes in with the - /// equivalent bit-reversed bytes. - /// - /// - /// Replaces specified number of bytes in with the - /// equivalent bit-reversed bytes. - /// - /// The buffer to replace bytes in. - /// The number of bytes to process. - /// - /// This operation is performed with a lookup table, which can be retrieved using the - /// method. - /// - public static void ReverseBits(byte[] buffer, int count) - { - ReverseBits(buffer, 0, count); - } - - /// - /// Replaces specified number of bytes in with the - /// equivalent bit-reversed bytes starting at specified offset. - /// - /// The buffer to replace bytes in. - /// The zero-based offset in at - /// which to begin processing bytes. - /// The number of bytes to process. - /// - /// This operation is performed with a lookup table, which can be retrieved using the - /// method. - /// - public static void ReverseBits(byte[] buffer, int offset, int count) - { - for (; count > 8; count -= 8) - { - buffer[offset + 0] = TIFFBitRevTable[buffer[offset + 0]]; - buffer[offset + 1] = TIFFBitRevTable[buffer[offset + 1]]; - buffer[offset + 2] = TIFFBitRevTable[buffer[offset + 2]]; - buffer[offset + 3] = TIFFBitRevTable[buffer[offset + 3]]; - buffer[offset + 4] = TIFFBitRevTable[buffer[offset + 4]]; - buffer[offset + 5] = TIFFBitRevTable[buffer[offset + 5]]; - buffer[offset + 6] = TIFFBitRevTable[buffer[offset + 6]]; - buffer[offset + 7] = TIFFBitRevTable[buffer[offset + 7]]; - offset += 8; - } - - while (count-- > 0) - { - buffer[offset] = TIFFBitRevTable[buffer[offset]]; - offset++; - } - } - - /// - /// Retrieves a bit reversal table. - /// - /// if set to true then bit reversal table will be - /// retrieved; otherwise, the table that do not reverse bit values will be retrieved. - /// The bit reversal table. - /// If is false then the table that do not - /// reverse bit values will be retrieved. It is a lookup table that can be used as an - /// identity function; i.e. NoBitRevTable[n] == n. - public static byte[] GetBitRevTable(bool reversed) - { - return (reversed ? TIFFBitRevTable : TIFFNoBitRevTable); - } - - /// - /// Converts a byte buffer into array of 32-bit values. - /// - /// The byte buffer. - /// The zero-based offset in at - /// which to begin converting bytes. - /// The number of bytes to convert. - /// The array of 32-bit values. - public static int[] ByteArrayToInts(byte[] buffer, int offset, int count) - { - int intCount = count / sizeof(int); - int[] integers = new int[intCount]; - Buffer.BlockCopy(buffer, offset, integers, 0, intCount * sizeof(int)); - return integers; - } - - /// - /// Converts array of 32-bit values into array of bytes. - /// - /// The array of 32-bit values. - /// The zero-based offset in at - /// which to begin converting bytes. - /// The number of 32-bit values to convert. - /// The byte array to store converted values at. - /// The zero-based offset in at - /// which to begin storing converted values. - public static void IntsToByteArray(int[] source, int srcOffset, int srcCount, byte[] bytes, int offset) - { - Buffer.BlockCopy(source, srcOffset * sizeof(int), bytes, offset, srcCount * sizeof(int)); - } - - /// - /// Converts a byte buffer into array of 16-bit values. - /// - /// The byte buffer. - /// The zero-based offset in at - /// which to begin converting bytes. - /// The number of bytes to convert. - /// The array of 16-bit values. - public static short[] ByteArrayToShorts(byte[] buffer, int offset, int count) - { - int shortCount = count / sizeof(short); - short[] shorts = new short[shortCount]; - Buffer.BlockCopy(buffer, offset, shorts, 0, shortCount * sizeof(short)); - return shorts; - } - - /// - /// Converts array of 16-bit values into array of bytes. - /// - /// The array of 16-bit values. - /// The zero-based offset in at - /// which to begin converting bytes. - /// The number of 16-bit values to convert. - /// The byte array to store converted values at. - /// The zero-based offset in at - /// which to begin storing converted values. - public static void ShortsToByteArray(short[] source, int srcOffset, int srcCount, byte[] bytes, int offset) - { - Buffer.BlockCopy(source, srcOffset * sizeof(short), bytes, offset, srcCount * sizeof(short)); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffCodec.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffCodec.cs deleted file mode 100644 index 1c8fffe3..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffCodec.cs +++ /dev/null @@ -1,380 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Base class for all codecs within the library. - /// - /// - /// A codec is a class that implements decoding, encoding, or decoding and encoding of a - /// compression algorithm. - /// - /// The library provides a collection of builtin codecs. More codecs may be registered - /// through calls to the library and/or the builtin implementations may be overridden. - /// -#if EXPOSE_LIBTIFF - public -#endif - class TiffCodec - { - /// - /// An instance of . - /// - protected Tiff m_tif; - - /// - /// Compression scheme this codec impelements. - /// - protected internal Compression m_scheme; - - /// - /// Codec name. - /// - protected internal string m_name; - - /// - /// Initializes a new instance of the class. - /// - /// An instance of class. - /// The compression scheme for the codec. - /// The name of the codec. - public TiffCodec(Tiff tif, Compression scheme, string name) - { - m_scheme = scheme; - m_tif = tif; - m_name = name; - } - - /// - /// Gets a value indicating whether this codec can encode data. - /// - /// - /// true if this codec can encode data; otherwise, false. - /// - public virtual bool CanEncode - { - get - { - return false; - } - } - - /// - /// Gets a value indicating whether this codec can decode data. - /// - /// - /// true if this codec can decode data; otherwise, false. - /// - public virtual bool CanDecode - { - get - { - return false; - } - } - - /// - /// Initializes this instance. - /// - /// true if initialized successfully - public virtual bool Init() - { - return true; - } - - /// - /// Setups the decoder part of the codec. - /// - /// - /// true if this codec successfully setup its decoder part and can decode data; - /// otherwise, false. - /// - /// - /// SetupDecode is called once before - /// . - public virtual bool SetupDecode() - { - return true; - } - - /// - /// Prepares the decoder part of the codec for a decoding. - /// - /// The zero-based sample plane index. - /// true if this codec successfully prepared its decoder part and ready - /// to decode data; otherwise, false. - /// - /// PreDecode is called after and before decoding. - /// - public virtual bool PreDecode(short plane) - { - return true; - } - - /// - /// Decodes one row of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to . - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public virtual bool DecodeRow(byte[] buffer, int offset, int count, short plane) - { - return noDecode("scanline"); - } - - /// - /// Decodes one strip of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to . - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public virtual bool DecodeStrip(byte[] buffer, int offset, int count, short plane) - { - return noDecode("strip"); - } - - /// - /// Decodes one tile of image data. - /// - /// The buffer to place decoded image data to. - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The number of decoded bytes that should be placed - /// to . - /// The zero-based sample plane index. - /// - /// true if image data was decoded successfully; otherwise, false. - /// - public virtual bool DecodeTile(byte[] buffer, int offset, int count, short plane) - { - return noDecode("tile"); - } - - /// - /// Setups the encoder part of the codec. - /// - /// - /// true if this codec successfully setup its encoder part and can encode data; - /// otherwise, false. - /// - /// - /// SetupEncode is called once before - /// . - public virtual bool SetupEncode() - { - return true; - } - - /// - /// Prepares the encoder part of the codec for a encoding. - /// - /// The zero-based sample plane index. - /// true if this codec successfully prepared its encoder part and ready - /// to encode data; otherwise, false. - /// - /// PreEncode is called after and before encoding. - /// - public virtual bool PreEncode(short plane) - { - return true; - } - - /// - /// Performs any actions after encoding required by the codec. - /// - /// true if all post-encode actions succeeded; otherwise, false - /// - /// PostEncode is called after encoding and can be used to release any external - /// resources needed during encoding. - /// - public virtual bool PostEncode() - { - return true; - } - - /// - /// Encodes one row of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to . - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public virtual bool EncodeRow(byte[] buffer, int offset, int count, short plane) - { - return noEncode("scanline"); - } - - /// - /// Encodes one strip of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to . - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public virtual bool EncodeStrip(byte[] buffer, int offset, int count, short plane) - { - return noEncode("strip"); - } - - /// - /// Encodes one tile of image data. - /// - /// The buffer with image data to be encoded. - /// The zero-based byte offset in at - /// which to begin read image data. - /// The maximum number of encoded bytes that can be placed - /// to . - /// The zero-based sample plane index. - /// - /// true if image data was encoded successfully; otherwise, false. - /// - public virtual bool EncodeTile(byte[] buffer, int offset, int count, short plane) - { - return noEncode("tile"); - } - - /// - /// Flushes any internal data buffers and terminates current operation. - /// - public virtual void Close() - { - } - - /// - /// Seeks the specified row in the strip being processed. - /// - /// The row to seek. - /// true if specified row was successfully found; otherwise, false - public virtual bool Seek(int row) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "Compression algorithm does not support random access"); - return false; - } - - /// - /// Cleanups the state of the codec. - /// - /// - /// Cleanup is called when codec is no longer needed (won't be used) and can be - /// used for example to restore tag methods that were substituted. - public virtual void Cleanup() - { - } - - /// - /// Calculates and/or constrains a strip size. - /// - /// The proposed strip size (may be zero or negative). - /// A strip size to use. - public virtual int DefStripSize(int size) - { - if (size < 1) - { - // If RowsPerStrip is unspecified, try to break the image up into strips that are - // approximately STRIP_SIZE_DEFAULT bytes long. - int scanline = m_tif.ScanlineSize(); - size = Tiff.STRIP_SIZE_DEFAULT / (scanline == 0 ? 1 : scanline); - if (size == 0) - { - // very wide images - size = 1; - } - } - - return size; - } - - /// - /// Calculate and/or constrains a tile size - /// - /// The proposed tile width upon the call / tile width to use after the call. - /// The proposed tile height upon the call / tile height to use after the call. - public virtual void DefTileSize(ref int width, ref int height) - { - if (width < 1) - width = 256; - - if (height < 1) - height = 256; - - // roundup to a multiple of 16 per the spec - if ((width & 0xf) != 0) - width = Tiff.roundUp(width, 16); - - if ((height & 0xf) != 0) - height = Tiff.roundUp(height, 16); - } - - private bool noEncode(string method) - { - TiffCodec c = m_tif.FindCodec(m_tif.m_dir.td_compression); - if (c != null) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "{0} {1} encoding is not implemented", c.m_name, method); - } - else - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "Compression scheme {0} {1} encoding is not implemented", - m_tif.m_dir.td_compression, method); - } - - return false; - } - - private bool noDecode(string method) - { - TiffCodec c = m_tif.FindCodec(m_tif.m_dir.td_compression); - if (c != null) - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "{0} {1} decoding is not implemented", c.m_name, method); - } - else - { - Tiff.ErrorExt(m_tif, m_tif.m_clientdata, m_tif.m_name, - "Compression scheme {0} {1} decoding is not implemented", - m_tif.m_dir.td_compression, method); - } - - return false; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffErrorHandler.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffErrorHandler.cs deleted file mode 100644 index ef003765..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffErrorHandler.cs +++ /dev/null @@ -1,130 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System; -using System.IO; - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Default error handler implementation. - /// - /// - /// TiffErrorHandler provides error and warning handling methods that write an - /// error or a warning messages to the . - /// - /// Applications that desire to capture control in the event of an error or a warning should - /// set their custom error and warning handler using method. - /// - /// -#if EXPOSE_LIBTIFF - public -#endif - class TiffErrorHandler - { - /// - /// Handles an error by writing it text to the . - /// - /// An instance of the class. Can be null. - /// The method where an error is detected. - /// A composite format string (see Remarks). - /// An object array that contains zero or more objects to format. - /// - /// The is a composite format string that uses the same format as - /// method. The parameter, if - /// not null, is printed before the message; it typically is used to identify the - /// method in which an error is detected. - /// - public virtual void ErrorHandler(Tiff tif, string method, string format, params object[] args) - { - using (TextWriter stderr = Console.Error) - { - if (method != null) - stderr.Write("{0}: ", method); - - stderr.Write(format, args); - stderr.Write("\n"); - } - } - - /// - /// Handles an error by writing it text to the . - /// - /// An instance of the class. Can be null. - /// A client data. - /// The method where an error is detected. - /// A composite format string (see Remarks). - /// An object array that contains zero or more objects to format. - /// - /// The is a composite format string that uses the same format as - /// method. The parameter, if - /// not null, is printed before the message; it typically is used to identify the - /// method in which an error is detected. - /// - /// The parameter can be anything. Its value and meaning is - /// defined by an application and not the library. - /// - public virtual void ErrorHandlerExt(Tiff tif, object clientData, string method, string format, params object[] args) - { - } - - /// - /// Handles a warning by writing it text to the . - /// - /// An instance of the class. Can be null. - /// The method where a warning is detected. - /// A composite format string (see Remarks). - /// An object array that contains zero or more objects to format. - /// - /// The is a composite format string that uses the same format as - /// method. The parameter, if - /// not null, is printed before the message; it typically is used to identify the - /// method in which a warning is detected. - /// - public virtual void WarningHandler(Tiff tif, string method, string format, params object[] args) - { - using (TextWriter stderr = Console.Error) - { - if (method != null) - stderr.Write("{0}: ", method); - - stderr.Write("Warning, "); - stderr.Write(format, args); - stderr.Write("\n"); - } - } - - /// - /// Handles a warning by writing it text to the . - /// - /// An instance of the class. Can be null. - /// A client data. - /// The method where a warning is detected. - /// A composite format string (see Remarks). - /// An object array that contains zero or more objects to format. - /// - /// The is a composite format string that uses the same format as - /// method. The parameter, if - /// not null, is printed before the message; it typically is used to identify the - /// method in which a warning is detected. - /// - /// The parameter can be anything. Its value and meaning is - /// defined by an application and not the library. - /// - public virtual void WarningHandlerExt(Tiff tif, object clientData, string method, string format, params object[] args) - { - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffFieldInfo.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffFieldInfo.cs deleted file mode 100644 index feca5999..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffFieldInfo.cs +++ /dev/null @@ -1,167 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Represents a TIFF field information. - /// - /// - /// TiffFieldInfo describes a field. It provides information about field name, type, - /// number of values etc. - /// -#if EXPOSE_LIBTIFF - public -#endif - class TiffFieldInfo - { - private TiffTag m_tag; - private short m_readCount; - private short m_writeCount; - private TiffType m_type; - private short m_bit; - private bool m_okToChange; - private bool m_passCount; - private string m_name; - - /// - /// marker for variable length tags - /// - public const short Variable = -1; - - /// - /// marker for SamplesPerPixel-bound tags - /// - public const short Spp = -2; - - /// - /// marker for integer variable length tags - /// - public const short Variable2 = -3; - - /// - /// Initializes a new instance of the class. - /// - /// The tag to describe. - /// The number of values to read when reading field information or - /// one of , and . - /// The number of values to write when writing field information - /// or one of , and . - /// The type of the field value. - /// Index of the bit to use in "Set Fields Vector" when this instance - /// is merged into field info collection. Take a look at class. - /// If true, then it is permissible to set the tag's value even - /// after writing has commenced. - /// If true, then number of value elements should be passed to - /// method as second parameter (right after tag type AND - /// before value itself). - /// The name (description) of the tag this instance describes. - public TiffFieldInfo(TiffTag tag, short readCount, short writeCount, - TiffType type, short bit, bool okToChange, bool passCount, string name) - { - m_tag = tag; - m_readCount = readCount; - m_writeCount = writeCount; - m_type = type; - m_bit = bit; - m_okToChange = okToChange; - m_passCount = passCount; - m_name = name; - } - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - if (m_bit != FieldBit.Custom || m_name.Length == 0) - return m_tag.ToString(); - - return m_name; - } - - /// - /// The tag described by this instance. - /// - public TiffTag Tag - { - get { return m_tag; } - } - - /// - /// Number of values to read when reading field information or - /// one of , and . - /// - public short ReadCount - { - get { return m_readCount; } - } - - /// - /// Number of values to write when writing field information or - /// one of , and . - /// - public short WriteCount - { - get { return m_writeCount; } - } - - /// - /// Type of the field values. - /// - public TiffType Type - { - get { return m_type; } - } - - /// - /// Index of the bit to use in "Set Fields Vector" when this instance - /// is merged into field info collection. Take a look at class. - /// - public short Bit - { - get { return m_bit; } - } - - /// - /// If true, then it is permissible to set the tag's value even after writing has commenced. - /// - public bool OkToChange - { - get { return m_okToChange; } - } - - /// - /// If true, then number of value elements should be passed to - /// method as second parameter (right after tag type AND before values itself). - /// - public bool PassCount - { - get { return m_passCount; } - } - - /// - /// The name (or description) of the tag this instance describes. - /// - public string Name - { - get { return m_name; } - internal set { m_name = value; } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffRgbaImage.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffRgbaImage.cs deleted file mode 100644 index e0809e6a..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffRgbaImage.cs +++ /dev/null @@ -1,3248 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System; -using System.Globalization; - -using BitMiracle.LibTiff.Classic.Internal; - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// RGBA-style image support. Provides methods for decoding images into RGBA (or other) format. - /// - /// - /// - /// TiffRgbaImage provide a high-level interface through which TIFF images may be read - /// into memory. Images may be strip- or tile-based and have a variety of different - /// characteristics: bits/sample, samples/pixel, photometric, etc. The target raster format - /// can be customized to a particular application's needs by installing custom methods that - /// manipulate image data according to application requirements. - /// - /// The default usage for this class: check if an image can be processed using - /// , construct an instance of - /// TiffRgbaImage using and then read and decode an image into a - /// target raster using . can be called - /// multiple times to decode an image using different state parameters. If multiple images - /// are to be displayed and there is not enough space for each of the decoded rasters, - /// multiple instances of TiffRgbaImage can be managed and then calls can be made to - /// as needed to display an image. - /// - /// To use the core support for reading and processing TIFF images, but write the resulting - /// raster data in a different format one need only override the "put methods" used to store - /// raster data. These methods are initially setup by to point to methods - /// that pack raster data in the default ABGR pixel format. Two different methods are used - /// according to the physical organization of the image data in the file: one for - /// = .CONTIG (packed samples), - /// and another for = .SEPARATE - /// (separated samples). Note that this mechanism can be used to transform the data before - /// storing it in the raster. For example one can convert data to colormap indices for display - /// on a colormap display. - /// To setup custom "put" method please use property for contiguously - /// packed samples and/or property for separated samples. - /// - /// The methods of TiffRgbaImage support the most commonly encountered flavors of TIFF. - /// It is possible to extend this support by overriding the "get method" invoked by - /// to read TIFF image data. Details of doing this are a bit involved, - /// it is best to make a copy of an existing get method and modify it to suit the needs of an - /// application. To setup custom "get" method please use property. - /// -#if EXPOSE_LIBTIFF - public -#endif - class TiffRgbaImage - { - internal const string photoTag = "PhotometricInterpretation"; - - /// - /// image handle - /// - private Tiff tif; - - /// - /// stop on read error - /// - private bool stoponerr; - - /// - /// data is packed/separate - /// - private bool isContig; - - /// - /// type of alpha data present - /// - private ExtraSample alpha; - - /// - /// image width - /// - private int width; - - /// - /// image height - /// - private int height; - - /// - /// image bits/sample - /// - private short bitspersample; - - /// - /// image samples/pixel - /// - private short samplesperpixel; - - /// - /// image orientation - /// - private Orientation orientation; - - /// - /// requested orientation - /// - private Orientation req_orientation; - - /// - /// image photometric interp - /// - private Photometric photometric; - - /// - /// colormap pallete - /// - private short[] redcmap; - - private short[] greencmap; - - private short[] bluecmap; - - private GetDelegate get; - private PutContigDelegate putContig; - private PutSeparateDelegate putSeparate; - - /// - /// sample mapping array - /// - private byte[] Map; - - /// - /// black and white map - /// - private int[][] BWmap; - - /// - /// palette image map - /// - private int[][] PALmap; - - /// - /// YCbCr conversion state - /// - private TiffYCbCrToRGB ycbcr; - - /// - /// CIE L*a*b conversion state - /// - private TiffCIELabToRGB cielab; - - private static readonly TiffDisplay display_sRGB = new TiffDisplay( - // XYZ -> luminance matrix - new float[] { 3.2410F, -1.5374F, -0.4986F }, - new float[] { -0.9692F, 1.8760F, 0.0416F }, - new float[] { 0.0556F, -0.2040F, 1.0570F }, - 100.0F, 100.0F, 100.0F, // Light o/p for reference white - 255, 255, 255, // Pixel values for ref. white - 1.0F, 1.0F, 1.0F, // Residual light o/p for black pixel - 2.4F, 2.4F, 2.4F // Gamma values for the three guns - ); - - private const int A1 = 0xff << 24; - - // Helper constants used in Orientation tag handling - private const int FLIP_VERTICALLY = 0x01; - private const int FLIP_HORIZONTALLY = 0x02; - - internal int row_offset; - internal int col_offset; - - /// - /// Delegate for "put" method (the method that is called to pack pixel data in the raster) - /// used when converting contiguously packed samples. - /// - /// An instance of the class. - /// The raster (the buffer to place decoded image data to). - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The value that should be added to - /// after each row processed. - /// The x-coordinate of the first pixel in block of pixels to be decoded. - /// The y-coordinate of the first pixel in block of pixels to be decoded. - /// The block width. - /// The block height. - /// The buffer with image data. - /// The zero-based byte offset in at - /// which to begin reading image bytes. - /// The value that should be added to - /// after each row processed. - /// - /// The image reading and conversion methods invoke "put" methods to copy/image/whatever - /// tiles of raw image data. A default set of methods is provided to convert/copy raw - /// image data to 8-bit packed ABGR format rasters. Applications can supply alternate - /// methods that unpack the data into a different format or, for example, unpack the data - /// and draw the unpacked raster on the display. - /// - /// To setup custom "put" method for contiguously packed samples please use - /// property. - /// - /// The is usually 0. It is greater than 0 if width of strip - /// being converted is greater than image width or part of the tile being converted is - /// outside the image (may be true for tiles on the right and bottom edge of the image). - /// In other words, is used to make up for any padding on - /// the end of each line of the buffer with image data. - /// - /// The is 0 if width of tile being converted is equal to - /// image width and image data should not be flipped vertically. In other circumstances - /// is used to make up for any padding on the end of each - /// line of the raster and/or for flipping purposes. - /// - public delegate void PutContigDelegate( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift); - - /// - /// Delegate for "put" method (the method that is called to pack pixel data in the raster) - /// used when converting separated samples. - /// - /// An instance of the class. - /// The raster (the buffer to place decoded image data to). - /// The zero-based byte offset in at - /// which to begin storing decoded bytes. - /// The value that should be added to - /// after each row processed. - /// The x-coordinate of the first pixel in block of pixels to be decoded. - /// The y-coordinate of the first pixel in block of pixels to be decoded. - /// The block width. - /// The block height. - /// The buffer with image data. - /// The zero-based byte offset in at - /// which to begin reading image bytes that constitute first sample plane. - /// The zero-based byte offset in at - /// which to begin reading image bytes that constitute second sample plane. - /// The zero-based byte offset in at - /// which to begin reading image bytes that constitute third sample plane. - /// The zero-based byte offset in at - /// which to begin reading image bytes that constitute fourth sample plane. - /// The value that should be added to , - /// , and - /// after each row processed. - /// - /// The image reading and conversion methods invoke "put" methods to copy/image/whatever - /// tiles of raw image data. A default set of methods is provided to convert/copy raw - /// image data to 8-bit packed ABGR format rasters. Applications can supply alternate - /// methods that unpack the data into a different format or, for example, unpack the data - /// and draw the unpacked raster on the display. - /// - /// To setup custom "put" method for separated samples please use - /// property. - /// - /// The is usually 0. It is greater than 0 if width of strip - /// being converted is greater than image width or part of the tile being converted is - /// outside the image (may be true for tiles on the right and bottom edge of the image). - /// In other words, is used to make up for any padding on - /// the end of each line of the buffer with image data. - /// - /// The is 0 if width of tile being converted is equal to - /// image width and image data should not be flipped vertically. In other circumstances - /// is used to make up for any padding on the end of each - /// line of the raster and/or for flipping purposes. - /// - public delegate void PutSeparateDelegate( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, - byte[] buffer, int offset1, int offset2, int offset3, int offset4, int bufferShift); - - /// - /// Delegate for "get" method (the method that is called to produce RGBA raster). - /// - /// An instance of the class. - /// The raster (the buffer to place decoded image data to). - /// The zero-based byte offset in at which - /// to begin storing decoded bytes. - /// The raster width. - /// The raster height. - /// true if the image was successfully read and decoded; otherwise, - /// false. - /// - /// A default set of methods is provided to read and convert/copy raw image data to 8-bit - /// packed ABGR format rasters. Applications can supply alternate method for this. - /// - /// To setup custom "get" method please use property. - /// - public delegate bool GetDelegate(TiffRgbaImage img, int[] raster, int offset, int width, int height); - - private TiffRgbaImage() - { - } - - /// - /// Creates new instance of the class. - /// - /// - /// The instance of the class used to retrieve - /// image data. - /// - /// - /// if set to true then an error will terminate the conversion; otherwise "get" - /// methods will continue processing data until all the possible data in the image have - /// been requested. - /// - /// The error message (if any) gets placed here. - /// - /// New instance of the class if the image specified - /// by can be converted to RGBA format; otherwise, null is - /// returned and contains the reason why it is being - /// rejected. - /// - public static TiffRgbaImage Create(Tiff tif, bool stopOnError, out string errorMsg) - { - errorMsg = null; - - // Initialize to normal values - TiffRgbaImage img = new TiffRgbaImage(); - img.row_offset = 0; - img.col_offset = 0; - img.redcmap = null; - img.greencmap = null; - img.bluecmap = null; - img.req_orientation = Orientation.BOTLEFT; // It is the default - img.tif = tif; - img.stoponerr = stopOnError; - - FieldValue[] result = tif.GetFieldDefaulted(TiffTag.BITSPERSAMPLE); - img.bitspersample = result[0].ToShort(); - switch (img.bitspersample) - { - case 1: - case 2: - case 4: - case 8: - case 16: - break; - - default: - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle images with {0}-bit samples", img.bitspersample); - return null; - } - - img.alpha = 0; - result = tif.GetFieldDefaulted(TiffTag.SAMPLESPERPIXEL); - img.samplesperpixel = result[0].ToShort(); - - result = tif.GetFieldDefaulted(TiffTag.EXTRASAMPLES); - short extrasamples = result[0].ToShort(); - byte[] sampleinfo = result[1].ToByteArray(); - - if (extrasamples >= 1) - { - switch ((ExtraSample)sampleinfo[0]) - { - case ExtraSample.UNSPECIFIED: - if (img.samplesperpixel > 3) - { - // Workaround for some images without correct info about alpha channel - img.alpha = ExtraSample.ASSOCALPHA; - } - break; - - case ExtraSample.ASSOCALPHA: - // data is pre-multiplied - case ExtraSample.UNASSALPHA: - // data is not pre-multiplied - img.alpha = (ExtraSample)sampleinfo[0]; - break; - } - } - - if (Tiff.DEFAULT_EXTRASAMPLE_AS_ALPHA) - { - result = tif.GetField(TiffTag.PHOTOMETRIC); - if (result == null) - img.photometric = Photometric.MINISWHITE; - - if (extrasamples == 0 && img.samplesperpixel == 4 && img.photometric == Photometric.RGB) - { - img.alpha = ExtraSample.ASSOCALPHA; - extrasamples = 1; - } - } - - int colorchannels = img.samplesperpixel - extrasamples; - - result = tif.GetFieldDefaulted(TiffTag.COMPRESSION); - Compression compress = (Compression)result[0].ToInt(); - - result = tif.GetFieldDefaulted(TiffTag.PLANARCONFIG); - PlanarConfig planarconfig = (PlanarConfig)result[0].ToShort(); - - result = tif.GetField(TiffTag.PHOTOMETRIC); - if (result == null) - { - switch (colorchannels) - { - case 1: - if (img.isCCITTCompression()) - img.photometric = Photometric.MINISWHITE; - else - img.photometric = Photometric.MINISBLACK; - break; - - case 3: - img.photometric = Photometric.RGB; - break; - - default: - errorMsg = string.Format(CultureInfo.InvariantCulture, "Missing needed {0} tag", photoTag); - return null; - } - } - else - img.photometric = (Photometric)result[0].ToInt(); - - switch (img.photometric) - { - case Photometric.PALETTE: - result = tif.GetField(TiffTag.COLORMAP); - if (result == null) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, "Missing required \"Colormap\" tag"); - return null; - } - - short[] red_orig = result[0].ToShortArray(); - short[] green_orig = result[1].ToShortArray(); - short[] blue_orig = result[2].ToShortArray(); - - // copy the colormaps so we can modify them - int n_color = (1 << img.bitspersample); - img.redcmap = new short[n_color]; - img.greencmap = new short[n_color]; - img.bluecmap = new short[n_color]; - - Buffer.BlockCopy(red_orig, 0, img.redcmap, 0, n_color * sizeof(short)); - Buffer.BlockCopy(green_orig, 0, img.greencmap, 0, n_color * sizeof(short)); - Buffer.BlockCopy(blue_orig, 0, img.bluecmap, 0, n_color * sizeof(short)); - - if (planarconfig == PlanarConfig.CONTIG && - img.samplesperpixel != 1 && img.bitspersample < 8) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle contiguous data with {0}={1}, and {2}={3} and Bits/Sample={4}", - photoTag, img.photometric, "Samples/pixel", img.samplesperpixel, img.bitspersample); - return null; - } - break; - - case Photometric.MINISWHITE: - case Photometric.MINISBLACK: - if (planarconfig == PlanarConfig.CONTIG && - img.samplesperpixel != 1 && img.bitspersample < 8) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle contiguous data with {0}={1}, and {2}={3} and Bits/Sample={4}", - photoTag, img.photometric, "Samples/pixel", img.samplesperpixel, img.bitspersample); - return null; - } - break; - - case Photometric.YCBCR: - // It would probably be nice to have a reality check here. - if (planarconfig == PlanarConfig.CONTIG) - { - // can rely on LibJpeg.Net to convert to RGB - // XXX should restore current state on exit - switch (compress) - { - case Compression.JPEG: - // TODO: when complete tests verify complete desubsampling and - // YCbCr handling, remove use of JPEGCOLORMODE in favor of native - // handling - tif.SetField(TiffTag.JPEGCOLORMODE, JpegColorMode.RGB); - img.photometric = Photometric.RGB; - break; - - default: - // do nothing - break; - } - } - - // TODO: if at all meaningful and useful, make more complete support check - // here, or better still, refactor to let supporting code decide whether there - // is support and what meaningfull error to return - break; - - case Photometric.RGB: - if (colorchannels < 3) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle RGB image with {0}={1}", "Color channels", colorchannels); - return null; - } - break; - - case Photometric.SEPARATED: - result = tif.GetFieldDefaulted(TiffTag.INKSET); - InkSet inkset = (InkSet)result[0].ToByte(); - - if (inkset != InkSet.CMYK) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle separated image with {0}={1}", "InkSet", inkset); - return null; - } - - if (img.samplesperpixel < 4) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle separated image with {0}={1}", "Samples/pixel", img.samplesperpixel); - return null; - } - break; - - case Photometric.LOGL: - if (compress != Compression.SGILOG) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, LogL data must have {0}={1}", "Compression", Compression.SGILOG); - return null; - } - - tif.SetField(TiffTag.SGILOGDATAFMT, 3); // 8-bit RGB monitor values. - img.photometric = Photometric.MINISBLACK; // little white lie - img.bitspersample = 8; - break; - - case Photometric.LOGLUV: - if (compress != Compression.SGILOG && compress != Compression.SGILOG24) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, LogLuv data must have {0}={1} or {2}", "Compression", Compression.SGILOG, Compression.SGILOG24); - return null; - } - - if (planarconfig != PlanarConfig.CONTIG) - { - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle LogLuv images with {0}={1}", "Planarconfiguration", planarconfig); - return null; - } - - tif.SetField(TiffTag.SGILOGDATAFMT, 3); // 8-bit RGB monitor values. - img.photometric = Photometric.RGB; // little white lie - img.bitspersample = 8; - break; - - case Photometric.CIELAB: - break; - - default: - errorMsg = string.Format(CultureInfo.InvariantCulture, - "Sorry, can not handle image with {0}={1}", photoTag, img.photometric); - return null; - } - - img.Map = null; - img.BWmap = null; - img.PALmap = null; - img.ycbcr = null; - img.cielab = null; - - result = tif.GetField(TiffTag.IMAGEWIDTH); - img.width = result[0].ToInt(); - - result = tif.GetField(TiffTag.IMAGELENGTH); - img.height = result[0].ToInt(); - - result = tif.GetFieldDefaulted(TiffTag.ORIENTATION); - img.orientation = (Orientation)result[0].ToByte(); - - img.isContig = !(planarconfig == PlanarConfig.SEPARATE && colorchannels > 1); - if (img.isContig) - { - if (!img.pickContigCase()) - { - errorMsg = "Sorry, can not handle image"; - return null; - } - } - else - { - if (!img.pickSeparateCase()) - { - errorMsg = "Sorry, can not handle image"; - return null; - } - } - - return img; - } - - /// - /// Gets a value indicating whether image data has contiguous (packed) or separated samples. - /// - /// true if this image data has contiguous (packed) samples; otherwise, - /// false. - public bool IsContig - { - get - { - return isContig; - } - } - - /// - /// Gets the type of alpha data present. - /// - /// The type of alpha data present. - public ExtraSample Alpha - { - get - { - return alpha; - } - } - - /// - /// Gets the image width. - /// - /// The image width. - public int Width - { - get - { - return width; - } - } - - /// - /// Gets the image height. - /// - /// The image height. - public int Height - { - get - { - return height; - } - } - - /// - /// Gets the image bits per sample count. - /// - /// The image bits per sample count. - public short BitsPerSample - { - get - { - return bitspersample; - } - } - - /// - /// Gets the image samples per pixel count. - /// - /// The image samples per pixel count. - public short SamplesPerPixel - { - get - { - return samplesperpixel; - } - } - - /// - /// Gets the image orientation. - /// - /// The image orientation. - public Orientation Orientation - { - get - { - return orientation; - } - } - - /// - /// Gets or sets the requested orientation. - /// - /// The requested orientation. - /// The method uses this value when placing converted - /// image data into raster buffer. - public Orientation ReqOrientation - { - get - { - return req_orientation; - } - set - { - req_orientation = value; - } - } - - /// - /// Gets the photometric interpretation of the image data. - /// - /// The photometric interpretation of the image data. - public Photometric Photometric - { - get - { - return photometric; - } - } - - /// - /// Gets or sets the "get" method (the method that is called to produce RGBA raster). - /// - /// The "get" method. - public GetDelegate Get - { - get - { - return get; - } - set - { - get = value; - } - } - - /// - /// Gets or sets the "put" method (the method that is called to pack pixel data in the - /// raster) used when converting contiguously packed samples. - /// - /// The "put" method used when converting contiguously packed samples. - public PutContigDelegate PutContig - { - get - { - return putContig; - } - set - { - putContig = value; - } - } - - /// - /// Gets or sets the "put" method (the method that is called to pack pixel data in the - /// raster) used when converting separated samples. - /// - /// The "put" method used when converting separated samples. - public PutSeparateDelegate PutSeparate - { - get - { - return putSeparate; - } - set - { - putSeparate = value; - } - } - - /// - /// Reads the underlaying TIFF image and decodes it into RGBA format raster. - /// - /// The raster (the buffer to place decoded image data to). - /// The zero-based byte offset in at which - /// to begin storing decoded bytes. - /// The raster width. - /// The raster height. - /// true if the image was successfully read and decoded; otherwise, - /// false. - /// - /// GetRaster reads image into memory using current "get" () method, - /// storing the result in the user supplied RGBA using one of - /// the "put" ( or ) methods. The raster - /// is assumed to be an array of times - /// 32-bit entries, where must be less than or equal to the width - /// of the image ( may be any non-zero size). If the raster - /// dimensions are smaller than the image, the image data is cropped to the raster bounds. - /// If the raster height is greater than that of the image, then the image data placement - /// depends on the value of property. Note that the raster is - /// assumed to be organized such that the pixel at location (x, y) is - /// [y * width + x]; with the raster origin specified by the - /// value of property. - /// - /// Raster pixels are 8-bit packed red, green, blue, alpha samples. The - /// , , , and - /// should be used to access individual samples. Images without - /// Associated Alpha matting information have a constant Alpha of 1.0 (255). - /// - /// GetRaster converts non-8-bit images by scaling sample values. Palette, - /// grayscale, bilevel, CMYK, and YCbCr images are converted to RGB transparently. - /// Raster pixels are returned uncorrected by any colorimetry information present in - /// the directory. - /// - /// Samples must be either 1, 2, 4, 8, or 16 bits. Colorimetric samples/pixel must be - /// either 1, 3, or 4 (i.e. SamplesPerPixel minus ExtraSamples). - /// - /// Palette image colormaps that appear to be incorrectly written as 8-bit values are - /// automatically scaled to 16-bits. - /// - /// All error messages are directed to the current error handler. - /// - public bool GetRaster(int[] raster, int offset, int width, int height) - { - if (get == null) - { - Tiff.ErrorExt(tif, tif.m_clientdata, tif.FileName(), "No \"get\" method setup"); - return false; - } - - return get(this, raster, offset, width, height); - } - - private static int PACK(int r, int g, int b) - { - return (r | (g << 8) | (b << 16) | A1); - } - - private static int PACK4(int r, int g, int b, int a) - { - return (r | (g << 8) | (b << 16) | (a << 24)); - } - - private static int W2B(short v) - { - return ((v >> 8) & 0xff); - } - - private static int PACKW(short r, short g, short b) - { - return (W2B(r) | (W2B(g) << 8) | (W2B(b) << 16) | (int)A1); - } - - private static int PACKW4(short r, short g, short b, short a) - { - return (W2B(r) | (W2B(g) << 8) | (W2B(b) << 16) | (W2B(a) << 24)); - } - - /// - /// Palette images with <= 8 bits/sample are handled with a table to avoid lots of shifts - /// and masks. The table is setup so that put*cmaptile (below) can retrieve 8 / bitspersample - /// pixel values simply by indexing into the table with one number. - /// - private void CMAP(int x, int i, ref int j) - { - PALmap[i][j++] = PACK(redcmap[x] & 0xff, greencmap[x] & 0xff, bluecmap[x] & 0xff); - } - - /// - /// Greyscale images with less than 8 bits/sample are handled with a table to avoid lots - /// of shifts and masks. The table is setup so that put*bwtile (below) can retrieve - /// 8 / bitspersample pixel values simply by indexing into the table with one number. - /// - private void GREY(int x, int i, ref int j) - { - int c = Map[x]; - BWmap[i][j++] = PACK(c, c, c); - } - - /// - /// Get an tile-organized image that has - /// PlanarConfiguration contiguous if SamplesPerPixel > 1 - /// or - /// SamplesPerPixel == 1 - /// - private static bool gtTileContig(TiffRgbaImage img, int[] raster, int offset, int width, int height) - { - byte[] buf = new byte[img.tif.TileSize()]; - - FieldValue[] result = img.tif.GetField(TiffTag.TILEWIDTH); - int tileWidth = result[0].ToInt(); - - result = img.tif.GetField(TiffTag.TILELENGTH); - int tileHeight = result[0].ToInt(); - - int flip = img.setorientation(); - int y; - int rasterShift; - if ((flip & FLIP_VERTICALLY) != 0) - { - y = height - 1; - rasterShift = -(tileWidth + width); - } - else - { - y = 0; - rasterShift = -(tileWidth - width); - } - - bool ret = true; - for (int row = 0; row < height; ) - { - int rowstoread = tileHeight - (row + img.row_offset) % tileHeight; - int nrow = (row + rowstoread > height ? height - row : rowstoread); - for (int col = 0; col < width; col += tileWidth) - { - if (img.tif.ReadTile(buf, 0, col + img.col_offset, row + img.row_offset, 0, 0) < 0 && img.stoponerr) - { - ret = false; - break; - } - - int pos = ((row + img.row_offset) % tileHeight) * img.tif.TileRowSize(); - - if (col + tileWidth > width) - { - // Tile is clipped horizontally. Calculate visible portion and - // skewing factors. - int npix = width - col; - int bufferShift = tileWidth - npix; - - img.putContig(img, raster, offset + y * width + col, rasterShift + bufferShift, - col, y, npix, nrow, buf, pos, bufferShift); - } - else - { - img.putContig(img, raster, offset + y * width + col, rasterShift, - col, y, tileWidth, nrow, buf, pos, 0); - } - } - - y += ((flip & FLIP_VERTICALLY) != 0 ? -nrow : nrow); - row += nrow; - } - - if ((flip & FLIP_HORIZONTALLY) != 0) - { - for (int line = 0; line < height; line++) - { - int left = offset + line * width; - int right = left + width - 1; - - while (left < right) - { - int temp = raster[left]; - raster[left] = raster[right]; - raster[right] = temp; - left++; - right--; - } - } - } - - return ret; - } - - /// - /// Get an tile-organized image that has - /// SamplesPerPixel > 1 - /// PlanarConfiguration separated - /// We assume that all such images are RGB. - /// - private static bool gtTileSeparate(TiffRgbaImage img, int[] raster, int offset, int width, int height) - { - int tilesize = img.tif.TileSize(); - byte[] buf = new byte[(img.alpha != 0 ? 4 : 3) * tilesize]; - - int p0 = 0; - int p1 = p0 + tilesize; - int p2 = p1 + tilesize; - int pa = (img.alpha != 0 ? (p2 + tilesize) : -1); - - FieldValue[] result = img.tif.GetField(TiffTag.TILEWIDTH); - int tileWidth = result[0].ToInt(); - - result = img.tif.GetField(TiffTag.TILELENGTH); - int tileHeight = result[0].ToInt(); - - int flip = img.setorientation(); - int y; - int rasterShift; - if ((flip & FLIP_VERTICALLY) != 0) - { - y = height - 1; - rasterShift = -(tileWidth + width); - } - else - { - y = 0; - rasterShift = -(tileWidth - width); - } - - bool ret = true; - for (int row = 0; row < height; ) - { - int rowstoread = tileHeight - (row + img.row_offset) % tileHeight; - int nrow = (row + rowstoread > height ? height - row : rowstoread); - for (int col = 0; col < width; col += tileWidth) - { - if (img.tif.ReadTile(buf, p0, col + img.col_offset, row + img.row_offset, 0, 0) < 0 && img.stoponerr) - { - ret = false; - break; - } - - if (img.tif.ReadTile(buf, p1, col + img.col_offset, row + img.row_offset, 0, 1) < 0 && img.stoponerr) - { - ret = false; - break; - } - - if (img.tif.ReadTile(buf, p2, col + img.col_offset, row + img.row_offset, 0, 2) < 0 && img.stoponerr) - { - ret = false; - break; - } - - if (img.alpha != 0) - { - if (img.tif.ReadTile(buf, pa, col + img.col_offset, row + img.row_offset, 0, 3) < 0 && img.stoponerr) - { - ret = false; - break; - } - } - - int pos = ((row + img.row_offset) % tileHeight) * img.tif.TileRowSize(); - - if (col + tileWidth > width) - { - // Tile is clipped horizontally. - // Calculate visible portion and skewing factors. - int npix = width - col; - int bufferShift = tileWidth - npix; - - img.putSeparate(img, raster, offset + y * width + col, rasterShift + bufferShift, - col, y, npix, nrow, - buf, p0 + pos, p1 + pos, p2 + pos, img.alpha != 0 ? (pa + pos) : -1, bufferShift); - } - else - { - img.putSeparate(img, raster, offset + y * width + col, rasterShift, - col, y, tileWidth, nrow, - buf, p0 + pos, p1 + pos, p2 + pos, img.alpha != 0 ? (pa + pos) : -1, 0); - } - } - - y += ((flip & FLIP_VERTICALLY) != 0 ? -nrow : nrow); - row += nrow; - } - - if ((flip & FLIP_HORIZONTALLY) != 0) - { - for (int line = 0; line < height; line++) - { - int left = offset + line * width; - int right = left + width - 1; - - while (left < right) - { - int temp = raster[left]; - raster[left] = raster[right]; - raster[right] = temp; - left++; - right--; - } - } - } - - return ret; - } - - /// - /// Get a strip-organized image that has - /// PlanarConfiguration contiguous if SamplesPerPixel > 1 - /// or - /// SamplesPerPixel == 1 - /// - private static bool gtStripContig(TiffRgbaImage img, int[] raster, int offset, int width, int height) - { - byte[] buf = new byte[img.tif.StripSize()]; - - int flip = img.setorientation(); - int y; - int rasterShift; - if ((flip & FLIP_VERTICALLY) != 0) - { - y = height - 1; - rasterShift = -(width + width); - } - else - { - y = 0; - rasterShift = -(width - width); - } - - FieldValue[] result = img.tif.GetFieldDefaulted(TiffTag.ROWSPERSTRIP); - int rowsperstrip = result[0].ToInt(); - if (rowsperstrip == -1) - { - // San Chen - // HACK: should be UInt32.MaxValue - rowsperstrip = Int32.MaxValue; - } - - result = img.tif.GetFieldDefaulted(TiffTag.YCBCRSUBSAMPLING); - short subsamplingver = result[1].ToShort(); - - int scanline = img.tif.newScanlineSize(); - int bufferShift = (width < img.width ? img.width - width : 0); - bool ret = true; - - for (int row = 0; row < height; ) - { - int rowstoread = rowsperstrip - (row + img.row_offset) % rowsperstrip; - int nrow = (row + rowstoread > height ? height - row : rowstoread); - int nrowsub = nrow; - if ((nrowsub % subsamplingver) != 0) - nrowsub += subsamplingver - nrowsub % subsamplingver; - - if (img.tif.ReadEncodedStrip(img.tif.ComputeStrip(row + img.row_offset, 0), buf, 0, ((row + img.row_offset) % rowsperstrip + nrowsub) * scanline) < 0 && img.stoponerr) - { - ret = false; - break; - } - - int pos = ((row + img.row_offset) % rowsperstrip) * scanline; - img.putContig(img, raster, offset + y * width, rasterShift, 0, y, width, nrow, buf, pos, bufferShift); - y += (flip & FLIP_VERTICALLY) != 0 ? -nrow : nrow; - row += nrow; - } - - if ((flip & FLIP_HORIZONTALLY) != 0) - { - for (int line = 0; line < height; line++) - { - int left = offset + line * width; - int right = left + width - 1; - - while (left < right) - { - int temp = raster[left]; - raster[left] = raster[right]; - raster[right] = temp; - left++; - right--; - } - } - } - - return ret; - } - - /// - /// Get a strip-organized image with - /// SamplesPerPixel > 1 - /// PlanarConfiguration separated - /// We assume that all such images are RGB. - /// - private static bool gtStripSeparate(TiffRgbaImage img, int[] raster, int offset, int width, int height) - { - int stripsize = img.tif.StripSize(); - byte[] buf = new byte[(img.alpha != 0 ? 4 : 3) * stripsize]; - - int p0 = 0; - int p1 = p0 + stripsize; - int p2 = p1 + stripsize; - int pa = p2 + stripsize; - pa = (img.alpha != 0 ? (p2 + stripsize) : -1); - - int flip = img.setorientation(); - int y; - int rasterShift; - if ((flip & FLIP_VERTICALLY) != 0) - { - y = height - 1; - rasterShift = -(width + width); - } - else - { - y = 0; - rasterShift = -(width - width); - } - - FieldValue[] result = img.tif.GetFieldDefaulted(TiffTag.ROWSPERSTRIP); - int rowsperstrip = result[0].ToInt(); - - int scanline = img.tif.ScanlineSize(); - int bufferShift = (width < img.width ? img.width - width : 0); - bool ret = true; - for (int row = 0; row < height; ) - { - int rowstoread = rowsperstrip - (row + img.row_offset) % rowsperstrip; - int nrow = (row + rowstoread > height ? height - row : rowstoread); - int offset_row = row + img.row_offset; - - if (img.tif.ReadEncodedStrip(img.tif.ComputeStrip(offset_row, 0), buf, p0, ((row + img.row_offset) % rowsperstrip + nrow) * scanline) < 0 && img.stoponerr) - { - ret = false; - break; - } - - if (img.tif.ReadEncodedStrip(img.tif.ComputeStrip(offset_row, 1), buf, p1, ((row + img.row_offset) % rowsperstrip + nrow) * scanline) < 0 && img.stoponerr) - { - ret = false; - break; - } - - if (img.tif.ReadEncodedStrip(img.tif.ComputeStrip(offset_row, 2), buf, p2, ((row + img.row_offset) % rowsperstrip + nrow) * scanline) < 0 && img.stoponerr) - { - ret = false; - break; - } - - if (img.alpha != 0) - { - if ((img.tif.ReadEncodedStrip(img.tif.ComputeStrip(offset_row, 3), buf, pa, ((row + img.row_offset) % rowsperstrip + nrow) * scanline) < 0 && img.stoponerr)) - { - ret = false; - break; - } - } - - int pos = ((row + img.row_offset) % rowsperstrip) * scanline; - - img.putSeparate(img, raster, offset + y * width, rasterShift, - 0, y, width, nrow, - buf, p0 + pos, p1 + pos, p2 + pos, img.alpha != 0 ? (pa + pos) : -1, bufferShift); - - y += (flip & FLIP_VERTICALLY) != 0 ? -nrow : nrow; - row += nrow; - } - - if ((flip & FLIP_HORIZONTALLY) != 0) - { - for (int line = 0; line < height; line++) - { - int left = offset + line * width; - int right = left + width - 1; - - while (left < right) - { - int temp = raster[left]; - raster[left] = raster[right]; - raster[right] = temp; - left++; - right--; - } - } - } - - return ret; - } - - private bool isCCITTCompression() - { - FieldValue[] result = tif.GetField(TiffTag.COMPRESSION); - Compression compress = (Compression)result[0].ToInt(); - - return (compress == Compression.CCITTFAX3 || - compress == Compression.CCITTFAX4 || - compress == Compression.CCITTRLE || - compress == Compression.CCITTRLEW); - } - - private int setorientation() - { - switch (orientation) - { - case Orientation.TOPLEFT: - case Orientation.LEFTTOP: - if (req_orientation == Orientation.TOPRIGHT || req_orientation == Orientation.RIGHTTOP) - return FLIP_HORIZONTALLY; - else if (req_orientation == Orientation.BOTRIGHT || req_orientation == Orientation.RIGHTBOT) - return FLIP_HORIZONTALLY | FLIP_VERTICALLY; - else if (req_orientation == Orientation.BOTLEFT || req_orientation == Orientation.LEFTBOT) - return FLIP_VERTICALLY; - - return 0; - - case Orientation.TOPRIGHT: - case Orientation.RIGHTTOP: - if (req_orientation == Orientation.TOPLEFT || req_orientation == Orientation.LEFTTOP) - return FLIP_HORIZONTALLY; - else if (req_orientation == Orientation.BOTRIGHT || req_orientation == Orientation.RIGHTBOT) - return FLIP_VERTICALLY; - else if (req_orientation == Orientation.BOTLEFT || req_orientation == Orientation.LEFTBOT) - return FLIP_HORIZONTALLY | FLIP_VERTICALLY; - - return 0; - - case Orientation.BOTRIGHT: - case Orientation.RIGHTBOT: - if (req_orientation == Orientation.TOPLEFT || req_orientation == Orientation.LEFTTOP) - return FLIP_HORIZONTALLY | FLIP_VERTICALLY; - else if (req_orientation == Orientation.TOPRIGHT || req_orientation == Orientation.RIGHTTOP) - return FLIP_VERTICALLY; - else if (req_orientation == Orientation.BOTLEFT || req_orientation == Orientation.LEFTBOT) - return FLIP_HORIZONTALLY; - - return 0; - - case Orientation.BOTLEFT: - case Orientation.LEFTBOT: - if (req_orientation == Orientation.TOPLEFT || req_orientation == Orientation.LEFTTOP) - return FLIP_VERTICALLY; - else if (req_orientation == Orientation.TOPRIGHT || req_orientation == Orientation.RIGHTTOP) - return FLIP_HORIZONTALLY | FLIP_VERTICALLY; - else if (req_orientation == Orientation.BOTRIGHT || req_orientation == Orientation.RIGHTBOT) - return FLIP_HORIZONTALLY; - - return 0; - } - - return 0; - } - - /// - /// Select the appropriate conversion routine for packed data. - /// - private bool pickContigCase() - { - get = tif.IsTiled() ? new GetDelegate(gtTileContig) : new GetDelegate(gtStripContig); - putContig = null; - - switch (photometric) - { - case Photometric.RGB: - switch (bitspersample) - { - case 8: - if (alpha == ExtraSample.ASSOCALPHA) - putContig = putRGBAAcontig8bittile; - else if (alpha == ExtraSample.UNASSALPHA) - putContig = putRGBUAcontig8bittile; - else - putContig = putRGBcontig8bittile; - break; - - case 16: - if (alpha == ExtraSample.ASSOCALPHA) - putContig = putRGBAAcontig16bittile; - else if (alpha == ExtraSample.UNASSALPHA) - putContig = putRGBUAcontig16bittile; - else - putContig = putRGBcontig16bittile; - break; - } - break; - - case Photometric.SEPARATED: - if (buildMap()) - { - if (bitspersample == 8) - { - if (Map == null) - putContig = putRGBcontig8bitCMYKtile; - else - putContig = putRGBcontig8bitCMYKMaptile; - } - } - break; - - case Photometric.PALETTE: - if (buildMap()) - { - switch (bitspersample) - { - case 8: - putContig = put8bitcmaptile; - break; - case 4: - putContig = put4bitcmaptile; - break; - case 2: - putContig = put2bitcmaptile; - break; - case 1: - putContig = put1bitcmaptile; - break; - } - } - break; - - case Photometric.MINISWHITE: - case Photometric.MINISBLACK: - if (buildMap()) - { - switch (bitspersample) - { - case 16: - putContig = put16bitbwtile; - break; - case 8: - putContig = putgreytile; - break; - case 4: - putContig = put4bitbwtile; - break; - case 2: - putContig = put2bitbwtile; - break; - case 1: - putContig = put1bitbwtile; - break; - } - } - break; - - case Photometric.YCBCR: - if (bitspersample == 8) - { - if (initYCbCrConversion()) - { - // The 6.0 spec says that subsampling must be one of 1, 2, or 4, and - // that vertical subsampling must always be <= horizontal subsampling; - // so there are only a few possibilities and we just enumerate the cases. - // Joris: added support for the [1, 2] case, nonetheless, to accommodate - // some OJPEG files - FieldValue[] result = tif.GetFieldDefaulted(TiffTag.YCBCRSUBSAMPLING); - short SubsamplingHor = result[0].ToShort(); - short SubsamplingVer = result[1].ToShort(); - - switch (((ushort)SubsamplingHor << 4) | (ushort)SubsamplingVer) - { - case 0x44: - putContig = putcontig8bitYCbCr44tile; - break; - case 0x42: - putContig = putcontig8bitYCbCr42tile; - break; - case 0x41: - putContig = putcontig8bitYCbCr41tile; - break; - case 0x22: - putContig = putcontig8bitYCbCr22tile; - break; - case 0x21: - putContig = putcontig8bitYCbCr21tile; - break; - case 0x12: - putContig = putcontig8bitYCbCr12tile; - break; - case 0x11: - putContig = putcontig8bitYCbCr11tile; - break; - } - } - } - break; - - case Photometric.CIELAB: - if (buildMap()) - { - if (bitspersample == 8) - putContig = initCIELabConversion(); - } - break; - } - - return (putContig != null); - } - - /// - /// Select the appropriate conversion routine for unpacked data. - /// NB: we assume that unpacked single channel data is directed to the "packed routines. - /// - private bool pickSeparateCase() - { - get = tif.IsTiled() ? new GetDelegate(gtTileSeparate) : new GetDelegate(gtStripSeparate); - putSeparate = null; - - switch (photometric) - { - case Photometric.RGB: - switch (bitspersample) - { - case 8: - if (alpha == ExtraSample.ASSOCALPHA) - putSeparate = putRGBAAseparate8bittile; - else if (alpha == ExtraSample.UNASSALPHA) - putSeparate = putRGBUAseparate8bittile; - else - putSeparate = putRGBseparate8bittile; - break; - - case 16: - if (alpha == ExtraSample.ASSOCALPHA) - putSeparate = putRGBAAseparate16bittile; - else if (alpha == ExtraSample.UNASSALPHA) - putSeparate = putRGBUAseparate16bittile; - else - putSeparate = putRGBseparate16bittile; - break; - } - break; - - case Photometric.YCBCR: - if ((bitspersample == 8) && (samplesperpixel == 3)) - { - if (initYCbCrConversion()) - { - FieldValue[] result = tif.GetFieldDefaulted(TiffTag.YCBCRSUBSAMPLING); - short hs = result[0].ToShort(); - short vs = result[0].ToShort(); - - switch (((ushort)hs << 4) | (ushort)vs) - { - case 0x11: - putSeparate = putseparate8bitYCbCr11tile; - break; - // TODO: add other cases here - } - } - } - break; - } - - return (putSeparate != null); - } - - private bool initYCbCrConversion() - { - if (ycbcr == null) - ycbcr = new TiffYCbCrToRGB(); - - FieldValue[] result = tif.GetFieldDefaulted(TiffTag.YCBCRCOEFFICIENTS); - float[] luma = result[0].ToFloatArray(); - - result = tif.GetFieldDefaulted(TiffTag.REFERENCEBLACKWHITE); - float[] refBlackWhite = result[0].ToFloatArray(); - - ycbcr.Init(luma, refBlackWhite); - return true; - } - - private PutContigDelegate initCIELabConversion() - { - if (cielab == null) - cielab = new TiffCIELabToRGB(); - - FieldValue[] result = tif.GetFieldDefaulted(TiffTag.WHITEPOINT); - float[] whitePoint = result[0].ToFloatArray(); - - float[] refWhite = new float[3]; - refWhite[1] = 100.0F; - refWhite[0] = whitePoint[0] / whitePoint[1] * refWhite[1]; - refWhite[2] = (1.0F - whitePoint[0] - whitePoint[1]) / whitePoint[1] * refWhite[1]; - cielab.Init(display_sRGB, refWhite); - - return putcontig8bitCIELab; - } - - /// - /// Construct any mapping table used by the associated put method. - /// - private bool buildMap() - { - switch (photometric) - { - case Photometric.RGB: - case Photometric.YCBCR: - case Photometric.SEPARATED: - if (bitspersample == 8) - break; - if (!setupMap()) - return false; - break; - - case Photometric.MINISBLACK: - case Photometric.MINISWHITE: - if (!setupMap()) - return false; - break; - - case Photometric.PALETTE: - // Convert 16-bit colormap to 8-bit - // (unless it looks like an old-style 8-bit colormap). - if (checkcmap() == 16) - cvtcmap(); - else - Tiff.WarningExt(tif, tif.m_clientdata, tif.FileName(), "Assuming 8-bit colormap"); - - // Use mapping table and colormap to construct unpacking - // tables for samples < 8 bits. - if (bitspersample <= 8 && !makecmap()) - return false; - break; - } - - return true; - } - - /// - /// Construct a mapping table to convert from the range of the data samples to [0, 255] - - /// for display. This process also handles inverting B&W images when needed. - /// - private bool setupMap() - { - int range = (1 << bitspersample) - 1; - - // treat 16 bit the same as eight bit - if (bitspersample == 16) - range = 255; - - Map = new byte[range + 1]; - - if (photometric == Photometric.MINISWHITE) - { - for (int x = 0; x <= range; x++) - Map[x] = (byte)(((range - x) * 255) / range); - } - else - { - for (int x = 0; x <= range; x++) - Map[x] = (byte)((x * 255) / range); - } - - if (bitspersample <= 16 && (photometric == Photometric.MINISBLACK || photometric == Photometric.MINISWHITE)) - { - // Use photometric mapping table to construct unpacking tables for samples <= 8 bits. - if (!makebwmap()) - return false; - - // no longer need Map - Map = null; - } - - return true; - } - - private int checkcmap() - { - int r = 0; - int g = 0; - int b = 0; - int n = 1 << bitspersample; - while (n-- > 0) - { - if (redcmap[r] >= 256 || greencmap[g] >= 256 || bluecmap[b] >= 256) - return 16; - - r++; - g++; - b++; - } - - return 8; - } - - private void cvtcmap() - { - for (int i = (1 << bitspersample) - 1; i >= 0; i--) - { - redcmap[i] = (short)(redcmap[i] >> 8); - greencmap[i] = (short)(greencmap[i] >> 8); - bluecmap[i] = (short)(bluecmap[i] >> 8); - } - } - - private bool makecmap() - { - int nsamples = 8 / bitspersample; - - PALmap = new int[256][]; - for (int i = 0; i < 256; i++) - PALmap[i] = new int[nsamples]; - - for (int i = 0; i < 256; i++) - { - int j = 0; - switch (bitspersample) - { - case 1: - CMAP(i >> 7, i, ref j); - CMAP((i >> 6) & 1, i, ref j); - CMAP((i >> 5) & 1, i, ref j); - CMAP((i >> 4) & 1, i, ref j); - CMAP((i >> 3) & 1, i, ref j); - CMAP((i >> 2) & 1, i, ref j); - CMAP((i >> 1) & 1, i, ref j); - CMAP(i & 1, i, ref j); - break; - case 2: - CMAP(i >> 6, i, ref j); - CMAP((i >> 4) & 3, i, ref j); - CMAP((i >> 2) & 3, i, ref j); - CMAP(i & 3, i, ref j); - break; - case 4: - CMAP(i >> 4, i, ref j); - CMAP(i & 0xf, i, ref j); - break; - case 8: - CMAP(i, i, ref j); - break; - } - } - - return true; - } - - private bool makebwmap() - { - int nsamples = 8 / bitspersample; - if (nsamples == 0) - nsamples = 1; - - BWmap = new int[256][]; - for (int i = 0; i < 256; i++) - BWmap[i] = new int[nsamples]; - - for (int i = 0; i < 256; i++) - { - int j = 0; - switch (bitspersample) - { - case 1: - GREY(i >> 7, i, ref j); - GREY((i >> 6) & 1, i, ref j); - GREY((i >> 5) & 1, i, ref j); - GREY((i >> 4) & 1, i, ref j); - GREY((i >> 3) & 1, i, ref j); - GREY((i >> 2) & 1, i, ref j); - GREY((i >> 1) & 1, i, ref j); - GREY(i & 1, i, ref j); - break; - case 2: - GREY(i >> 6, i, ref j); - GREY((i >> 4) & 3, i, ref j); - GREY((i >> 2) & 3, i, ref j); - GREY(i & 3, i, ref j); - break; - case 4: - GREY(i >> 4, i, ref j); - GREY(i & 0xf, i, ref j); - break; - case 8: - case 16: - GREY(i, i, ref j); - break; - } - } - - return true; - } - - /// - /// YCbCr -> RGB conversion and packing routines. - /// - private void YCbCrtoRGB(out int dst, int Y, int Cb, int Cr) - { - int r, g, b; - ycbcr.YCbCrtoRGB(Y, Cb, Cr, out r, out g, out b); - dst = PACK(r, g, b); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // The following routines move decoded data returned from the TIFF library into rasters - // filled with packed ABGR pixels - // - // The routines have been created according to the most important cases and optimized. - // pickTileContigCase and pickTileSeparateCase analyze the parameters and select the - // appropriate "put" routine to use. - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Contiguous cases - // - - /// - /// 8-bit palette => colormap/RGB - /// - private static void put8bitcmaptile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int[][] PALmap = img.PALmap; - int samplesperpixel = img.samplesperpixel; - - while (height-- > 0) - { - for (x = width; x-- > 0; ) - { - raster[rasterOffset] = PALmap[buffer[offset]][0]; - rasterOffset++; - offset += samplesperpixel; - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 4-bit palette => colormap/RGB - /// - private static void put4bitcmaptile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int[][] PALmap = img.PALmap; - bufferShift /= 2; - - while (height-- > 0) - { - int[] bw = null; - - int _x; - for (_x = width; _x >= 2; _x -= 2) - { - bw = PALmap[buffer[offset]]; - offset++; - for (int rc = 0; rc < 2; rc++) - { - raster[rasterOffset] = bw[rc]; - rasterOffset++; - } - } - - if (_x != 0) - { - bw = PALmap[buffer[offset]]; - offset++; - - raster[rasterOffset] = bw[0]; - rasterOffset++; - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 2-bit palette => colormap/RGB - /// - private static void put2bitcmaptile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int[][] PALmap = img.PALmap; - bufferShift /= 4; - - while (height-- > 0) - { - int[] bw = null; - - int _x; - for (_x = width; _x >= 4; _x -= 4) - { - bw = PALmap[buffer[offset]]; - offset++; - for (int rc = 0; rc < 4; rc++) - { - raster[rasterOffset] = bw[rc]; - rasterOffset++; - } - } - - if (_x > 0) - { - bw = PALmap[buffer[offset]]; - offset++; - - if (_x <= 3 && _x > 0) - { - for (int i = 0; i < _x; i++) - { - raster[rasterOffset] = bw[i]; - rasterOffset++; - } - } - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 1-bit palette => colormap/RGB - /// - private static void put1bitcmaptile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int[][] PALmap = img.PALmap; - bufferShift /= 8; - - while (height-- > 0) - { - int[] bw; - int bwPos = 0; - - int _x; - for (_x = width; _x >= 8; _x -= 8) - { - bw = PALmap[buffer[offset++]]; - bwPos = 0; - - for (int i = 0; i < 8; i++) - raster[rasterOffset++] = bw[bwPos++]; - } - - if (_x > 0) - { - bw = PALmap[buffer[offset++]]; - bwPos = 0; - - if (_x <= 7 && _x > 0) - { - for (int i = 0; i < _x; i++) - raster[rasterOffset++] = bw[bwPos++]; - } - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 8-bit greyscale => colormap/RGB - /// - private static void putgreytile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int samplesperpixel = img.samplesperpixel; - int[][] BWmap = img.BWmap; - - while (height-- > 0) - { - for (x = width; x-- > 0; ) - { - raster[rasterOffset] = BWmap[buffer[offset]][0]; - rasterOffset++; - offset += samplesperpixel; - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 16-bit greyscale => colormap/RGB - /// - private static void put16bitbwtile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int samplesperpixel = img.samplesperpixel; - int[][] BWmap = img.BWmap; - - while (height-- > 0) - { - short[] wp = Tiff.ByteArrayToShorts(buffer, offset, buffer.Length - offset); - int wpPos = 0; - - for (x = width; x-- > 0; ) - { - // use high order byte of 16bit value - raster[rasterOffset] = BWmap[(wp[wpPos] & 0xffff) >> 8][0]; - rasterOffset++; - offset += 2 * samplesperpixel; - wpPos += samplesperpixel; - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 1-bit bilevel => colormap/RGB - /// - private static void put1bitbwtile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int[][] BWmap = img.BWmap; - bufferShift /= 8; - - while (height-- > 0) - { - int[] bw = null; - - int _x; - for (_x = width; _x >= 8; _x -= 8) - { - bw = BWmap[buffer[offset]]; - offset++; - - for (int rc = 0; rc < 8; rc++) - { - raster[rasterOffset] = bw[rc]; - rasterOffset++; - } - } - - if (_x > 0) - { - bw = BWmap[buffer[offset]]; - offset++; - - if (_x <= 7 && _x > 0) - { - for (int i = 0; i < _x; i++) - { - raster[rasterOffset] = bw[i]; - rasterOffset++; - } - } - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 2-bit greyscale => colormap/RGB - /// - private static void put2bitbwtile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int[][] BWmap = img.BWmap; - bufferShift /= 4; - - while (height-- > 0) - { - int[] bw = null; - - int _x; - for (_x = width; _x >= 4; _x -= 4) - { - bw = BWmap[buffer[offset]]; - offset++; - for (int rc = 0; rc < 4; rc++) - { - raster[rasterOffset] = bw[rc]; - rasterOffset++; - } - } - - if (_x > 0) - { - bw = BWmap[buffer[offset]]; - offset++; - - if (_x <= 3 && _x > 0) - { - for (int i = 0; i < _x; i++) - { - raster[rasterOffset] = bw[i]; - rasterOffset++; - } - } - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 4-bit greyscale => colormap/RGB - /// - private static void put4bitbwtile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int[][] BWmap = img.BWmap; - bufferShift /= 2; - - while (height-- > 0) - { - int[] bw = null; - - int _x; - for (_x = width; _x >= 2; _x -= 2) - { - bw = BWmap[buffer[offset]]; - offset++; - for (int rc = 0; rc < 2; rc++) - { - raster[rasterOffset] = bw[rc]; - rasterOffset++; - } - } - - if (_x != 0) - { - bw = BWmap[buffer[offset]]; - offset++; - - raster[rasterOffset] = bw[0]; - rasterOffset++; - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 8-bit packed samples, no Map => RGB - /// - private static void putRGBcontig8bittile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int samplesperpixel = img.samplesperpixel; - bufferShift *= samplesperpixel; - - while (height-- > 0) - { - int _x; - for (_x = width; _x >= 8; _x -= 8) - { - for (int rc = 0; rc < 8; rc++) - { - raster[rasterOffset] = PACK(buffer[offset], buffer[offset + 1], buffer[offset + 2]); - rasterOffset++; - offset += samplesperpixel; - } - } - - if (_x > 0) - { - if (_x <= 7 && _x > 0) - { - for (int i = _x; i > 0; i--) - { - raster[rasterOffset] = PACK(buffer[offset], buffer[offset + 1], buffer[offset + 2]); - rasterOffset++; - offset += samplesperpixel; - } - } - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 8-bit packed samples => RGBA w/ associated alpha (known to have Map == null) - /// - private static void putRGBAAcontig8bittile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int samplesperpixel = img.samplesperpixel; - bufferShift *= samplesperpixel; - - while (height-- > 0) - { - int _x; - for (_x = width; _x >= 8; _x -= 8) - { - for (int rc = 0; rc < 8; rc++) - { - raster[rasterOffset] = PACK4(buffer[offset], buffer[offset + 1], buffer[offset + 2], buffer[offset + 3]); - rasterOffset++; - offset += samplesperpixel; - } - } - - if (_x > 0) - { - if (_x <= 7 && _x > 0) - { - for (int i = _x; i > 0; i--) - { - raster[rasterOffset] = PACK4(buffer[offset], buffer[offset + 1], buffer[offset + 2], buffer[offset + 3]); - rasterOffset++; - offset += samplesperpixel; - } - } - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 8-bit packed samples => RGBA w/ unassociated alpha (known to have Map == null) - /// - private static void putRGBUAcontig8bittile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int samplesperpixel = img.samplesperpixel; - bufferShift *= samplesperpixel; - while (height-- > 0) - { - for (x = width; x-- > 0; ) - { - int a = buffer[offset + 3]; - int r = (buffer[offset] * a + 127) / 255; - int g = (buffer[offset + 1] * a + 127) / 255; - int b = (buffer[offset + 2] * a + 127) / 255; - raster[rasterOffset] = PACK4(r, g, b, a); - rasterOffset++; - offset += samplesperpixel; - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 16-bit packed samples => RGB - /// - private static void putRGBcontig16bittile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int samplesperpixel = img.samplesperpixel; - bufferShift *= samplesperpixel; - - short[] wp = Tiff.ByteArrayToShorts(buffer, offset, buffer.Length); - int wpPos = 0; - - while (height-- > 0) - { - for (x = width; x-- > 0; ) - { - raster[rasterOffset] = PACKW(wp[wpPos], wp[wpPos + 1], wp[wpPos + 2]); - rasterOffset++; - wpPos += samplesperpixel; - } - - rasterOffset += rasterShift; - wpPos += bufferShift; - } - } - - /// - /// 16-bit packed samples => RGBA w/ associated alpha (known to have Map == null) - /// - private static void putRGBAAcontig16bittile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int samplesperpixel = img.samplesperpixel; - - short[] wp = Tiff.ByteArrayToShorts(buffer, offset, buffer.Length); - int wpPos = 0; - - bufferShift *= samplesperpixel; - while (height-- > 0) - { - for (x = width; x-- > 0; ) - { - raster[rasterOffset] = PACKW4(wp[wpPos], wp[wpPos + 1], wp[wpPos + 2], wp[wpPos + 3]); - rasterOffset++; - wpPos += samplesperpixel; - } - - rasterOffset += rasterShift; - wpPos += bufferShift; - } - } - - /// - /// 16-bit packed samples => RGBA w/ unassociated alpha (known to have Map == null) - /// - private static void putRGBUAcontig16bittile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int samplesperpixel = img.samplesperpixel; - bufferShift *= samplesperpixel; - - short[] wp = Tiff.ByteArrayToShorts(buffer, offset, buffer.Length); - int wpPos = 0; - - while (height-- > 0) - { - for (x = width; x-- > 0; ) - { - int a = W2B(wp[wpPos + 3]); - int r = (W2B(wp[wpPos]) * a + 127) / 255; - int g = (W2B(wp[wpPos + 1]) * a + 127) / 255; - int b = (W2B(wp[wpPos + 2]) * a + 127) / 255; - raster[rasterOffset] = PACK4(r, g, b, a); - rasterOffset++; - wpPos += samplesperpixel; - } - - rasterOffset += rasterShift; - wpPos += bufferShift; - } - } - - /// - /// 8-bit packed CMYK samples w/o Map => RGB. - /// NB: The conversion of CMYK->RGB is *very* crude. - /// - private static void putRGBcontig8bitCMYKtile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int samplesperpixel = img.samplesperpixel; - bufferShift *= samplesperpixel; - - while (height-- > 0) - { - int _x; - for (_x = width; _x >= 8; _x -= 8) - { - for (int rc = 0; rc < 8; rc++) - { - short k = (short)(255 - buffer[offset + 3]); - short r = (short)((k * (255 - buffer[offset])) / 255); - short g = (short)((k * (255 - buffer[offset + 1])) / 255); - short b = (short)((k * (255 - buffer[offset + 2])) / 255); - raster[rasterOffset] = PACK(r, g, b); - rasterOffset++; - offset += samplesperpixel; - } - } - - if (_x > 0) - { - if (_x <= 7 && _x > 0) - { - for (int i = _x; i > 0; i--) - { - short k = (short)(255 - buffer[offset + 3]); - short r = (short)((k * (255 - buffer[offset])) / 255); - short g = (short)((k * (255 - buffer[offset + 1])) / 255); - short b = (short)((k * (255 - buffer[offset + 2])) / 255); - raster[rasterOffset] = PACK(r, g, b); - rasterOffset++; - offset += samplesperpixel; - } - } - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 8-bit packed CIE L*a*b 1976 samples => RGB - /// - private static void putcontig8bitCIELab( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - bufferShift *= 3; - - while (height-- > 0) - { - for (x = width; x-- > 0; ) - { - float X, Y, Z; - img.cielab.CIELabToXYZ(buffer[offset], (sbyte)buffer[offset + 1], (sbyte)buffer[offset + 2], out X, out Y, out Z); - - int r, g, b; - img.cielab.XYZToRGB(X, Y, Z, out r, out g, out b); - - raster[rasterOffset] = PACK(r, g, b); - rasterOffset++; - offset += 3; - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - } - - /// - /// 8-bit packed YCbCr samples w/ 2,2 subsampling => RGB - /// - private static void putcontig8bitYCbCr22tile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - bufferShift = (bufferShift / 2) * 6; - int rasterOffset2 = rasterOffset + width + rasterShift; - - while (height >= 2) - { - x = width; - while (x >= 2) - { - int Cb = buffer[offset + 4]; - int Cr = buffer[offset + 5]; - img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset2 + 0], buffer[offset + 2], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset2 + 1], buffer[offset + 3], Cb, Cr); - rasterOffset += 2; - rasterOffset2 += 2; - offset += 6; - x -= 2; - } - - if (x == 1) - { - int Cb = buffer[offset + 4]; - int Cr = buffer[offset + 5]; - img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset2 + 0], buffer[offset + 2], Cb, Cr); - rasterOffset++; - rasterOffset2++; - offset += 6; - } - - rasterOffset += rasterShift * 2 + width; - rasterOffset2 += rasterShift * 2 + width; - offset += bufferShift; - height -= 2; - } - - if (height == 1) - { - x = width; - while (x >= 2) - { - int Cb = buffer[offset + 4]; - int Cr = buffer[offset + 5]; - img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); - rasterOffset += 2; - rasterOffset2 += 2; - offset += 6; - x -= 2; - } - - if (x == 1) - { - int Cb = buffer[offset + 4]; - int Cr = buffer[offset + 5]; - img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); - } - } - } - - /// - /// 8-bit packed YCbCr samples w/ 2,1 subsampling => RGB - /// - private static void putcontig8bitYCbCr21tile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - bufferShift = (bufferShift * 4) / 2; - - do - { - x = width >> 1; - do - { - int Cb = buffer[offset + 2]; - int Cr = buffer[offset + 3]; - - img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); - - rasterOffset += 2; - offset += 4; - } - while (--x != 0); - - if ((width & 1) != 0) - { - int Cb = buffer[offset + 2]; - int Cr = buffer[offset + 3]; - - img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); - - rasterOffset += 1; - offset += 4; - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - while (--height != 0); - } - - /// - /// 8-bit packed YCbCr samples w/ 4,4 subsampling => RGB - /// - private static void putcontig8bitYCbCr44tile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int rasterOffset1 = rasterOffset + width + rasterShift; - int rasterOffset2 = rasterOffset1 + width + rasterShift; - int rasterOffset3 = rasterOffset2 + width + rasterShift; - int incr = 3 * width + 4 * rasterShift; - - // adjust bufferShift - bufferShift = (bufferShift * 18) / 4; - if ((height & 3) == 0 && (width & 3) == 0) - { - for (; height >= 4; height -= 4) - { - x = width >> 2; - do - { - int Cb = buffer[offset + 16]; - int Cr = buffer[offset + 17]; - - img.YCbCrtoRGB(out raster[rasterOffset], buffer[offset + 0], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset + 2], buffer[offset + 2], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset + 3], buffer[offset + 3], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset1 + 0], buffer[offset + 4], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset1 + 1], buffer[offset + 5], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset1 + 2], buffer[offset + 6], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset1 + 3], buffer[offset + 7], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset2 + 0], buffer[offset + 8], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset2 + 1], buffer[offset + 9], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset2 + 2], buffer[offset + 10], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset2 + 3], buffer[offset + 11], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset3 + 0], buffer[offset + 12], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset3 + 1], buffer[offset + 13], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset3 + 2], buffer[offset + 14], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset3 + 3], buffer[offset + 15], Cb, Cr); - - rasterOffset += 4; - rasterOffset1 += 4; - rasterOffset2 += 4; - rasterOffset3 += 4; - offset += 18; - } - while (--x != 0); - - rasterOffset += incr; - rasterOffset1 += incr; - rasterOffset2 += incr; - rasterOffset3 += incr; - offset += bufferShift; - } - } - else - { - while (height > 0) - { - for (x = width; x > 0; ) - { - int Cb = buffer[offset + 16]; - int Cr = buffer[offset + 17]; - - bool h_goOn = false; - bool x_goOn = false; - - // order of if's is important - if (x < 1 || x > 3) - { - // order of if's is important - h_goOn = false; - if (height < 1 || height > 3) - { - img.YCbCrtoRGB(out raster[rasterOffset3 + 3], buffer[offset + 15], Cb, Cr); - h_goOn = true; - } - - if (height == 3 || h_goOn) - { - img.YCbCrtoRGB(out raster[rasterOffset2 + 3], buffer[offset + 11], Cb, Cr); - h_goOn = true; - } - - if (height == 2 || h_goOn) - { - img.YCbCrtoRGB(out raster[rasterOffset1 + 3], buffer[offset + 7], Cb, Cr); - h_goOn = true; - } - - if (height == 1 || h_goOn) - img.YCbCrtoRGB(out raster[rasterOffset + 3], buffer[offset + 3], Cb, Cr); - - x_goOn = true; - } - - if (x == 3 || x_goOn) - { - // order of if's is important - h_goOn = false; - if (height < 1 || height > 3) - { - img.YCbCrtoRGB(out raster[rasterOffset3 + 2], buffer[offset + 14], Cb, Cr); - h_goOn = true; - } - - if (height == 3 || h_goOn) - { - img.YCbCrtoRGB(out raster[rasterOffset2 + 2], buffer[offset + 10], Cb, Cr); - h_goOn = true; - } - - if (height == 2 || h_goOn) - { - img.YCbCrtoRGB(out raster[rasterOffset1 + 2], buffer[offset + 6], Cb, Cr); - h_goOn = true; - } - - if (height == 1 || h_goOn) - img.YCbCrtoRGB(out raster[rasterOffset + 2], buffer[offset + 2], Cb, Cr); - - x_goOn = true; - } - - if (x == 2 || x_goOn) - { - // order of if's is important - h_goOn = false; - if (height < 1 || height > 3) - { - img.YCbCrtoRGB(out raster[rasterOffset3 + 1], buffer[offset + 13], Cb, Cr); - h_goOn = true; - } - - if (height == 3 || h_goOn) - { - img.YCbCrtoRGB(out raster[rasterOffset2 + 1], buffer[offset + 9], Cb, Cr); - h_goOn = true; - } - - if (height == 2 || h_goOn) - { - img.YCbCrtoRGB(out raster[rasterOffset1 + 1], buffer[offset + 5], Cb, Cr); - h_goOn = true; - } - - if (height == 1 || h_goOn) - img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); - } - - if (x == 1 || x_goOn) - { - // order of if's is important - h_goOn = false; - if (height < 1 || height > 3) - { - img.YCbCrtoRGB(out raster[rasterOffset3 + 0], buffer[offset + 12], Cb, Cr); - h_goOn = true; - } - - if (height == 3 || h_goOn) - { - img.YCbCrtoRGB(out raster[rasterOffset2 + 0], buffer[offset + 8], Cb, Cr); - h_goOn = true; - } - - if (height == 2 || h_goOn) - { - img.YCbCrtoRGB(out raster[rasterOffset1 + 0], buffer[offset + 4], Cb, Cr); - h_goOn = true; - } - - if (height == 1 || h_goOn) - img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); - } - - if (x < 4) - { - rasterOffset += x; - rasterOffset1 += x; - rasterOffset2 += x; - rasterOffset3 += x; - x = 0; - } - else - { - rasterOffset += 4; - rasterOffset1 += 4; - rasterOffset2 += 4; - rasterOffset3 += 4; - x -= 4; - } - - offset += 18; - } - - if (height <= 4) - break; - - height -= 4; - rasterOffset += incr; - rasterOffset1 += incr; - rasterOffset2 += incr; - rasterOffset3 += incr; - offset += bufferShift; - } - } - } - - /// - /// 8-bit packed YCbCr samples w/ 4,2 subsampling => RGB - /// - private static void putcontig8bitYCbCr42tile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int rasterOffset2 = rasterOffset + width + rasterShift; - int incr = 2 * rasterShift + width; - - bufferShift = (bufferShift * 10) / 4; - if ((height & 3) == 0 && (width & 1) == 0) - { - for (; height >= 2; height -= 2) - { - x = width >> 2; - do - { - int Cb = buffer[offset + 8]; - int Cr = buffer[offset + 9]; - - img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset + 2], buffer[offset + 2], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset + 3], buffer[offset + 3], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset2 + 0], buffer[offset + 4], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset2 + 1], buffer[offset + 5], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset2 + 2], buffer[offset + 6], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset2 + 3], buffer[offset + 7], Cb, Cr); - - rasterOffset += 4; - rasterOffset2 += 4; - offset += 10; - } - while (--x != 0); - - rasterOffset += incr; - rasterOffset2 += incr; - offset += bufferShift; - } - } - else - { - while (height > 0) - { - for (x = width; x > 0; ) - { - int Cb = buffer[offset + 8]; - int Cr = buffer[offset + 9]; - - bool x_goOn = false; - if (x < 1 || x > 3) - { - if (height != 1) - img.YCbCrtoRGB(out raster[rasterOffset2 + 3], buffer[offset + 7], Cb, Cr); - - img.YCbCrtoRGB(out raster[rasterOffset + 3], buffer[offset + 3], Cb, Cr); - x_goOn = true; - } - - if (x == 3 || x_goOn) - { - if (height != 1) - img.YCbCrtoRGB(out raster[rasterOffset2 + 2], buffer[offset + 6], Cb, Cr); - - img.YCbCrtoRGB(out raster[rasterOffset + 2], buffer[offset + 2], Cb, Cr); - x_goOn = true; - } - - if (x == 2 || x_goOn) - { - if (height != 1) - img.YCbCrtoRGB(out raster[rasterOffset2 + 1], buffer[offset + 5], Cb, Cr); - - img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); - x_goOn = true; - } - - if (x == 1 || x_goOn) - { - if (height != 1) - img.YCbCrtoRGB(out raster[rasterOffset2 + 0], buffer[offset + 4], Cb, Cr); - - img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); - } - - if (x < 4) - { - rasterOffset += x; - rasterOffset2 += x; - x = 0; - } - else - { - rasterOffset += 4; - rasterOffset2 += 4; - x -= 4; - } - - offset += 10; - } - - if (height <= 2) - break; - - height -= 2; - rasterOffset += incr; - rasterOffset2 += incr; - offset += bufferShift; - } - } - } - - /// - /// 8-bit packed YCbCr samples w/ 4,1 subsampling => RGB - /// - private static void putcontig8bitYCbCr41tile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - // XXX adjust bufferShift - do - { - x = width >> 2; - do - { - int Cb = buffer[offset + 4]; - int Cr = buffer[offset + 5]; - - img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset + 2], buffer[offset + 2], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset + 3], buffer[offset + 3], Cb, Cr); - - rasterOffset += 4; - offset += 6; - } - while (--x != 0); - - if ((width & 3) != 0) - { - int Cb = buffer[offset + 4]; - int Cr = buffer[offset + 5]; - - int xx = width & 3; - if (xx == 3) - img.YCbCrtoRGB(out raster[rasterOffset + 2], buffer[offset + 2], Cb, Cr); - - if (xx == 3 || xx == 2) - img.YCbCrtoRGB(out raster[rasterOffset + 1], buffer[offset + 1], Cb, Cr); - - if (xx == 3 || xx == 2 || xx == 1) - img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); - - rasterOffset += xx; - offset += 6; - } - - rasterOffset += rasterShift; - offset += bufferShift; - } - while (--height != 0); - } - - /// - /// 8-bit packed YCbCr samples w/ no subsampling => RGB - /// - private static void putcontig8bitYCbCr11tile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - bufferShift *= 3; - do - { - x = width; // was x = w >> 1; patched 2000/09/25 warmerda@home.com - do - { - int Cb = buffer[offset + 1]; - int Cr = buffer[offset + 2]; - - img.YCbCrtoRGB(out raster[rasterOffset], buffer[offset + 0], Cb, Cr); - rasterOffset++; - offset += 3; - } - while (--x != 0); - - rasterOffset += rasterShift; - offset += bufferShift; - } - while (--height != 0); - } - - /// - /// 8-bit packed YCbCr samples w/ 1,2 subsampling => RGB - /// - private static void putcontig8bitYCbCr12tile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - bufferShift = (bufferShift / 2) * 4; - int rasterOffset2 = rasterOffset + width + rasterShift; - - while (height >= 2) - { - x = width; - do - { - int Cb = buffer[offset + 2]; - int Cr = buffer[offset + 3]; - img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); - img.YCbCrtoRGB(out raster[rasterOffset2 + 0], buffer[offset + 1], Cb, Cr); - rasterOffset++; - rasterOffset2++; - offset += 4; - } while (--x != 0); - - rasterOffset += rasterShift * 2 + width; - rasterOffset2 += rasterShift * 2 + width; - offset += bufferShift; - height -= 2; - } - - if (height == 1) - { - x = width; - do - { - int Cb = buffer[offset + 2]; - int Cr = buffer[offset + 3]; - img.YCbCrtoRGB(out raster[rasterOffset + 0], buffer[offset + 0], Cb, Cr); - rasterOffset++; - offset += 4; - } while (--x != 0); - } - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // Separated cases - // - - /// - /// 8-bit unpacked samples => RGB - /// - private static void putRGBseparate8bittile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, - byte[] buffer, int offset1, int offset2, int offset3, int offset4, int bufferShift) - { - while (height-- > 0) - { - int _x; - for (_x = width; _x >= 8; _x -= 8) - { - for (int rc = 0; rc < 8; rc++) - { - raster[rasterOffset] = PACK(buffer[offset1], buffer[offset2], buffer[offset3]); - rasterOffset++; - offset1++; - offset2++; - offset3++; - } - } - - if (_x > 0) - { - if (_x <= 7 && _x > 0) - { - for (int i = _x; i > 0; i--) - { - raster[rasterOffset] = PACK(buffer[offset1], buffer[offset2], buffer[offset3]); - rasterOffset++; - offset1++; - offset2++; - offset3++; - } - } - } - - offset1 += bufferShift; - offset2 += bufferShift; - offset3 += bufferShift; - rasterOffset += rasterShift; - } - } - - /// - /// 8-bit unpacked samples => RGBA w/ associated alpha - /// - private static void putRGBAAseparate8bittile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, - byte[] buffer, int offset1, int offset2, int offset3, int offset4, int bufferShift) - { - while (height-- > 0) - { - int _x; - for (_x = width; _x >= 8; _x -= 8) - { - for (int rc = 0; rc < 8; rc++) - { - raster[rasterOffset] = PACK4(buffer[offset1], buffer[offset2], buffer[offset3], buffer[offset4]); - rasterOffset++; - offset1++; - offset2++; - offset3++; - offset4++; - } - } - - if (_x > 0) - { - if (_x <= 7 && _x > 0) - { - for (int i = _x; i > 0; i--) - { - raster[rasterOffset] = PACK4(buffer[offset1], buffer[offset2], buffer[offset3], buffer[offset4]); - rasterOffset++; - offset1++; - offset2++; - offset3++; - offset4++; - } - } - } - - offset1 += bufferShift; - offset2 += bufferShift; - offset3 += bufferShift; - offset4 += bufferShift; - - rasterOffset += rasterShift; - } - } - - /// - /// 8-bit unpacked samples => RGBA w/ unassociated alpha - /// - private static void putRGBUAseparate8bittile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, - byte[] buffer, int offset1, int offset2, int offset3, int offset4, int bufferShift) - { - while (height-- > 0) - { - for (x = width; x-- > 0; ) - { - int av = buffer[offset4]; - int rv = (buffer[offset1] * av + 127) / 255; - int gv = (buffer[offset2] * av + 127) / 255; - int bv = (buffer[offset3] * av + 127) / 255; - raster[rasterOffset] = PACK4(rv, gv, bv, av); - rasterOffset++; - offset1++; - offset2++; - offset3++; - offset4++; - } - - offset1 += bufferShift; - offset2 += bufferShift; - offset3 += bufferShift; - offset4 += bufferShift; - - rasterOffset += rasterShift; - } - } - - /// - /// 16-bit unpacked samples => RGB - /// - private static void putRGBseparate16bittile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, - byte[] buffer, int offset1, int offset2, int offset3, int offset4, int bufferShift) - { - short[] wrgba = Tiff.ByteArrayToShorts(buffer, 0, buffer.Length); - - offset1 /= sizeof(short); - offset2 /= sizeof(short); - offset3 /= sizeof(short); - - while (height-- > 0) - { - for (x = 0; x < width; x++) - { - raster[rasterOffset] = PACKW(wrgba[offset1], wrgba[offset2], wrgba[offset3]); - rasterOffset++; - offset1++; - offset2++; - offset3++; - } - - offset1 += bufferShift; - offset2 += bufferShift; - offset3 += bufferShift; - rasterOffset += rasterShift; - } - } - - /// - /// 16-bit unpacked samples => RGBA w/ associated alpha - /// - private static void putRGBAAseparate16bittile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, - byte[] buffer, int offset1, int offset2, int offset3, int offset4, int bufferShift) - { - short[] wrgba = Tiff.ByteArrayToShorts(buffer, 0, buffer.Length); - - offset1 /= sizeof(short); - offset2 /= sizeof(short); - offset3 /= sizeof(short); - offset4 /= sizeof(short); - - while (height-- > 0) - { - for (x = 0; x < width; x++) - { - raster[rasterOffset] = PACKW4(wrgba[offset1], wrgba[offset2], wrgba[offset3], wrgba[offset4]); - rasterOffset++; - offset1++; - offset2++; - offset3++; - offset4++; - } - - offset1 += bufferShift; - offset2 += bufferShift; - offset3 += bufferShift; - offset4 += bufferShift; - - rasterOffset += rasterShift; - } - } - - /// - /// 16-bit unpacked samples => RGBA w/ unassociated alpha - /// - private static void putRGBUAseparate16bittile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, - byte[] buffer, int offset1, int offset2, int offset3, int offset4, int bufferShift) - { - short[] wrgba = Tiff.ByteArrayToShorts(buffer, 0, buffer.Length); - - offset1 /= sizeof(short); - offset2 /= sizeof(short); - offset3 /= sizeof(short); - offset4 /= sizeof(short); - - while (height-- > 0) - { - for (x = width; x-- > 0; ) - { - int a = W2B(wrgba[offset4]); - int r = (W2B(wrgba[offset1]) * a + 127) / 255; - int g = (W2B(wrgba[offset2]) * a + 127) / 255; - int b = (W2B(wrgba[offset3]) * a + 127) / 255; - raster[rasterOffset] = PACK4(r, g, b, a); - rasterOffset++; - offset1++; - offset2++; - offset3++; - offset4++; - } - - offset1 += bufferShift; - offset2 += bufferShift; - offset3 += bufferShift; - offset4 += bufferShift; - - rasterOffset += rasterShift; - } - } - - /// - /// 8-bit packed YCbCr samples w/ no subsampling => RGB - /// - private static void putseparate8bitYCbCr11tile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, - byte[] buffer, int offset1, int offset2, int offset3, int offset4, int bufferShift) - { - while (height-- > 0) - { - x = width; - do - { - int r, g, b; - img.ycbcr.YCbCrtoRGB(buffer[offset1], buffer[offset2], buffer[offset3], out r, out g, out b); - - raster[rasterOffset] = PACK(r, g, b); - rasterOffset++; - offset1++; - offset2++; - offset3++; - } while (--x != 0); - - offset1 += bufferShift; - offset2 += bufferShift; - offset3 += bufferShift; - rasterOffset += rasterShift; - } - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // Untested methods - // - // This methods are untested (and, probably, should be deleted). - // - // pickContigCase implicitly *excludes* putRGBcontig8bitCMYKMaptile from possible "put" - // methods when it requires images to have Photometric.SEPARATED *and* 8-bit - // samples *and* a Map to use putRGBcontig8bitCMYKMaptile. The problem is: - // no Map is ever built for Photometric.SEPARATED *and* 8-bit samples. - - /// - /// 8-bit packed CMYK samples w/Map => RGB - /// NB: The conversion of CMYK->RGB is *very* crude. - /// - private static void putRGBcontig8bitCMYKMaptile( - TiffRgbaImage img, int[] raster, int rasterOffset, int rasterShift, - int x, int y, int width, int height, byte[] buffer, int offset, int bufferShift) - { - int samplesperpixel = img.samplesperpixel; - byte[] Map = img.Map; - bufferShift *= samplesperpixel; - - while (height-- > 0) - { - for (x = width; x-- > 0; ) - { - short k = (short)(255 - buffer[offset + 3]); - short r = (short)((k * (255 - buffer[offset])) / 255); - short g = (short)((k * (255 - buffer[offset + 1])) / 255); - short b = (short)((k * (255 - buffer[offset + 2])) / 255); - raster[rasterOffset] = PACK(Map[r], Map[g], Map[b]); - rasterOffset++; - offset += samplesperpixel; - } - - offset += bufferShift; - rasterOffset += rasterShift; - } - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffStream.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffStream.cs deleted file mode 100644 index 03cd5ff3..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffStream.cs +++ /dev/null @@ -1,120 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System; -using System.IO; - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// A stream used by the library for TIFF reading and writing. - /// -#if EXPOSE_LIBTIFF - public -#endif - class TiffStream - { - /// - /// Reads a sequence of bytes from the stream and advances the position within the stream - /// by the number of bytes read. - /// - /// A client data (by default, an underlying stream). - /// An array of bytes. When this method returns, the - /// contains the specified byte array with the values between - /// and ( + - 1) - /// replaced by the bytes read from the current source. - /// The zero-based byte offset in at which - /// to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// The total number of bytes read into the . This can - /// be less than the number of bytes requested if that many bytes are not currently - /// available, or zero (0) if the end of the stream has been reached. - public virtual int Read(object clientData, byte[] buffer, int offset, int count) - { - Stream stream = clientData as Stream; - if (stream == null) - throw new ArgumentException("Can't get underlying stream to read from"); - - return stream.Read(buffer, offset, count); - } - - /// - /// Writes a sequence of bytes to the current stream and advances the current position - /// within this stream by the number of bytes written. - /// - /// A client data (by default, an underlying stream). - /// An array of bytes. This method copies - /// bytes from to the current stream. - /// The zero-based byte offset in at which - /// to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - public virtual void Write(object clientData, byte[] buffer, int offset, int count) - { - Stream stream = clientData as Stream; - if (stream == null) - throw new ArgumentException("Can't get underlying stream to write to"); - - stream.Write(buffer, offset, count); - } - - /// - /// Sets the position within the current stream. - /// - /// A client data (by default, an underlying stream). - /// A byte offset relative to the parameter. - /// A value of type indicating the - /// reference point used to obtain the new position. - /// The new position within the current stream. - public virtual long Seek(object clientData, long offset, SeekOrigin origin) - { - // we use this as a special code, so avoid accepting it - if (offset == -1) - return -1; // was 0xFFFFFFFF - - Stream stream = clientData as Stream; - if (stream == null) - throw new ArgumentException("Can't get underlying stream to seek in"); - - return stream.Seek(offset, origin); - } - - /// - /// Closes the current stream. - /// - /// A client data (by default, an underlying stream). - public virtual void Close(object clientData) - { - Stream stream = clientData as Stream; - if (stream == null) - throw new ArgumentException("Can't get underlying stream to close"); - - stream.Close(); - } - - /// - /// Gets the length in bytes of the stream. - /// - /// A client data (by default, an underlying stream). - /// The length of the stream in bytes. - public virtual long Size(object clientData) - { - Stream stream = clientData as Stream; - if (stream == null) - throw new ArgumentException("Can't get underlying stream to retrieve size from"); - - return stream.Length; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffTagMethods.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffTagMethods.cs deleted file mode 100644 index a4a530f6..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/TiffTagMethods.cs +++ /dev/null @@ -1,1013 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -/* Copyright (C) 2008-2011, Bit Miracle - * http://www.bitmiracle.com - * - * This software is based in part on the work of the Sam Leffler, Silicon - * Graphics, Inc. and contributors. - * - * Copyright (c) 1988-1997 Sam Leffler - * Copyright (c) 1991-1997 Silicon Graphics, Inc. - * For conditions of distribution and use, see the accompanying README file. - */ - -using System; -using System.IO; - -using BitMiracle.LibTiff.Classic.Internal; - -namespace BitMiracle.LibTiff.Classic -{ - /// - /// Tiff tag methods. - /// -#if EXPOSE_LIBTIFF - public -#endif - class TiffTagMethods - { - // - // These are used in the backwards compatibility code... - // - - /// - /// untyped data - /// - private const short DATATYPE_VOID = 0; - - /// - /// signed integer data - /// - private const short DATATYPE_INT = 1; - - /// - /// unsigned integer data - /// - private const short DATATYPE_UINT = 2; - - /// - /// IEEE floating point data - /// - private const short DATATYPE_IEEEFP = 3; - - /// - /// Sets the value(s) of a tag in a TIFF file/stream open for writing. - /// - /// An instance of the class. - /// The tag. - /// The tag value(s). - /// - /// true if tag value(s) were set successfully; otherwise, false. - /// - /// - public virtual bool SetField(Tiff tif, TiffTag tag, FieldValue[] value) - { - const string module = "vsetfield"; - - TiffDirectory td = tif.m_dir; - bool status = true; - int v32 = 0; - int v = 0; - - bool end = false; - bool badvalue = false; - bool badvalue32 = false; - - switch (tag) - { - case TiffTag.SUBFILETYPE: - td.td_subfiletype = (FileType)value[0].ToByte(); - break; - case TiffTag.IMAGEWIDTH: - td.td_imagewidth = value[0].ToInt(); - break; - case TiffTag.IMAGELENGTH: - td.td_imagelength = value[0].ToInt(); - break; - case TiffTag.BITSPERSAMPLE: - td.td_bitspersample = value[0].ToShort(); - // If the data require post-decoding processing to byte-swap samples, set it - // up here. Note that since tags are required to be ordered, compression code - // can override this behavior in the setup method if it wants to roll the post - // decoding work in with its normal work. - if ((tif.m_flags & TiffFlags.SWAB) == TiffFlags.SWAB) - { - if (td.td_bitspersample == 16) - tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab16Bit; - else if (td.td_bitspersample == 24) - tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab24Bit; - else if (td.td_bitspersample == 32) - tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab32Bit; - else if (td.td_bitspersample == 64) - tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab64Bit; - else if (td.td_bitspersample == 128) - { - // two 64's - tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab64Bit; - } - } - break; - case TiffTag.COMPRESSION: - v = value[0].ToInt() & 0xffff; - Compression comp = (Compression)v; - // If we're changing the compression scheme, then notify the previous module - // so that it can cleanup any state it's setup. - if (tif.fieldSet(FieldBit.Compression)) - { - if (td.td_compression == comp) - break; - - tif.m_currentCodec.Cleanup(); - tif.m_flags &= ~TiffFlags.CODERSETUP; - } - // Setup new compression scheme. - status = tif.setCompressionScheme(comp); - if (status) - td.td_compression = comp; - else - status = false; - break; - - case TiffTag.PHOTOMETRIC: - td.td_photometric = (Photometric)value[0].ToInt(); - break; - case TiffTag.THRESHHOLDING: - td.td_threshholding = (Threshold)value[0].ToByte(); - break; - case TiffTag.FILLORDER: - v = value[0].ToInt(); - FillOrder fo = (FillOrder)v; - if (fo != FillOrder.LSB2MSB && fo != FillOrder.MSB2LSB) - { - badvalue = true; - break; - } - - td.td_fillorder = fo; - break; - case TiffTag.ORIENTATION: - v = value[0].ToInt(); - Orientation or = (Orientation)v; - if (or < Orientation.TOPLEFT || Orientation.LEFTBOT < or) - { - badvalue = true; - break; - } - else - td.td_orientation = or; - break; - case TiffTag.SAMPLESPERPIXEL: - // XXX should cross check - e.g. if pallette, then 1 - v = value[0].ToInt(); - if (v == 0) - { - badvalue = true; - break; - } - - td.td_samplesperpixel = (short)v; - break; - case TiffTag.ROWSPERSTRIP: - v32 = value[0].ToInt(); - if (v32 == 0) - { - badvalue32 = true; - break; - } - - td.td_rowsperstrip = v32; - if (!tif.fieldSet(FieldBit.TileDimensions)) - { - td.td_tilelength = v32; - td.td_tilewidth = td.td_imagewidth; - } - break; - case TiffTag.MINSAMPLEVALUE: - td.td_minsamplevalue = value[0].ToShort(); - break; - case TiffTag.MAXSAMPLEVALUE: - td.td_maxsamplevalue = value[0].ToShort(); - break; - case TiffTag.SMINSAMPLEVALUE: - td.td_sminsamplevalue = value[0].ToDouble(); - break; - case TiffTag.SMAXSAMPLEVALUE: - td.td_smaxsamplevalue = value[0].ToDouble(); - break; - case TiffTag.XRESOLUTION: - td.td_xresolution = value[0].ToFloat(); - break; - case TiffTag.YRESOLUTION: - td.td_yresolution = value[0].ToFloat(); - break; - case TiffTag.PLANARCONFIG: - v = value[0].ToInt(); - PlanarConfig pc = (PlanarConfig)v; - if (pc != PlanarConfig.CONTIG && pc != PlanarConfig.SEPARATE) - { - badvalue = true; - break; - } - td.td_planarconfig = pc; - break; - case TiffTag.XPOSITION: - td.td_xposition = value[0].ToFloat(); - break; - case TiffTag.YPOSITION: - td.td_yposition = value[0].ToFloat(); - break; - case TiffTag.RESOLUTIONUNIT: - v = value[0].ToInt(); - ResUnit ru = (ResUnit)v; - if (ru < ResUnit.NONE || ResUnit.CENTIMETER < ru) - { - badvalue = true; - break; - } - - td.td_resolutionunit = ru; - break; - case TiffTag.PAGENUMBER: - td.td_pagenumber[0] = value[0].ToShort(); - td.td_pagenumber[1] = value[1].ToShort(); - break; - case TiffTag.HALFTONEHINTS: - td.td_halftonehints[0] = value[0].ToShort(); - td.td_halftonehints[1] = value[1].ToShort(); - break; - case TiffTag.COLORMAP: - v32 = 1 << td.td_bitspersample; - Tiff.setShortArray(out td.td_colormap[0], value[0].ToShortArray(), v32); - Tiff.setShortArray(out td.td_colormap[1], value[1].ToShortArray(), v32); - Tiff.setShortArray(out td.td_colormap[2], value[2].ToShortArray(), v32); - break; - case TiffTag.EXTRASAMPLES: - if (!setExtraSamples(td, ref v, value)) - { - badvalue = true; - break; - } - - break; - case TiffTag.MATTEING: - if (value[0].ToShort() != 0) - td.td_extrasamples = 1; - else - td.td_extrasamples = 0; - - if (td.td_extrasamples != 0) - { - td.td_sampleinfo = new ExtraSample[1]; - td.td_sampleinfo[0] = ExtraSample.ASSOCALPHA; - } - break; - case TiffTag.TILEWIDTH: - v32 = value[0].ToInt(); - if ((v32 % 16) != 0) - { - if (tif.m_mode != Tiff.O_RDONLY) - { - badvalue32 = true; - break; - } - - Tiff.WarningExt(tif, tif.m_clientdata, tif.m_name, - "Nonstandard tile width {0}, convert file", v32); - } - td.td_tilewidth = v32; - tif.m_flags |= TiffFlags.ISTILED; - break; - case TiffTag.TILELENGTH: - v32 = value[0].ToInt(); - if ((v32 % 16) != 0) - { - if (tif.m_mode != Tiff.O_RDONLY) - { - badvalue32 = true; - break; - } - - Tiff.WarningExt(tif, tif.m_clientdata, tif.m_name, - "Nonstandard tile length {0}, convert file", v32); - } - td.td_tilelength = v32; - tif.m_flags |= TiffFlags.ISTILED; - break; - case TiffTag.TILEDEPTH: - v32 = value[0].ToInt(); - if (v32 == 0) - { - badvalue32 = true; - break; - } - - td.td_tiledepth = v32; - break; - case TiffTag.DATATYPE: - v = value[0].ToInt(); - SampleFormat sf = SampleFormat.VOID; - switch (v) - { - case DATATYPE_VOID: - sf = SampleFormat.VOID; - break; - case DATATYPE_INT: - sf = SampleFormat.INT; - break; - case DATATYPE_UINT: - sf = SampleFormat.UINT; - break; - case DATATYPE_IEEEFP: - sf = SampleFormat.IEEEFP; - break; - default: - badvalue = true; - break; - } - - if (!badvalue) - td.td_sampleformat = sf; - - break; - case TiffTag.SAMPLEFORMAT: - v = value[0].ToInt(); - sf = (SampleFormat)v; - if (sf < SampleFormat.UINT || SampleFormat.COMPLEXIEEEFP < sf) - { - badvalue = true; - break; - } - - td.td_sampleformat = sf; - - // Try to fix up the SWAB function for complex data. - if (td.td_sampleformat == SampleFormat.COMPLEXINT && - td.td_bitspersample == 32 && tif.m_postDecodeMethod == Tiff.PostDecodeMethodType.pdmSwab32Bit) - { - tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab16Bit; - } - else if ((td.td_sampleformat == SampleFormat.COMPLEXINT || - td.td_sampleformat == SampleFormat.COMPLEXIEEEFP) && - td.td_bitspersample == 64 && tif.m_postDecodeMethod == Tiff.PostDecodeMethodType.pdmSwab64Bit) - { - tif.m_postDecodeMethod = Tiff.PostDecodeMethodType.pdmSwab32Bit; - } - break; - case TiffTag.IMAGEDEPTH: - td.td_imagedepth = value[0].ToInt(); - break; - case TiffTag.SUBIFD: - if ((tif.m_flags & TiffFlags.INSUBIFD) != TiffFlags.INSUBIFD) - { - td.td_nsubifd = value[0].ToShort(); - Tiff.setLongArray(out td.td_subifd, value[1].ToIntArray(), td.td_nsubifd); - } - else - { - Tiff.ErrorExt(tif, tif.m_clientdata, module, - "{0}: Sorry, cannot nest SubIFDs", tif.m_name); - status = false; - } - break; - case TiffTag.YCBCRPOSITIONING: - td.td_ycbcrpositioning = (YCbCrPosition)value[0].ToByte(); - break; - case TiffTag.YCBCRSUBSAMPLING: - td.td_ycbcrsubsampling[0] = value[0].ToShort(); - td.td_ycbcrsubsampling[1] = value[1].ToShort(); - break; - case TiffTag.TRANSFERFUNCTION: - v = ((td.td_samplesperpixel - td.td_extrasamples) > 1 ? 3 : 1); - for (int i = 0; i < v; i++) - { - Tiff.setShortArray(out td.td_transferfunction[i], value[0].ToShortArray(), 1 << td.td_bitspersample); - } - break; - case TiffTag.REFERENCEBLACKWHITE: - // XXX should check for null range - Tiff.setFloatArray(out td.td_refblackwhite, value[0].ToFloatArray(), 6); - break; - case TiffTag.INKNAMES: - v = value[0].ToInt(); - string s = value[1].ToString(); - v = checkInkNamesString(tif, v, s); - status = v > 0; - if (v > 0) - { - setNString(out td.td_inknames, s, v); - td.td_inknameslen = v; - } - break; - default: - // This can happen if multiple images are open with - // different codecs which have private tags. The global tag - // information table may then have tags that are valid for - // one file but not the other. If the client tries to set a - // tag that is not valid for the image's codec then we'll - // arrive here. This happens, for example, when tiffcp is - // used to convert between compression schemes and - // codec-specific tags are blindly copied. - TiffFieldInfo fip = tif.FindFieldInfo(tag, TiffType.ANY); - if (fip == null || fip.Bit != FieldBit.Custom) - { - Tiff.ErrorExt(tif, tif.m_clientdata, module, - "{0}: Invalid {1}tag \"{2}\" (not supported by codec)", - tif.m_name, Tiff.isPseudoTag(tag) ? "pseudo-" : "", - fip != null ? fip.Name : "Unknown"); - status = false; - break; - } - - // Find the existing entry for this custom value. - int tvIndex = -1; - for (int iCustom = 0; iCustom < td.td_customValueCount; iCustom++) - { - if (td.td_customValues[iCustom].info.Tag == tag) - { - tvIndex = iCustom; - td.td_customValues[iCustom].value = null; - break; - } - } - - // Grow the custom list if the entry was not found. - if (tvIndex == -1) - { - td.td_customValueCount++; - TiffTagValue[] new_customValues = Tiff.Realloc( - td.td_customValues, td.td_customValueCount - 1, td.td_customValueCount); - td.td_customValues = new_customValues; - - tvIndex = td.td_customValueCount - 1; - td.td_customValues[tvIndex].info = fip; - td.td_customValues[tvIndex].value = null; - td.td_customValues[tvIndex].count = 0; - } - - // Set custom value ... save a copy of the custom tag value. - int tv_size = Tiff.dataSize(fip.Type); - if (tv_size == 0) - { - status = false; - Tiff.ErrorExt(tif, tif.m_clientdata, module, - "{0}: Bad field type {1} for \"{2}\"", - tif.m_name, fip.Type, fip.Name); - end = true; - break; - } - - int paramIndex = 0; - if (fip.PassCount) - { - if (fip.WriteCount == TiffFieldInfo.Variable2) - td.td_customValues[tvIndex].count = value[paramIndex++].ToInt(); - else - td.td_customValues[tvIndex].count = value[paramIndex++].ToInt(); - } - else if (fip.WriteCount == TiffFieldInfo.Variable || - fip.WriteCount == TiffFieldInfo.Variable2) - { - td.td_customValues[tvIndex].count = 1; - } - else if (fip.WriteCount == TiffFieldInfo.Spp) - { - td.td_customValues[tvIndex].count = td.td_samplesperpixel; - } - else - { - td.td_customValues[tvIndex].count = fip.WriteCount; - } - - if (fip.Type == TiffType.ASCII) - { - string ascii; - Tiff.setString(out ascii, value[paramIndex++].ToString()); - td.td_customValues[tvIndex].value = Tiff.Latin1Encoding.GetBytes(ascii); - } - else - { - td.td_customValues[tvIndex].value = new byte[tv_size * td.td_customValues[tvIndex].count]; - if ((fip.PassCount || - fip.WriteCount == TiffFieldInfo.Variable || - fip.WriteCount == TiffFieldInfo.Variable2 || - fip.WriteCount == TiffFieldInfo.Spp || - td.td_customValues[tvIndex].count > 1) && - fip.Tag != TiffTag.PAGENUMBER && - fip.Tag != TiffTag.HALFTONEHINTS && - fip.Tag != TiffTag.YCBCRSUBSAMPLING && - fip.Tag != TiffTag.DOTRANGE) - { - byte[] apBytes = value[paramIndex++].GetBytes(); - Buffer.BlockCopy(apBytes, 0, td.td_customValues[tvIndex].value, 0, Math.Min(apBytes.Length, td.td_customValues[tvIndex].value.Length)); - } - else - { - // XXX: The following loop required to handle - // PAGENUMBER, HALFTONEHINTS, - // YCBCRSUBSAMPLING and DOTRANGE tags. - // These tags are actually arrays and should be - // passed as arrays to SetField() function, but - // actually passed as a list of separate values. - // This behavior must be changed in the future! - - // Upd: This loop also processes some EXIF tags with - // UNDEFINED type (like EXIF_FILESOURCE or EXIF_SCENETYPE) - // In this case input value is string-based, so - // in TiffType.UNDEFINED case we use FieldValue.GetBytes()[0] - // construction instead of direct call of FieldValue.ToByte() method. - byte[] val = td.td_customValues[tvIndex].value; - int valPos = 0; - for (int i = 0; i < td.td_customValues[tvIndex].count; i++, valPos += tv_size) - { - switch (fip.Type) - { - case TiffType.BYTE: - case TiffType.UNDEFINED: - val[valPos] = value[paramIndex + i].GetBytes()[0]; - break; - case TiffType.SBYTE: - val[valPos] = value[paramIndex + i].ToByte(); - break; - case TiffType.SHORT: - Buffer.BlockCopy(BitConverter.GetBytes(value[paramIndex + i].ToShort()), 0, val, valPos, tv_size); - break; - case TiffType.SSHORT: - Buffer.BlockCopy(BitConverter.GetBytes(value[paramIndex + i].ToShort()), 0, val, valPos, tv_size); - break; - case TiffType.LONG: - case TiffType.IFD: - Buffer.BlockCopy(BitConverter.GetBytes(value[paramIndex + i].ToInt()), 0, val, valPos, tv_size); - break; - case TiffType.SLONG: - Buffer.BlockCopy(BitConverter.GetBytes(value[paramIndex + i].ToInt()), 0, val, valPos, tv_size); - break; - case TiffType.RATIONAL: - case TiffType.SRATIONAL: - case TiffType.FLOAT: - Buffer.BlockCopy(BitConverter.GetBytes(value[paramIndex + i].ToFloat()), 0, val, valPos, tv_size); - break; - case TiffType.DOUBLE: - Buffer.BlockCopy(BitConverter.GetBytes(value[paramIndex + i].ToDouble()), 0, val, valPos, tv_size); - break; - default: - Array.Clear(val, valPos, tv_size); - status = false; - break; - } - } - } - } - break; - } - - if (!end && !badvalue && !badvalue32) - { - if (status) - { - tif.setFieldBit(tif.FieldWithTag(tag).Bit); - tif.m_flags |= TiffFlags.DIRTYDIRECT; - } - } - - if (badvalue) - { - Tiff.ErrorExt(tif, tif.m_clientdata, module, - "{0}: Bad value {1} for \"{2}\" tag", - tif.m_name, v, tif.FieldWithTag(tag).Name); - return false; - } - - if (badvalue32) - { - Tiff.ErrorExt(tif, tif.m_clientdata, module, - "{0}: Bad value {1} for \"{2}\" tag", - tif.m_name, v32, tif.FieldWithTag(tag).Name); - return false; - } - - return status; - } - - /// - /// Gets the value(s) of a tag in an open TIFF file. - /// - /// An instance of the class. - /// The tag. - /// The value(s) of a tag in an open TIFF file/stream as array of - /// objects or null if there is no such tag set. - /// - public virtual FieldValue[] GetField(Tiff tif, TiffTag tag) - { - TiffDirectory td = tif.m_dir; - FieldValue[] result = null; - - switch (tag) - { - case TiffTag.SUBFILETYPE: - result = new FieldValue[1]; - result[0].Set(td.td_subfiletype); - break; - case TiffTag.IMAGEWIDTH: - result = new FieldValue[1]; - result[0].Set(td.td_imagewidth); - break; - case TiffTag.IMAGELENGTH: - result = new FieldValue[1]; - result[0].Set(td.td_imagelength); - break; - case TiffTag.BITSPERSAMPLE: - result = new FieldValue[1]; - result[0].Set(td.td_bitspersample); - break; - case TiffTag.COMPRESSION: - result = new FieldValue[1]; - result[0].Set(td.td_compression); - break; - case TiffTag.PHOTOMETRIC: - result = new FieldValue[1]; - result[0].Set(td.td_photometric); - break; - case TiffTag.THRESHHOLDING: - result = new FieldValue[1]; - result[0].Set(td.td_threshholding); - break; - case TiffTag.FILLORDER: - result = new FieldValue[1]; - result[0].Set(td.td_fillorder); - break; - case TiffTag.ORIENTATION: - result = new FieldValue[1]; - result[0].Set(td.td_orientation); - break; - case TiffTag.SAMPLESPERPIXEL: - result = new FieldValue[1]; - result[0].Set(td.td_samplesperpixel); - break; - case TiffTag.ROWSPERSTRIP: - result = new FieldValue[1]; - result[0].Set(td.td_rowsperstrip); - break; - case TiffTag.MINSAMPLEVALUE: - result = new FieldValue[1]; - result[0].Set(td.td_minsamplevalue); - break; - case TiffTag.MAXSAMPLEVALUE: - result = new FieldValue[1]; - result[0].Set(td.td_maxsamplevalue); - break; - case TiffTag.SMINSAMPLEVALUE: - result = new FieldValue[1]; - result[0].Set(td.td_sminsamplevalue); - break; - case TiffTag.SMAXSAMPLEVALUE: - result = new FieldValue[1]; - result[0].Set(td.td_smaxsamplevalue); - break; - case TiffTag.XRESOLUTION: - result = new FieldValue[1]; - result[0].Set(td.td_xresolution); - break; - case TiffTag.YRESOLUTION: - result = new FieldValue[1]; - result[0].Set(td.td_yresolution); - break; - case TiffTag.PLANARCONFIG: - result = new FieldValue[1]; - result[0].Set(td.td_planarconfig); - break; - case TiffTag.XPOSITION: - result = new FieldValue[1]; - result[0].Set(td.td_xposition); - break; - case TiffTag.YPOSITION: - result = new FieldValue[1]; - result[0].Set(td.td_yposition); - break; - case TiffTag.RESOLUTIONUNIT: - result = new FieldValue[1]; - result[0].Set(td.td_resolutionunit); - break; - case TiffTag.PAGENUMBER: - result = new FieldValue[2]; - result[0].Set(td.td_pagenumber[0]); - result[1].Set(td.td_pagenumber[1]); - break; - case TiffTag.HALFTONEHINTS: - result = new FieldValue[2]; - result[0].Set(td.td_halftonehints[0]); - result[1].Set(td.td_halftonehints[1]); - break; - case TiffTag.COLORMAP: - result = new FieldValue[3]; - result[0].Set(td.td_colormap[0]); - result[1].Set(td.td_colormap[1]); - result[2].Set(td.td_colormap[2]); - break; - case TiffTag.STRIPOFFSETS: - case TiffTag.TILEOFFSETS: - result = new FieldValue[1]; - result[0].Set(td.td_stripoffset); - break; - case TiffTag.STRIPBYTECOUNTS: - case TiffTag.TILEBYTECOUNTS: - result = new FieldValue[1]; - result[0].Set(td.td_stripbytecount); - break; - case TiffTag.MATTEING: - result = new FieldValue[1]; - result[0].Set((td.td_extrasamples == 1 && td.td_sampleinfo[0] == ExtraSample.ASSOCALPHA)); - break; - case TiffTag.EXTRASAMPLES: - result = new FieldValue[2]; - result[0].Set(td.td_extrasamples); - result[1].Set(td.td_sampleinfo); - break; - case TiffTag.TILEWIDTH: - result = new FieldValue[1]; - result[0].Set(td.td_tilewidth); - break; - case TiffTag.TILELENGTH: - result = new FieldValue[1]; - result[0].Set(td.td_tilelength); - break; - case TiffTag.TILEDEPTH: - result = new FieldValue[1]; - result[0].Set(td.td_tiledepth); - break; - case TiffTag.DATATYPE: - switch (td.td_sampleformat) - { - case SampleFormat.UINT: - result = new FieldValue[1]; - result[0].Set(DATATYPE_UINT); - break; - case SampleFormat.INT: - result = new FieldValue[1]; - result[0].Set(DATATYPE_INT); - break; - case SampleFormat.IEEEFP: - result = new FieldValue[1]; - result[0].Set(DATATYPE_IEEEFP); - break; - case SampleFormat.VOID: - result = new FieldValue[1]; - result[0].Set(DATATYPE_VOID); - break; - } - break; - case TiffTag.SAMPLEFORMAT: - result = new FieldValue[1]; - result[0].Set(td.td_sampleformat); - break; - case TiffTag.IMAGEDEPTH: - result = new FieldValue[1]; - result[0].Set(td.td_imagedepth); - break; - case TiffTag.SUBIFD: - result = new FieldValue[2]; - result[0].Set(td.td_nsubifd); - result[1].Set(td.td_subifd); - break; - case TiffTag.YCBCRPOSITIONING: - result = new FieldValue[1]; - result[0].Set(td.td_ycbcrpositioning); - break; - case TiffTag.YCBCRSUBSAMPLING: - result = new FieldValue[2]; - result[0].Set(td.td_ycbcrsubsampling[0]); - result[1].Set(td.td_ycbcrsubsampling[1]); - break; - case TiffTag.TRANSFERFUNCTION: - result = new FieldValue[3]; - result[0].Set(td.td_transferfunction[0]); - if (td.td_samplesperpixel - td.td_extrasamples > 1) - { - result[1].Set(td.td_transferfunction[1]); - result[2].Set(td.td_transferfunction[2]); - } - break; - case TiffTag.REFERENCEBLACKWHITE: - if (td.td_refblackwhite != null) - { - result = new FieldValue[1]; - result[0].Set(td.td_refblackwhite); - } - break; - case TiffTag.INKNAMES: - result = new FieldValue[1]; - result[0].Set(td.td_inknames); - break; - default: - // This can happen if multiple images are open with - // different codecs which have private tags. The global tag - // information table may then have tags that are valid for - // one file but not the other. If the client tries to get a - // tag that is not valid for the image's codec then we'll - // arrive here. - TiffFieldInfo fip = tif.FindFieldInfo(tag, TiffType.ANY); - if (fip == null || fip.Bit != FieldBit.Custom) - { - Tiff.ErrorExt(tif, tif.m_clientdata, "_TIFFVGetField", - "{0}: Invalid {1}tag \"{2}\" (not supported by codec)", - tif.m_name, Tiff.isPseudoTag(tag) ? "pseudo-" : "", - fip != null ? fip.Name : "Unknown"); - result = null; - break; - } - - // Do we have a custom value? - result = null; - for (int i = 0; i < td.td_customValueCount; i++) - { - TiffTagValue tv = td.td_customValues[i]; - if (tv.info.Tag != tag) - continue; - - if (fip.PassCount) - { - result = new FieldValue[2]; - - if (fip.ReadCount == TiffFieldInfo.Variable2) - { - result[0].Set(tv.count); - } - else - { - // Assume TiffFieldInfo.Variable - result[0].Set(tv.count); - } - - result[1].Set(tv.value); - } - else - { - if ((fip.Type == TiffType.ASCII || - fip.ReadCount == TiffFieldInfo.Variable || - fip.ReadCount == TiffFieldInfo.Variable2 || - fip.ReadCount == TiffFieldInfo.Spp || - tv.count > 1) && fip.Tag != TiffTag.PAGENUMBER && - fip.Tag != TiffTag.HALFTONEHINTS && - fip.Tag != TiffTag.YCBCRSUBSAMPLING && - fip.Tag != TiffTag.DOTRANGE) - { - result = new FieldValue[1]; - byte[] value = tv.value; - - if (fip.Type == TiffType.ASCII && - tv.value.Length > 0 && - tv.value[tv.value.Length - 1] == 0) - { - // cut unwanted zero at the end - value = new byte[Math.Max(tv.value.Length - 1, 0)]; - Buffer.BlockCopy(tv.value, 0, value, 0, value.Length); - } - - result[0].Set(value); - } - else - { - result = new FieldValue[tv.count]; - byte[] val = tv.value; - int valPos = 0; - for (int j = 0; j < tv.count; j++, valPos += Tiff.dataSize(tv.info.Type)) - { - switch (fip.Type) - { - case TiffType.BYTE: - case TiffType.UNDEFINED: - case TiffType.SBYTE: - result[j].Set(val[valPos]); - break; - case TiffType.SHORT: - case TiffType.SSHORT: - result[j].Set(BitConverter.ToInt16(val, valPos)); - break; - case TiffType.LONG: - case TiffType.IFD: - case TiffType.SLONG: - result[j].Set(BitConverter.ToInt32(val, valPos)); - break; - case TiffType.RATIONAL: - case TiffType.SRATIONAL: - case TiffType.FLOAT: - result[j].Set(BitConverter.ToSingle(val, valPos)); - break; - case TiffType.DOUBLE: - result[j].Set(BitConverter.ToDouble(val, valPos)); - break; - default: - result = null; - break; - } - } - } - } - break; - } - break; - } - - return result; - } - - /// - /// Prints formatted description of the contents of the current directory to the - /// specified stream using specified print (formatting) options. - /// - /// An instance of the class. - /// The stream to print to. - /// The print (formatting) options. - public virtual void PrintDir(Tiff tif, Stream stream, TiffPrintFlags flags) - { - } - - /// - /// Install extra samples information. - /// - private static bool setExtraSamples(TiffDirectory td, ref int v, FieldValue[] ap) - { - // XXX: Unassociated alpha data == 999 is a known Corel Draw bug, see below - const short EXTRASAMPLE_COREL_UNASSALPHA = 999; - - v = ap[0].ToInt(); - if (v > td.td_samplesperpixel) - return false; - - byte[] va = ap[1].ToByteArray(); - if (v > 0 && va == null) - { - // typically missing param - return false; - } - - for (int i = 0; i < v; i++) - { - if ((ExtraSample)va[i] > ExtraSample.UNASSALPHA) - { - // XXX: Corel Draw is known to produce incorrect - // ExtraSamples tags which must be patched here if we - // want to be able to open some of the damaged TIFF files: - if (i < v - 1) - { - short s = BitConverter.ToInt16(va, i); - if (s == EXTRASAMPLE_COREL_UNASSALPHA) - va[i] = (byte)ExtraSample.UNASSALPHA; - } - else - return false; - } - } - - td.td_extrasamples = (short)v; - td.td_sampleinfo = new ExtraSample[td.td_extrasamples]; - for (int i = 0; i < td.td_extrasamples; i++) - td.td_sampleinfo[i] = (ExtraSample)va[i]; - - return true; - } - - private static int checkInkNamesString(Tiff tif, int slen, string s) - { - bool failed = false; - short i = tif.m_dir.td_samplesperpixel; - - if (slen > 0) - { - int endPos = slen; - int pos = 0; - - for (; i > 0; i--) - { - for (; s[pos] != '\0'; pos++) - { - if (pos >= endPos) - { - failed = true; - break; - } - } - - if (failed) - break; - - pos++; // skip \0 - } - - if (!failed) - return pos; - } - - Tiff.ErrorExt(tif, tif.m_clientdata, "TIFFSetField", - "{0}: Invalid InkNames value; expecting {1} names, found {2}", - tif.m_name, tif.m_dir.td_samplesperpixel, tif.m_dir.td_samplesperpixel - i); - return 0; - } - - private static void setNString(out string cpp, string cp, int n) - { - cpp = cp.Substring(0, n); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/preprocess.script.csx b/MuPDF.NET/Barcode/Encoders/libtiff.net/preprocess.script.csx deleted file mode 100644 index d3d03ddf..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/preprocess.script.csx +++ /dev/null @@ -1,213 +0,0 @@ -using System; -using System.IO; -using System.Text; - -namespace ActiveQueryBuilder.Scripts -{ - class Preprocess - { - [STAThread] - static public int Main(string[] args) - { - int result = 0; - string errorMessage = String.Empty; - - ConsoleColor defaultColor = Console.ForegroundColor; - - if (args.Length != 1) - { - errorMessage = "Invalid command line parameter."; - result = 1; - goto exit; - } - - try - { - if (Directory.Exists(args[0])) - { - string dir = Path.GetFullPath(args[0]); - - Console.WriteLine("Process folder <" + dir + ">? (y/n)"); - - char c = (char) Console.Read(); - - if (c != 'y' && c != 'Y') - { - result = 1; - errorMessage = "Aborted."; - goto exit; - } - - var files = Directory.EnumerateFiles(args[0], "*.cs", SearchOption.AllDirectories); - - foreach (string file in files) - { - if (Path.GetFileName(file) != "preprocess.script.cs") // skip itself - { - ProcessFile(file); - } - } - } - else if (File.Exists(args[0])) - { - if (Path.GetExtension(args[0]).ToLower() == ".cs") - { - ProcessFile(args[0]); - } - } - else - { - errorMessage = "Invalid command line parameter."; - result = 1; - goto exit; - } - } - catch (Exception ex) - { - errorMessage = ex.Message; - result = 1; - } - -exit: - if (result != 0) - { - const string errorPerfix = "ERROR [preprocess.script.cs]: "; - - if (errorMessage.Length == 0) - { - errorMessage = "Unknown error"; - } - - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine(errorPerfix + errorMessage); - Console.ForegroundColor = defaultColor; - } - else - { - Console.WriteLine("Done."); - } - -#if DEBUG - Console.WriteLine("Press any key to continue..."); - Console.ReadKey(); -#endif - - return result; - } - - static private void ProcessFile(string file) - { - const string beginIfDef = "#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1"; - const string endIfDef = "#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1"; - const string beginComment = "#/*"; - const string endComment = "#*/"; - const string lineComment = "#//"; - - StringBuilder sb = new StringBuilder(); - bool skip = false; - bool needToWrap = false; - Encoding encoding = Encoding.Unicode; - - Console.WriteLine("Processing " + Path.GetFileName(file) + "..."); - - using (StreamReader sr = new StreamReader(file)) - { - encoding = sr.CurrentEncoding; - - while (!sr.EndOfStream) - { - string line = sr.ReadLine(); - string trimmed = line.Trim(); - - if (sb.Length == 0) - { - if (line != beginIfDef) - { - needToWrap = true; - } - } - - if (trimmed.StartsWith(lineComment)) - { - continue; - } - - if (skip) - { - int end = line.IndexOf(endComment); - - if (end != -1) - { - int pos = end + endComment.Length; - line = line.Substring(pos, line.Length - pos); - skip = false; - } - else - { - continue; - } - } - else - { - while (true) - { - int pos = 0; - int start = line.IndexOf(beginComment, pos), end; - - if (start != -1) - { - pos = start + beginComment.Length; - end = line.IndexOf(endComment, pos); - - if (end != -1) - { - pos = end + endComment.Length; - line = line.Substring(0, start) + line.Substring(pos, line.Length - pos); - } - else - { - line = line.Substring(0, start); - skip = true; - break; - } - } - else - { - break; - } - } - } - - line = line.Replace("public class ", "internal class "); - line = line.Replace("public abstract class ", "internal abstract class "); - line = line.Replace("public sealed class ", "internal sealed class "); - line = line.Replace("public interface ", "internal interface "); - line = line.Replace("public enum ", "internal enum "); - - sb.AppendLine(line); - } - - sr.Close(); - } - - using (StreamWriter sw = new StreamWriter(file, false, encoding)) - { - if (needToWrap) - { - sw.WriteLine(beginIfDef); - sw.WriteLine(); - } - - sw.Write(sb.ToString()); - - if (needToWrap) - { - sw.WriteLine(); - sw.WriteLine(endIfDef); - } - - sw.Flush(); - } - } - } -} diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Adler32.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Adler32.cs deleted file mode 100644 index 8acb16d9..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Adler32.cs +++ /dev/null @@ -1,111 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -// Copyright (c) 2006, ComponentAce -// http://www.componentace.com -// *. - -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -// Neither the name of ComponentAce nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. *. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/* -* This program is based on zlib-1.1.3, so all credit should go authors -* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) -* and contributors of zlib. -*/ -using System; -namespace ComponentAce.Compression.Libs.zlib -{ - sealed class Adler32 - { - - // largest prime smaller than 65536 - private const int BASE = 65521; - // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 - private const int NMAX = 5552; - - internal long adler32(long adler, byte[] buf, int index, int len) - { - if (buf == null) - { - return 1L; - } - - long s1 = adler & 0xffff; - long s2 = (adler >> 16) & 0xffff; - int k; - - while (len > 0) - { - k = len < NMAX?len:NMAX; - len -= k; - while (k >= 16) - { - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - s1 += (buf[index++] & 0xff); s2 += s1; - k -= 16; - } - if (k != 0) - { - do - { - s1 += (buf[index++] & 0xff); s2 += s1; - } - while (--k != 0); - } - s1 %= BASE; - s2 %= BASE; - } - return (s2 << 16) | s1; - } - - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Deflate.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Deflate.cs deleted file mode 100644 index 868916fb..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Deflate.cs +++ /dev/null @@ -1,1802 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -// Copyright (c) 2006, ComponentAce -// http://www.componentace.com -// *. - -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -// Neither the name of ComponentAce nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. *. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/* -* This program is based on zlib-1.1.3, so all credit should go authors -* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) -* and contributors of zlib. -*/ -using System; -namespace ComponentAce.Compression.Libs.zlib -{ -#if EXPOSE_ZLIB - public -#endif - sealed class Deflate - { - - private const int MAX_MEM_LEVEL = 9; - - private const int Z_DEFAULT_COMPRESSION = - 1; - - private const int MAX_WBITS = 15; // 32K LZ77 window - private const int DEF_MEM_LEVEL = 8; - - internal class Config - { - internal int good_length; // reduce lazy search above this match length - internal int max_lazy; // do not perform lazy search above this match length - internal int nice_length; // quit search above this match length - internal int max_chain; - internal int func; - internal Config(int good_length, int max_lazy, int nice_length, int max_chain, int func) - { - this.good_length = good_length; - this.max_lazy = max_lazy; - this.nice_length = nice_length; - this.max_chain = max_chain; - this.func = func; - } - } - - private const int STORED = 0; - private const int FAST = 1; - private const int SLOW = 2; - - private static readonly Config[] config_table = new Config[] - { - // good lazy nice chain - new Config(0, 0, 0, 0, STORED), - new Config(4, 4, 8, 4, FAST), - new Config(4, 5, 16, 8, FAST), - new Config(4, 6, 32, 32, FAST), - - new Config(4, 4, 16, 16, SLOW), - new Config(8, 16, 32, 32, SLOW), - new Config(8, 16, 128, 128, SLOW), - new Config(8, 32, 128, 256, SLOW), - new Config(32, 128, 258, 1024, SLOW), - new Config(32, 258, 258, 4096, SLOW), - }; - - private static readonly System.String[] z_errmsg = new System.String[]{"need dictionary", "stream end", "", "file error", "stream error", "data error", "insufficient memory", "buffer error", "incompatible version", ""}; - - // block not completed, need more input or more output - private const int NeedMore = 0; - - // block flush performed - private const int BlockDone = 1; - - // finish started, need only more output at next deflate - private const int FinishStarted = 2; - - // finish done, accept no more input or output - private const int FinishDone = 3; - - // preset dictionary flag in zlib header - private const int PRESET_DICT = 0x20; - - private const int Z_FILTERED = 1; - private const int Z_HUFFMAN_ONLY = 2; - private const int Z_DEFAULT_STRATEGY = 0; - - private const int Z_NO_FLUSH = 0; - private const int Z_PARTIAL_FLUSH = 1; - private const int Z_SYNC_FLUSH = 2; - private const int Z_FULL_FLUSH = 3; - private const int Z_FINISH = 4; - - private const int Z_OK = 0; - private const int Z_STREAM_END = 1; - private const int Z_NEED_DICT = 2; - private const int Z_ERRNO = - 1; - private const int Z_STREAM_ERROR = - 2; - private const int Z_DATA_ERROR = - 3; - private const int Z_MEM_ERROR = - 4; - private const int Z_BUF_ERROR = - 5; - private const int Z_VERSION_ERROR = - 6; - - private const int INIT_STATE = 42; - private const int BUSY_STATE = 113; - private const int FINISH_STATE = 666; - - // The deflate compression method - private const int Z_DEFLATED = 8; - - private const int STORED_BLOCK = 0; - private const int STATIC_TREES = 1; - private const int DYN_TREES = 2; - - // The three kinds of block type - private const int Z_BINARY = 0; - private const int Z_ASCII = 1; - private const int Z_UNKNOWN = 2; - - private const int Buf_size = 8 * 2; - - // repeat previous bit length 3-6 times (2 bits of repeat count) - private const int REP_3_6 = 16; - - // repeat a zero length 3-10 times (3 bits of repeat count) - private const int REPZ_3_10 = 17; - - // repeat a zero length 11-138 times (7 bits of repeat count) - private const int REPZ_11_138 = 18; - - private const int MIN_MATCH = 3; - private const int MAX_MATCH = 258; - private const int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); - - private const int MAX_BITS = 15; - private const int D_CODES = 30; - private const int BL_CODES = 19; - private const int LENGTH_CODES = 29; - private const int LITERALS = 256; - private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); - private const int HEAP_SIZE = (2 * L_CODES + 1); - - private const int END_BLOCK = 256; - - internal ZStream strm; // pointer back to this zlib stream - internal int status; // as the name implies - internal byte[] pending_buf; // output still pending - internal int pending_buf_size; // size of pending_buf - internal int pending_out; // next pending byte to output to the stream - internal int pending; // nb of bytes in the pending buffer - internal int noheader; // suppress zlib header and adler32 - internal byte data_type; // UNKNOWN, BINARY or ASCII - internal byte method; // STORED (for zip only) or DEFLATED - internal int last_flush; // value of flush param for previous deflate call - - internal int w_size; // LZ77 window size (32K by default) - internal int w_bits; // log2(w_size) (8..16) - internal int w_mask; // w_size - 1 - - internal byte[] window; - // Sliding window. Input bytes are read into the second half of the window, - // and move to the first half later to keep a dictionary of at least wSize - // bytes. With this organization, matches are limited to a distance of - // wSize-MAX_MATCH bytes, but this ensures that IO is always - // performed with a length multiple of the block size. Also, it limits - // the window size to 64K, which is quite useful on MSDOS. - // To do: use the user input buffer as sliding window. - - internal int window_size; - // Actual size of window: 2*wSize, except when the user input buffer - // is directly used as sliding window. - - internal short[] prev; - // Link to older string with same hash index. To limit the size of this - // array to 64K, this link is maintained only for the last 32K strings. - // An index in this array is thus a window index modulo 32K. - - internal short[] head; // Heads of the hash chains or NIL. - - internal int ins_h; // hash index of string to be inserted - internal int hash_size; // number of elements in hash table - internal int hash_bits; // log2(hash_size) - internal int hash_mask; // hash_size-1 - - // Number of bits by which ins_h must be shifted at each input - // step. It must be such that after MIN_MATCH steps, the oldest - // byte no longer takes part in the hash key, that is: - // hash_shift * MIN_MATCH >= hash_bits - internal int hash_shift; - - // Window position at the beginning of the current output block. Gets - // negative when the window is moved backwards. - - internal int block_start; - - internal int match_length; // length of best match - internal int prev_match; // previous match - internal int match_available; // set if previous match exists - internal int strstart; // start of string to insert - internal int match_start; // start of matching string - internal int lookahead; // number of valid bytes ahead in window - - // Length of the best match at previous step. Matches not greater than this - // are discarded. This is used in the lazy match evaluation. - internal int prev_length; - - // To speed up deflation, hash chains are never searched beyond this - // length. A higher limit improves compression ratio but degrades the speed. - internal int max_chain_length; - - // Attempt to find a better match only when the current match is strictly - // smaller than this value. This mechanism is used only for compression - // levels >= 4. - internal int max_lazy_match; - - // Insert new strings in the hash table only if the match length is not - // greater than this length. This saves time but degrades compression. - // max_insert_length is used only for compression levels <= 3. - - internal int level; // compression level (1..9) - internal int strategy; // favor or force Huffman coding - - // Use a faster search when the previous match is longer than this - internal int good_match; - - // Stop searching when current match exceeds this - internal int nice_match; - - internal short[] dyn_ltree; // literal and length tree - internal short[] dyn_dtree; // distance tree - internal short[] bl_tree; // Huffman tree for bit lengths - - internal Tree l_desc = new Tree(); // desc for literal tree - internal Tree d_desc = new Tree(); // desc for distance tree - internal Tree bl_desc = new Tree(); // desc for bit length tree - - // number of codes at each bit length for an optimal tree - internal short[] bl_count = new short[MAX_BITS + 1]; - - // heap used to build the Huffman trees - internal int[] heap = new int[2 * L_CODES + 1]; - - internal int heap_len; // number of elements in the heap - internal int heap_max; // element of largest frequency - // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. - // The same heap array is used to build all trees. - - // Depth of each subtree used as tie breaker for trees of equal frequency - internal byte[] depth = new byte[2 * L_CODES + 1]; - - internal int l_buf; // index for literals or lengths */ - - // Size of match buffer for literals/lengths. There are 4 reasons for - // limiting lit_bufsize to 64K: - // - frequencies can be kept in 16 bit counters - // - if compression is not successful for the first block, all input - // data is still in the window so we can still emit a stored block even - // when input comes from standard input. (This can also be done for - // all blocks if lit_bufsize is not greater than 32K.) - // - if compression is not successful for a file smaller than 64K, we can - // even emit a stored file instead of a stored block (saving 5 bytes). - // This is applicable only for zip (not gzip or zlib). - // - creating new Huffman trees less frequently may not provide fast - // adaptation to changes in the input data statistics. (Take for - // example a binary file with poorly compressible code followed by - // a highly compressible string table.) Smaller buffer sizes give - // fast adaptation but have of course the overhead of transmitting - // trees more frequently. - // - I can't count above 4 - internal int lit_bufsize; - - internal int last_lit; // running index in l_buf - - // Buffer for distances. To simplify the code, d_buf and l_buf have - // the same number of elements. To use different lengths, an extra flag - // array would be necessary. - - internal int d_buf; // index of pendig_buf - - internal int opt_len; // bit length of current block with optimal trees - internal int static_len; // bit length of current block with static trees - internal int matches; // number of string matches in current block - internal int last_eob_len; // bit length of EOB code for last block - - // Output buffer. bits are inserted starting at the bottom (least - // significant bits). - internal short bi_buf; - - // Number of valid bits in bi_buf. All bits above the last valid bit - // are always zero. - internal int bi_valid; - - internal Deflate() - { - dyn_ltree = new short[HEAP_SIZE * 2]; - dyn_dtree = new short[(2 * D_CODES + 1) * 2]; // distance tree - bl_tree = new short[(2 * BL_CODES + 1) * 2]; // Huffman tree for bit lengths - } - - internal void lm_init() - { - window_size = 2 * w_size; - - head[hash_size - 1] = 0; - for (int i = 0; i < hash_size - 1; i++) - { - head[i] = 0; - } - - // Set the default configuration parameters: - max_lazy_match = Deflate.config_table[level].max_lazy; - good_match = Deflate.config_table[level].good_length; - nice_match = Deflate.config_table[level].nice_length; - max_chain_length = Deflate.config_table[level].max_chain; - - strstart = 0; - block_start = 0; - lookahead = 0; - match_length = prev_length = MIN_MATCH - 1; - match_available = 0; - ins_h = 0; - } - - // Initialize the tree data structures for a new zlib stream. - internal void tr_init() - { - - l_desc.dyn_tree = dyn_ltree; - l_desc.stat_desc = StaticTree.static_l_desc; - - d_desc.dyn_tree = dyn_dtree; - d_desc.stat_desc = StaticTree.static_d_desc; - - bl_desc.dyn_tree = bl_tree; - bl_desc.stat_desc = StaticTree.static_bl_desc; - - bi_buf = 0; - bi_valid = 0; - last_eob_len = 8; // enough lookahead for inflate - - // Initialize the first block of the first file: - init_block(); - } - - internal void init_block() - { - // Initialize the trees. - for (int i = 0; i < L_CODES; i++) - dyn_ltree[i * 2] = 0; - for (int i = 0; i < D_CODES; i++) - dyn_dtree[i * 2] = 0; - for (int i = 0; i < BL_CODES; i++) - bl_tree[i * 2] = 0; - - dyn_ltree[END_BLOCK * 2] = 1; - opt_len = static_len = 0; - last_lit = matches = 0; - } - - // Restore the heap property by moving down the tree starting at node k, - // exchanging a node with the smallest of its two sons if necessary, stopping - // when the heap property is re-established (each father smaller than its - // two sons). - internal void pqdownheap(short[] tree, int k) - { - int v = heap[k]; - int j = k << 1; // left son of k - while (j <= heap_len) - { - // Set j to the smallest of the two sons: - if (j < heap_len && smaller(tree, heap[j + 1], heap[j], depth)) - { - j++; - } - // Exit if v is smaller than both sons - if (smaller(tree, v, heap[j], depth)) - break; - - // Exchange v with the smallest son - heap[k] = heap[j]; k = j; - // And continue down the tree, setting j to the left son of k - j <<= 1; - } - heap[k] = v; - } - - internal static bool smaller(short[] tree, int n, int m, byte[] depth) - { - return (tree[n * 2] < tree[m * 2] || (tree[n * 2] == tree[m * 2] && depth[n] <= depth[m])); - } - - // Scan a literal or distance tree to determine the frequencies of the codes - // in the bit length tree. - internal void scan_tree(short[] tree, int max_code) - { - int n; // iterates over all tree elements - int prevlen = - 1; // last emitted length - int curlen; // length of current code - int nextlen = tree[0 * 2 + 1]; // length of next code - int count = 0; // repeat count of the current code - int max_count = 7; // max repeat count - int min_count = 4; // min repeat count - - if (nextlen == 0) - { - max_count = 138; min_count = 3; - } - tree[(max_code + 1) * 2 + 1] = (short) SupportClass.Identity(0xffff); // guard - - for (n = 0; n <= max_code; n++) - { - curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; - if (++count < max_count && curlen == nextlen) - { - continue; - } - else if (count < min_count) - { - bl_tree[curlen * 2] = (short) (bl_tree[curlen * 2] + count); - } - else if (curlen != 0) - { - if (curlen != prevlen) - bl_tree[curlen * 2]++; - bl_tree[REP_3_6 * 2]++; - } - else if (count <= 10) - { - bl_tree[REPZ_3_10 * 2]++; - } - else - { - bl_tree[REPZ_11_138 * 2]++; - } - count = 0; prevlen = curlen; - if (nextlen == 0) - { - max_count = 138; min_count = 3; - } - else if (curlen == nextlen) - { - max_count = 6; min_count = 3; - } - else - { - max_count = 7; min_count = 4; - } - } - } - - // Construct the Huffman tree for the bit lengths and return the index in - // bl_order of the last bit length code to send. - internal int build_bl_tree() - { - int max_blindex; // index of last bit length code of non zero freq - - // Determine the bit length frequencies for literal and distance trees - scan_tree(dyn_ltree, l_desc.max_code); - scan_tree(dyn_dtree, d_desc.max_code); - - // Build the bit length tree: - bl_desc.build_tree(this); - // opt_len now includes the length of the tree representations, except - // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. - - // Determine the number of bit length codes to send. The pkzip format - // requires that at least 4 bit length codes be sent. (appnote.txt says - // 3 but the actual value used is 4.) - for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) - { - if (bl_tree[Tree.bl_order[max_blindex] * 2 + 1] != 0) - break; - } - // Update opt_len to include the bit length tree and counts - opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; - - return max_blindex; - } - - - // Send the header for a block using dynamic Huffman trees: the counts, the - // lengths of the bit length codes, the literal tree and the distance tree. - // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. - internal void send_all_trees(int lcodes, int dcodes, int blcodes) - { - int rank; // index in bl_order - - send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt - send_bits(dcodes - 1, 5); - send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt - for (rank = 0; rank < blcodes; rank++) - { - send_bits(bl_tree[Tree.bl_order[rank] * 2 + 1], 3); - } - send_tree(dyn_ltree, lcodes - 1); // literal tree - send_tree(dyn_dtree, dcodes - 1); // distance tree - } - - // Send a literal or distance tree in compressed form, using the codes in - // bl_tree. - internal void send_tree(short[] tree, int max_code) - { - int n; // iterates over all tree elements - int prevlen = - 1; // last emitted length - int curlen; // length of current code - int nextlen = tree[0 * 2 + 1]; // length of next code - int count = 0; // repeat count of the current code - int max_count = 7; // max repeat count - int min_count = 4; // min repeat count - - if (nextlen == 0) - { - max_count = 138; min_count = 3; - } - - for (n = 0; n <= max_code; n++) - { - curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; - if (++count < max_count && curlen == nextlen) - { - continue; - } - else if (count < min_count) - { - do - { - send_code(curlen, bl_tree); - } - while (--count != 0); - } - else if (curlen != 0) - { - if (curlen != prevlen) - { - send_code(curlen, bl_tree); count--; - } - send_code(REP_3_6, bl_tree); - send_bits(count - 3, 2); - } - else if (count <= 10) - { - send_code(REPZ_3_10, bl_tree); - send_bits(count - 3, 3); - } - else - { - send_code(REPZ_11_138, bl_tree); - send_bits(count - 11, 7); - } - count = 0; prevlen = curlen; - if (nextlen == 0) - { - max_count = 138; min_count = 3; - } - else if (curlen == nextlen) - { - max_count = 6; min_count = 3; - } - else - { - max_count = 7; min_count = 4; - } - } - } - - // Output a byte on the stream. - // IN assertion: there is enough room in pending_buf. - internal void put_byte(byte[] p, int start, int len) - { - Buffer.BlockCopy(p, start, pending_buf, pending, len); - pending += len; - } - - internal void put_byte(byte c) - { - pending_buf[pending++] = c; - } - internal void put_short(int w) - { - put_byte((byte) (w)); - put_byte((byte) (SupportClass.URShift(w, 8))); - } - internal void putShortMSB(int b) - { - put_byte((byte) (b >> 8)); - put_byte((byte) (b)); - } - - internal void send_code(int c, short[] tree) - { - send_bits((tree[c * 2] & 0xffff), (tree[c * 2 + 1] & 0xffff)); - } - - internal void send_bits(int value_Renamed, int length) - { - int len = length; - if (bi_valid > (int) Buf_size - len) - { - int val = value_Renamed; - // bi_buf |= (val << bi_valid); - bi_buf = (short) ((ushort) bi_buf | (ushort) (((val << bi_valid) & 0xffff))); - put_short(bi_buf); - bi_buf = (short) (SupportClass.URShift(val, (Buf_size - bi_valid))); - bi_valid += len - Buf_size; - } - else - { - // bi_buf |= (value) << bi_valid; - bi_buf = (short)((ushort)bi_buf | (ushort)((((value_Renamed) << bi_valid) & 0xffff))); - bi_valid += len; - } - } - - // Send one empty static block to give enough lookahead for inflate. - // This takes 10 bits, of which 7 may remain in the bit buffer. - // The current inflate code requires 9 bits of lookahead. If the - // last two codes for the previous block (real code plus EOB) were coded - // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode - // the last real code. In this case we send two empty static blocks instead - // of one. (There are no problems if the previous block is stored or fixed.) - // To simplify the code, we assume the worst case of last real code encoded - // on one bit only. - internal void _tr_align() - { - send_bits(STATIC_TREES << 1, 3); - send_code(END_BLOCK, StaticTree.static_ltree); - - bi_flush(); - - // Of the 10 bits for the empty block, we have already sent - // (10 - bi_valid) bits. The lookahead for the last real code (before - // the EOB of the previous block) was thus at least one plus the length - // of the EOB plus what we have just sent of the empty static block. - if (1 + last_eob_len + 10 - bi_valid < 9) - { - send_bits(STATIC_TREES << 1, 3); - send_code(END_BLOCK, StaticTree.static_ltree); - bi_flush(); - } - last_eob_len = 7; - } - - - // Save the match info and tally the frequency counts. Return true if - // the current block must be flushed. - internal bool _tr_tally(int dist, int lc) - { - - pending_buf[d_buf + last_lit * 2] = (byte) (SupportClass.URShift(dist, 8)); - pending_buf[d_buf + last_lit * 2 + 1] = (byte) dist; - - pending_buf[l_buf + last_lit] = (byte) lc; last_lit++; - - if (dist == 0) - { - // lc is the unmatched char - dyn_ltree[lc * 2]++; - } - else - { - matches++; - // Here, lc is the match length - MIN_MATCH - dist--; // dist = match distance - 1 - dyn_ltree[(Tree._length_code[lc] + LITERALS + 1) * 2]++; - dyn_dtree[Tree.d_code(dist) * 2]++; - } - - if ((last_lit & 0x1fff) == 0 && level > 2) - { - // Compute an upper bound for the compressed length - int out_length = last_lit * 8; - int in_length = strstart - block_start; - int dcode; - for (dcode = 0; dcode < D_CODES; dcode++) - { - out_length = (int) (out_length + (int) dyn_dtree[dcode * 2] * (5L + Tree.extra_dbits[dcode])); - } - out_length = SupportClass.URShift(out_length, 3); - if ((matches < (last_lit / 2)) && out_length < in_length / 2) - return true; - } - - return (last_lit == lit_bufsize - 1); - // We avoid equality with lit_bufsize because of wraparound at 64K - // on 16 bit machines and because stored blocks are restricted to - // 64K-1 bytes. - } - - // Send the block data compressed using the given Huffman trees - internal void compress_block(short[] ltree, short[] dtree) - { - int dist; // distance of matched string - int lc; // match length or unmatched char (if dist == 0) - int lx = 0; // running index in l_buf - int code; // the code to send - int extra; // number of extra bits to send - - if (last_lit != 0) - { - do - { - dist = ((pending_buf[d_buf + lx * 2] << 8) & 0xff00) | (pending_buf[d_buf + lx * 2 + 1] & 0xff); - lc = (pending_buf[l_buf + lx]) & 0xff; lx++; - - if (dist == 0) - { - send_code(lc, ltree); // send a literal byte - } - else - { - // Here, lc is the match length - MIN_MATCH - code = Tree._length_code[lc]; - - send_code(code + LITERALS + 1, ltree); // send the length code - extra = Tree.extra_lbits[code]; - if (extra != 0) - { - lc -= Tree.base_length[code]; - send_bits(lc, extra); // send the extra length bits - } - dist--; // dist is now the match distance - 1 - code = Tree.d_code(dist); - - send_code(code, dtree); // send the distance code - extra = Tree.extra_dbits[code]; - if (extra != 0) - { - dist -= Tree.base_dist[code]; - send_bits(dist, extra); // send the extra distance bits - } - } // literal or match pair ? - - // Check that the overlay between pending_buf and d_buf+l_buf is ok: - } - while (lx < last_lit); - } - - send_code(END_BLOCK, ltree); - last_eob_len = ltree[END_BLOCK * 2 + 1]; - } - - // Set the data type to ASCII or BINARY, using a crude approximation: - // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. - // IN assertion: the fields freq of dyn_ltree are set and the total of all - // frequencies does not exceed 64K (to fit in an int on 16 bit machines). - internal void set_data_type() - { - int n = 0; - int ascii_freq = 0; - int bin_freq = 0; - while (n < 7) - { - bin_freq += dyn_ltree[n * 2]; n++; - } - while (n < 128) - { - ascii_freq += dyn_ltree[n * 2]; n++; - } - while (n < LITERALS) - { - bin_freq += dyn_ltree[n * 2]; n++; - } - data_type = (byte) (bin_freq > (SupportClass.URShift(ascii_freq, 2))?Z_BINARY:Z_ASCII); - } - - // Flush the bit buffer, keeping at most 7 bits in it. - internal void bi_flush() - { - if (bi_valid == 16) - { - put_short(bi_buf); - bi_buf = 0; - bi_valid = 0; - } - else if (bi_valid >= 8) - { - put_byte((byte) bi_buf); - bi_buf = (short) (SupportClass.URShift(bi_buf, 8)); - bi_valid -= 8; - } - } - - // Flush the bit buffer and align the output on a byte boundary - internal void bi_windup() - { - if (bi_valid > 8) - { - put_short(bi_buf); - } - else if (bi_valid > 0) - { - put_byte((byte) bi_buf); - } - bi_buf = 0; - bi_valid = 0; - } - - // Copy a stored block, storing first the length and its - // one's complement if requested. - internal void copy_block(int buf, int len, bool header) - { - - bi_windup(); // align on byte boundary - last_eob_len = 8; // enough lookahead for inflate - - if (header) - { - put_short((short) len); - put_short((short) ~ len); - } - - // while(len--!=0) { - // put_byte(window[buf+index]); - // index++; - // } - put_byte(window, buf, len); - } - - internal void flush_block_only(bool eof) - { - _tr_flush_block(block_start >= 0?block_start:- 1, strstart - block_start, eof); - block_start = strstart; - strm.flush_pending(); - } - - // Copy without compression as much as possible from the input stream, return - // the current block state. - // This function does not insert new strings in the dictionary since - // uncompressible data is probably not useful. This function is used - // only for the level=0 compression option. - // NOTE: this function should be optimized to avoid extra copying from - // window to pending_buf. - internal int deflate_stored(int flush) - { - // Stored blocks are limited to 0xffff bytes, pending_buf is limited - // to pending_buf_size, and each stored block has a 5 byte header: - - int max_block_size = 0xffff; - int max_start; - - if (max_block_size > pending_buf_size - 5) - { - max_block_size = pending_buf_size - 5; - } - - // Copy as much as possible from input to output: - while (true) - { - // Fill the window as much as possible: - if (lookahead <= 1) - { - fill_window(); - if (lookahead == 0 && flush == Z_NO_FLUSH) - return NeedMore; - if (lookahead == 0) - break; // flush the current block - } - - strstart += lookahead; - lookahead = 0; - - // Emit a stored block if pending_buf will be full: - max_start = block_start + max_block_size; - if (strstart == 0 || strstart >= max_start) - { - // strstart == 0 is possible when wraparound on 16-bit machine - lookahead = (int) (strstart - max_start); - strstart = (int) max_start; - - flush_block_only(false); - if (strm.avail_out == 0) - return NeedMore; - } - - // Flush if we may have to slide, otherwise block_start may become - // negative and the data will be gone: - if (strstart - block_start >= w_size - MIN_LOOKAHEAD) - { - flush_block_only(false); - if (strm.avail_out == 0) - return NeedMore; - } - } - - flush_block_only(flush == Z_FINISH); - if (strm.avail_out == 0) - return (flush == Z_FINISH)?FinishStarted:NeedMore; - - return flush == Z_FINISH?FinishDone:BlockDone; - } - - // Send a stored block - internal void _tr_stored_block(int buf, int stored_len, bool eof) - { - send_bits((STORED_BLOCK << 1) + (eof?1:0), 3); // send block type - copy_block(buf, stored_len, true); // with header - } - - // Determine the best encoding for the current block: dynamic trees, static - // trees or store, and output the encoded block to the zip file. - internal void _tr_flush_block(int buf, int stored_len, bool eof) - { - int opt_lenb, static_lenb; // opt_len and static_len in bytes - int max_blindex = 0; // index of last bit length code of non zero freq - - // Build the Huffman trees unless a stored block is forced - if (level > 0) - { - // Check if the file is ascii or binary - if (data_type == Z_UNKNOWN) - set_data_type(); - - // Construct the literal and distance trees - l_desc.build_tree(this); - - d_desc.build_tree(this); - - // At this point, opt_len and static_len are the total bit lengths of - // the compressed block data, excluding the tree representations. - - // Build the bit length tree for the above two trees, and get the index - // in bl_order of the last bit length code to send. - max_blindex = build_bl_tree(); - - // Determine the best encoding. Compute first the block length in bytes - opt_lenb = SupportClass.URShift((opt_len + 3 + 7), 3); - static_lenb = SupportClass.URShift((static_len + 3 + 7), 3); - - if (static_lenb <= opt_lenb) - opt_lenb = static_lenb; - } - else - { - opt_lenb = static_lenb = stored_len + 5; // force a stored block - } - - if (stored_len + 4 <= opt_lenb && buf != - 1) - { - // 4: two words for the lengths - // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. - // Otherwise we can't have processed more than WSIZE input bytes since - // the last block flush, because compression would have been - // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to - // transform a block into a stored block. - _tr_stored_block(buf, stored_len, eof); - } - else if (static_lenb == opt_lenb) - { - send_bits((STATIC_TREES << 1) + (eof?1:0), 3); - compress_block(StaticTree.static_ltree, StaticTree.static_dtree); - } - else - { - send_bits((DYN_TREES << 1) + (eof?1:0), 3); - send_all_trees(l_desc.max_code + 1, d_desc.max_code + 1, max_blindex + 1); - compress_block(dyn_ltree, dyn_dtree); - } - - // The above check is made mod 2^32, for files larger than 512 MB - // and uLong implemented on 32 bits. - - init_block(); - - if (eof) - { - bi_windup(); - } - } - - // Fill the window when the lookahead becomes insufficient. - // Updates strstart and lookahead. - // - // IN assertion: lookahead < MIN_LOOKAHEAD - // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD - // At least one byte has been read, or avail_in == 0; reads are - // performed for at least two bytes (required for the zip translate_eol - // option -- not supported here). - internal void fill_window() - { - int n, m; - int p; - int more; // Amount of free space at the end of the window. - - do - { - more = (window_size - lookahead - strstart); - - // Deal with !@#$% 64K limit: - if (more == 0 && strstart == 0 && lookahead == 0) - { - more = w_size; - } - else if (more == - 1) - { - // Very unlikely, but possible on 16 bit machine if strstart == 0 - // and lookahead == 1 (input done one byte at time) - more--; - - // If the window is almost full and there is insufficient lookahead, - // move the upper half to the lower one to make room in the upper half. - } - else if (strstart >= w_size + w_size - MIN_LOOKAHEAD) - { - Buffer.BlockCopy(window, w_size, window, 0, w_size); - match_start -= w_size; - strstart -= w_size; // we now have strstart >= MAX_DIST - block_start -= w_size; - - // Slide the hash table (could be avoided with 32 bit values - // at the expense of memory usage). We slide even when level == 0 - // to keep the hash table consistent if we switch back to level > 0 - // later. (Using level 0 permanently is not an optimal usage of - // zlib, so we don't care about this pathological case.) - - n = hash_size; - p = n; - do - { - m = (head[--p] & 0xffff); - head[p] = (short)(m >= w_size?(m - w_size):0); - //head[p] = (m >= w_size?(short) (m - w_size):0); - } - while (--n != 0); - - n = w_size; - p = n; - do - { - m = (prev[--p] & 0xffff); - prev[p] = (short)(m >= w_size?(m - w_size):0); - //prev[p] = (m >= w_size?(short) (m - w_size):0); - // If n is not on any hash chain, prev[n] is garbage but - // its value will never be used. - } - while (--n != 0); - more += w_size; - } - - if (strm.avail_in == 0) - return ; - - // If there was no sliding: - // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && - // more == window_size - lookahead - strstart - // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) - // => more >= window_size - 2*WSIZE + 2 - // In the BIG_MEM or MMAP case (not yet supported), - // window_size == input_size + MIN_LOOKAHEAD && - // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. - // Otherwise, window_size == 2*WSIZE so more >= 2. - // If there was sliding, more >= WSIZE. So in all cases, more >= 2. - - n = strm.read_buf(window, strstart + lookahead, more); - lookahead += n; - - // Initialize the hash value now that we have some input: - if (lookahead >= MIN_MATCH) - { - ins_h = window[strstart] & 0xff; - ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; - } - // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, - // but this is not important since only literal bytes will be emitted. - } - while (lookahead < MIN_LOOKAHEAD && strm.avail_in != 0); - } - - // Compress as much as possible from the input stream, return the current - // block state. - // This function does not perform lazy evaluation of matches and inserts - // new strings in the dictionary only for unmatched strings or for short - // matches. It is used only for the fast compression options. - internal int deflate_fast(int flush) - { - // short hash_head = 0; // head of the hash chain - int hash_head = 0; // head of the hash chain - bool bflush; // set if current block must be flushed - - while (true) - { - // Make sure that we always have enough lookahead, except - // at the end of the input file. We need MAX_MATCH bytes - // for the next match, plus MIN_MATCH bytes to insert the - // string following the next match. - if (lookahead < MIN_LOOKAHEAD) - { - fill_window(); - if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) - { - return NeedMore; - } - if (lookahead == 0) - break; // flush the current block - } - - // Insert the string window[strstart .. strstart+2] in the - // dictionary, and set hash_head to the head of the hash chain: - if (lookahead >= MIN_MATCH) - { - ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = (short) strstart; - } - - // Find the longest match, discarding those <= prev_length. - // At this point we have always match_length < MIN_MATCH - - if (hash_head != 0L && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) - { - // To simplify the code, we prevent matches with the string - // of window index 0 (in particular we have to avoid a match - // of the string with itself at the start of the input file). - if (strategy != Z_HUFFMAN_ONLY) - { - match_length = longest_match(hash_head); - } - // longest_match() sets match_start - } - if (match_length >= MIN_MATCH) - { - // check_match(strstart, match_start, match_length); - - bflush = _tr_tally(strstart - match_start, match_length - MIN_MATCH); - - lookahead -= match_length; - - // Insert new strings in the hash table only if the match length - // is not too large. This saves time but degrades compression. - if (match_length <= max_lazy_match && lookahead >= MIN_MATCH) - { - match_length--; // string at strstart already in hash table - do - { - strstart++; - - ins_h = ((ins_h << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = (short) strstart; - - // strstart never exceeds WSIZE-MAX_MATCH, so there are - // always MIN_MATCH bytes ahead. - } - while (--match_length != 0); - strstart++; - } - else - { - strstart += match_length; - match_length = 0; - ins_h = window[strstart] & 0xff; - - ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; - // If lookahead < MIN_MATCH, ins_h is garbage, but it does not - // matter since it will be recomputed at next deflate call. - } - } - else - { - // No match, output a literal byte - - bflush = _tr_tally(0, window[strstart] & 0xff); - lookahead--; - strstart++; - } - if (bflush) - { - - flush_block_only(false); - if (strm.avail_out == 0) - return NeedMore; - } - } - - flush_block_only(flush == Z_FINISH); - if (strm.avail_out == 0) - { - if (flush == Z_FINISH) - return FinishStarted; - else - return NeedMore; - } - return flush == Z_FINISH?FinishDone:BlockDone; - } - - // Same as above, but achieves better compression. We use a lazy - // evaluation for matches: a match is finally adopted only if there is - // no better match at the next window position. - internal int deflate_slow(int flush) - { - // short hash_head = 0; // head of hash chain - int hash_head = 0; // head of hash chain - bool bflush; // set if current block must be flushed - - // Process the input block. - while (true) - { - // Make sure that we always have enough lookahead, except - // at the end of the input file. We need MAX_MATCH bytes - // for the next match, plus MIN_MATCH bytes to insert the - // string following the next match. - - if (lookahead < MIN_LOOKAHEAD) - { - fill_window(); - if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) - { - return NeedMore; - } - if (lookahead == 0) - break; // flush the current block - } - - // Insert the string window[strstart .. strstart+2] in the - // dictionary, and set hash_head to the head of the hash chain: - - if (lookahead >= MIN_MATCH) - { - ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - // prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = (short) strstart; - } - - // Find the longest match, discarding those <= prev_length. - prev_length = match_length; prev_match = match_start; - match_length = MIN_MATCH - 1; - - if (hash_head != 0 && prev_length < max_lazy_match && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) - { - // To simplify the code, we prevent matches with the string - // of window index 0 (in particular we have to avoid a match - // of the string with itself at the start of the input file). - - if (strategy != Z_HUFFMAN_ONLY) - { - match_length = longest_match(hash_head); - } - // longest_match() sets match_start - - if (match_length <= 5 && (strategy == Z_FILTERED || (match_length == MIN_MATCH && strstart - match_start > 4096))) - { - - // If prev_match is also MIN_MATCH, match_start is garbage - // but we will ignore the current match anyway. - match_length = MIN_MATCH - 1; - } - } - - // If there was a match at the previous step and the current - // match is not better, output the previous match: - if (prev_length >= MIN_MATCH && match_length <= prev_length) - { - int max_insert = strstart + lookahead - MIN_MATCH; - // Do not insert strings in hash table beyond this. - - // check_match(strstart-1, prev_match, prev_length); - - bflush = _tr_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH); - - // Insert in hash table all strings up to the end of the match. - // strstart-1 and strstart are already inserted. If there is not - // enough lookahead, the last two strings are not inserted in - // the hash table. - lookahead -= (prev_length - 1); - prev_length -= 2; - do - { - if (++strstart <= max_insert) - { - ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - //prev[strstart&w_mask]=hash_head=head[ins_h]; - hash_head = (head[ins_h] & 0xffff); - prev[strstart & w_mask] = head[ins_h]; - head[ins_h] = (short) strstart; - } - } - while (--prev_length != 0); - match_available = 0; - match_length = MIN_MATCH - 1; - strstart++; - - if (bflush) - { - flush_block_only(false); - if (strm.avail_out == 0) - return NeedMore; - } - } - else if (match_available != 0) - { - - // If there was no match at the previous position, output a - // single literal. If there was a match but the current match - // is longer, truncate the previous match to a single literal. - - bflush = _tr_tally(0, window[strstart - 1] & 0xff); - - if (bflush) - { - flush_block_only(false); - } - strstart++; - lookahead--; - if (strm.avail_out == 0) - return NeedMore; - } - else - { - // There is no previous match to compare with, wait for - // the next step to decide. - - match_available = 1; - strstart++; - lookahead--; - } - } - - if (match_available != 0) - { - bflush = _tr_tally(0, window[strstart - 1] & 0xff); - match_available = 0; - } - flush_block_only(flush == Z_FINISH); - - if (strm.avail_out == 0) - { - if (flush == Z_FINISH) - return FinishStarted; - else - return NeedMore; - } - - return flush == Z_FINISH?FinishDone:BlockDone; - } - - internal int longest_match(int cur_match) - { - int chain_length = max_chain_length; // max hash chain length - int scan = strstart; // current string - int match; // matched string - int len; // length of current match - int best_len = prev_length; // best match length so far - int limit = strstart > (w_size - MIN_LOOKAHEAD)?strstart - (w_size - MIN_LOOKAHEAD):0; - int nice_match = this.nice_match; - - // Stop when cur_match becomes <= limit. To simplify the code, - // we prevent matches with the string of window index 0. - - int wmask = w_mask; - - int strend = strstart + MAX_MATCH; - byte scan_end1 = window[scan + best_len - 1]; - byte scan_end = window[scan + best_len]; - - // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. - // It is easy to get rid of this optimization if necessary. - - // Do not waste too much time if we already have a good match: - if (prev_length >= good_match) - { - chain_length >>= 2; - } - - // Do not look for matches beyond the end of the input. This is necessary - // to make deflate deterministic. - if (nice_match > lookahead) - nice_match = lookahead; - - do - { - match = cur_match; - - // Skip to next match if the match length cannot increase - // or if the match length is less than 2: - if (window[match + best_len] != scan_end || window[match + best_len - 1] != scan_end1 || window[match] != window[scan] || window[++match] != window[scan + 1]) - continue; - - // The check at best_len-1 can be removed because it will be made - // again later. (This heuristic is not always a win.) - // It is not necessary to compare scan[2] and match[2] since they - // are always equal when the other bytes match, given that - // the hash keys are equal and that HASH_BITS >= 8. - scan += 2; match++; - - // We check for insufficient lookahead only every 8th comparison; - // the 256th check will be made at strstart+258. - do - { - } - while (window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match] && scan < strend); - - len = MAX_MATCH - (int) (strend - scan); - scan = strend - MAX_MATCH; - - if (len > best_len) - { - match_start = cur_match; - best_len = len; - if (len >= nice_match) - break; - scan_end1 = window[scan + best_len - 1]; - scan_end = window[scan + best_len]; - } - } - while ((cur_match = (prev[cur_match & wmask] & 0xffff)) > limit && --chain_length != 0); - - if (best_len <= lookahead) - return best_len; - return lookahead; - } - - internal int deflateInit(ZStream strm, int level, int bits) - { - return deflateInit2(strm, level, Z_DEFLATED, bits, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); - } - internal int deflateInit(ZStream strm, int level) - { - return deflateInit(strm, level, MAX_WBITS); - } - internal int deflateInit2(ZStream strm, int level, int method, int windowBits, int memLevel, int strategy) - { - int noheader = 0; - // byte[] my_version=ZLIB_VERSION; - - // - // if (version == null || version[0] != my_version[0] - // || stream_size != sizeof(z_stream)) { - // return Z_VERSION_ERROR; - // } - - strm.msg = null; - - if (level == Z_DEFAULT_COMPRESSION) - level = 6; - - if (windowBits < 0) - { - // undocumented feature: suppress zlib header - noheader = 1; - windowBits = - windowBits; - } - - if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || windowBits < 9 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) - { - return Z_STREAM_ERROR; - } - - strm.dstate = (Deflate) this; - - this.noheader = noheader; - w_bits = windowBits; - w_size = 1 << w_bits; - w_mask = w_size - 1; - - hash_bits = memLevel + 7; - hash_size = 1 << hash_bits; - hash_mask = hash_size - 1; - hash_shift = ((hash_bits + MIN_MATCH - 1) / MIN_MATCH); - - window = new byte[w_size * 2]; - prev = new short[w_size]; - head = new short[hash_size]; - - lit_bufsize = 1 << (memLevel + 6); // 16K elements by default - - // We overlay pending_buf and d_buf+l_buf. This works since the average - // output size for (length,distance) codes is <= 24 bits. - pending_buf = new byte[lit_bufsize * 4]; - pending_buf_size = lit_bufsize * 4; - - d_buf = lit_bufsize; - l_buf = (1 + 2) * lit_bufsize; - - this.level = level; - - //System.out.println("level="+level); - - this.strategy = strategy; - this.method = (byte) method; - - return deflateReset(strm); - } - - internal int deflateReset(ZStream strm) - { - strm.total_in = strm.total_out = 0; - strm.msg = null; // - strm.data_type = Z_UNKNOWN; - - pending = 0; - pending_out = 0; - - if (noheader < 0) - { - noheader = 0; // was set to -1 by deflate(..., Z_FINISH); - } - status = (noheader != 0)?BUSY_STATE:INIT_STATE; - strm.adler = strm._adler.adler32(0, null, 0, 0); - - last_flush = Z_NO_FLUSH; - - tr_init(); - lm_init(); - return Z_OK; - } - - internal int deflateEnd() - { - if (status != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE) - { - return Z_STREAM_ERROR; - } - // Deallocate in reverse order of allocations: - pending_buf = null; - head = null; - prev = null; - window = null; - // free - // dstate=null; - return status == BUSY_STATE?Z_DATA_ERROR:Z_OK; - } - - internal int deflateParams(ZStream strm, int _level, int _strategy) - { - int err = Z_OK; - - if (_level == Z_DEFAULT_COMPRESSION) - { - _level = 6; - } - if (_level < 0 || _level > 9 || _strategy < 0 || _strategy > Z_HUFFMAN_ONLY) - { - return Z_STREAM_ERROR; - } - - if (config_table[level].func != config_table[_level].func && strm.total_in != 0) - { - // Flush the last buffer: - err = strm.deflate(Z_PARTIAL_FLUSH); - } - - if (level != _level) - { - level = _level; - max_lazy_match = config_table[level].max_lazy; - good_match = config_table[level].good_length; - nice_match = config_table[level].nice_length; - max_chain_length = config_table[level].max_chain; - } - strategy = _strategy; - return err; - } - - internal int deflateSetDictionary(ZStream strm, byte[] dictionary, int dictLength) - { - int length = dictLength; - int index = 0; - - if (dictionary == null || status != INIT_STATE) - return Z_STREAM_ERROR; - - strm.adler = strm._adler.adler32(strm.adler, dictionary, 0, dictLength); - - if (length < MIN_MATCH) - return Z_OK; - if (length > w_size - MIN_LOOKAHEAD) - { - length = w_size - MIN_LOOKAHEAD; - index = dictLength - length; // use the tail of the dictionary - } - Buffer.BlockCopy(dictionary, index, window, 0, length); - strstart = length; - block_start = length; - - // Insert all strings in the hash table (except for the last two bytes). - // s->lookahead stays null, so s->ins_h will be recomputed at the next - // call of fill_window. - - ins_h = window[0] & 0xff; - ins_h = (((ins_h) << hash_shift) ^ (window[1] & 0xff)) & hash_mask; - - for (int n = 0; n <= length - MIN_MATCH; n++) - { - ins_h = (((ins_h) << hash_shift) ^ (window[(n) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; - prev[n & w_mask] = head[ins_h]; - head[ins_h] = (short) n; - } - return Z_OK; - } - - internal int deflate(ZStream strm, int flush) - { - int old_flush; - - if (flush > Z_FINISH || flush < 0) - { - return Z_STREAM_ERROR; - } - - if (strm.next_out == null || (strm.next_in == null && strm.avail_in != 0) || (status == FINISH_STATE && flush != Z_FINISH)) - { - strm.msg = z_errmsg[Z_NEED_DICT - (Z_STREAM_ERROR)]; - return Z_STREAM_ERROR; - } - if (strm.avail_out == 0) - { - strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)]; - return Z_BUF_ERROR; - } - - this.strm = strm; // just in case - old_flush = last_flush; - last_flush = flush; - - // Write the zlib header - if (status == INIT_STATE) - { - int header = (Z_DEFLATED + ((w_bits - 8) << 4)) << 8; - int level_flags = ((level - 1) & 0xff) >> 1; - - if (level_flags > 3) - level_flags = 3; - header |= (level_flags << 6); - if (strstart != 0) - header |= PRESET_DICT; - header += 31 - (header % 31); - - status = BUSY_STATE; - putShortMSB(header); - - - // Save the adler32 of the preset dictionary: - if (strstart != 0) - { - putShortMSB((int) (SupportClass.URShift(strm.adler, 16))); - putShortMSB((int) (strm.adler & 0xffff)); - } - strm.adler = strm._adler.adler32(0, null, 0, 0); - } - - // Flush as much pending output as possible - if (pending != 0) - { - strm.flush_pending(); - if (strm.avail_out == 0) - { - //System.out.println(" avail_out==0"); - // Since avail_out is 0, deflate will be called again with - // more output space, but possibly with both pending and - // avail_in equal to zero. There won't be anything to do, - // but this is not an error situation so make sure we - // return OK instead of BUF_ERROR at next call of deflate: - last_flush = - 1; - return Z_OK; - } - - // Make sure there is something to do and avoid duplicate consecutive - // flushes. For repeated and useless calls with Z_FINISH, we keep - // returning Z_STREAM_END instead of Z_BUFF_ERROR. - } - else if (strm.avail_in == 0 && flush <= old_flush && flush != Z_FINISH) - { - strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)]; - return Z_BUF_ERROR; - } - - // User must not provide more input after the first FINISH: - if (status == FINISH_STATE && strm.avail_in != 0) - { - strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)]; - return Z_BUF_ERROR; - } - - // Start a new block or continue the current one. - if (strm.avail_in != 0 || lookahead != 0 || (flush != Z_NO_FLUSH && status != FINISH_STATE)) - { - int bstate = - 1; - switch (config_table[level].func) - { - - case STORED: - bstate = deflate_stored(flush); - break; - - case FAST: - bstate = deflate_fast(flush); - break; - - case SLOW: - bstate = deflate_slow(flush); - break; - - default: - break; - - } - - if (bstate == FinishStarted || bstate == FinishDone) - { - status = FINISH_STATE; - } - if (bstate == NeedMore || bstate == FinishStarted) - { - if (strm.avail_out == 0) - { - last_flush = - 1; // avoid BUF_ERROR next call, see above - } - return Z_OK; - // If flush != Z_NO_FLUSH && avail_out == 0, the next call - // of deflate should use the same flush parameter to make sure - // that the flush is complete. So we don't have to output an - // empty block here, this will be done at next call. This also - // ensures that for a very small output buffer, we emit at most - // one empty block. - } - - if (bstate == BlockDone) - { - if (flush == Z_PARTIAL_FLUSH) - { - _tr_align(); - } - else - { - // FULL_FLUSH or SYNC_FLUSH - _tr_stored_block(0, 0, false); - // For a full flush, this empty block will be recognized - // as a special marker by inflate_sync(). - if (flush == Z_FULL_FLUSH) - { - //state.head[s.hash_size-1]=0; - for (int i = 0; i < hash_size; i++) - // forget history - head[i] = 0; - } - } - strm.flush_pending(); - if (strm.avail_out == 0) - { - last_flush = - 1; // avoid BUF_ERROR at next call, see above - return Z_OK; - } - } - } - - if (flush != Z_FINISH) - return Z_OK; - if (noheader != 0) - return Z_STREAM_END; - - // Write the zlib trailer (adler32) - putShortMSB((int) (SupportClass.URShift(strm.adler, 16))); - putShortMSB((int) (strm.adler & 0xffff)); - strm.flush_pending(); - - // If avail_out is zero, the application will call deflate again - // to flush the rest. - noheader = - 1; // write the trailer only once! - return pending != 0?Z_OK:Z_STREAM_END; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/InfBlocks.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/InfBlocks.cs deleted file mode 100644 index cb258a6c..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/InfBlocks.cs +++ /dev/null @@ -1,723 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -// Copyright (c) 2006, ComponentAce -// http://www.componentace.com -// *. - -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -// Neither the name of ComponentAce nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. *. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/* -* This program is based on zlib-1.1.3, so all credit should go authors -* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) -* and contributors of zlib. -*/ -using System; -namespace ComponentAce.Compression.Libs.zlib -{ - sealed class InfBlocks - { - private const int MANY = 1440; - - // And'ing with mask[n] masks the lower n bits - private static readonly int[] inflate_mask = new int[]{0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff}; - - // Table for deflate from PKZIP's appnote.txt. - internal static readonly int[] border = new int[]{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - - private const int Z_OK = 0; - private const int Z_STREAM_END = 1; - private const int Z_NEED_DICT = 2; - private const int Z_ERRNO = - 1; - private const int Z_STREAM_ERROR = - 2; - private const int Z_DATA_ERROR = - 3; - private const int Z_MEM_ERROR = - 4; - private const int Z_BUF_ERROR = - 5; - private const int Z_VERSION_ERROR = - 6; - - private const int TYPE = 0; // get type bits (3, including end bit) - private const int LENS = 1; // get lengths for stored - private const int STORED = 2; // processing stored block - private const int TABLE = 3; // get table lengths - private const int BTREE = 4; // get bit lengths tree for a dynamic block - private const int DTREE = 5; // get length, distance trees for a dynamic block - private const int CODES = 6; // processing fixed or dynamic block - private const int DRY = 7; // output remaining window bytes - private const int DONE = 8; // finished last block, done - private const int BAD = 9; // ot a data error--stuck here - - internal int mode; // current inflate_block mode - - internal int left; // if STORED, bytes left to copy - - internal int table; // table lengths (14 bits) - internal int index; // index into blens (or border) - internal int[] blens; // bit lengths of codes - internal int[] bb = new int[1]; // bit length tree depth - internal int[] tb = new int[1]; // bit length decoding tree - - internal InfCodes codes; // if CODES, current state - - internal int last; // true if this block is the last block - - // mode independent information - internal int bitk; // bits in bit buffer - internal int bitb; // bit buffer - internal int[] hufts; // single malloc for tree space - internal byte[] window; // sliding window - internal int end; // one byte after sliding window - internal int read; // window read pointer - internal int write; // window write pointer - internal System.Object checkfn; // check function - internal long check; // check on output - - internal InfBlocks(ZStream z, System.Object checkfn, int w) - { - hufts = new int[MANY * 3]; - window = new byte[w]; - end = w; - this.checkfn = checkfn; - mode = TYPE; - reset(z, null); - } - - internal void reset(ZStream z, long[] c) - { - if (c != null) - c[0] = check; - if (mode == BTREE || mode == DTREE) - { - blens = null; - } - if (mode == CODES) - { - codes.free(z); - } - mode = TYPE; - bitk = 0; - bitb = 0; - read = write = 0; - - if (checkfn != null) - z.adler = check = z._adler.adler32(0L, null, 0, 0); - } - - internal int proc(ZStream z, int r) - { - int t; // temporary storage - int b; // bit buffer - int k; // bits in bit buffer - int p; // input data pointer - int n; // bytes available there - int q; // output window write pointer - int m; // bytes to end of window or read pointer - - // copy input/output information to locals (UPDATE macro restores) - { - p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; - } - { - q = write; m = (int) (q < read?read - q - 1:end - q); - } - - // process input based on current state - while (true) - { - switch (mode) - { - - case TYPE: - - while (k < (3)) - { - if (n != 0) - { - r = Z_OK; - } - else - { - bitb = b; bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - ; - n--; - b |= (z.next_in[p++] & 0xff) << k; - k += 8; - } - t = (int) (b & 7); - last = t & 1; - - switch (SupportClass.URShift(t, 1)) - { - - case 0: // stored - { - b = SupportClass.URShift(b, (3)); k -= (3); - } - t = k & 7; // go to byte boundary - - { - b = SupportClass.URShift(b, (t)); k -= (t); - } - mode = LENS; // get length of stored block - break; - - case 1: // fixed - { - int[] bl = new int[1]; - int[] bd = new int[1]; - int[][] tl = new int[1][]; - int[][] td = new int[1][]; - - InfTree.inflate_trees_fixed(bl, bd, tl, td, z); - codes = new InfCodes(bl[0], bd[0], tl[0], td[0], z); - } - - { - b = SupportClass.URShift(b, (3)); k -= (3); - } - - mode = CODES; - break; - - case 2: // dynamic - - { - b = SupportClass.URShift(b, (3)); k -= (3); - } - - mode = TABLE; - break; - - case 3: // illegal - - { - b = SupportClass.URShift(b, (3)); k -= (3); - } - mode = BAD; - z.msg = "invalid block type"; - r = Z_DATA_ERROR; - - bitb = b; bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - break; - - case LENS: - - while (k < (32)) - { - if (n != 0) - { - r = Z_OK; - } - else - { - bitb = b; bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - ; - n--; - b |= (z.next_in[p++] & 0xff) << k; - k += 8; - } - - if (((SupportClass.URShift((~ b), 16)) & 0xffff) != (b & 0xffff)) - { - mode = BAD; - z.msg = "invalid stored block lengths"; - r = Z_DATA_ERROR; - - bitb = b; bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - left = (b & 0xffff); - b = k = 0; // dump bits - mode = left != 0?STORED:(last != 0?DRY:TYPE); - break; - - case STORED: - if (n == 0) - { - bitb = b; bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - - if (m == 0) - { - if (q == end && read != 0) - { - q = 0; m = (int) (q < read?read - q - 1:end - q); - } - if (m == 0) - { - write = q; - r = inflate_flush(z, r); - q = write; m = (int) (q < read?read - q - 1:end - q); - if (q == end && read != 0) - { - q = 0; m = (int) (q < read?read - q - 1:end - q); - } - if (m == 0) - { - bitb = b; bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - } - } - r = Z_OK; - - t = left; - if (t > n) - t = n; - if (t > m) - t = m; - Buffer.BlockCopy(z.next_in, p, window, q, t); - p += t; n -= t; - q += t; m -= t; - if ((left -= t) != 0) - break; - mode = last != 0?DRY:TYPE; - break; - - case TABLE: - - while (k < (14)) - { - if (n != 0) - { - r = Z_OK; - } - else - { - bitb = b; bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - ; - n--; - b |= (z.next_in[p++] & 0xff) << k; - k += 8; - } - - table = t = (b & 0x3fff); - if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) - { - mode = BAD; - z.msg = "too many length or distance symbols"; - r = Z_DATA_ERROR; - - bitb = b; bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); - blens = new int[t]; - - { - b = SupportClass.URShift(b, (14)); k -= (14); - } - - index = 0; - mode = BTREE; - goto case BTREE; - - case BTREE: - while (index < 4 + (SupportClass.URShift(table, 10))) - { - while (k < (3)) - { - if (n != 0) - { - r = Z_OK; - } - else - { - bitb = b; bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - ; - n--; - b |= (z.next_in[p++] & 0xff) << k; - k += 8; - } - - blens[border[index++]] = b & 7; - - { - b = SupportClass.URShift(b, (3)); k -= (3); - } - } - - while (index < 19) - { - blens[border[index++]] = 0; - } - - bb[0] = 7; - t = InfTree.inflate_trees_bits(blens, bb, tb, hufts, z); - if (t != Z_OK) - { - r = t; - if (r == Z_DATA_ERROR) - { - blens = null; - mode = BAD; - } - - bitb = b; bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - - index = 0; - mode = DTREE; - goto case DTREE; - - case DTREE: - while (true) - { - t = table; - if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) - { - break; - } - - - int i, j, c; - - t = bb[0]; - - while (k < (t)) - { - if (n != 0) - { - r = Z_OK; - } - else - { - bitb = b; bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - ; - n--; - b |= (z.next_in[p++] & 0xff) << k; - k += 8; - } - - if (tb[0] == - 1) - { - //System.err.println("null..."); - } - - t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; - c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; - - if (c < 16) - { - b = SupportClass.URShift(b, (t)); k -= (t); - blens[index++] = c; - } - else - { - // c == 16..18 - i = c == 18?7:c - 14; - j = c == 18?11:3; - - while (k < (t + i)) - { - if (n != 0) - { - r = Z_OK; - } - else - { - bitb = b; bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - ; - n--; - b |= (z.next_in[p++] & 0xff) << k; - k += 8; - } - - b = SupportClass.URShift(b, (t)); k -= (t); - - j += (b & inflate_mask[i]); - - b = SupportClass.URShift(b, (i)); k -= (i); - - i = index; - t = table; - if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || (c == 16 && i < 1)) - { - blens = null; - mode = BAD; - z.msg = "invalid bit length repeat"; - r = Z_DATA_ERROR; - - bitb = b; bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - - c = c == 16?blens[i - 1]:0; - do - { - blens[i++] = c; - } - while (--j != 0); - index = i; - } - } - - tb[0] = - 1; - { - int[] bl = new int[1]; - int[] bd = new int[1]; - int[] tl = new int[1]; - int[] td = new int[1]; - - - bl[0] = 9; // must be <= 9 for lookahead assumptions - bd[0] = 6; // must be <= 9 for lookahead assumptions - t = table; - t = InfTree.inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), blens, bl, bd, tl, td, hufts, z); - if (t != Z_OK) - { - if (t == Z_DATA_ERROR) - { - blens = null; - mode = BAD; - } - r = t; - - bitb = b; bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - - codes = new InfCodes(bl[0], bd[0], hufts, tl[0], hufts, td[0], z); - } - blens = null; - mode = CODES; - goto case CODES; - - case CODES: - bitb = b; bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - - if ((r = codes.proc(this, z, r)) != Z_STREAM_END) - { - return inflate_flush(z, r); - } - r = Z_OK; - codes.free(z); - - p = z.next_in_index; n = z.avail_in; b = bitb; k = bitk; - q = write; m = (int) (q < read?read - q - 1:end - q); - - if (last == 0) - { - mode = TYPE; - break; - } - mode = DRY; - goto case DRY; - - case DRY: - write = q; - r = inflate_flush(z, r); - q = write; m = (int) (q < read?read - q - 1:end - q); - if (read != write) - { - bitb = b; bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - } - mode = DONE; - goto case DONE; - - case DONE: - r = Z_STREAM_END; - - bitb = b; bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - - case BAD: - r = Z_DATA_ERROR; - - bitb = b; bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - - - default: - r = Z_STREAM_ERROR; - - bitb = b; bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - write = q; - return inflate_flush(z, r); - - } - } - } - - internal void free(ZStream z) - { - reset(z, null); - window = null; - hufts = null; - //ZFREE(z, s); - } - - internal void set_dictionary(byte[] d, int start, int n) - { - Buffer.BlockCopy(d, start, window, 0, n); - read = write = n; - } - - // Returns true if inflate is currently at the end of a block generated - // by Z_SYNC_FLUSH or Z_FULL_FLUSH. - internal int sync_point() - { - return mode == LENS?1:0; - } - - // copy as much as possible from the sliding window to the output area - internal int inflate_flush(ZStream z, int r) - { - int n; - int p; - int q; - - // local copies of source and destination pointers - p = z.next_out_index; - q = read; - - // compute number of bytes to copy as far as end of window - n = (int) ((q <= write?write:end) - q); - if (n > z.avail_out) - n = z.avail_out; - if (n != 0 && r == Z_BUF_ERROR) - r = Z_OK; - - // update counters - z.avail_out -= n; - z.total_out += n; - - // update check information - if (checkfn != null) - z.adler = check = z._adler.adler32(check, window, q, n); - - // copy as far as end of window - Buffer.BlockCopy(window, q, z.next_out, p, n); - p += n; - q += n; - - // see if more to copy at beginning of window - if (q == end) - { - // wrap pointers - q = 0; - if (write == end) - write = 0; - - // compute bytes to copy - n = write - q; - if (n > z.avail_out) - n = z.avail_out; - if (n != 0 && r == Z_BUF_ERROR) - r = Z_OK; - - // update counters - z.avail_out -= n; - z.total_out += n; - - // update check information - if (checkfn != null) - z.adler = check = z._adler.adler32(check, window, q, n); - - // copy - Buffer.BlockCopy(window, q, z.next_out, p, n); - p += n; - q += n; - } - - // update pointers - z.next_out_index = p; - read = q; - - // done - return r; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/InfCodes.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/InfCodes.cs deleted file mode 100644 index 270e8487..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/InfCodes.cs +++ /dev/null @@ -1,719 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -// Copyright (c) 2006, ComponentAce -// http://www.componentace.com -// *. - -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -// Neither the name of ComponentAce nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. *. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/* -* This program is based on zlib-1.1.3, so all credit should go authors -* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) -* and contributors of zlib. -*/ -using System; -namespace ComponentAce.Compression.Libs.zlib -{ - sealed class InfCodes - { - - private static readonly int[] inflate_mask = new int[]{0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff}; - - private const int Z_OK = 0; - private const int Z_STREAM_END = 1; - private const int Z_NEED_DICT = 2; - private const int Z_ERRNO = - 1; - private const int Z_STREAM_ERROR = - 2; - private const int Z_DATA_ERROR = - 3; - private const int Z_MEM_ERROR = - 4; - private const int Z_BUF_ERROR = - 5; - private const int Z_VERSION_ERROR = - 6; - - // waiting for "i:"=input, - // "o:"=output, - // "x:"=nothing - private const int START = 0; // x: set up for LEN - private const int LEN = 1; // i: get length/literal/eob next - private const int LENEXT = 2; // i: getting length extra (have base) - private const int DIST = 3; // i: get distance next - private const int DISTEXT = 4; // i: getting distance extra - private const int COPY = 5; // o: copying bytes in window, waiting for space - private const int LIT = 6; // o: got literal, waiting for output space - private const int WASH = 7; // o: got eob, possibly still output waiting - private const int END = 8; // x: got eob and all data flushed - private const int BADCODE = 9; // x: got error - - internal int mode; // current inflate_codes mode - - // mode dependent information - internal int len; - - internal int[] tree; // pointer into tree - internal int tree_index = 0; - internal int need; // bits needed - - internal int lit; - - // if EXT or COPY, where and how much - internal int get_Renamed; // bits to get for extra - internal int dist; // distance back to copy from - - internal byte lbits; // ltree bits decoded per branch - internal byte dbits; // dtree bits decoder per branch - internal int[] ltree; // literal/length/eob tree - internal int ltree_index; // literal/length/eob tree - internal int[] dtree; // distance tree - internal int dtree_index; // distance tree - - internal InfCodes(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index, ZStream z) - { - mode = START; - lbits = (byte) bl; - dbits = (byte) bd; - ltree = tl; - ltree_index = tl_index; - dtree = td; - dtree_index = td_index; - } - - internal InfCodes(int bl, int bd, int[] tl, int[] td, ZStream z) - { - mode = START; - lbits = (byte) bl; - dbits = (byte) bd; - ltree = tl; - ltree_index = 0; - dtree = td; - dtree_index = 0; - } - - internal int proc(InfBlocks s, ZStream z, int r) - { - int j; // temporary storage - //int[] t; // temporary pointer - int tindex; // temporary pointer - int e; // extra bits or operation - int b = 0; // bit buffer - int k = 0; // bits in bit buffer - int p = 0; // input data pointer - int n; // bytes available there - int q; // output window write pointer - int m; // bytes to end of window or read pointer - int f; // pointer to copy strings from - - // copy input/output information to locals (UPDATE macro restores) - p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; - q = s.write; m = q < s.read?s.read - q - 1:s.end - q; - - // process input and output based on current state - while (true) - { - switch (mode) - { - - // waiting for "i:"=input, "o:"=output, "x:"=nothing - case START: // x: set up for LEN - if (m >= 258 && n >= 10) - { - - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - r = inflate_fast(lbits, dbits, ltree, ltree_index, dtree, dtree_index, s, z); - - p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; - q = s.write; m = q < s.read?s.read - q - 1:s.end - q; - - if (r != Z_OK) - { - mode = r == Z_STREAM_END?WASH:BADCODE; - break; - } - } - need = lbits; - tree = ltree; - tree_index = ltree_index; - - mode = LEN; - goto case LEN; - - case LEN: // i: get length/literal/eob next - j = need; - - while (k < (j)) - { - if (n != 0) - r = Z_OK; - else - { - - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - n--; - b |= (z.next_in[p++] & 0xff) << k; - k += 8; - } - - tindex = (tree_index + (b & inflate_mask[j])) * 3; - - b = SupportClass.URShift(b, (tree[tindex + 1])); - k -= (tree[tindex + 1]); - - e = tree[tindex]; - - if (e == 0) - { - // literal - lit = tree[tindex + 2]; - mode = LIT; - break; - } - if ((e & 16) != 0) - { - // length - get_Renamed = e & 15; - len = tree[tindex + 2]; - mode = LENEXT; - break; - } - if ((e & 64) == 0) - { - // next table - need = e; - tree_index = tindex / 3 + tree[tindex + 2]; - break; - } - if ((e & 32) != 0) - { - // end of block - mode = WASH; - break; - } - mode = BADCODE; // invalid code - z.msg = "invalid literal/length code"; - r = Z_DATA_ERROR; - - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - - - case LENEXT: // i: getting length extra (have base) - j = get_Renamed; - - while (k < (j)) - { - if (n != 0) - r = Z_OK; - else - { - - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - n--; b |= (z.next_in[p++] & 0xff) << k; - k += 8; - } - - len += (b & inflate_mask[j]); - - b >>= j; - k -= j; - - need = dbits; - tree = dtree; - tree_index = dtree_index; - mode = DIST; - goto case DIST; - - case DIST: // i: get distance next - j = need; - - while (k < (j)) - { - if (n != 0) - r = Z_OK; - else - { - - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - n--; b |= (z.next_in[p++] & 0xff) << k; - k += 8; - } - - tindex = (tree_index + (b & inflate_mask[j])) * 3; - - b >>= tree[tindex + 1]; - k -= tree[tindex + 1]; - - e = (tree[tindex]); - if ((e & 16) != 0) - { - // distance - get_Renamed = e & 15; - dist = tree[tindex + 2]; - mode = DISTEXT; - break; - } - if ((e & 64) == 0) - { - // next table - need = e; - tree_index = tindex / 3 + tree[tindex + 2]; - break; - } - mode = BADCODE; // invalid code - z.msg = "invalid distance code"; - r = Z_DATA_ERROR; - - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - - - case DISTEXT: // i: getting distance extra - j = get_Renamed; - - while (k < (j)) - { - if (n != 0) - r = Z_OK; - else - { - - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - n--; b |= (z.next_in[p++] & 0xff) << k; - k += 8; - } - - dist += (b & inflate_mask[j]); - - b >>= j; - k -= j; - - mode = COPY; - goto case COPY; - - case COPY: // o: copying bytes in window, waiting for space - f = q - dist; - while (f < 0) - { - // modulo window size-"while" instead - f += s.end; // of "if" handles invalid distances - } - while (len != 0) - { - - if (m == 0) - { - if (q == s.end && s.read != 0) - { - q = 0; m = q < s.read?s.read - q - 1:s.end - q; - } - if (m == 0) - { - s.write = q; r = s.inflate_flush(z, r); - q = s.write; m = q < s.read?s.read - q - 1:s.end - q; - - if (q == s.end && s.read != 0) - { - q = 0; m = q < s.read?s.read - q - 1:s.end - q; - } - - if (m == 0) - { - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - } - } - - s.window[q++] = s.window[f++]; m--; - - if (f == s.end) - f = 0; - len--; - } - mode = START; - break; - - case LIT: // o: got literal, waiting for output space - if (m == 0) - { - if (q == s.end && s.read != 0) - { - q = 0; m = q < s.read?s.read - q - 1:s.end - q; - } - if (m == 0) - { - s.write = q; r = s.inflate_flush(z, r); - q = s.write; m = q < s.read?s.read - q - 1:s.end - q; - - if (q == s.end && s.read != 0) - { - q = 0; m = q < s.read?s.read - q - 1:s.end - q; - } - if (m == 0) - { - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - } - } - r = Z_OK; - - s.window[q++] = (byte) lit; m--; - - mode = START; - break; - - case WASH: // o: got eob, possibly more output - if (k > 7) - { - // return unused byte, if any - k -= 8; - n++; - p--; // can always return one - } - - s.write = q; r = s.inflate_flush(z, r); - q = s.write; m = q < s.read?s.read - q - 1:s.end - q; - - if (s.read != s.write) - { - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - } - mode = END; - goto case END; - - case END: - r = Z_STREAM_END; - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - - - case BADCODE: // x: got error - - r = Z_DATA_ERROR; - - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - - - default: - r = Z_STREAM_ERROR; - - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - return s.inflate_flush(z, r); - - } - } - } - - internal void free(ZStream z) - { - // ZFREE(z, c); - } - - // Called with number of bytes left to write in window at least 258 - // (the maximum string length) and number of input bytes available - // at least ten. The ten bytes are six bytes for the longest length/ - // distance pair plus four bytes for overloading the bit buffer. - - internal int inflate_fast(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index, InfBlocks s, ZStream z) - { - int t; // temporary pointer - int[] tp; // temporary pointer - int tp_index; // temporary pointer - int e; // extra bits or operation - int b; // bit buffer - int k; // bits in bit buffer - int p; // input data pointer - int n; // bytes available there - int q; // output window write pointer - int m; // bytes to end of window or read pointer - int ml; // mask for literal/length tree - int md; // mask for distance tree - int c; // bytes to copy - int d; // distance back to copy from - int r; // copy source pointer - - // load input, output, bit values - p = z.next_in_index; n = z.avail_in; b = s.bitb; k = s.bitk; - q = s.write; m = q < s.read?s.read - q - 1:s.end - q; - - // initialize masks - ml = inflate_mask[bl]; - md = inflate_mask[bd]; - - // do until not enough input or output space for fast loop - do - { - // assume called with m >= 258 && n >= 10 - // get literal/length code - while (k < (20)) - { - // max bits for literal/length code - n--; - b |= (z.next_in[p++] & 0xff) << k; k += 8; - } - - t = b & ml; - tp = tl; - tp_index = tl_index; - if ((e = tp[(tp_index + t) * 3]) == 0) - { - b >>= (tp[(tp_index + t) * 3 + 1]); k -= (tp[(tp_index + t) * 3 + 1]); - - s.window[q++] = (byte) tp[(tp_index + t) * 3 + 2]; - m--; - continue; - } - do - { - - b >>= (tp[(tp_index + t) * 3 + 1]); k -= (tp[(tp_index + t) * 3 + 1]); - - if ((e & 16) != 0) - { - e &= 15; - c = tp[(tp_index + t) * 3 + 2] + ((int) b & inflate_mask[e]); - - b >>= e; k -= e; - - // decode distance base of block to copy - while (k < (15)) - { - // max bits for distance code - n--; - b |= (z.next_in[p++] & 0xff) << k; k += 8; - } - - t = b & md; - tp = td; - tp_index = td_index; - e = tp[(tp_index + t) * 3]; - - do - { - - b >>= (tp[(tp_index + t) * 3 + 1]); k -= (tp[(tp_index + t) * 3 + 1]); - - if ((e & 16) != 0) - { - // get extra bits to add to distance base - e &= 15; - while (k < (e)) - { - // get extra bits (up to 13) - n--; - b |= (z.next_in[p++] & 0xff) << k; k += 8; - } - - d = tp[(tp_index + t) * 3 + 2] + (b & inflate_mask[e]); - - b >>= (e); k -= (e); - - // do the copy - m -= c; - if (q >= d) - { - // offset before dest - // just copy - r = q - d; - if (q - r > 0 && 2 > (q - r)) - { - s.window[q++] = s.window[r++]; c--; // minimum count is three, - s.window[q++] = s.window[r++]; c--; // so unroll loop a little - } - else - { - Buffer.BlockCopy(s.window, r, s.window, q, 2); - q += 2; r += 2; c -= 2; - } - } - else - { - // else offset after destination - r = q - d; - do - { - r += s.end; // force pointer in window - } - while (r < 0); // covers invalid distances - e = s.end - r; - if (c > e) - { - // if source crosses, - c -= e; // wrapped copy - if (q - r > 0 && e > (q - r)) - { - do - { - s.window[q++] = s.window[r++]; - } - while (--e != 0); - } - else - { - Buffer.BlockCopy(s.window, r, s.window, q, e); - q += e; r += e; e = 0; - } - r = 0; // copy rest from start of window - } - } - - // copy all or what's left - if (q - r > 0 && c > (q - r)) - { - do - { - s.window[q++] = s.window[r++]; - } - while (--c != 0); - } - else - { - Buffer.BlockCopy(s.window, r, s.window, q, c); - q += c; r += c; c = 0; - } - break; - } - else if ((e & 64) == 0) - { - t += tp[(tp_index + t) * 3 + 2]; - t += (b & inflate_mask[e]); - e = tp[(tp_index + t) * 3]; - } - else - { - z.msg = "invalid distance code"; - - c = z.avail_in - n; c = (k >> 3) < c?k >> 3:c; n += c; p -= c; k -= (c << 3); - - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - - return Z_DATA_ERROR; - } - } - while (true); - break; - } - - if ((e & 64) == 0) - { - t += tp[(tp_index + t) * 3 + 2]; - t += (b & inflate_mask[e]); - if ((e = tp[(tp_index + t) * 3]) == 0) - { - - b >>= (tp[(tp_index + t) * 3 + 1]); k -= (tp[(tp_index + t) * 3 + 1]); - - s.window[q++] = (byte) tp[(tp_index + t) * 3 + 2]; - m--; - break; - } - } - else if ((e & 32) != 0) - { - - c = z.avail_in - n; c = (k >> 3) < c?k >> 3:c; n += c; p -= c; k -= (c << 3); - - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - - return Z_STREAM_END; - } - else - { - z.msg = "invalid literal/length code"; - - c = z.avail_in - n; c = (k >> 3) < c?k >> 3:c; n += c; p -= c; k -= (c << 3); - - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - - return Z_DATA_ERROR; - } - } - while (true); - } - while (m >= 258 && n >= 10); - - // not enough input or output--restore pointers and return - c = z.avail_in - n; c = (k >> 3) < c?k >> 3:c; n += c; p -= c; k -= (c << 3); - - s.bitb = b; s.bitk = k; - z.avail_in = n; z.total_in += p - z.next_in_index; z.next_in_index = p; - s.write = q; - - return Z_OK; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/InfTree.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/InfTree.cs deleted file mode 100644 index 6b2896a5..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/InfTree.cs +++ /dev/null @@ -1,380 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -// Copyright (c) 2006, ComponentAce -// http://www.componentace.com -// *. - -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -// Neither the name of ComponentAce nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. *. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/* -* This program is based on zlib-1.1.3, so all credit should go authors -* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) -* and contributors of zlib. -*/ -using System; -namespace ComponentAce.Compression.Libs.zlib -{ - sealed class InfTree - { - - private const int MANY = 1440; - - private const int Z_OK = 0; - private const int Z_STREAM_END = 1; - private const int Z_NEED_DICT = 2; - private const int Z_ERRNO = - 1; - private const int Z_STREAM_ERROR = - 2; - private const int Z_DATA_ERROR = - 3; - private const int Z_MEM_ERROR = - 4; - private const int Z_BUF_ERROR = - 5; - private const int Z_VERSION_ERROR = - 6; - - internal const int fixed_bl = 9; - internal const int fixed_bd = 5; - - - internal static readonly int[] fixed_tl = new int[]{96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 192, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 160, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 224, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 144, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 208, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 176, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 240, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 200, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 168, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 232, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 152, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 216, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 184, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 248, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 196, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 164, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 228, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 148, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 212, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 180, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 244, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 204, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 172, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 236, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 156, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 220, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 188, 0, 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 252, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 194, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 162, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 226, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 146, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 210, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 178, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 242, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 202, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 170, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 234, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 154, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 218, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 186, - 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 250, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 198, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 166, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 230, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 150, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 214, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 182, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 246, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 206, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 174, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 238, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 158, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 222, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 190, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 254, 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 193, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 161, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 225, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 145, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 209, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 177, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 241, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 201, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 169, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 233, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 153, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 217, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 185, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 249, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 197, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 165, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 229, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 149, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 213, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 181, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 245, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 205, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 173, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 237, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 157, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 221, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 189, 0, 8, - 14, 0, 8, 142, 0, 8, 78, 0, 9, 253, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 195, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 163, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 227, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 147, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 211, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 179, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 243, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 203, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 171, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 235, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 155, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 219, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 187, 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 251, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 199, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 167, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 231, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 151, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 215, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 183, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 247, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 207, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 175, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 239, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 159, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 223, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 191, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 255}; - - internal static readonly int[] fixed_td = new int[]{80, 5, 1, 87, 5, 257, 83, 5, 17, 91, 5, 4097, 81, 5, 5, 89, 5, 1025, 85, 5, 65, 93, 5, 16385, 80, 5, 3, 88, 5, 513, 84, 5, 33, 92, 5, 8193, 82, 5, 9, 90, 5, 2049, 86, 5, 129, 192, 5, 24577, 80, 5, 2, 87, 5, 385, 83, 5, 25, 91, 5, 6145, 81, 5, 7, 89, 5, 1537, 85, 5, 97, 93, 5, 24577, 80, 5, 4, 88, 5, 769, 84, 5, 49, 92, 5, 12289, 82, 5, 13, 90, 5, 3073, 86, 5, 193, 192, 5, 24577}; - - // Tables for deflate from PKZIP's appnote.txt. - internal static readonly int[] cplens = new int[]{3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; - - internal static readonly int[] cplext = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; - - internal static readonly int[] cpdist = new int[]{1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; - - internal static readonly int[] cpdext = new int[]{0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; - - // If BMAX needs to be larger than 16, then h and x[] should be uLong. - internal const int BMAX = 15; // maximum bit length of any code - - internal static int huft_build(int[] b, int bindex, int n, int s, int[] d, int[] e, int[] t, int[] m, int[] hp, int[] hn, int[] v) - { - // Given a list of code lengths and a maximum table size, make a set of - // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR - // if the given code set is incomplete (the tables are still built in this - // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of - // lengths), or Z_MEM_ERROR if not enough memory. - - int a; // counter for codes of length k - int[] c = new int[BMAX + 1]; // bit length count table - int f; // i repeats in table every f entries - int g; // maximum code length - int h; // table level - int i; // counter, current code - int j; // counter - int k; // number of bits in current code - int l; // bits per table (returned in m) - int mask; // (1 << w) - 1, to avoid cc -O bug on HP - int p; // pointer into c[], b[], or v[] - int q; // points to current table - int[] r = new int[3]; // table entry for structure assignment - int[] u = new int[BMAX]; // table stack - int w; // bits before this table == (l * h) - int[] x = new int[BMAX + 1]; // bit offsets, then code stack - int xp; // pointer into x - int y; // number of dummy codes added - int z; // number of entries in current table - - // Generate counts for each bit length - - p = 0; i = n; - do - { - c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX - } - while (i != 0); - - if (c[0] == n) - { - // null input--all zero length codes - t[0] = - 1; - m[0] = 0; - return Z_OK; - } - - // Find minimum and maximum length, bound *m by those - l = m[0]; - for (j = 1; j <= BMAX; j++) - if (c[j] != 0) - break; - k = j; // minimum code length - if (l < j) - { - l = j; - } - for (i = BMAX; i != 0; i--) - { - if (c[i] != 0) - break; - } - g = i; // maximum code length - if (l > i) - { - l = i; - } - m[0] = l; - - // Adjust last length count to fill out codes, if needed - for (y = 1 << j; j < i; j++, y <<= 1) - { - if ((y -= c[j]) < 0) - { - return Z_DATA_ERROR; - } - } - if ((y -= c[i]) < 0) - { - return Z_DATA_ERROR; - } - c[i] += y; - - // Generate starting offsets into the value table for each length - x[1] = j = 0; - p = 1; xp = 2; - while (--i != 0) - { - // note that i == g from above - x[xp] = (j += c[p]); - xp++; - p++; - } - - // Make a table of values in order of bit lengths - i = 0; p = 0; - do - { - if ((j = b[bindex + p]) != 0) - { - v[x[j]++] = i; - } - p++; - } - while (++i < n); - n = x[g]; // set n to length of v - - // Generate the Huffman codes and for each, make the table entries - x[0] = i = 0; // first Huffman code is zero - p = 0; // grab values in bit order - h = - 1; // no tables yet--level -1 - w = - l; // bits decoded == (l * h) - u[0] = 0; // just to keep compilers happy - q = 0; // ditto - z = 0; // ditto - - // go through the bit lengths (k already is bits in shortest code) - for (; k <= g; k++) - { - a = c[k]; - while (a-- != 0) - { - // here i is the Huffman code of length k bits for value *p - // make tables up to required level - while (k > w + l) - { - h++; - w += l; // previous table always l bits - // compute minimum size table less than or equal to l bits - z = g - w; - z = (z > l)?l:z; // table size upper limit - if ((f = 1 << (j = k - w)) > a + 1) - { - // try a k-w bit table - // too few codes for k-w bit table - f -= (a + 1); // deduct codes from patterns left - xp = k; - if (j < z) - { - while (++j < z) - { - // try smaller tables up to z bits - if ((f <<= 1) <= c[++xp]) - break; // enough codes to use up j bits - f -= c[xp]; // else deduct codes from patterns - } - } - } - z = 1 << j; // table entries for j-bit table - - // allocate new table - if (hn[0] + z > MANY) - // (note: doesn't matter for fixed) - return Z_DATA_ERROR; // overflow of MANY - u[h] = q = hn[0]; // DEBUG - hn[0] += z; - - // connect to last table, if there is one - if (h != 0) - { - x[h] = i; // save pattern for backing up - r[0] = (byte) j; // bits in this table - r[1] = (byte) l; // bits to dump before this table - j = SupportClass.URShift(i, (w - l)); - r[2] = (int) (q - u[h - 1] - j); // offset to this table - Buffer.BlockCopy(r, 0, hp, (u[h - 1] + j) * 3 * sizeof(int), 3 * sizeof(int)); // connect to last table - } - else - { - t[0] = q; // first table is returned result - } - } - - // set up table entry in r - r[1] = (byte) (k - w); - if (p >= n) - { - r[0] = 128 + 64; // out of values--invalid code - } - else if (v[p] < s) - { - r[0] = (byte) (v[p] < 256?0:32 + 64); // 256 is end-of-block - r[2] = v[p++]; // simple code is just the value - } - else - { - r[0] = (byte) (e[v[p] - s] + 16 + 64); // non-simple--look up in lists - r[2] = d[v[p++] - s]; - } - - // fill code-like entries with r - f = 1 << (k - w); - for (j = SupportClass.URShift(i, w); j < z; j += f) - { - Buffer.BlockCopy(r, 0, hp, (q + j) * 3 * sizeof(int), 3 * sizeof(int)); - } - - // backwards increment the k-bit code i - for (j = 1 << (k - 1); (i & j) != 0; j = SupportClass.URShift(j, 1)) - { - i ^= j; - } - i ^= j; - - // backup over finished tables - mask = (1 << w) - 1; // needed on HP, cc -O bug - while ((i & mask) != x[h]) - { - h--; // don't need to update q - w -= l; - mask = (1 << w) - 1; - } - } - } - // Return Z_BUF_ERROR if we were given an incomplete table - return y != 0 && g != 1?Z_BUF_ERROR:Z_OK; - } - - internal static int inflate_trees_bits(int[] c, int[] bb, int[] tb, int[] hp, ZStream z) - { - int r; - int[] hn = new int[1]; // hufts used in space - int[] v = new int[19]; // work area for huft_build - - r = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); - - if (r == Z_DATA_ERROR) - { - z.msg = "oversubscribed dynamic bit lengths tree"; - } - else if (r == Z_BUF_ERROR || bb[0] == 0) - { - z.msg = "incomplete dynamic bit lengths tree"; - r = Z_DATA_ERROR; - } - return r; - } - - internal static int inflate_trees_dynamic(int nl, int nd, int[] c, int[] bl, int[] bd, int[] tl, int[] td, int[] hp, ZStream z) - { - int r; - int[] hn = new int[1]; // hufts used in space - int[] v = new int[288]; // work area for huft_build - - // build literal/length tree - r = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); - if (r != Z_OK || bl[0] == 0) - { - if (r == Z_DATA_ERROR) - { - z.msg = "oversubscribed literal/length tree"; - } - else if (r != Z_MEM_ERROR) - { - z.msg = "incomplete literal/length tree"; - r = Z_DATA_ERROR; - } - return r; - } - - // build distance tree - r = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); - - if (r != Z_OK || (bd[0] == 0 && nl > 257)) - { - if (r == Z_DATA_ERROR) - { - z.msg = "oversubscribed distance tree"; - } - else if (r == Z_BUF_ERROR) - { - z.msg = "incomplete distance tree"; - r = Z_DATA_ERROR; - } - else if (r != Z_MEM_ERROR) - { - z.msg = "empty distance tree with lengths"; - r = Z_DATA_ERROR; - } - return r; - } - - return Z_OK; - } - - internal static int inflate_trees_fixed(int[] bl, int[] bd, int[][] tl, int[][] td, ZStream z) - { - bl[0] = fixed_bl; - bd[0] = fixed_bd; - tl[0] = fixed_tl; - td[0] = fixed_td; - return Z_OK; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Inflate.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Inflate.cs deleted file mode 100644 index 911e7efe..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Inflate.cs +++ /dev/null @@ -1,453 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -// Copyright (c) 2006, ComponentAce -// http://www.componentace.com -// *. - -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -// Neither the name of ComponentAce nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. *. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/* -* This program is based on zlib-1.1.3, so all credit should go authors -* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) -* and contributors of zlib. -*/ -using System; -namespace ComponentAce.Compression.Libs.zlib -{ - sealed class Inflate - { - - private const int MAX_WBITS = 15; // 32K LZ77 window - - // preset dictionary flag in zlib header - private const int PRESET_DICT = 0x20; - - internal const int Z_NO_FLUSH = 0; - internal const int Z_PARTIAL_FLUSH = 1; - internal const int Z_SYNC_FLUSH = 2; - internal const int Z_FULL_FLUSH = 3; - internal const int Z_FINISH = 4; - - private const int Z_DEFLATED = 8; - - private const int Z_OK = 0; - private const int Z_STREAM_END = 1; - private const int Z_NEED_DICT = 2; - private const int Z_ERRNO = - 1; - private const int Z_STREAM_ERROR = - 2; - private const int Z_DATA_ERROR = - 3; - private const int Z_MEM_ERROR = - 4; - private const int Z_BUF_ERROR = - 5; - private const int Z_VERSION_ERROR = - 6; - - private const int METHOD = 0; // waiting for method byte - private const int FLAG = 1; // waiting for flag byte - private const int DICT4 = 2; // four dictionary check bytes to go - private const int DICT3 = 3; // three dictionary check bytes to go - private const int DICT2 = 4; // two dictionary check bytes to go - private const int DICT1 = 5; // one dictionary check byte to go - private const int DICT0 = 6; // waiting for inflateSetDictionary - private const int BLOCKS = 7; // decompressing blocks - private const int CHECK4 = 8; // four check bytes to go - private const int CHECK3 = 9; // three check bytes to go - private const int CHECK2 = 10; // two check bytes to go - private const int CHECK1 = 11; // one check byte to go - private const int DONE = 12; // finished check, done - private const int BAD = 13; // got an error--stay here - - internal int mode; // current inflate mode - - // mode dependent information - internal int method; // if FLAGS, method byte - - // if CHECK, check values to compare - internal long[] was = new long[1]; // computed check value - internal long need; // stream check value - - // if BAD, inflateSync's marker bytes count - internal int marker; - - // mode independent information - internal int nowrap; // flag for no wrapper - internal int wbits; // log2(window size) (8..15, defaults to 15) - - internal InfBlocks blocks; // current inflate_blocks state - - internal int inflateReset(ZStream z) - { - if (z == null || z.istate == null) - return Z_STREAM_ERROR; - - z.total_in = z.total_out = 0; - z.msg = null; - z.istate.mode = z.istate.nowrap != 0?BLOCKS:METHOD; - z.istate.blocks.reset(z, null); - return Z_OK; - } - - internal int inflateEnd(ZStream z) - { - if (blocks != null) - blocks.free(z); - blocks = null; - // ZFREE(z, z->state); - return Z_OK; - } - - internal int inflateInit(ZStream z, int w) - { - z.msg = null; - blocks = null; - - // handle undocumented nowrap option (no zlib header or check) - nowrap = 0; - if (w < 0) - { - w = - w; - nowrap = 1; - } - - // set window size - if (w < 8 || w > 15) - { - inflateEnd(z); - return Z_STREAM_ERROR; - } - wbits = w; - - z.istate.blocks = new InfBlocks(z, z.istate.nowrap != 0?null:this, 1 << w); - - // reset state - inflateReset(z); - return Z_OK; - } - - internal int inflate(ZStream z, int f) - { - int r; - int b; - - if (z == null || z.istate == null || z.next_in == null) - return Z_STREAM_ERROR; - f = f == Z_FINISH?Z_BUF_ERROR:Z_OK; - r = Z_BUF_ERROR; - while (true) - { - //System.out.println("mode: "+z.istate.mode); - switch (z.istate.mode) - { - - case METHOD: - - if (z.avail_in == 0) - return r; r = f; - - z.avail_in--; z.total_in++; - if (((z.istate.method = z.next_in[z.next_in_index++]) & 0xf) != Z_DEFLATED) - { - z.istate.mode = BAD; - z.msg = "unknown compression method"; - z.istate.marker = 5; // can't try inflateSync - break; - } - if ((z.istate.method >> 4) + 8 > z.istate.wbits) - { - z.istate.mode = BAD; - z.msg = "invalid window size"; - z.istate.marker = 5; // can't try inflateSync - break; - } - z.istate.mode = FLAG; - goto case FLAG; - - case FLAG: - - if (z.avail_in == 0) - return r; r = f; - - z.avail_in--; z.total_in++; - b = (z.next_in[z.next_in_index++]) & 0xff; - - if ((((z.istate.method << 8) + b) % 31) != 0) - { - z.istate.mode = BAD; - z.msg = "incorrect header check"; - z.istate.marker = 5; // can't try inflateSync - break; - } - - if ((b & PRESET_DICT) == 0) - { - z.istate.mode = BLOCKS; - break; - } - z.istate.mode = DICT4; - goto case DICT4; - - case DICT4: - - if (z.avail_in == 0) - return r; r = f; - - z.avail_in--; z.total_in++; - z.istate.need = ((z.next_in[z.next_in_index++] & 0xff) << 24) & unchecked((int) 0xff000000L); - z.istate.mode = DICT3; - goto case DICT3; - - case DICT3: - - if (z.avail_in == 0) - return r; r = f; - - z.avail_in--; z.total_in++; - z.istate.need += (((z.next_in[z.next_in_index++] & 0xff) << 16) & 0xff0000L); - z.istate.mode = DICT2; - goto case DICT2; - - case DICT2: - - if (z.avail_in == 0) - return r; r = f; - - z.avail_in--; z.total_in++; - z.istate.need += (((z.next_in[z.next_in_index++] & 0xff) << 8) & 0xff00L); - z.istate.mode = DICT1; - goto case DICT1; - - case DICT1: - - if (z.avail_in == 0) - return r; r = f; - - z.avail_in--; z.total_in++; - z.istate.need += (z.next_in[z.next_in_index++] & 0xffL); - z.adler = z.istate.need; - z.istate.mode = DICT0; - return Z_NEED_DICT; - - case DICT0: - z.istate.mode = BAD; - z.msg = "need dictionary"; - z.istate.marker = 0; // can try inflateSync - return Z_STREAM_ERROR; - - case BLOCKS: - - r = z.istate.blocks.proc(z, r); - if (r == Z_DATA_ERROR) - { - z.istate.mode = BAD; - z.istate.marker = 0; // can try inflateSync - break; - } - if (r == Z_OK) - { - r = f; - } - if (r != Z_STREAM_END) - { - return r; - } - r = f; - z.istate.blocks.reset(z, z.istate.was); - if (z.istate.nowrap != 0) - { - z.istate.mode = DONE; - break; - } - z.istate.mode = CHECK4; - goto case CHECK4; - - case CHECK4: - - if (z.avail_in == 0) - return r; r = f; - - z.avail_in--; z.total_in++; - z.istate.need = ((z.next_in[z.next_in_index++] & 0xff) << 24) & unchecked((int) 0xff000000L); - z.istate.mode = CHECK3; - goto case CHECK3; - - case CHECK3: - - if (z.avail_in == 0) - return r; r = f; - - z.avail_in--; z.total_in++; - z.istate.need += (((z.next_in[z.next_in_index++] & 0xff) << 16) & 0xff0000L); - z.istate.mode = CHECK2; - goto case CHECK2; - - case CHECK2: - - if (z.avail_in == 0) - return r; r = f; - - z.avail_in--; z.total_in++; - z.istate.need += (((z.next_in[z.next_in_index++] & 0xff) << 8) & 0xff00L); - z.istate.mode = CHECK1; - goto case CHECK1; - - case CHECK1: - - if (z.avail_in == 0) - return r; r = f; - - z.avail_in--; z.total_in++; - z.istate.need += (z.next_in[z.next_in_index++] & 0xffL); - - if (((int) (z.istate.was[0])) != ((int) (z.istate.need))) - { - z.istate.mode = BAD; - z.msg = "incorrect data check"; - z.istate.marker = 5; // can't try inflateSync - break; - } - - z.istate.mode = DONE; - goto case DONE; - - case DONE: - return Z_STREAM_END; - - case BAD: - return Z_DATA_ERROR; - - default: - return Z_STREAM_ERROR; - - } - } - } - - - internal int inflateSetDictionary(ZStream z, byte[] dictionary, int dictLength) - { - int index = 0; - int length = dictLength; - if (z == null || z.istate == null || z.istate.mode != DICT0) - return Z_STREAM_ERROR; - - if (z._adler.adler32(1L, dictionary, 0, dictLength) != z.adler) - { - return Z_DATA_ERROR; - } - - z.adler = z._adler.adler32(0, null, 0, 0); - - if (length >= (1 << z.istate.wbits)) - { - length = (1 << z.istate.wbits) - 1; - index = dictLength - length; - } - z.istate.blocks.set_dictionary(dictionary, index, length); - z.istate.mode = BLOCKS; - return Z_OK; - } - - private static readonly byte[] mark = new byte[]{(byte) 0, (byte) 0, (byte) SupportClass.Identity(0xff), (byte) SupportClass.Identity(0xff)}; - - internal int inflateSync(ZStream z) - { - int n; // number of bytes to look at - int p; // pointer to bytes - int m; // number of marker bytes found in a row - long r, w; // temporaries to save total_in and total_out - - // set up - if (z == null || z.istate == null) - return Z_STREAM_ERROR; - if (z.istate.mode != BAD) - { - z.istate.mode = BAD; - z.istate.marker = 0; - } - if ((n = z.avail_in) == 0) - return Z_BUF_ERROR; - p = z.next_in_index; - m = z.istate.marker; - - // search - while (n != 0 && m < 4) - { - if (z.next_in[p] == mark[m]) - { - m++; - } - else if (z.next_in[p] != 0) - { - m = 0; - } - else - { - m = 4 - m; - } - p++; n--; - } - - // restore - z.total_in += p - z.next_in_index; - z.next_in_index = p; - z.avail_in = n; - z.istate.marker = m; - - // return no joy or set up to restart on a new block - if (m != 4) - { - return Z_DATA_ERROR; - } - r = z.total_in; w = z.total_out; - inflateReset(z); - z.total_in = r; z.total_out = w; - z.istate.mode = BLOCKS; - return Z_OK; - } - - // Returns true if inflate is currently at the end of a block generated - // by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP - // implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH - // but removes the length bytes of the resulting empty stored block. When - // decompressing, PPP checks that at the end of input packet, inflate is - // waiting for these length bytes. - internal int inflateSyncPoint(ZStream z) - { - if (z == null || z.istate == null || z.istate.blocks == null) - return Z_STREAM_ERROR; - return z.istate.blocks.sync_point(); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/StaticTree.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/StaticTree.cs deleted file mode 100644 index 12fde716..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/StaticTree.cs +++ /dev/null @@ -1,90 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -// Copyright (c) 2006, ComponentAce -// http://www.componentace.com -// *. - -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -// Neither the name of ComponentAce nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. *. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/* -* This program is based on zlib-1.1.3, so all credit should go authors -* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) -* and contributors of zlib. -*/ -using System; -namespace ComponentAce.Compression.Libs.zlib -{ - sealed class StaticTree - { - private const int MAX_BITS = 15; - - private const int BL_CODES = 19; - private const int D_CODES = 30; - private const int LITERALS = 256; - private const int LENGTH_CODES = 29; - private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); - - // Bit length codes must not exceed MAX_BL_BITS bits - internal const int MAX_BL_BITS = 7; - - internal static readonly short[] static_ltree = new short[]{12, 8, 140, 8, 76, 8, 204, 8, 44, 8, 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, 230, 8, 22, 8, 150, 8, 86, 8, 214, 8, 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, 511, 9, 0, 7, 64, 7 - , 32, 7, 96, 7, 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, 163, 8, 99, 8, 227, 8}; - - internal static readonly short[] static_dtree = new short[]{0, 5, 16, 5, 8, 5, 24, 5, 4, 5, 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, 19, 5, 11, 5, 27, 5, 7, 5, 23, 5}; - - internal static readonly StaticTree static_l_desc = new StaticTree(static_ltree, Tree.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); - - internal static readonly StaticTree static_d_desc = new StaticTree(static_dtree, Tree.extra_dbits, 0, D_CODES, MAX_BITS); - - internal static readonly StaticTree static_bl_desc = new StaticTree(null, Tree.extra_blbits, 0, BL_CODES, MAX_BL_BITS); - - internal short[] static_tree; // static tree or null - internal int[] extra_bits; // extra bits for each code or null - internal int extra_base; // base index for extra_bits - internal int elems; // max number of elements in the tree - internal int max_length; // max bit length for the codes - - internal StaticTree(short[] static_tree, int[] extra_bits, int extra_base, int elems, int max_length) - { - this.static_tree = static_tree; - this.extra_bits = extra_bits; - this.extra_base = extra_base; - this.elems = elems; - this.max_length = max_length; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/SupportClass.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/SupportClass.cs deleted file mode 100644 index 2460be40..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/SupportClass.cs +++ /dev/null @@ -1,176 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -using System; - -namespace ComponentAce.Compression.Libs.zlib -{ -#if EXPOSE_ZLIB - public -#endif - class SupportClass - { - /// - /// This method returns the literal value received - /// - /// The literal to return - /// The received value - public static long Identity(long literal) - { - return literal; - } - - /// - /// This method returns the literal value received - /// - /// The literal to return - /// The received value - public static ulong Identity(ulong literal) - { - return literal; - } - - /// - /// This method returns the literal value received - /// - /// The literal to return - /// The received value - public static float Identity(float literal) - { - return literal; - } - - /// - /// This method returns the literal value received - /// - /// The literal to return - /// The received value - public static double Identity(double literal) - { - return literal; - } - - /*******************************/ - /// - /// Performs an unsigned bitwise right shift with the specified number - /// - /// Number to operate on - /// Ammount of bits to shift - /// The resulting number from the shift operation - public static int URShift(int number, int bits) - { - if ( number >= 0) - return number >> bits; - else - return (number >> bits) + (2 << ~bits); - } - - /// - /// Performs an unsigned bitwise right shift with the specified number - /// - /// Number to operate on - /// Ammount of bits to shift - /// The resulting number from the shift operation - public static int URShift(int number, long bits) - { - return URShift(number, (int)bits); - } - - /// - /// Performs an unsigned bitwise right shift with the specified number - /// - /// Number to operate on - /// Ammount of bits to shift - /// The resulting number from the shift operation - public static long URShift(long number, int bits) - { - if ( number >= 0) - return number >> bits; - else - return (number >> bits) + (2L << ~bits); - } - - /// - /// Performs an unsigned bitwise right shift with the specified number - /// - /// Number to operate on - /// Ammount of bits to shift - /// The resulting number from the shift operation - public static long URShift(long number, long bits) - { - return URShift(number, (int)bits); - } - - /*******************************/ - /// Reads a number of characters from the current source Stream and writes the data to the target array at the specified index. - /// The source Stream to read from. - /// Contains the array of characteres read from the source Stream. - /// The starting index of the target array. - /// The maximum number of characters to read from the source Stream. - /// The number of characters read. The number will be less than or equal to count depending on the data available in the source Stream. Returns -1 if the end of the stream is reached. - public static System.Int32 ReadInput(System.IO.Stream sourceStream, byte[] target, int start, int count) - { - // Returns 0 bytes if not enough space in target - if (target.Length == 0) - return 0; - - byte[] receiver = new byte[target.Length]; - int bytesRead = sourceStream.Read(receiver, start, count); - - // Returns -1 if EOF - if (bytesRead == 0) - return -1; - - for(int i = start; i < start + bytesRead; i++) - target[i] = (byte)receiver[i]; - - return bytesRead; - } - - /// Reads a number of characters from the current source TextReader and writes the data to the target array at the specified index. - /// The source TextReader to read from - /// Contains the array of characteres read from the source TextReader. - /// The starting index of the target array. - /// The maximum number of characters to read from the source TextReader. - /// The number of characters read. The number will be less than or equal to count depending on the data available in the source TextReader. Returns -1 if the end of the stream is reached. - public static System.Int32 ReadInput(System.IO.TextReader sourceTextReader, byte[] target, int start, int count) - { - // Returns 0 bytes if not enough space in target - if (target.Length == 0) return 0; - - char[] charArray = new char[target.Length]; - int bytesRead = sourceTextReader.Read(charArray, start, count); - - // Returns -1 if EOF - if (bytesRead == 0) return -1; - - for(int index=start; index - /// Converts a string to an array of bytes - /// - /// The string to be converted - /// The new array of bytes - public static byte[] ToByteArray(System.String sourceString) - { - return System.Text.UTF8Encoding.UTF8.GetBytes(sourceString); - } - - /// - /// Converts an array of bytes to an array of chars - /// - /// The array of bytes to convert - /// The new array of chars - public static char[] ToCharArray(byte[] byteArray) - { - return System.Text.UTF8Encoding.UTF8.GetChars(byteArray); - } - - - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Tree.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Tree.cs deleted file mode 100644 index cfa1ba7d..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Tree.cs +++ /dev/null @@ -1,352 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -// Copyright (c) 2006, ComponentAce -// http://www.componentace.com -// *. - -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -// Neither the name of ComponentAce nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. *. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/* -* This program is based on zlib-1.1.3, so all credit should go authors -* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) -* and contributors of zlib. -*/ -using System; -namespace ComponentAce.Compression.Libs.zlib -{ - sealed class Tree - { - private const int MAX_BITS = 15; - private const int BL_CODES = 19; - private const int D_CODES = 30; - private const int LITERALS = 256; - private const int LENGTH_CODES = 29; - private const int L_CODES = (LITERALS + 1 + LENGTH_CODES); - private const int HEAP_SIZE = (2 * L_CODES + 1); - - // Bit length codes must not exceed MAX_BL_BITS bits - internal const int MAX_BL_BITS = 7; - - // end of block literal code - internal const int END_BLOCK = 256; - - // repeat previous bit length 3-6 times (2 bits of repeat count) - internal const int REP_3_6 = 16; - - // repeat a zero length 3-10 times (3 bits of repeat count) - internal const int REPZ_3_10 = 17; - - // repeat a zero length 11-138 times (7 bits of repeat count) - internal const int REPZ_11_138 = 18; - - // extra bits for each length code - internal static readonly int[] extra_lbits = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; - - // extra bits for each distance code - internal static readonly int[] extra_dbits = new int[]{0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; - - // extra bits for each bit length code - internal static readonly int[] extra_blbits = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7}; - - internal static readonly byte[] bl_order = new byte[]{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - - - // The lengths of the bit length codes are sent in order of decreasing - // probability, to avoid transmitting the lengths for unused bit - // length codes. - - internal const int Buf_size = 8 * 2; - - // see definition of array dist_code below - internal const int DIST_CODE_LEN = 512; - - internal static readonly byte[] _dist_code = new byte[]{0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29}; - - internal static readonly byte[] _length_code = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28}; - - internal static readonly int[] base_length = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0}; - - internal static readonly int[] base_dist = new int[]{0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576}; - - // Mapping from a distance to a distance code. dist is the distance - 1 and - // must not have side effects. _dist_code[256] and _dist_code[257] are never - // used. - internal static int d_code(int dist) - { - return ((dist) < 256?_dist_code[dist]:_dist_code[256 + (SupportClass.URShift((dist), 7))]); - } - - internal short[] dyn_tree; // the dynamic tree - internal int max_code; // largest code with non zero frequency - internal StaticTree stat_desc; // the corresponding static tree - - // Compute the optimal bit lengths for a tree and update the total bit length - // for the current block. - // IN assertion: the fields freq and dad are set, heap[heap_max] and - // above are the tree nodes sorted by increasing frequency. - // OUT assertions: the field len is set to the optimal bit length, the - // array bl_count contains the frequencies for each bit length. - // The length opt_len is updated; static_len is also updated if stree is - // not null. - internal void gen_bitlen(Deflate s) - { - short[] tree = dyn_tree; - short[] stree = stat_desc.static_tree; - int[] extra = stat_desc.extra_bits; - int base_Renamed = stat_desc.extra_base; - int max_length = stat_desc.max_length; - int h; // heap index - int n, m; // iterate over the tree elements - int bits; // bit length - int xbits; // extra bits - short f; // frequency - int overflow = 0; // number of elements with bit length too large - - for (bits = 0; bits <= MAX_BITS; bits++) - s.bl_count[bits] = 0; - - // In a first pass, compute the optimal bit lengths (which may - // overflow in the case of the bit length tree). - tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap - - for (h = s.heap_max + 1; h < HEAP_SIZE; h++) - { - n = s.heap[h]; - bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; - if (bits > max_length) - { - bits = max_length; overflow++; - } - tree[n * 2 + 1] = (short) bits; - // We overwrite tree[n*2+1] which is no longer needed - - if (n > max_code) - continue; // not a leaf node - - s.bl_count[bits]++; - xbits = 0; - if (n >= base_Renamed) - xbits = extra[n - base_Renamed]; - f = tree[n * 2]; - s.opt_len += f * (bits + xbits); - if (stree != null) - s.static_len += f * (stree[n * 2 + 1] + xbits); - } - if (overflow == 0) - return ; - - // This happens for example on obj2 and pic of the Calgary corpus - // Find the first bit length which could increase: - do - { - bits = max_length - 1; - while (s.bl_count[bits] == 0) - bits--; - s.bl_count[bits]--; // move one leaf down the tree - s.bl_count[bits + 1] = (short) (s.bl_count[bits + 1] + 2); // move one overflow item as its brother - s.bl_count[max_length]--; - // The brother of the overflow item also moves one step up, - // but this does not affect bl_count[max_length] - overflow -= 2; - } - while (overflow > 0); - - for (bits = max_length; bits != 0; bits--) - { - n = s.bl_count[bits]; - while (n != 0) - { - m = s.heap[--h]; - if (m > max_code) - continue; - if (tree[m * 2 + 1] != bits) - { - s.opt_len = (int) (s.opt_len + ((long) bits - (long) tree[m * 2 + 1]) * (long) tree[m * 2]); - tree[m * 2 + 1] = (short) bits; - } - n--; - } - } - } - - // Construct one Huffman tree and assigns the code bit strings and lengths. - // Update the total bit length for the current block. - // IN assertion: the field freq is set for all tree elements. - // OUT assertions: the fields len and code are set to the optimal bit length - // and corresponding code. The length opt_len is updated; static_len is - // also updated if stree is not null. The field max_code is set. - internal void build_tree(Deflate s) - { - short[] tree = dyn_tree; - short[] stree = stat_desc.static_tree; - int elems = stat_desc.elems; - int n, m; // iterate over heap elements - int max_code = - 1; // largest code with non zero frequency - int node; // new node being created - - // Construct the initial heap, with least frequent element in - // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. - // heap[0] is not used. - s.heap_len = 0; - s.heap_max = HEAP_SIZE; - - for (n = 0; n < elems; n++) - { - if (tree[n * 2] != 0) - { - s.heap[++s.heap_len] = max_code = n; - s.depth[n] = 0; - } - else - { - tree[n * 2 + 1] = 0; - } - } - - // The pkzip format requires that at least one distance code exists, - // and that at least one bit should be sent even if there is only one - // possible code. So to avoid special checks later on we force at least - // two codes of non zero frequency. - while (s.heap_len < 2) - { - node = s.heap[++s.heap_len] = (max_code < 2?++max_code:0); - tree[node * 2] = 1; - s.depth[node] = 0; - s.opt_len--; - if (stree != null) - s.static_len -= stree[node * 2 + 1]; - // node is 0 or 1 so it does not have extra bits - } - this.max_code = max_code; - - // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, - // establish sub-heaps of increasing lengths: - - for (n = s.heap_len / 2; n >= 1; n--) - s.pqdownheap(tree, n); - - // Construct the Huffman tree by repeatedly combining the least two - // frequent nodes. - - node = elems; // next internal node of the tree - do - { - // n = node of least frequency - n = s.heap[1]; - s.heap[1] = s.heap[s.heap_len--]; - s.pqdownheap(tree, 1); - m = s.heap[1]; // m = node of next least frequency - - s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency - s.heap[--s.heap_max] = m; - - // Create a new node father of n and m - tree[node * 2] = (short) (tree[n * 2] + tree[m * 2]); - s.depth[node] = (byte) (System.Math.Max((byte) s.depth[n], (byte) s.depth[m]) + 1); - tree[n * 2 + 1] = tree[m * 2 + 1] = (short) node; - - // and insert the new node in the heap - s.heap[1] = node++; - s.pqdownheap(tree, 1); - } - while (s.heap_len >= 2); - - s.heap[--s.heap_max] = s.heap[1]; - - // At this point, the fields freq and dad are set. We can now - // generate the bit lengths. - - gen_bitlen(s); - - // The field len is now set, we can generate the bit codes - gen_codes(tree, max_code, s.bl_count); - } - - // Generate the codes for a given tree and bit counts (which need not be - // optimal). - // IN assertion: the array bl_count contains the bit length statistics for - // the given tree and the field len is set for all tree elements. - // OUT assertion: the field code is set for all tree elements of non - // zero code length. - internal static void gen_codes(short[] tree, int max_code, short[] bl_count) - { - short[] next_code = new short[MAX_BITS + 1]; // next code value for each bit length - short code = 0; // running code value - int bits; // bit index - int n; // code index - - // The distribution counts are first used to generate the code values - // without bit reversal. - for (bits = 1; bits <= MAX_BITS; bits++) - { - next_code[bits] = code = (short) ((code + bl_count[bits - 1]) << 1); - } - - // Check that the bit counts in bl_count are consistent. The last code - // must be all ones. - //Assert (code + bl_count[MAX_BITS]-1 == (1< 0); - return SupportClass.URShift(res, 1); - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/ZInputStream.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/ZInputStream.cs deleted file mode 100644 index a8f99d36..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/ZInputStream.cs +++ /dev/null @@ -1,187 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -// Copyright (c) 2006, ComponentAce -// http://www.componentace.com -// *. - -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -// Neither the name of ComponentAce nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* -Copyright (c) 2001 Lapo Luchini. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS -OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/* -* This program is based on zlib-1.1.3, so all credit should go authors -* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) -* and contributors of zlib. -*/ -using System; -namespace ComponentAce.Compression.Libs.zlib -{ -#if EXPOSE_ZLIB - public -#endif - class ZInputStream : System.IO.BinaryReader - { - internal void InitBlock() - { - flush = zlibConst.Z_NO_FLUSH; - buf = new byte[bufsize]; - } - virtual public int FlushMode - { - get - { - return (flush); - } - - set - { - this.flush = value; - } - - } - /// Returns the total number of bytes input so far. - virtual public long TotalIn - { - get - { - return z.total_in; - } - - } - /// Returns the total number of bytes output so far. - virtual public long TotalOut - { - get - { - return z.total_out; - } - - } - - protected ZStream z = new ZStream(); - protected int bufsize = 512; - protected int flush; - protected byte[] buf, buf1 = new byte[1]; - protected bool compress; - - internal System.IO.Stream in_Renamed = null; - - public ZInputStream(System.IO.Stream in_Renamed):base(in_Renamed) - { - InitBlock(); - this.in_Renamed = in_Renamed; - z.inflateInit(); - compress = false; - z.next_in = buf; - z.next_in_index = 0; - z.avail_in = 0; - } - - public ZInputStream(System.IO.Stream in_Renamed, int level):base(in_Renamed) - { - InitBlock(); - this.in_Renamed = in_Renamed; - z.deflateInit(level); - compress = true; - z.next_in = buf; - z.next_in_index = 0; - z.avail_in = 0; - } - - /*public int available() throws IOException { - return inf.finished() ? 0 : 1; - }*/ - - public override int Read() - { - if (read(buf1, 0, 1) == - 1) - return (- 1); - return (buf1[0] & 0xFF); - } - - internal bool nomoreinput = false; - - public int read(byte[] b, int off, int len) - { - if (len == 0) - return (0); - int err; - z.next_out = b; - z.next_out_index = off; - z.avail_out = len; - do - { - if ((z.avail_in == 0) && (!nomoreinput)) - { - // if buffer is empty and more input is avaiable, refill it - z.next_in_index = 0; - z.avail_in = SupportClass.ReadInput(in_Renamed, buf, 0, bufsize); //(bufsize Returns the total number of bytes input so far. - virtual public long TotalIn - { - get - { - return z.total_in; - } - - } - /// Returns the total number of bytes output so far. - virtual public long TotalOut - { - get - { - return z.total_out; - } - - } - - protected internal ZStream z = new ZStream(); - protected internal int bufsize = 4096; - protected internal int flush_Renamed_Field; - protected internal byte[] buf, buf1 = new byte[1]; - protected internal bool compress; - - private System.IO.Stream out_Renamed; - - public ZOutputStream(System.IO.Stream out_Renamed):base() - { - InitBlock(); - this.out_Renamed = out_Renamed; - z.inflateInit(); - compress = false; - } - - public ZOutputStream(System.IO.Stream out_Renamed, int level):base() - { - InitBlock(); - this.out_Renamed = out_Renamed; - z.deflateInit(level); - compress = true; - } - - public void WriteByte(int b) - { - buf1[0] = (byte) b; - Write(buf1, 0, 1); - } - //UPGRADE_TODO: The differences in the Expected value of parameters for method 'WriteByte' may cause compilation errors. 'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="jlca1092_3"' - public override void WriteByte(byte b) - { - WriteByte((int) b); - } - - public override void Write(System.Byte[] b1, int off, int len) - { - if (len == 0) - return ; - int err; - byte[] b = new byte[b1.Length]; - Buffer.BlockCopy(b1, 0, b, 0, b1.Length); - z.next_in = b; - z.next_in_index = off; - z.avail_in = len; - do - { - z.next_out = buf; - z.next_out_index = 0; - z.avail_out = bufsize; - if (compress) - err = z.deflate(flush_Renamed_Field); - else - err = z.inflate(flush_Renamed_Field); - if (err != zlibConst.Z_OK && err != zlibConst.Z_STREAM_END) - throw new ZStreamException((compress?"de":"in") + "flating: " + z.msg); - out_Renamed.Write(buf, 0, bufsize - z.avail_out); - } - while (z.avail_in > 0 || z.avail_out == 0); - } - - public virtual void finish() - { - int err; - do - { - z.next_out = buf; - z.next_out_index = 0; - z.avail_out = bufsize; - if (compress) - { - err = z.deflate(zlibConst.Z_FINISH); - } - else - { - err = z.inflate(zlibConst.Z_FINISH); - } - if (err != zlibConst.Z_STREAM_END && err != zlibConst.Z_OK) - throw new ZStreamException((compress?"de":"in") + "flating: " + z.msg); - if (bufsize - z.avail_out > 0) - { - out_Renamed.Write(buf, 0, bufsize - z.avail_out); - } - } - while (z.avail_in > 0 || z.avail_out == 0); - try - { - Flush(); - } - catch - { - } - } - public virtual void end() - { - if (compress) - { - z.deflateEnd(); - } - else - { - z.inflateEnd(); - } - z.free(); - z = null; - } - public override void Close() - { - try - { - try - { - finish(); - } - catch - { - } - } - finally - { - end(); - out_Renamed.Close(); - out_Renamed = null; - } - } - - public override void Flush() - { - out_Renamed.Flush(); - } - //UPGRADE_TODO: The following method was automatically generated and it must be implemented in order to preserve the class logic. 'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="jlca1232_3"' - public override System.Int32 Read(System.Byte[] buffer, System.Int32 offset, System.Int32 count) - { - return 0; - } - //UPGRADE_TODO: The following method was automatically generated and it must be implemented in order to preserve the class logic. 'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="jlca1232_3"' - public override void SetLength(System.Int64 value) - { - } - //UPGRADE_TODO: The following method was automatically generated and it must be implemented in order to preserve the class logic. 'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="jlca1232_3"' - public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) - { - return 0; - } - //UPGRADE_TODO: The following property was automatically generated and it must be implemented in order to preserve the class logic. 'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="jlca1232_3"' - public override System.Boolean CanRead - { - get - { - return false; - } - - } - //UPGRADE_TODO: The following property was automatically generated and it must be implemented in order to preserve the class logic. 'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="jlca1232_3"' - public override System.Boolean CanSeek - { - get - { - return false; - } - - } - //UPGRADE_TODO: The following property was automatically generated and it must be implemented in order to preserve the class logic. 'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="jlca1232_3"' - public override System.Boolean CanWrite - { - get - { - return false; - } - - } - //UPGRADE_TODO: The following property was automatically generated and it must be implemented in order to preserve the class logic. 'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="jlca1232_3"' - public override System.Int64 Length - { - get - { - return 0; - } - - } - //UPGRADE_TODO: The following property was automatically generated and it must be implemented in order to preserve the class logic. 'ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="jlca1232_3"' - public override System.Int64 Position - { - get - { - return 0; - } - - set - { - } - - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/ZStream.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/ZStream.cs deleted file mode 100644 index 951470e2..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/ZStream.cs +++ /dev/null @@ -1,247 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -// Copyright (c) 2006, ComponentAce -// http://www.componentace.com -// *. - -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -// Neither the name of ComponentAce nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. *. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/* -* This program is based on zlib-1.1.3, so all credit should go authors -* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) -* and contributors of zlib. -*/ -using System; -namespace ComponentAce.Compression.Libs.zlib -{ -#if EXPOSE_ZLIB - public -#endif - sealed class ZStream - { - private const int MAX_WBITS = 15; // 32K LZ77 window - private const int DEF_WBITS = MAX_WBITS; - - private const int Z_NO_FLUSH = 0; - private const int Z_PARTIAL_FLUSH = 1; - private const int Z_SYNC_FLUSH = 2; - private const int Z_FULL_FLUSH = 3; - private const int Z_FINISH = 4; - - private const int MAX_MEM_LEVEL = 9; - - private const int Z_OK = 0; - private const int Z_STREAM_END = 1; - private const int Z_NEED_DICT = 2; - private const int Z_ERRNO = - 1; - private const int Z_STREAM_ERROR = - 2; - private const int Z_DATA_ERROR = - 3; - private const int Z_MEM_ERROR = - 4; - private const int Z_BUF_ERROR = - 5; - private const int Z_VERSION_ERROR = - 6; - - public byte[] next_in; // next input byte - public int next_in_index; - public int avail_in; // number of bytes available at next_in - public long total_in; // total nb of input bytes read so far - - public byte[] next_out; // next output byte should be put there - public int next_out_index; - public int avail_out; // remaining free space at next_out - public long total_out; // total nb of bytes output so far - - public System.String msg; - - internal Deflate dstate; - internal Inflate istate; - - internal int data_type; // best guess about the data type: ascii or binary - - public long adler; - internal Adler32 _adler = new Adler32(); - - public int inflateInit() - { - return inflateInit(DEF_WBITS); - } - - public int inflateReset() - { - if (istate == null) - return Z_STREAM_ERROR; - - return istate.inflateReset(this); - } - - public int inflateInit(int w) - { - istate = new Inflate(); - return istate.inflateInit(this, w); - } - - public int inflate(int f) - { - if (istate == null) - return Z_STREAM_ERROR; - return istate.inflate(this, f); - } - public int inflateEnd() - { - if (istate == null) - return Z_STREAM_ERROR; - int ret = istate.inflateEnd(this); - istate = null; - return ret; - } - public int inflateSync() - { - if (istate == null) - return Z_STREAM_ERROR; - return istate.inflateSync(this); - } - public int inflateSetDictionary(byte[] dictionary, int dictLength) - { - if (istate == null) - return Z_STREAM_ERROR; - return istate.inflateSetDictionary(this, dictionary, dictLength); - } - - public int deflateInit(int level) - { - return deflateInit(level, MAX_WBITS); - } - public int deflateInit(int level, int bits) - { - dstate = new Deflate(); - return dstate.deflateInit(this, level, bits); - } - public int deflate(int flush) - { - if (dstate == null) - { - return Z_STREAM_ERROR; - } - return dstate.deflate(this, flush); - } - public int deflateEnd() - { - if (dstate == null) - return Z_STREAM_ERROR; - int ret = dstate.deflateEnd(); - dstate = null; - return ret; - } - public int deflateParams(int level, int strategy) - { - if (dstate == null) - return Z_STREAM_ERROR; - return dstate.deflateParams(this, level, strategy); - } - public int deflateSetDictionary(byte[] dictionary, int dictLength) - { - if (dstate == null) - return Z_STREAM_ERROR; - return dstate.deflateSetDictionary(this, dictionary, dictLength); - } - - // Flush as much pending output as possible. All deflate() output goes - // through this function so some applications may wish to modify it - // to avoid allocating a large strm->next_out buffer and copying into it. - // (See also read_buf()). - internal void flush_pending() - { - int len = dstate.pending; - - if (len > avail_out) - len = avail_out; - if (len == 0) - return ; - - if (dstate.pending_buf.Length <= dstate.pending_out || next_out.Length <= next_out_index || dstate.pending_buf.Length < (dstate.pending_out + len) || next_out.Length < (next_out_index + len)) - { - //System.Console.Out.WriteLine(dstate.pending_buf.Length + ", " + dstate.pending_out + ", " + next_out.Length + ", " + next_out_index + ", " + len); - //System.Console.Out.WriteLine("avail_out=" + avail_out); - } - - Buffer.BlockCopy(dstate.pending_buf, dstate.pending_out, next_out, next_out_index, len); - - next_out_index += len; - dstate.pending_out += len; - total_out += len; - avail_out -= len; - dstate.pending -= len; - if (dstate.pending == 0) - { - dstate.pending_out = 0; - } - } - - // Read a new buffer from the current input stream, update the adler32 - // and total number of bytes read. All deflate() input goes through - // this function so some applications may wish to modify it to avoid - // allocating a large strm->next_in buffer and copying from it. - // (See also flush_pending()). - internal int read_buf(byte[] buf, int start, int size) - { - int len = avail_in; - - if (len > size) - len = size; - if (len == 0) - return 0; - - avail_in -= len; - - if (dstate.noheader == 0) - { - adler = _adler.adler32(adler, next_in, next_in_index, len); - } - Buffer.BlockCopy(next_in, next_in_index, buf, start, len); - next_in_index += len; - total_in += len; - return len; - } - - public void free() - { - next_in = null; - next_out = null; - msg = null; - _adler = null; - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/ZStreamException.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/ZStreamException.cs deleted file mode 100644 index 98fe2c27..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/ZStreamException.cs +++ /dev/null @@ -1,63 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -// Copyright (c) 2006, ComponentAce -// http://www.componentace.com -// *. - -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -// Neither the name of ComponentAce nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. *. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/* -* This program is based on zlib-1.1.3, so all credit should go authors -* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) -* and contributors of zlib. -*/ -using System; -namespace ComponentAce.Compression.Libs.zlib -{ -#if EXPOSE_ZLIB - public -#endif - class ZStreamException : System.IO.IOException - { - public ZStreamException():base() - { - } - public ZStreamException(System.String s):base(s) - { - } - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Zlib.cs b/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Zlib.cs deleted file mode 100644 index 88aa8bba..00000000 --- a/MuPDF.NET/Barcode/Encoders/libtiff.net/zLib/Zlib.cs +++ /dev/null @@ -1,89 +0,0 @@ -#if !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 - -// Copyright (c) 2006, ComponentAce -// http://www.componentace.com -// *. - -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -// Neither the name of ComponentAce nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* -Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. *. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, -this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in -the documentation and/or other materials provided with the distribution. - -3. The names of the authors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, -INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, -OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/* -* This program is based on zlib-1.1.3, so all credit should go authors -* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) -* and contributors of zlib. -*/ -using System; -namespace ComponentAce.Compression.Libs.zlib -{ -#if EXPOSE_ZLIB - public -#endif - sealed class zlibConst - { - private const System.String version_Renamed_Field = "1.0.2"; - public static System.String version() - { - return version_Renamed_Field; - } - - // compression levels - public const int Z_NO_COMPRESSION = 0; - public const int Z_BEST_SPEED = 1; - public const int Z_BEST_COMPRESSION = 9; - public const int Z_DEFAULT_COMPRESSION = (- 1); - - // compression strategy - public const int Z_FILTERED = 1; - public const int Z_HUFFMAN_ONLY = 2; - public const int Z_DEFAULT_STRATEGY = 0; - - public const int Z_NO_FLUSH = 0; - public const int Z_PARTIAL_FLUSH = 1; - public const int Z_SYNC_FLUSH = 2; - public const int Z_FULL_FLUSH = 3; - public const int Z_FINISH = 4; - - public const int Z_OK = 0; - public const int Z_STREAM_END = 1; - public const int Z_NEED_DICT = 2; - public const int Z_ERRNO = - 1; - public const int Z_STREAM_ERROR = - 2; - public const int Z_DATA_ERROR = - 3; - public const int Z_MEM_ERROR = - 4; - public const int Z_BUF_ERROR = - 5; - public const int Z_VERSION_ERROR = - 6; - } -} - -#endif // !PocketPC && !WindowsCE && !TARGETTING_FX_1_1 diff --git a/MuPDF.NET/Colorspace.cs b/MuPDF.NET/Colorspace.cs new file mode 100644 index 00000000..84840409 --- /dev/null +++ b/MuPDF.NET/Colorspace.cs @@ -0,0 +1,59 @@ +using System; + +namespace MuPDF.NET +{ + /// + /// Represents a PDF colorspace. + /// + public class Colorspace + { + internal mupdf.FzColorspace _native; + + /// + /// Predefined RGB colorspace. + /// + public static readonly Colorspace CsRGB = new Colorspace(ColorspaceType.RGB); + /// + /// Predefined GRAY colorspace. + /// + public static readonly Colorspace CsGRAY = new Colorspace(ColorspaceType.GRAY); + /// + /// Predefined CMYK colorspace. + /// + public static readonly Colorspace CsCMYK = new Colorspace(ColorspaceType.CMYK); + + /// + /// Initializes a new colorspace. Supported are GRAY, RGB and CMYK. + /// + public Colorspace(ColorspaceType type) + { + _native = type switch + { + ColorspaceType.RGB => mupdf.mupdf.fz_device_rgb(), + ColorspaceType.GRAY => mupdf.mupdf.fz_device_gray(), + ColorspaceType.CMYK => mupdf.mupdf.fz_device_cmyk(), + _ => throw new ArgumentException($"Unknown colorspace type: {type}") + }; + } + + internal Colorspace(mupdf.FzColorspace native) { _native = native; } + + /// + /// Number of components per pixel. + /// + public int N => mupdf.mupdf.fz_colorspace_n(_native); + /// + /// Name of the colorspace. + /// + public string Name => mupdf.mupdf.fz_colorspace_name(_native); + internal mupdf.FzColorspace Native => _native; + + internal mupdf.FzColorspace ToFzColorspace() => _native; + + /// + /// Returns a string that represents the current colorspace. + /// + public override string ToString() => $"Colorspace({Name}, n={N})"; + public static implicit operator mupdf.FzColorspace(Colorspace cs) => cs._native; + } +} diff --git a/MuPDF.NET/Constants.cs b/MuPDF.NET/Constants.cs new file mode 100644 index 00000000..fe05cdcd --- /dev/null +++ b/MuPDF.NET/Constants.cs @@ -0,0 +1,198 @@ +using System.Collections.Generic; + +namespace MuPDF.NET +{ + /// + /// Static constants mirroring PyMuPDF's global constant values. + /// + public static class Constants + { + /// Small value used for floating-point comparisons. + public const double Epsilon = 1e-5; + + /// Small value used for floating-point comparisons. + public const double FltEpsilon = 1e-5; + + /// MuPDF internal minimum infinite rectangle coordinate. + public const int FzMinInfRect = unchecked((int)0x80000000); + + /// MuPDF internal maximum infinite rectangle coordinate. + public const int FzMaxInfRect = 0x7fffff80; + + // Link kinds + public const int LINK_NONE = 0; + public const int LINK_GOTO = 1; + public const int LINK_URI = 2; + public const int LINK_LAUNCH = 3; + public const int LINK_NAMED = 4; + public const int LINK_GOTOR = 5; + + // Link flags + public const int LINK_FLAG_L_VALID = 1; + public const int LINK_FLAG_T_VALID = 2; + public const int LINK_FLAG_R_VALID = 4; + public const int LINK_FLAG_B_VALID = 8; + public const int LINK_FLAG_FIT_H = 16; + public const int LINK_FLAG_FIT_V = 32; + public const int LINK_FLAG_R_IS_ZOOM = 64; + + // Signature flags + public const int SigFlagSignaturesExist = 1; + public const int SigFlagAppendOnly = 2; + + // Stamp types + public const int STAMP_Approved = 0; + public const int STAMP_AsIs = 1; + public const int STAMP_Confidential = 2; + public const int STAMP_Departmental = 3; + public const int STAMP_Experimental = 4; + public const int STAMP_Expired = 5; + public const int STAMP_Final = 6; + public const int STAMP_ForComment = 7; + public const int STAMP_ForPublicRelease = 8; + public const int STAMP_NotApproved = 9; + public const int STAMP_NotForPublicRelease = 10; + public const int STAMP_Sold = 11; + public const int STAMP_TopSecret = 12; + public const int STAMP_Draft = 13; + + // Text alignment + public const int TEXT_ALIGN_LEFT = 0; + public const int TEXT_ALIGN_CENTER = 1; + public const int TEXT_ALIGN_RIGHT = 2; + public const int TEXT_ALIGN_JUSTIFY = 3; + + // Text font flags + public const int TEXT_FONT_SUPERSCRIPT = 1; + public const int TEXT_FONT_ITALIC = 2; + public const int TEXT_FONT_SERIFED = 4; + public const int TEXT_FONT_MONOSPACED = 8; + public const int TEXT_FONT_BOLD = 16; + + // Text output types + public const int TEXT_OUTPUT_TEXT = 0; + public const int TEXT_OUTPUT_HTML = 1; + public const int TEXT_OUTPUT_JSON = 2; + public const int TEXT_OUTPUT_XML = 3; + public const int TEXT_OUTPUT_XHTML = 4; + + // Text encoding + public const int TEXT_ENCODING_LATIN = 0; + public const int TEXT_ENCODING_GREEK = 1; + public const int TEXT_ENCODING_CYRILLIC = 2; + + // Colorspace identifiers + public const int CS_RGB = 1; + public const int CS_GRAY = 2; + public const int CS_CMYK = 3; + + // PDF Optional Content + public const int PDF_OC_ON = 0; + public const int PDF_OC_TOGGLE = 1; + public const int PDF_OC_OFF = 2; + + // PDF Blend Modes + public const string PDF_BM_Color = "Color"; + public const string PDF_BM_ColorBurn = "ColorBurn"; + public const string PDF_BM_ColorDodge = "ColorDodge"; + public const string PDF_BM_Darken = "Darken"; + public const string PDF_BM_Difference = "Difference"; + public const string PDF_BM_Exclusion = "Exclusion"; + public const string PDF_BM_HardLight = "HardLight"; + public const string PDF_BM_Hue = "Hue"; + public const string PDF_BM_Lighten = "Lighten"; + public const string PDF_BM_Luminosity = "Luminosity"; + public const string PDF_BM_Multiply = "Multiply"; + public const string PDF_BM_Normal = "Normal"; + public const string PDF_BM_Overlay = "Overlay"; + public const string PDF_BM_Saturation = "Saturation"; + public const string PDF_BM_Screen = "Screen"; + public const string PDF_BM_SoftLight = "Softlight"; + + // Error messages + internal const string MSG_BAD_ANNOT_TYPE = "bad annot type"; + internal const string MSG_BAD_APN = "bad or missing annot AP/N"; + internal const string MSG_BAD_ARG_INK_ANNOT = "arg must be seq of seq of float pairs"; + internal const string MSG_BAD_ARG_POINTS = "bad seq of points"; + internal const string MSG_BAD_BUFFER = "bad type: 'buffer'"; + internal const string MSG_BAD_COLOR_SEQ = "bad color sequence"; + internal const string MSG_BAD_DOCUMENT = "cannot open broken document"; + internal const string MSG_BAD_FILETYPE = "bad filetype"; + internal const string MSG_BAD_LOCATION = "bad location"; + internal const string MSG_BAD_OC_CONFIG = "bad config number"; + internal const string MSG_BAD_OC_LAYER = "bad layer number"; + internal const string MSG_BAD_OC_REF = "bad 'oc' reference"; + internal const string MSG_BAD_PAGEID = "bad page id"; + internal const string MSG_BAD_PAGENO = "bad page number(s)"; + internal const string MSG_BAD_PDFROOT = "PDF has no root"; + internal const string MSG_BAD_RECT = "rect is infinite or empty"; + internal const string MSG_BAD_TEXT = "bad type: 'text'"; + internal const string MSG_BAD_XREF = "bad xref"; + internal const string MSG_COLOR_COUNT_FAILED = "color count failed"; + internal const string MSG_FILE_OR_BUFFER = "need font file or buffer"; + internal const string MSG_FONT_FAILED = "cannot create font"; + internal const string MSG_IS_NO_ANNOT = "is no annotation"; + internal const string MSG_IS_NO_IMAGE = "is no image"; + internal const string MSG_IS_NO_PDF = "is no PDF"; + internal const string MSG_IS_NO_DICT = "object is no PDF dict"; + internal const string MSG_PIX_NOALPHA = "source pixmap has no alpha"; + internal const string MSG_PIXEL_OUTSIDE = "pixel(s) outside image"; + + /// The 14 standard PDF font names available in every PDF viewer. + public static readonly string[] Base14FontNames = new[] + { + "Courier", + "Courier-Oblique", + "Courier-Bold", + "Courier-BoldOblique", + "Helvetica", + "Helvetica-Oblique", + "Helvetica-Bold", + "Helvetica-BoldOblique", + "Times-Roman", + "Times-Italic", + "Times-Bold", + "Times-BoldItalic", + "Symbol", + "ZapfDingbats" + }; + + /// + /// Maps lower-case font names (including short aliases like "helv") + /// to their canonical Base-14 font name. + /// + public static readonly Dictionary Base14FontDict = new Dictionary + { + ["courier"] = "Courier", + ["courier-oblique"] = "Courier-Oblique", + ["courier-bold"] = "Courier-Bold", + ["courier-boldoblique"] = "Courier-BoldOblique", + ["helvetica"] = "Helvetica", + ["helvetica-oblique"] = "Helvetica-Oblique", + ["helvetica-bold"] = "Helvetica-Bold", + ["helvetica-boldoblique"] = "Helvetica-BoldOblique", + ["times-roman"] = "Times-Roman", + ["times-italic"] = "Times-Italic", + ["times-bold"] = "Times-Bold", + ["times-bolditalic"] = "Times-BoldItalic", + ["symbol"] = "Symbol", + ["zapfdingbats"] = "ZapfDingbats", + ["helv"] = "Helvetica", + ["heit"] = "Helvetica-Oblique", + ["hebo"] = "Helvetica-Bold", + ["hebi"] = "Helvetica-BoldOblique", + ["cour"] = "Courier", + ["coit"] = "Courier-Oblique", + ["cobo"] = "Courier-Bold", + ["cobi"] = "Courier-BoldOblique", + ["tiro"] = "Times-Roman", + ["tibo"] = "Times-Bold", + ["tiit"] = "Times-Italic", + ["tibi"] = "Times-BoldItalic", + ["symb"] = "Symbol", + ["zadb"] = "ZapfDingbats" + }; + + internal const string JM_ANNOT_ID_STEM = "fitz"; + } +} diff --git a/MuPDF.NET/DeviceWrapper.cs b/MuPDF.NET/DeviceWrapper.cs deleted file mode 100644 index a65cad84..00000000 --- a/MuPDF.NET/DeviceWrapper.cs +++ /dev/null @@ -1,59 +0,0 @@ -using mupdf; -using System; - -namespace MuPDF.NET -{ - public class DeviceWrapper : IDisposable - { - static DeviceWrapper() - { - Utils.InitApp(); - } - - internal FzDevice _nativeDevice; - - public DeviceWrapper(FzDevice device) - { - _nativeDevice = device; - } - - public DeviceWrapper(Pixmap pixmap, Rect clip) - { - FzIrect bbox = new IRect(clip).ToFzIrect(); - if (bbox.fz_is_infinite_irect() != 0) - _nativeDevice = mupdf.mupdf.fz_new_draw_device(new FzMatrix(), pixmap.ToFzPixmap()); - else - _nativeDevice = mupdf.mupdf.fz_new_draw_device_with_bbox( - new FzMatrix(), - pixmap.ToFzPixmap(), - bbox - ); - } - - public DeviceWrapper(FzDisplayList dl) - { - _nativeDevice = dl.fz_new_list_device(); - } - - public DeviceWrapper(TextPage stpage, int flags) - { - FzStextOptions opts = new FzStextOptions(flags); - _nativeDevice = stpage._nativeTextPage.fz_new_stext_device(opts); - } - - public FzDevice ToFzDevice() - { - return _nativeDevice; - } - - public void Dispose() - { - if (_nativeDevice != null) - { - _nativeDevice.fz_close_device(); - _nativeDevice.Dispose(); - _nativeDevice = null; - } - } - } -} diff --git a/MuPDF.NET/DisplayList.cs b/MuPDF.NET/DisplayList.cs index 45719801..735a16cf 100644 --- a/MuPDF.NET/DisplayList.cs +++ b/MuPDF.NET/DisplayList.cs @@ -1,151 +1,135 @@ -using mupdf; using System; +using System.Collections.Generic; namespace MuPDF.NET { + /// + /// Represents a display list (recorded drawing operations for a page). + /// public class DisplayList : IDisposable { + private mupdf.FzDisplayList _nativeDl; + private bool _disposed; - static DisplayList() - { - Utils.InitApp(); - } - - private FzDisplayList _nativeDisplayList; - private FzPage _fzPage = null; - - /// - /// Contains the display list's mediabox. - /// - public Rect Rect + internal mupdf.FzDisplayList NativeDisplayList { get { - return new Rect(_nativeDisplayList.fz_bound_display_list()); + if (_disposed) throw new ObjectDisposedException(nameof(DisplayList)); + return _nativeDl; } } - public bool ThisOwn { get; set; } + internal DisplayList(mupdf.FzDisplayList dl) + { + _nativeDl = dl; + } /// - /// + /// Initializes a new display list with the given media box. /// - /// The page's rectangle. - public DisplayList(Rect rect) + public DisplayList(Rect mediabox) { - lock (Utils.MuPDFLock) - { - _nativeDisplayList = new FzDisplayList(rect.ToFzRect()); - } - ThisOwn = true; + _nativeDl = mupdf.mupdf.fz_new_display_list(mediabox.ToFzRect()); } - public DisplayList(DisplayList displayList) + /// + /// MediaBox of the display list. + /// + public Rect Rect { - lock (Utils.MuPDFLock) + get { - _nativeDisplayList = displayList.ToFzDisplayList(); + var r = mupdf.mupdf.fz_bound_display_list(NativeDisplayList); + return new Rect(r.x0, r.y0, r.x1, r.y1); } } - public DisplayList(PdfPage pdfPage, int annots = 1) + /// + /// Generate a Pixmap from the display list. + /// + public Pixmap GetPixmap(Matrix matrix = null, Colorspace cs = null, bool alpha = false, IRect clip = null) { - lock (Utils.MuPDFLock) - { - _fzPage = new FzPage(pdfPage); - if (annots != 0) - _nativeDisplayList = mupdf.mupdf.fz_new_display_list_from_page(_fzPage); - else - _nativeDisplayList = mupdf.mupdf.fz_new_display_list_from_page_contents(_fzPage); - } + var ctm = (matrix ?? Matrix.Identity).ToFzMatrix(); + var colorspace = (cs ?? Colorspace.CsRGB).ToFzColorspace(); + var pix = mupdf.mupdf.fz_new_pixmap_from_display_list(NativeDisplayList, ctm, colorspace, alpha ? 1 : 0); + return new Pixmap(pix); } - public void Dispose() + /// + /// Generate a TextPage from the display list. + /// + public TextPage GetTextPage(int flags = 3) { - if (_nativeDisplayList != null) - { - lock (Utils.MuPDFLock) - { - _nativeDisplayList.Dispose(); - } - _nativeDisplayList = null; - ThisOwn = false; - } - - if (_fzPage != null) - { - lock (Utils.MuPDFLock) - { - _fzPage.Dispose(); - } - _fzPage = null; - } + var opts = new mupdf.fz_stext_options(); + opts.flags = flags; + var stp = new mupdf.FzStextPage(NativeDisplayList, new mupdf.FzStextOptions(opts)); + return new TextPage(stp); } /// - /// Returns native object + /// Replay the display list through a device. /// - /// FzDisplayList - public FzDisplayList ToFzDisplayList() + public void Run(mupdf.FzDevice dev, Matrix ctm, Rect area = null) { - return _nativeDisplayList; + var cookie = new mupdf.FzCookie(); + mupdf.mupdf.fz_run_display_list(NativeDisplayList, dev, ctm.ToFzMatrix(), new mupdf.FzRect(), cookie); } /// - /// Run the display list through a draw device and return a pixmap. + /// Search for a string. Returns list of Quad hit rectangles. /// - /// matrix to use. Default is the identity matrix. - /// the desired colorspace. Default is RGB. - /// determine whether or not (0, default) to include a transparency channel. - /// restrict rendering to the intersection of this area with Rect. - /// pixmap of the display list. - public Pixmap GetPixmap(Matrix matrix = null, ColorSpace colorSpace = null, int alpha = 0, Rect clip = null) + public List Search(string needle, int maxHits = 16) { - FzColorspace _colorSpace; - if (colorSpace != null) - _colorSpace = colorSpace.ToFzColorspace(); - else - _colorSpace = new FzColorspace(mupdf.FzColorspace.Fixed.Fixed_RGB); - - if (matrix == null) - matrix = new Matrix(1.0f, 1.0f); - - Pixmap val = Utils.GetPixmapFromDisplaylist(_nativeDisplayList, matrix, _colorSpace, alpha, clip, null); - ThisOwn = true; - - return val; + using var tp = GetTextPage(); + return tp.Search(needle, maxHits); } /// - /// Run the display list through a text device and return a text page. + /// Extract text from the display list. + /// + /// Options: 'text', 'html', 'xhtml', 'xml'. /// - /// - /// text page of the display list. - public TextPage GetTextPage(int flags = 3) + public string GetText(string option = "text", int flags = 0) { - FzStextOptions opts = new FzStextOptions(); - opts.flags = flags; - - FzStextPage textPage = new FzStextPage(_nativeDisplayList, opts); - TextPage ret = new TextPage(textPage); - ret.ThisOwn = true; - - return ret; + using var tp = GetTextPage(flags); + switch (option.ToLower()) + { + case "text": return tp.ExtractText(); + case "html": return tp.ExtractHtml(); + case "xhtml": return tp.ExtractXhtml(); + case "xml": return tp.ExtractXml(); + default: return tp.ExtractText(); + } } + // ─── IDisposable ──────────────────────────────────────────────── + /// - /// Run the display list through a device. The device will populate the display list with its "commands" (i.e. text extraction or image creation). The display list can later be used to "read" a page many times without having to re-interpret it from the document file. + /// Releases all resources used by the . /// - /// Device - /// Transformation matrix to apply to the display list contents. - /// Only the part visible within this area will be considered when the list is run through the device. - public void Run(DeviceWrapper dw, Matrix matrix, Rect area) + public void Dispose() { - _nativeDisplayList.fz_run_display_list( - dw.ToFzDevice(), - matrix.ToFzMatrix(), - area.ToFzRect(), - new FzCookie()); + if (!_disposed) + { + _nativeDl?.Dispose(); + _nativeDl = null; + _disposed = true; + } + GC.SuppressFinalize(this); } + + ~DisplayList() { Dispose(); } + + /// + /// Returns a string that represents the current display list. + /// + public override string ToString() => $"DisplayList({Rect})"; + + // Python/legacy compatibility aliases (mirrors _alias(DisplayList, ...)). + public Pixmap get_pixmap(Matrix matrix = null, Colorspace cs = null, bool alpha = false, IRect clip = null) + => GetPixmap(matrix, cs, alpha, clip); + public TextPage get_textpage(int flags = 3) => GetTextPage(flags); + public TextPage getTextPage(int flags = 3) => get_textpage(flags); } } diff --git a/MuPDF.NET/Document.PythonCompat.cs b/MuPDF.NET/Document.PythonCompat.cs new file mode 100644 index 00000000..e2890307 --- /dev/null +++ b/MuPDF.NET/Document.PythonCompat.cs @@ -0,0 +1,574 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace MuPDF.NET +{ + /// + /// Python-name compatibility surface for Document. + /// These wrappers preserve naming traceability to src/__init__.py:class Document. + /// + public partial class Document + { + /// Python fitz.open() / fitz.open(path) / memory open. + public static Document open() => Open(); + + public static Document open(string filename, string filetype = null, Rect rect = null, float width = 0, float height = 0, float fontsize = 11) + => Open(filename, filetype, rect, width, height, fontsize); + + public static Document open(byte[] data, string filetype = null, Rect rect = null, float width = 0, float height = 0, float fontsize = 11) + => Open(data, filetype, rect, width, height, fontsize); + + public static Document open(Stream stream, string filetype = null, Rect rect = null, float width = 0, float height = 0, float fontsize = 11) + => Open(stream, filetype, rect, width, height, fontsize); + + // Python-style wrappers. + public int page_count() => PageCount; + public int chapter_count() => ChapterCount; + public bool needs_pass() => NeedsPass; + public bool is_reflowable() => IsReflowable; + public bool is_closed() => IsClosed; + public bool is_encrypted() => IsEncrypted; + /// Python Document.init_doc() — safe to call after authenticate(); throws if the document is still encrypted. + public void init_doc() + { + if (IsEncrypted) + throw new ValueErrorException("cannot initialize - document still encrypted"); + InitDoc(); + } + public bool is_dirty() => IsDirty; + public bool is_form_pdf() => IsFormPdf; + public bool is_fast_webaccess() => IsFastWebaccess; + public bool is_pdf() => IsPdf; + public bool is_repaired() => IsRepaired; + public (int, int) last_location() => LastLocation; + public bool journal_is_enabled() => JournalIsEnabled; + public string pagelayout() => PageLayout; + public string pagemode() => PageMode; + public int permissions() => Permissions; + public string name() => Name; + public string language() => Language; + public int version_count() => VersionCount; + public int xref_length() => XrefLength; + public int pdf_catalog() => PdfCatalog; + public Dictionary markinfo() => MarkInfo; + public bool set_markinfo(Dictionary markinfo) => SetMarkInfo(markinfo); + public bool has_annots() => HasAnnots; + public bool has_links() => HasLinks; + + // Python dunder compatibility helpers. + public int __len__() => PageCount; + public bool __contains__(int page_number) => ContainsPage(page_number); + public bool __contains__((int chapter, int page) loc) => ContainsLocation(loc); + public bool contains_chapter_page(int chapter, int page_in_chapter) => ContainsChapterPage(chapter, page_in_chapter); + public bool contains_location((int chapter, int page) loc) => ContainsLocation(loc); + public string __repr__() => ToString(); + public Page __getitem__(int page_number) => GetItemPageForIndexer(page_number); + public Page __getitem__((int chapter, int page) loc) => GetItemPageForIndexer(loc.chapter, loc.page); + + /// Python with fitz.open(...) as doc: — returns self; exit closes the document. + public Document __enter__() => this; + + /// Python Document.__exit__ (exception args ignored). + public void __exit__(object exc_type = null, object exc_value = null, object traceback = null) => Close(); + + // Page loading/navigation. + public Page load_page(int page_id) => LoadPage(page_id); + public Page load_page(int chapter, int pno) => LoadPage(chapter, pno); + public IEnumerable pages(int? start = null, int? stop = null, int? step = null) => Pages(start, stop, step); + public (int chapter, int pageInChapter) next_location((int chapter, int page) loc) => NextLocation(loc); + public (int chapter, int pageInChapter) prev_location((int chapter, int page) loc) => PrevLocation(loc); + public (int chapter, int page) location_from_page_number(int pno) => LocationFromPageNumber(pno); + public int page_number_from_location((int chapter, int page) loc) => PageNumberFromLocation(loc); + public int chapter_page_count(int chapter) => ChapterPageCount(chapter); + public ulong make_bookmark((int chapter, int page) loc) => MakeBookmark(loc); + public (int chapter, int page) find_bookmark(ulong bm) => FindBookmark(bm); + + // Metadata and outlines. + public Dictionary metadata() => GetMetadata(); + public void set_metadata(Dictionary metadata) => SetMetadata(metadata); + public List<(int level, string title, int page, Dictionary link)> get_toc(bool simple = true) => GetToc(simple); + public int set_toc(IList toc, int collapse = 1) => SetToc(toc, collapse); + public void set_toc_item(int idx, Dictionary dest_dict = null, int? kind = null, int? pno = null, + string uri = null, string title = null, Point to = null, string filename = null, float zoom = 0) + => SetTocItem(idx, dest_dict, kind, pno, uri, title, to, filename, zoom); + public void del_toc_item(int idx) => DelTocItem(idx); + public Outline get_outline() => GetOutline(); + public List get_outline_xrefs() => GetOutlineXrefs(); + + // Page editing. + public Page new_page(int pno = -1, float width = 595, float height = 842) => NewPage(pno, width, height); + public void delete_page(int pno) => DeletePage(pno); + public void _delete_page(int pno) => DeletePage(pno); + public void delete_pages(params int[] pages) => DeletePages(pages); + public void delete_pages(int from_page, int to_page) => DeletePages(from_page, to_page); + public void delete_pages_by_slice(int start, int stop, int step = 1) => DeletePagesBySlice(start, stop, step); + public List load_pages_by_slice(int start, int stop, int step = 1) => LoadPagesBySlice(start, stop, step); + public void __delitem__(int page_number) + { + EnsurePdf(); + DeletePage(page_number); + } + public void __delitem__(int[] pages) + { + EnsurePdf(); + DeletePages(pages); + } + public Page insert_page(int pno = -1, string text = null, float fontsize = 11, float width = 595, float height = 842, string fontname = "helv", float[] color = null) + => InsertPage(pno, text, fontsize, width, height, fontname, color); + public void copy_page(int pno, int to = -1) => CopyPage(pno, to); + public void fullcopy_page(int pno, int to = -1) => FullcopyPage(pno, to); + public void move_page(int pno, int to = -1) => MovePage(pno, to); + public Page _newPage(int pno = -1, float width = 595, float height = 842) => NewPage(pno, width, height); + public void select(int[] pages) => Select(pages); + public Page reload_page(Page page) => ReloadPage(page); + + public byte[] tobytes(bool garbage = false, bool clean = false, bool deflate = false) => ToBytes(garbage, clean, deflate); + public byte[] convert_to_pdf(int from_page = 0, int to_page = -1, int rotate = 0) => ConvertToPdf(from_page, to_page, rotate); + public void save_incr() => SaveIncr(); + public void saveIncr() => SaveIncr(); + public bool can_save_incrementally() => CanSaveIncrementally(); + public bool authenticate(string password) => Authenticate(password); + public void ez_save(string filename, int garbage = 1, int clean = 0, int deflate = 1, + int deflate_images = 1, int deflate_fonts = 1, int incremental = 0) + => EzSave(filename, garbage, clean, deflate, deflate_images, deflate_fonts, incremental); + + // Xref/object APIs. + public int page_xref(int pno) => PageXref(pno); + public string xref_object(int xref, bool compressed = false, bool ascii = false) => XrefObject(xref, compressed, ascii); + public bool xref_is_stream(int xref = 0) => XrefIsStream(xref); + public bool xref_is_font(int xref) => XrefIsFont(xref); + public bool xref_is_image(int xref) => XrefIsImage(xref); + public bool xref_is_xobject(int xref) => XrefIsXobject(xref); + public byte[] xref_stream(int xref) => XrefStream(xref); + public byte[] xref_stream_raw(int xref) => XrefStreamRaw(xref); + public (string type, string value) xref_get_key(int xref, string key) => XrefGetKey(xref, key); + public List xref_get_keys(int xref) => XrefGetKeys(xref); + public void xref_set_key(int xref, string key, string value) => XrefSetKey(xref, key, value); + public void _deleteObject(int xref) + { + if (xref < 1 || xref >= XrefLength) + throw new ValueErrorException(Constants.MSG_BAD_XREF); + mupdf.mupdf.pdf_delete_object(NativePdfDocument, xref); + } + public int xref() => GetNewXref(); + public int get_new_xref() => GetNewXref(); + + // XML metadata. + public string xref_xml_metadata() => GetXmlMetadata(); + public string get_xml_metadata() => GetXmlMetadata(); + public void set_xml_metadata(string metadata) => SetXmlMetadata(metadata); + public void del_xml_metadata() => DelXmlMetadata(); + + // Object / stream updates. + public void update_object(int xref, string text) => UpdateObject(xref, text); + public void update_object(int xref, string text, Page page) => UpdateObject(xref, text, page); + public void update_stream(int xref, byte[] stream, bool compress = true) => UpdateStream(xref, stream, compress); + + /// Python update_stream(xref, stream, new=1, compress=1) when new and compress are passed as ints (e.g. 0/1). is ignored (unused in PyMuPDF). + public void update_stream(int xref, byte[] stream, int new_, int compress) + => UpdateStream(xref, stream, compress != 0); + public static void xref_copy(Document doc, int source, int target, List keep = null) + => Document.XrefCopy(doc, source, target, keep); + public string pdf_trailer(bool compressed = false, bool ascii = false) => PdfTrailer(compressed, ascii); + + // Embedded files. + public List embfile_names() => EmbfileNames(); + public byte[] embfile_get(string name) => EmbfileGet(name); + public byte[] embfile_get(int idx) => EmbfileGetByIndex(idx); + public byte[] embfile_get_by_index(int idx) => EmbfileGetByIndex(idx); + public byte[] _embeddedFileGet(int idx) => EmbfileGetByIndex(idx); + public int _embeddedFileIndex(object item) + { + if (item is int i) + return i; + if (item is string s) + return EmbfileIndex(s); + throw new ValueErrorException($"'{item}' not in EmbeddedFiles array."); + } + public int embfile_add(string name, byte[] buffer_, string filename = null, string ufilename = null, string desc = null) + => EmbfileAdd(name, buffer_, filename, ufilename, desc); + public int _embfile_add(string name, byte[] buffer_, string filename = null, string ufilename = null, string desc = null) + => EmbfileAdd(name, buffer_, filename, ufilename, desc); + public Dictionary embfile_info(string item) => EmbfileInfo(item); + public Dictionary embfile_info(int item) => EmbfileInfo(item); + public int _embfile_info(int idx, Dictionary infodict) + { + var info = EmbfileInfo(idx); + if (infodict != null) + { + foreach (var kv in info) + infodict[kv.Key] = kv.Value; + } + return info.TryGetValue("xref", out var xrefObj) ? Convert.ToInt32(xrefObj) : 0; + } + public void _embfile_names(List namelist) + { + if (namelist == null) return; + namelist.AddRange(EmbfileNames()); + } + public int embfile_upd(string item, byte[] buffer_ = null, string filename = null, string ufilename = null, string desc = null) + => EmbfileUpd(item, buffer_, filename, ufilename, desc); + public int embfile_upd(int item, byte[] buffer_ = null, string filename = null, string ufilename = null, string desc = null) + => EmbfileUpd(item, buffer_, filename, ufilename, desc); + public int _embfile_upd(int idx, byte[] buffer_ = null, string filename = null, string ufilename = null, string desc = null) + => EmbfileUpd(idx, buffer_, filename, ufilename, desc); + public void _embfile_del(int idx) => EmbfileDel(idx); + public void embfile_del(string name) => EmbfileDel(name); + public void embfile_del(int idx) => EmbfileDel(idx); + public int embfile_count() => EmbfileCount; + + // Page resource extraction helpers. + public List<(int xref, string ext, string type, string baseName, string name, string encoding)> get_page_fonts(int pno, bool full = false) => GetPageFonts(pno, full); + public List<(int xref, string smask, int width, int height, int bpc, string colorspace, string altCs, string name, string filter)> get_page_images(int pno, bool full = false) => GetPageImages(pno, full); + /// + /// Python-shape compatibility helper for get_page_fonts. + /// Returns list entries as object arrays so callers can compare tuple lengths: + /// full=False => 6 fields, full=True => 7 fields (last entry is referencer placeholder). + /// + public List get_page_fonts_py(int pno, bool full = false) + { + var src = GetPageFontsCore(pno, true); + var ret = new List(src.Count); + foreach (var t in src) + { + if (full) + ret.Add(new object[] { t.xref, t.ext, t.type, t.baseName, t.name, t.encoding, 0 }); + else + ret.Add(new object[] { t.xref, t.ext, t.type, t.baseName, t.name, t.encoding }); + } + return ret; + } + + /// + /// Python-shape compatibility helper for get_page_images. + /// Returns list entries as object arrays so callers can compare tuple lengths: + /// full=False => 9 fields, full=True => 10 fields (last entry is referencer placeholder). + /// + public List get_page_images_py(int pno, bool full = false) + { + var src = GetPageImagesCore(pno, true); + var ret = new List(src.Count); + foreach (var t in src) + { + if (full) + ret.Add(new object[] { t.xref, t.smask, t.width, t.height, t.bpc, t.colorspace, t.altCs, t.name, t.filter, 0 }); + else + ret.Add(new object[] { t.xref, t.smask, t.width, t.height, t.bpc, t.colorspace, t.altCs, t.name, t.filter }); + } + return ret; + } + public List<(int glyph, double width)> get_char_widths(int xref, int limit = 256, int idx = 0, Dictionary fontdict = null) + => GetCharWidths(xref, limit, idx, fontdict); + public List<(int xref, AnnotationType type, string id)> page_annot_xrefs(int n) => PageAnnotXrefs(n); + public List> get_page_xobjects(int pno) => GetPageXobjects(pno); + public (string name, string ext, string type, byte[] content) extract_font(int xref) => ExtractFont(xref); + public Dictionary extract_image(int xref) => ExtractImage(xref); + public List> get_page_labels() => GetPageLabels(); + public List> _get_page_labels() => GetPageLabels(); + public void set_page_labels(List> labels) => SetPageLabels(labels); + public void _set_page_labels(List> labels) => SetPageLabels(labels); + public List get_page_numbers(string label, bool only_one = false) => GetPageNumbers(label, only_one); + public List search_page_for(int pno, string needle, int max_hits = 16, Quad clip = null, int flags = 0, TextPage textpage = null) + => SearchPageFor(pno, needle, max_hits, clip, flags, textpage); + + public List search_page_for_rects(int pno, string needle, int max_hits = 16, Quad clip = null, int flags = 0, TextPage textpage = null) + => SearchPageForRects(pno, needle, max_hits, clip, flags, textpage); + public Pixmap get_page_pixmap(int pno, Matrix matrix = null, Colorspace cs = null, bool alpha = false, IRect clip = null) + => GetPagePixmap(pno, matrix, cs, alpha, clip); + public string get_page_text(int pno, string option = "text", int flags = 0) => GetPageText(pno, option, flags); + + // Layout and journalling. + public void layout(float width = 400, float height = 600, float fontsize = 11) => Layout(width, height, fontsize); + public void layout(Rect rect, float fontsize = 11) => Layout(rect, fontsize); + public void journal_enable() => JournalEnable(); + public Dictionary journal_can_do() + { + var state = JournalCanDo(); + return new Dictionary + { + ["undo"] = state.canUndo, + ["redo"] = state.canRedo + }; + } + public bool journal_undo() + { + JournalUndo(); + return true; + } + public bool journal_redo() + { + JournalRedo(); + return true; + } + public void journal_start_op(string name = null) => JournalStartOp(name); + public void journal_stop_op() => JournalStopOp(); + public void journal_save(string filename) => JournalSave(filename); + public void journal_load(string filename) => JournalLoad(filename); + public void journal_load(byte[] data) => JournalLoad(data); + public string journal_op_name(int step) => JournalOpName(step); + public (int rc, int steps) journal_position() => JournalPosition(); + public void repair() => Repair(); + + // Merge and layers. + public void insert_pdf(Document src, int from_page = 0, int to_page = -1, int start_at = -1, int rotate = -1, bool links = true, bool annots = true) + => InsertPdf(src, from_page, to_page, start_at, rotate, links, annots); + public void insert_file(object infile, int from_page = -1, int to_page = -1, int start_at = -1, int rotate = -1, bool links = true, bool annots = true, int show_progress = 0, int final = 1) + => InsertFile(infile, from_page, to_page, start_at, rotate, links, annots, show_progress, final); + public Dictionary> get_ocgs() => GetOcgs(); + public Dictionary get_layer(int config = -1) => GetLayer(config); + public List> get_layers() => GetLayers(); + public int get_oc(int xref) => GetOc(xref); + public Dictionary get_ocmd(int xref) => GetOcmd(xref); + public void set_oc(int xref, int oc) => SetOc(xref, oc); + public int set_ocmd(int xref = 0, List ocgs = null, string policy = null, object ve = null) => SetOcmd(xref, ocgs, policy, ve); + public Dictionary> resolve_names() => ResolveNames(); + public void add_layer(string name, string creator = null, bool on = true) => AddLayer(name, creator, on); + public int add_ocg(string name, int config = -1, int on = 1, string intent = null, string usage = null) => AddOcg(name, config, on != 0, intent, usage); + public void set_layer(int config, string basestate = null, object on = null, object off = null, object rbgroups = null, object locked = null) + => SetLayer(config, basestate, on, off, rbgroups, locked); + public void switch_layer(int config, bool as_default = false) => SwitchLayer(config, as_default); + public List> layer_ui_configs() => LayerUiConfigs(); + public void set_layer_ui_config(object number, int action = 0) => SetLayerUiConfig(number, action); + + // Cleanup and misc. + public void bake(bool annots = true, bool widgets = true) => Bake(annots, widgets); + public void scrub(bool attached_files = true, bool clean_pages = true, bool embedded_files = true, + bool hidden_text = true, bool javascript = true, bool metadata = true, bool redactions = true, int redact_images = 0, bool remove_links = true, bool reset_fields = true, bool reset_responses = true, bool thumbnails = true, bool xml_metadata = true) + => Scrub(attached_files, clean_pages, embedded_files, hidden_text, javascript, metadata, redactions, redact_images != 0, remove_links, reset_fields, reset_responses, thumbnails, xml_metadata); + public (object page_id, float x, float y) resolve_link(string uri = null, bool chapters = false) => ResolveLink(uri, chapters); + public void subset_fonts() => SubsetFonts(); + public void recolor(int components = 1) => Recolor(components); + public void rewrite_images(int quality = 0, int dpi_threshold = 0, int dpi_target = 0, bool lossy = true, bool lossless = true, bool bitonal = true, bool color = true, bool gray = true) + => RewriteImages(quality, dpi_threshold, dpi_target, lossy, lossless, bitonal, color, gray); + public bool set_language(string language) + { + SetLanguage(language); + return true; + } + public void set_need_appearances(bool value) => SetNeedAppearances(value); + public bool? need_appearances(bool? value = null) + { + if (!IsFormPdf) + return null; + var pdf = NativePdfDocument; + var form = mupdf.mupdf.pdf_dict_getp(mupdf.mupdf.pdf_trailer(pdf), "Root/AcroForm"); + var app = form.m_internal != null ? mupdf.mupdf.pdf_dict_gets(form, "NeedAppearances") : new mupdf.PdfObj(); + bool old = app.m_internal != null && mupdf.mupdf.pdf_is_bool(app) != 0; + if (value.HasValue) + SetNeedAppearances(value.Value); + return value ?? old; + } + public bool set_pagelayout(string layout) + { + SetPageLayout(layout); + return true; + } + public bool set_pagemode(string mode) + { + SetPageMode(mode); + return true; + } + public int get_sigflags() => GetSigFlags(); + public Rect page_cropbox(int pno) => PageCropBox(pno); + public void save_snapshot(string filename) => SaveSnapshot(filename); + public void save_snapshot(object filename) => SaveSnapshot(filename); + public void close() => Close(); + + // Private Python helper-name parity. + public List _delToC() + { + var xrefs = GetOutlineXrefs(); + foreach (var xref in xrefs) + RemoveTocItemByXref(xref); + return xrefs; + } + public void _remove_toc_item(int xref) => RemoveTocItemByXref(xref); + public void _update_toc_item(int xref, string action = null, string title = null, int flags = 0, bool? collapse = null, float[] color = null) + => UpdateTocItemByXref(xref, action, title, flags, collapse, color); + public string _getMetadata(string key) + { + try { return mupdf.mupdf.fz_lookup_metadata2(NativeDocument, key); } + catch { return ""; } + } + public int _getOLRootNumber() + { + var pdf = NativePdfDocument; + var root = mupdf.mupdf.pdf_dict_get(mupdf.mupdf.pdf_trailer(pdf), mupdf.mupdf.pdf_new_name("Root")); + var olroot = mupdf.mupdf.pdf_dict_get(root, mupdf.mupdf.pdf_new_name("Outlines")); + if (olroot.m_internal == null) + { + olroot = mupdf.mupdf.pdf_new_dict(pdf, 4); + mupdf.mupdf.pdf_dict_put(olroot, mupdf.mupdf.pdf_new_name("Type"), mupdf.mupdf.pdf_new_name("Outlines")); + var indObj = mupdf.mupdf.pdf_add_object(pdf, olroot); + mupdf.mupdf.pdf_dict_put(root, mupdf.mupdf.pdf_new_name("Outlines"), indObj); + olroot = mupdf.mupdf.pdf_dict_get(root, mupdf.mupdf.pdf_new_name("Outlines")); + } + return mupdf.mupdf.pdf_to_num(olroot); + } + public List _getPDFfileid() + { + var ret = new List(); + var pdf = NativePdfDocument; + var identity = mupdf.mupdf.pdf_dict_get(mupdf.mupdf.pdf_trailer(pdf), mupdf.mupdf.pdf_new_name("ID")); + if (identity.m_internal == null) return ret; + int n = mupdf.mupdf.pdf_array_len(identity); + for (int i = 0; i < n; i++) + { + var o = mupdf.mupdf.pdf_array_get(identity, i); + string text = mupdf.mupdf.pdf_to_text_string(o) ?? ""; + ret.Add(BitConverter.ToString(System.Text.Encoding.UTF8.GetBytes(text)).Replace("-", "").ToLowerInvariant()); + } + return ret; + } + public List _getPageInfo(int pno, object what) + { + if (what is string ws) + { + ws = ws.ToLowerInvariant(); + if (ws.Contains("font")) return new List(GetPageFontsCore(pno, true).ConvertAll(x => (object)x)); + if (ws.Contains("image")) return new List(GetPageImagesCore(pno, true).ConvertAll(x => (object)x)); + return new List(GetPageXobjects(pno).ConvertAll(x => (object)x)); + } + int wi = Convert.ToInt32(what); + if (wi == 1) return new List(GetPageFontsCore(pno, true).ConvertAll(x => (object)x)); + if (wi == 2) return new List(GetPageImagesCore(pno, true).ConvertAll(x => (object)x)); + return new List(GetPageXobjects(pno).ConvertAll(x => (object)x)); + } + /// + /// Python-shape compatibility overload for _getPageInfo. + /// + public List _getPageInfo_py(int pno, int what, bool full = true) + { + if (what == 1) return get_page_fonts_py(pno, full); + if (what == 2) return get_page_images_py(pno, full); + var xobjs = GetPageXobjects(pno); + var ret = new List(xobjs.Count); + foreach (var xo in xobjs) + ret.Add(new object[] { xo }); + return ret; + } + public object[] _insert_font(string fontfile = null, byte[] fontbuffer = null) + { + /* + * Utility: insert font from file or binary. + */ + var pdf = NativePdfDocument; + if (string.IsNullOrEmpty(fontfile) && (fontbuffer == null || fontbuffer.Length == 0)) + throw new ValueErrorException(Constants.MSG_FILE_OR_BUFFER); + + mupdf.FzFont font; + mupdf.PdfObj fontObj; + if (!string.IsNullOrEmpty(fontfile)) + { + font = mupdf.mupdf.fz_new_font_from_file(null, fontfile, 0, 0); + fontObj = mupdf.mupdf.pdf_add_cid_font(pdf, font); + } + else + { + var res = Helpers.BufferFromBytes(fontbuffer); + if (res.m_internal == null) + throw new ValueErrorException(Constants.MSG_FILE_OR_BUFFER); + font = mupdf.mupdf.fz_new_font_from_buffer(null, res, 0, 0); + fontObj = mupdf.mupdf.pdf_add_cid_font(pdf, font); + } + + int ixref = mupdf.mupdf.pdf_to_num(fontObj); + string name = mupdf.mupdf.pdf_to_name(mupdf.mupdf.pdf_dict_get(fontObj, mupdf.mupdf.pdf_new_name("BaseFont"))); + string subt = mupdf.mupdf.pdf_to_name(mupdf.mupdf.pdf_dict_get(fontObj, mupdf.mupdf.pdf_new_name("Subtype"))); + string exto = ""; + try { exto = ExtractFont(ixref).ext; } catch { exto = ""; } + float asc = font.fz_font_ascender(); + float dsc = font.fz_font_descender(); + var info = new Dictionary + { + ["name"] = name, + ["type"] = subt, + ["ext"] = exto, + ["simple"] = false, + ["ordering"] = -1, + ["ascender"] = asc, + ["descender"] = dsc, + }; + return new object[] { ixref, info }; + } + public Outline _loadOutline() => GetOutline(); + public void _forget_page(Page page) => ForgetPageRef(page); + public void _reset_page_refs() => ResetPageRefsInternal(); + public void _remove_links_to(object numbers) + { + var refs = new HashSet(); + if (numbers is IEnumerable ints) + { + foreach (var n in ints) refs.Add(n); + } + else if (numbers is IEnumerable objs) + { + foreach (var o in objs) refs.Add(Convert.ToInt32(o)); + } + else if (numbers is int n) + { + refs.Add(n); + } + else + { + throw new ArgumentException("bad page number(s)"); + } + Helpers.JM_remove_dest_range(NativePdfDocument, refs); + } + public void _addFormFont(string name, string font) + { + if (IsClosed || IsEncrypted) + throw new ValueErrorException("document closed or encrypted"); + var pdf = NativePdfDocument; + var fonts = Helpers.PdfDictGetl( + mupdf.mupdf.pdf_trailer(pdf), + mupdf.mupdf.pdf_new_name("Root"), + mupdf.mupdf.pdf_new_name("AcroForm"), + mupdf.mupdf.pdf_new_name("DR"), + mupdf.mupdf.pdf_new_name("Font")); + if (fonts.m_internal == null || mupdf.mupdf.pdf_is_dict(fonts) == 0) + throw new InvalidOperationException("PDF has no form fonts yet"); + var k = mupdf.mupdf.pdf_new_name(name); + var v = Helpers.JM_pdf_obj_from_str(pdf, font); + mupdf.mupdf.pdf_dict_put(fonts, k, v); + } + + // Legacy alias names from Python _alias(Document, ...). + public byte[] convertToPDF(int from_page = 0, int to_page = -1, int rotate = 0) => convert_to_pdf(from_page, to_page, rotate); + public void deletePageRange(int from_page, int to_page) => delete_pages(from_page, to_page); + public int embeddedFileAdd(string name, byte[] buffer_, string filename = null, string ufilename = null, string desc = null) => embfile_add(name, buffer_, filename, ufilename, desc); + public int embeddedFileCount() => embfile_count(); + public void embeddedFileDel(string name) => embfile_del(name); + public void embeddedFileDel(int idx) => embfile_del(idx); + public byte[] embeddedFileGet(string name) => embfile_get(name); + public byte[] embeddedFileGet(int idx) => embfile_get(idx); + public Dictionary embeddedFileInfo(string item) => embfile_info(item); + public Dictionary embeddedFileInfo(int item) => embfile_info(item); + public List embeddedFileNames() => embfile_names(); + public int embeddedFileUpd(string item, byte[] buffer_ = null, string filename = null, string ufilename = null, string desc = null) => embfile_upd(item, buffer_, filename, ufilename, desc); + public int embeddedFileUpd(int item, byte[] buffer_ = null, string filename = null, string ufilename = null, string desc = null) => embfile_upd(item, buffer_, filename, ufilename, desc); + public Dictionary> getOCGs() => get_ocgs(); + public List<(int xref, string ext, string type, string baseName, string name, string encoding)> getPageFontList(int pno, bool full = false) => get_page_fonts(pno, full); + public List<(int xref, string smask, int width, int height, int bpc, string colorspace, string altCs, string name, string filter)> getPageImageList(int pno, bool full = false) => get_page_images(pno, full); + public List> getPageXObjectList(int pno) => get_page_xobjects(pno); + public int getSigFlags() => get_sigflags(); + public List<(int level, string title, int page, Dictionary link)> getToC(bool simple = true) => get_toc(simple); + public void insertPDF(Document src, int from_page = 0, int to_page = -1, int start_at = -1, int rotate = -1, bool links = true, bool annots = true) => insert_pdf(src, from_page, to_page, start_at, rotate, links, annots); + public bool isFormPDF() => is_form_pdf(); + public bool isPDF() => is_pdf(); + public Rect pageCropBox(int pno) => page_cropbox(pno); + public int PDFCatalog() => pdf_catalog(); + public string PDFTrailer(bool compressed = false, bool ascii = false) => pdf_trailer(compressed, ascii); + public (int chapter, int pageInChapter) previousLocation((int chapter, int page) loc) => prev_location(loc); + public int setToC(IList toc, int collapse = 1) => set_toc(toc, collapse); + public bool isStream(int xref = 0) => xref_is_stream(xref); + public string metadataXML() => xref_xml_metadata(); + public void setToCItem(int idx, Dictionary dest_dict = null, int? kind = null, int? pno = null, + string uri = null, string title = null, Point to = null, string filename = null, float zoom = 0) + => set_toc_item(idx, dest_dict, kind, pno, uri, title, to, filename, zoom); + public void delToCItem(int idx) => del_toc_item(idx); + public List<(int glyph, double width)> getCharWidths(int xref, int limit = 256, int idx = 0, Dictionary fontdict = null) + => get_char_widths(xref, limit, idx, fontdict); + } +} diff --git a/MuPDF.NET/Document.cs b/MuPDF.NET/Document.cs index ce1dbe40..9f8dbdb2 100644 --- a/MuPDF.NET/Document.cs +++ b/MuPDF.NET/Document.cs @@ -1,1097 +1,1632 @@ -using mupdf; -using Newtonsoft.Json.Linq; using System; +using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; -using System.Linq.Expressions; -using System.Runtime.InteropServices; -using System.Security.Cryptography; using System.Text; +using System.Threading; namespace MuPDF.NET { - public class Document : IDisposable + /// + /// Represents a document. Mirrors PyMuPDF's Document class. + /// + public partial class Document : IDisposable, IEnumerable { - static Document() - { - Utils.InitApp(); - } + private mupdf.FzDocument? _nativeDoc; + private bool _disposed; + private int _graftId; + private static int _nextGraftId; + private static int _nextPageRefId; + private Dictionary> _resolvedNames; /// - /// False if document is still open. If closed, most other attributes and methods will have been deleted / disabled. + /// Tracks live wrappers for this document, analogous to Python + /// Document._page_refs (weakref.WeakValueDictionary keyed by id(page)). /// - public bool IsClosed { get; set; } + private readonly Dictionary> _pageRefs = new Dictionary>(); + private int _suppressPageRefReset; /// - /// True if this is a PDF document and contains unsaved changes, else False. + /// Indicate whether document has been closed. /// - public bool IsEncrypted { get; set; } - - internal int GraftID { get; set; } - - public Dictionary MetaData { get; set; } - - public List FontInfos { get; set; } - - public Dictionary GraftMaps { get; set; } = - new Dictionary(); - - public Dictionary<(int, int), int> ShownPages { get; set; } = - new Dictionary<(int, int), int>(); - - public Dictionary InsertedImages { get; set; } = new Dictionary(); + public bool IsClosed { get; private set; } - public Dictionary PageRefs { get; set; } + /// + /// Indicate whether document is still encrypted. + /// + public bool IsEncrypted { get; private set; } /// - /// Contains the filename or filetype value with which Document was created. + /// The filename or "<memory>" if created from data. /// - public string Name { get; set; } + public string Name { get; private set; } = ""; - public List Stream { get; set; } + /// + /// The binary document data if created from a byte array. + /// + public byte[] StreamData { get; private set; } - private bool _isPDF; + internal List FontInfos { get; } = new List(); + internal Dictionary Graftmaps { get; } = new Dictionary(); + internal Dictionary ShownPages { get; } = new Dictionary(); + internal Dictionary InsertedImages { get; } = new Dictionary(); - private FzDocument _nativeDocument; + internal mupdf.FzDocument NativeDocument + { + get + { + if (IsClosed || _nativeDoc == null) + throw new ValueErrorException("document closed"); + return _nativeDoc; + } + } - /// - /// Indicates whether the document is password-protected against access. - ///
- /// This indicator remains unchanged – even after the document has been authenticated. Precludes incremental saves if true. - ///
- public bool NeedsPass + internal mupdf.PdfDocument NativePdfDocument { get { - if (IsClosed) - throw new Exception("Document closed"); - FzDocument doc = _nativeDocument; - int ret = doc.fz_needs_password(); - return ret != 0 ? true : false; + var pdf = Helpers.AsPdfDocument(NativeDocument, required: true); + return pdf; } } + internal int GraftId => _graftId; + + // ─── Constructors ─────────────────────────────────────────────── + /// - /// True if this is a PDF document, else False. + /// Creates a document. Use 'open' as a synonym. + /// + /// Basic usages: + /// Document() - new PDF document + /// Document(filename) - open from file path. + /// Document(data, filetype) - open from byte array. + /// rect, width, height, fontsize: layout reflowable document on open (e.g. EPUB). /// - public Outline Outline { get; set; } + public Document() + { + var pdf = new mupdf.PdfDocument(); + _nativeDoc = new mupdf.FzDocument(pdf); + _graftId = _nextGraftId++; + Name = ""; + InitDoc(); + } /// - /// True if this is a PDF document, else False. + /// Creates a document. Use 'open' as a synonym. + /// + /// Basic usages: + /// Document() - new PDF document + /// Document(filename) - open from file path. + /// Document(data, filetype) - open from byte array. + /// rect, width, height, fontsize: layout reflowable document on open (e.g. EPUB). /// - public bool IsPDF + public Document(string filename, string filetype = null, Rect rect = null, float width = 0, float height = 0, float fontsize = 11) { - get + _graftId = _nextGraftId++; + if (!File.Exists(filename)) + throw new FileNotFoundException($"no such file: '{filename}'"); + if (Directory.Exists(filename)) + throw new FileDataException($"'{filename}' is no file"); + if (new FileInfo(filename).Length == 0) + throw new EmptyFileException($"Cannot open empty file: {filename}"); + + Name = filename; + float w = width, h = height; + if (rect != null) + { + var r = rect.ToFzRect(); + if (mupdf.mupdf.fz_is_infinite_rect(r) == 0) { w = r.x1 - r.x0; h = r.y1 - r.y0; } + } + + mupdf.FzDocument doc; + try { - if (mupdf.mupdf.ll_pdf_specifics(_nativeDocument.m_internal) != null) - return true; + if (filetype != null) + { + var stream = mupdf.mupdf.fz_open_file(filename); + doc = mupdf.mupdf.fz_open_document_with_stream(filetype, stream); + } else - return false; + doc = mupdf.mupdf.fz_open_document(filename); + } + catch (Exception e) + { + throw new FileDataException($"Failed to open file '{filename}'.", e); } - set { _isPDF = value; } - } - public bool ThisOwn { get; set; } + bool laidOut = LayoutDoc(doc, w, h, fontsize); + _nativeDoc = doc; + if (laidOut) + ResetPageRefsInternal(); + FinishOpen(filename, filetype); + } /// - /// An integer counting the number of versions present in the document. Zero if not a PDF, otherwise the number of incremental saves plus one. + /// Creates a document. Use 'open' as a synonym. + /// + /// Basic usages: + /// Document() - new PDF document + /// Document(filename) - open from file path. + /// Document(data, filetype) - open from byte array. + /// rect, width, height, fontsize: layout reflowable document on open (e.g. EPUB). /// - public int VersionCount + public Document(byte[] data, string filetype = null, Rect rect = null, float width = 0, float height = 0, float fontsize = 11) { - get + _graftId = _nextGraftId++; + if (data == null || data.Length == 0) + throw new EmptyFileException("Cannot open empty stream."); + + StreamData = data; + Name = ""; + float w = width, h = height; + if (rect != null) { - PdfDocument pdf = Document.AsPdfDocument(this); - if (pdf.m_internal != null) - pdf.pdf_count_versions(); + var r = rect.ToFzRect(); + if (mupdf.mupdf.fz_is_infinite_rect(r) == 0) { w = r.x1 - r.x0; h = r.y1 - r.y0; } + } - pdf.Dispose(); - - return 0; + mupdf.FzDocument doc; + try + { + var mem = Helpers.BufferFromBytes(data); + var stream = mupdf.mupdf.fz_open_buffer(mem); + doc = mupdf.mupdf.fz_open_document_with_stream(filetype ?? "", stream); + } + catch (Exception e) + { + throw new FileDataException("Failed to open stream", e); } + + bool laidOut = LayoutDoc(doc, w, h, fontsize); + _nativeDoc = doc; + if (laidOut) + ResetPageRefsInternal(); + FinishOpen(null, filetype); } /// - /// Number of pages. + /// Open from a readable stream (Python fitz.open(stream=buffer, filetype=type)). Reads the stream to completion; when is true, the position is reset to 0 first. /// - public int PageCount + public Document(Stream stream, string filetype = null, Rect rect = null, float width = 0, float height = 0, float fontsize = 11) + : this(ReadStreamFully(stream), filetype, rect, width, height, fontsize) { - get + } + + // ─── Static factory (Python fitz.open) ─────────────────────────── + + /// Python fitz.open() — new empty PDF. + public static Document Open() => new Document(); + + /// Python fitz.open(filename, ...). + public static Document Open(string filename, string filetype = null, Rect rect = null, float width = 0, float height = 0, float fontsize = 11) + => new Document(filename, filetype, rect, width, height, fontsize); + + /// Python fitz.open(stream=buffer, filetype=type, ...). + public static Document Open(byte[] data, string filetype = null, Rect rect = null, float width = 0, float height = 0, float fontsize = 11) + => new Document(data, filetype, rect, width, height, fontsize); + + /// Python fitz.open(stream=stream, filetype=type, ...) for any readable . + public static Document Open(Stream stream, string filetype = null, Rect rect = null, float width = 0, float height = 0, float fontsize = 11) + => new Document(stream, filetype, rect, width, height, fontsize); + + private static byte[] ReadStreamFully(Stream stream) + { + if (stream == null) throw new ArgumentNullException(nameof(stream)); + if (stream.CanSeek && stream.Position != 0) + stream.Position = 0; + if (stream is MemoryStream mem) + return mem.ToArray(); + using (var buffer = new MemoryStream()) { - if (IsClosed) - throw new Exception("document closed"); - - lock (Utils.MuPDFLock) - { - return _nativeDocument.fz_count_pages(); - } + stream.CopyTo(buffer); + return buffer.ToArray(); } } - public bool IsDirty + private bool LayoutDoc(mupdf.FzDocument doc, float w, float h, float fontsize) { - get + if (w > 0 && h > 0) + { + mupdf.mupdf.fz_layout_document(doc, w, h, fontsize); + return true; + } + if (mupdf.mupdf.fz_is_document_reflowable(doc) != 0) { - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - if (pdf.m_internal == null) - return false; - int r = pdf.pdf_has_unsaved_changes(); - pdf.Dispose(); + mupdf.mupdf.fz_layout_document(doc, 400, 600, 11); + return true; + } + return false; + } + + private void FinishOpen(string filename, string filetype) + { + if (NeedsPass) + IsEncrypted = true; + else + InitDoc(); - return r != 0; + if (filename != null && filename.ToLower().EndsWith("svg") || filetype != null && filetype.ToLower().Contains("svg")) + { + try { ConvertToPdf(); } + catch (Exception e) { throw new FileDataException("cannot open broken document", e); } } } + // ─── Properties ───────────────────────────────────────────────── + /// - /// Contains the number of chapters in the document. Always at least 1. - ///
- /// Relevant only for document types with chapter support (EPUB currently). Other documents will return 1. + /// Check for PDF. ///
- public int ChapterCount + public bool IsPdf { get { - if (IsClosed) - throw new Exception("document closed"); - - lock (Utils.MuPDFLock) - { - return _nativeDocument.fz_count_chapters(); - } + try { var p = new mupdf.PdfDocument(NativeDocument); return p.m_internal != null; } + catch { return false; } } } /// - /// True if PDF is in linearized format. False for non-PDF documents. + /// Number of pages. /// - public bool IsFastWebaccess + public int PageCount => mupdf.mupdf.fz_count_pages(NativeDocument); + /// + /// Number of chapters. + /// + public int ChapterCount => mupdf.mupdf.fz_count_chapters(NativeDocument); + /// + /// Indicate password required. + /// + public bool NeedsPass => mupdf.mupdf.fz_needs_password(NativeDocument) != 0; + /// + /// Check if document is layoutable. + /// + public bool IsReflowable => mupdf.mupdf.fz_is_document_reflowable(NativeDocument) != 0; + + /// + /// Check if document has unsaved changes. + /// + public bool IsDirty { get { - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - if (pdf.m_internal != null) - { - bool ret = pdf.pdf_doc_was_linearized() != 0; - pdf.Dispose(); - return ret; - } - - return false; + if (!IsPdf) return false; + return mupdf.mupdf.pdf_has_unsaved_changes(NativePdfDocument) != 0; } } /// - /// False if this is not a PDF or has no form fields, otherwise the number of root form fields (fields with no ancestors). + /// Either False or PDF field count. /// - public int IsFormPDF // return -1 or fields count + public bool IsFormPdf { get { - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - if (pdf.m_internal == null) - return -1; - - int count = -1; + if (!IsPdf) return false; try { - PdfObj fields = Utils.pdf_dict_getl( - pdf.pdf_trailer(), - new string[] { "Root", "AcroForm", "Fields" } - ); - if (fields.pdf_is_array() != 0) - count = fields.pdf_array_len(); + var pdf = NativePdfDocument; + var fields = Helpers.PdfDictGetl( + mupdf.mupdf.pdf_trailer(pdf), + mupdf.mupdf.pdf_new_name("Root"), + mupdf.mupdf.pdf_new_name("AcroForm"), + mupdf.mupdf.pdf_new_name("Fields")); + return fields.m_internal != null && mupdf.mupdf.pdf_array_len(fields) > 0; } - catch (Exception) - { - pdf.Dispose(); - return -1; - } - - pdf.Dispose(); - if (count >= 0) - return count; - - return -1; + catch { return false; } } } /// - /// True if document has a variable page layout (like e-books or HTML). + /// Check whether PDF was repaired. /// - public bool IsReflowable + public bool IsRepaired { get { - if (IsClosed) - throw new Exception("document is closed"); - - return _nativeDocument.fz_is_document_reflowable() != 0; + if (!IsPdf) return false; + return mupdf.mupdf.pdf_was_repaired(NativePdfDocument) != 0; } } /// - /// True if PDF has been repaired during open (because of major structure issues). Always False for non-PDF documents. + /// Check if PDF is linearized (fast web access). /// - public bool IsRepaired + public bool IsFastWebaccess { get { - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - if (pdf.m_internal == null) - return false; - - bool ret = pdf.pdf_was_repaired() != 0; - - pdf.Dispose(); - - return ret; + try { return mupdf.mupdf.pdf_doc_was_linearized(NativePdfDocument) != 0; } + catch { return false; } } } - public string Language + /// + /// Count PDF document versions. + /// + public int VersionCount { get { - PdfDocument pdf = AsPdfDocument(_nativeDocument); - if (pdf.m_internal == null) - return null; - - fz_text_language lang = mupdf.mupdf.pdf_document_language(pdf); - pdf.Dispose(); - - if (lang == fz_text_language.FZ_LANG_UNSET) - return null; - - return mupdf.mupdf.fz_string_from_text_language2(lang); + try { return mupdf.mupdf.pdf_count_versions(NativePdfDocument); } + catch { return 0; } } } /// - /// Contains (chapter, pno) of the document’s last page. - ///
- /// Relevant only for document types with chapter support (EPUB currently). Other documents will return (0, page_count - 1) and (0, -1) if it has no pages. + /// Number of xref table entries. ///
- public (int, int) LastLocation + public int XrefLength { get { - if (IsClosed) - throw new Exception("document closed"); - - FzLocation lastLoc = _nativeDocument.fz_last_page(); - - return (lastLoc.chapter, lastLoc.page); + try { return mupdf.mupdf.pdf_xref_len(NativePdfDocument); } + catch { return 0; } } } /// - /// A string containing the /PageLayout value. If not specified, the default “SinglePage” is returned. If not a PDF, None is returned. + /// Document permissions. /// - public string PageLayout + public int Permissions { get { - int xref = GetPdfCatalog(); - if (xref == 0) - return null; - (string, string) rc = GetKeyXref(xref, "PageLayout"); - if (rc.Item1 == "null") - return "SinglePage"; - if (rc.Item1 == "name") - return rc.Item2.Substring(1); - - return "SinglePage"; + try { return mupdf.mupdf.pdf_document_permissions(NativePdfDocument); } + catch { return 0; } } } /// - /// A string containing the /PageMode value. If not specified, the default “UseNone” is returned. If not a PDF, None is returned. + /// Document language. /// - public string PageMode + public string Language { get { - int xref = GetPdfCatalog(); - if (xref == 0) - return null; - (string, string) rc = GetKeyXref(xref, "PageMode"); - if (rc.Item1 == "null") - return "UseNone"; - if (rc.Item1 == "name") - return rc.Item2.Substring(1); - - return "UseNone"; + try + { + var pdf = NativePdfDocument; + var lang = Helpers.PdfDictGetl( + mupdf.mupdf.pdf_trailer(pdf), + mupdf.mupdf.pdf_new_name("Root"), + mupdf.mupdf.pdf_new_name("Lang")); + if (lang.m_internal != null) return mupdf.mupdf.pdf_to_text_string(lang); + } + catch { } + return null; } } /// - /// A dictionary indicating the /MarkInfo value. If not specified, the empty dictionary is returned. If not a PDF, None is returned. + /// Id (chapter, page) of last page. /// - public Dictionary MarkInfo + public (int, int) LastLocation => (ChapterCount - 1, ChapterPageCount(ChapterCount - 1) - 1); + + // ─── Core Methods ─────────────────────────────────────────────── + + /// + /// Decrypt document. + /// + public bool Authenticate(string password) { - get - { - int xref = GetPdfCatalog(); - string val; - if (xref == 0) - return null; - - (string, string) rc = GetKeyXref(xref, "MarkInfo"); - if (rc.Item1 == "null") - return new Dictionary(); - - if (rc.Item1 == "xref") - { - xref = Convert.ToInt32(rc.Item2.Split(' ')[0]); - val = GetXrefObject(xref, compressed: 1); - } - else if (rc.Item1 == "dict") - val = rc.Item2; - else - val = null; - - if (val == null || (val.Substring(0, 2) == "<<" && val.Substring(-2) == ">>")) - return new Dictionary(); - Dictionary valid = new Dictionary() - { - { "Marked", false }, - { "UserProperties", false }, - { "Suspects", false } - }; + bool ok = mupdf.mupdf.fz_authenticate_password(NativeDocument, password) != 0; + if (ok) { IsEncrypted = false; InitDoc(); } + return ok; + } - string[] valArray = val.Substring(2, -2).Split('/').Skip(1).ToArray(); - foreach (string v in valArray) - { - string[] kv = v.Split(' '); - if (kv.Length == 2 && kv[1] == "true") - valid.Add(kv[0], true); - } - - return valid; + /// + /// Load a page. + /// + public Page LoadPage(int pageNo) + { + if (IsClosed || IsEncrypted) + throw new ValueErrorException("document closed or encrypted"); + int pc = PageCount; + // Python Document.__contains__(int): loc < page_count; load_page then wraps negatives. + if (!(pageNo < pc)) + throw new ValueErrorException("page not in document"); + int idx = pageNo; + if (idx < 0) + { + while (idx < 0) + idx += pc; } + if (idx < 0 || idx >= pc) + throw new ValueErrorException("page not in document"); + var fzPage = mupdf.mupdf.fz_load_page(NativeDocument, idx); + return new Page(fzPage, this); } /// - /// Get list of field font resource names. + /// Load a page. /// - public List FormFonts + public Page LoadPage(int chapter, int pageInChapter) { - get - { - PdfDocument pdf = Document.AsPdfDocument(this); - if (pdf.m_internal == null) - return null; - - PdfObj fonts = Utils.pdf_dict_getl( - pdf.pdf_trailer(), - new string[] { "Root", "AcroForm", "DR", "Font" }); - List ret = new List(); - if (fonts.m_internal != null && fonts.pdf_is_dict() != 0) - { - int n = fonts.pdf_dict_len(); - for (int i = 0; i < n; i ++) - { - PdfObj f = fonts.pdf_dict_get_key(i); - ret.Add(Utils.UnicodeFromStr(f.pdf_to_name())); - } - } + if (IsClosed || IsEncrypted) + throw new ValueErrorException("document closed or encrypted"); + if (!ContainsChapterPage(chapter, pageInChapter)) + throw new ValueErrorException("page not in document"); + var fzPage = mupdf.mupdf.fz_load_chapter_page(NativeDocument, chapter, pageInChapter); + return new Page(fzPage, this); + } - pdf.Dispose(); + /// Python Document.__getitem__(i) for an int: if i not in self: raise IndexError then load_page(i). + internal Page GetItemPageForIndexer(int pageNo) + { + int pc = PageCount; + if (!(pageNo < pc)) + throw new IndexOutOfRangeException($"page {pageNo} not in document"); + return LoadPage(pageNo); + } - return ret; - } + /// Python Document.__getitem__((chapter, pno)) membership then load_page. + internal Page GetItemPageForIndexer(int chapter, int pageInChapter) + { + _ = PageCount; + if (!ContainsChapterPage(chapter, pageInChapter)) + throw new IndexOutOfRangeException($"page ({chapter}, {pageInChapter}) not in document"); + return LoadPage(chapter, pageInChapter); } /// - /// Document permissions. + /// Load a page (Python doc[i] / __getitem__). + /// + public Page this[int pageNo] => GetItemPageForIndexer(pageNo); + + /// + /// Page count of chapter. + /// + public int ChapterPageCount(int chapter) => mupdf.mupdf.fz_count_chapter_pages(NativeDocument, chapter); + + /// + /// Check if a page number is valid for this document. + /// + public bool ContainsPage(int pageNo) => pageNo >= 0 && pageNo < PageCount; + + /// + /// Check if a chapter index and page-within-chapter index are valid (Python (chapter, pno) in doc). /// - public uint Permissions + public bool ContainsChapterPage(int chapter, int pageInChapter) { - get - { - if (IsEncrypted) - return 0; - FzDocument doc = _nativeDocument; - PdfDocument pdf = doc.pdf_document_from_fz_document(); + if (chapter < 0 || chapter >= ChapterCount) return false; + if (pageInChapter < 0 || pageInChapter >= ChapterPageCount(chapter)) return false; + return true; + } - if (pdf.m_internal != null) - return (uint)pdf.pdf_document_permissions(); + /// Same as using a (chapter, page) pair. + public bool ContainsLocation((int chapter, int page) loc) => ContainsChapterPage(loc.chapter, loc.page); - uint perm = 0xFFFFFFFC; - if (doc.fz_has_permission(fz_permission.FZ_PERMISSION_PRINT) == 0) - perm = perm ^ (uint)mupdf.mupdf.PDF_PERM_PRINT; - if (doc.fz_has_permission(fz_permission.FZ_PERMISSION_EDIT) == 0) - perm = perm ^ (uint)mupdf.mupdf.PDF_PERM_MODIFY; - if (doc.fz_has_permission(fz_permission.FZ_PERMISSION_COPY) == 0) - perm = perm ^ (uint)mupdf.mupdf.PDF_PERM_COPY; - if (doc.fz_has_permission(fz_permission.FZ_PERMISSION_ANNOTATE) == 0) - perm = perm ^ (uint)mupdf.mupdf.PDF_PERM_ANNOTATE; - - return perm; - } + /// + /// Return a generator iterator over a page range. + /// + public IEnumerable Pages(int? start = null, int? stop = null, int? step = null) + { + int s = start ?? 0, e = stop ?? PageCount, st = step ?? 1; + for (int i = s; i < e; i += st) + yield return LoadPage(i); } - public Document(PdfDocument doc) + /// + /// Get (chapter, page) of next page. + /// + public (int chapter, int pageInChapter) NextLocation((int chapter, int page) loc) { - _nativeDocument = doc.super(); - IsPDF = true; + var fzLoc = new mupdf.fz_location(); + fzLoc.chapter = loc.chapter; + fzLoc.page = loc.page; + var next = mupdf.mupdf.fz_next_page(NativeDocument, new mupdf.FzLocation(fzLoc)); + return (next.chapter, next.page); } - public Document( - string fileName = null, - byte[] stream = null, - string fileType = null, - Rect rect = null, - float width = 0, - float height = 0, - int fontSize = 11 - ) + /// + /// Get (chapter, page) of previous page. + /// + public (int chapter, int pageInChapter) PrevLocation((int chapter, int page) loc) { - try - { - IsClosed = false; - IsEncrypted = false; - MetaData = null; - FontInfos = new List(); - PageRefs = new Dictionary(); + var fzLoc = new mupdf.fz_location(); + fzLoc.chapter = loc.chapter; + fzLoc.page = loc.page; + var prev = mupdf.mupdf.fz_previous_page(NativeDocument, new mupdf.FzLocation(fzLoc)); + return (prev.chapter, prev.page); + } - if (stream != null) - Stream = new List(stream); - else - Stream = null; + /// + /// Convert page number to (chapter, page). + /// + public (int chapter, int page) LocationFromPageNumber(int pno) + { + var loc = mupdf.mupdf.fz_location_from_page_number(NativeDocument, pno); + return (loc.chapter, loc.page); + } - bool fromFile; - if (!string.IsNullOrEmpty(fileName) && stream == null) - { - fromFile = true; - Name = fileName; - } - else - { - fromFile = false; - Name = ""; - } + /// + /// Convert (chapter, pno) to page number. + /// + public int PageNumberFromLocation((int chapter, int page) loc) + { + var fzLoc = new mupdf.fz_location(); + fzLoc.chapter = loc.chapter; + fzLoc.page = loc.page; + return mupdf.mupdf.fz_page_number_from_location(NativeDocument, new mupdf.FzLocation(fzLoc)); + } - string msg; - if (fromFile) - { - if (!File.Exists(fileName)) - { - msg = $"No such file: {fileName}"; - throw new FileNotFoundException(msg); - } - //_nativeDocument = mupdf.mupdf.fz_open_document(fileName); - } + /// + /// Make a page pointer before layouting document. + /// + public ulong MakeBookmark((int chapter, int page) loc) + { + if (IsClosed || IsEncrypted) + throw new ValueErrorException("document closed or encrypted"); + var fzLoc = new mupdf.FzLocation(loc.chapter, loc.page); + return mupdf.mupdf.ll_fz_make_bookmark2(NativeDocument.m_internal, fzLoc.internal_()); + } - if ( - fromFile - && Stream != null - && (new System.IO.FileInfo(fileName).Length == 0 || Stream.Count == 0) - ) - { - msg = $"cannot open empty document"; - throw new Exception(msg); - } + /// + /// Find new location after layouting a document. + /// + public (int chapter, int page) FindBookmark(ulong bm) + { + if (IsClosed || IsEncrypted) + throw new ValueErrorException("document closed or encrypted"); + var location = mupdf.mupdf.fz_lookup_bookmark2(NativeDocument, bm); + return (location.chapter, location.page); + } - float w = width; - float h = height; - FzRect r = (rect == null) ? new FzRect(FzRect.Fixed.Fixed_INFINITE) : rect.ToFzRect(); - if (r.fz_is_infinite_rect() != 0) - { - w = r.x1 - r.x0; - h = r.y1 - r.y0; - } + // ─── Metadata ─────────────────────────────────────────────────── - FzDocument doc = null; - lock (Utils.MuPDFLock) + /// + /// Get document metadata. + /// + public Dictionary GetMetadata() + { + var result = new Dictionary(); + string[] keys = { "format", "encryption", "title", "author", "subject", "keywords", "creator", "producer", "creationDate", "modDate", "trapped" }; + foreach (var key in keys) + { + try { - if (stream != null) - { - IntPtr dataPtr = Marshal.AllocHGlobal(stream.Length); - Marshal.Copy(stream, 0, dataPtr, stream.Length); - SWIGTYPE_p_unsigned_char swigData = new SWIGTYPE_p_unsigned_char(dataPtr, true); - FzStream data = mupdf.mupdf.fz_open_memory(swigData, (uint)stream.Length); - if (string.IsNullOrEmpty(fileName) || string.IsNullOrEmpty(fileType)) - fileName = "pdf"; - - string magic = fileName; - if (magic == null) - magic = fileType; - try - { - doc = mupdf.mupdf.fz_open_document_with_stream(magic, data); - } - catch(Exception e) - { - throw new Exception("Failed to open stream : " + e.Message); - } - data.Dispose(); - } - else + if (IsPdf && key != "format" && key != "encryption") { - if (!string.IsNullOrEmpty(fileName)) - { - if (string.IsNullOrEmpty(fileType)) - { - - try - { - doc = mupdf.mupdf.fz_open_document(fileName); - } - catch(Exception) - { - throw new Exception("Failed to open document"); - } - } - else - { - fz_document_handler handler = mupdf.mupdf.ll_fz_recognize_document( - fileType - ); - if (handler != null) - { - if (handler.open != null) - { - try - { - FzStream _stream = new FzStream(fileName); - FzStream accel = new FzStream(); - FzArchive archive = new FzArchive(); - // mupdf version greater than 1.25.0 - /*{ - doc = new FzDocument( - mupdf.mupdf.ll_fz_document_handler_open(handler, _stream.m_internal, accel.m_internal, archive.m_internal, null) - ); - }*/ - { - doc = new FzDocument(mupdf.mupdf.ll_fz_document_handler_open(handler, _stream.m_internal, accel.m_internal, archive.m_internal, null)); - } - } - catch (Exception) - { - throw new Exception( - Utils.ErrorMessages["MSG_BAD_DOCUMENT"] - ); - } - } - else if ( - mupdf.mupdf.FZ_VERSION_MAJOR >= 1 - && mupdf.mupdf.FZ_VERSION_MINOR >= 24 - ) - { - Debug.Assert(false); - ///////////////////////// in less than version 1.24 - /*data = mupdf.mupdf.fz_open_file(filename); - doc.m_internal = mupdf.mupdf.ll_fz_document_open_with_stream_fn_call(handler.open_with_stream, data.m_internal);*/ - } - } - else - { - throw new Exception(Utils.ErrorMessages["MSG_BAD_FILETYPE"]); - } - } - } - else + var direct = TryGetMetadataFromPdfInfo(key); + if (!string.IsNullOrEmpty(direct)) { - PdfDocument pdf = new PdfDocument(); - doc = new FzDocument(pdf); + result[key] = direct; + continue; } } - if (w > 0 && h > 0) - doc.fz_layout_document(w, h, fontSize); - else if (doc.fz_is_document_reflowable() != 0) - doc.fz_layout_document(400, 600, 11); - _nativeDocument = doc; - } - - ThisOwn = true; - - if (ThisOwn) - { - GraftID = Utils.GenID(); - if (NeedsPass) - { - IsEncrypted = true; - } - else - InitDocument(); - string filename_ = fileName; - if ( - (fileName != null && filename_.ToLower().EndsWith("svg")) - || (fileType != null && fileType.ToLower().Contains("svg")) - ) - { - try - { - byte[] _ = Convert2Pdf(); - } - catch (Exception) - { - throw new Exception("cannot open broken document"); - } - } + string mkey = key == "format" || key == "encryption" ? key : $"info:{key}"; + result[key] = mupdf.mupdf.fz_lookup_metadata2(NativeDocument, mkey) ?? ""; } + catch { result[key] = ""; } } - finally { } + return result; } - public byte[] Convert2Pdf(int from = 0, int to = -1, int rotate = 0) - { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - FzDocument doc = _nativeDocument; - int fp = from; - int tp = to; - int srcCount = doc.fz_count_pages(); - - if (fp < 0) - fp = 0; - if (fp > srcCount - 1) - fp = srcCount - 1; - if (tp < 0) - tp = srcCount - 1; - if (tp > srcCount - 1) - tp = srcCount - 1; - - int len0 = Utils.MUPDF_WARNINGS_STORE.Count; - PdfDocument pdfout = new PdfDocument(); - PdfWriteOptions opts = new PdfWriteOptions(); - FzBuffer res = null; - FzOutput output = null; + private static string MetadataKeyToInfoPdfName(string key) => + key switch + { + "title" => "Title", + "author" => "Author", + "subject" => "Subject", + "keywords" => "Keywords", + "creator" => "Creator", + "producer" => "Producer", + "creationDate" => "CreationDate", + "modDate" => "ModDate", + "trapped" => "Trapped", + _ => null, + }; + /// + /// Read standard document info from the PDF /Info dictionary when + /// has not yet observed in-memory updates. + /// + private string TryGetMetadataFromPdfInfo(string key) + { + var pdfName = MetadataKeyToInfoPdfName(key); + if (pdfName == null) + return null; try { - int incr = 1; - if (fp > tp) + var pdf = NativePdfDocument; + var trailer = mupdf.mupdf.pdf_trailer(pdf); + var infoRef = mupdf.mupdf.pdf_dict_get(trailer, mupdf.mupdf.pdf_new_name("Info")); + if (infoRef.m_internal == null) + return null; + mupdf.PdfObj infoDict; + if (mupdf.mupdf.pdf_is_indirect(infoRef) != 0) { - incr = -1; - int t = tp; - tp = fp; - fp = t; + int xn = mupdf.mupdf.pdf_to_num(infoRef); + infoDict = mupdf.mupdf.pdf_load_object(pdf, xn); } + else + infoDict = infoRef; + if (infoDict.m_internal == null) + return null; + var val = infoDict.pdf_dict_get(mupdf.mupdf.pdf_new_name(pdfName)); + if (val.m_internal == null) + return null; + if (mupdf.mupdf.pdf_is_string(val) != 0) + return mupdf.mupdf.pdf_to_text_string(val); + if (mupdf.mupdf.pdf_is_name(val) != 0) + return mupdf.mupdf.pdf_to_name(val); + return null; + } + catch + { + return null; + } + } - int rot = Utils.NormalizeRotation(rotate); - int i = fp; - - while (true) - { - if (!Utils.INRANGE(i, fp, tp)) - break; + /// + /// Set document metadata. + /// + public void SetMetadata(Dictionary m) + { + EnsurePdf(); - FzPage page = null; - PdfObj resources = null; - FzBuffer contents = null; - FzDevice dev = null; + m ??= new Dictionary(); - try - { - page = doc.fz_load_page(i); - FzRect mediabox = page.fz_bound_page(); - resources = new PdfObj(); - contents = new FzBuffer(); - dev = pdfout.pdf_page_write(mediabox, resources, contents); - page.fz_run_page(dev, new FzMatrix(), new FzCookie()); - - PdfObj pageObj = pdfout.pdf_add_page(mediabox, rot, resources, contents); - pdfout.pdf_insert_page(-1, pageObj); - } - finally - { - if (dev != null) - { - dev.fz_close_device(); - dev.Dispose(); - dev = null; - } + // Keys accepted by PyMuPDF Document.set_metadata (src/__init__.py). + var keymap = new Dictionary + { + ["author"] = "Author", + ["producer"] = "Producer", + ["creator"] = "Creator", + ["title"] = "Title", + ["format"] = null, + ["encryption"] = null, + ["creationDate"] = "CreationDate", + ["modDate"] = "ModDate", + ["subject"] = "Subject", + ["keywords"] = "Keywords", + ["trapped"] = "Trapped", + }; - if (contents != null) - { - contents.Dispose(); - contents = null; - } + var invalidKeys = m.Keys.Where(k => !keymap.ContainsKey(k)).ToList(); + if (invalidKeys.Count > 0) + throw new ValueErrorException($"bad dict key(s): {{{string.Join(", ", invalidKeys)}}}"); - if (resources != null) - { - resources.Dispose(); - resources = null; - } + var pdf = NativePdfDocument; + var trailer = mupdf.mupdf.pdf_trailer(pdf); + var infoKey = mupdf.mupdf.pdf_new_name("Info"); + var info = mupdf.mupdf.pdf_dict_get(trailer, infoKey); - if (page != null) - { - page.Dispose(); - page = null; - } - } + if (m.Count == 0 && info.m_internal == null) + return; - i += incr; - } + if (m.Count == 0) + { + trailer.pdf_dict_del(infoKey); + InitDoc(); + return; + } - opts.do_garbage = 4; - opts.do_compress = 1; - opts.do_compress_images = 1; - opts.do_compress_fonts = 1; - opts.do_sanitize = 1; - opts.do_incremental = 0; - opts.do_ascii = 0; - opts.do_decompress = 0; - opts.do_linear = 0; - opts.do_clean = 1; - opts.do_pretty = 0; - - res = mupdf.mupdf.fz_new_buffer(8192); - output = new FzOutput(res); - pdfout.pdf_write_document(output, opts); - output.fz_close_output(); - - byte[] ret = Utils.BinFromBuffer(res); - int len1 = Utils.MUPDF_WARNINGS_STORE.Count; - - for (int j = len0; j < len1; j++) - { - Console.WriteLine($"{Utils.MUPDF_WARNINGS_STORE[j]}"); - } + mupdf.PdfObj infoObj; + int infoXref = 0; + if (info.m_internal == null) + { + // fz_lookup_metadata reads /Info as an indirect object; mirror PyMuPDF xref + trailer ref. + infoXref = GetNewXref(); + var emptyDict = mupdf.mupdf.pdf_new_dict(pdf, 4); + pdf.pdf_update_object(infoXref, emptyDict); + var ind = pdf.pdf_new_indirect(infoXref, 0); + trailer.pdf_dict_put(infoKey, ind); + info = mupdf.mupdf.pdf_dict_get(trailer, infoKey); + } - return ret; + if (info.m_internal != null && mupdf.mupdf.pdf_is_indirect(info) != 0) + { + infoXref = mupdf.mupdf.pdf_to_num(info); + infoObj = mupdf.mupdf.pdf_load_object(pdf, infoXref); } - finally + else + infoObj = info; + + foreach (var kv in m) { - if (output != null) - { - output.Dispose(); - output = null; - } + if (!keymap.TryGetValue(kv.Key, out var pdfKey) || pdfKey == null) + continue; - if (res != null) - { - res.Dispose(); - res = null; - } + var nameObj = mupdf.mupdf.pdf_new_name(pdfKey); + if (string.IsNullOrEmpty(kv.Value) || string.Equals(kv.Value, "none", StringComparison.OrdinalIgnoreCase) + || string.Equals(kv.Value, "null", StringComparison.OrdinalIgnoreCase)) + infoObj.pdf_dict_del(nameObj); + else + infoObj.pdf_dict_put_text_string(nameObj, kv.Value); + } - if (opts != null) - { - opts.Dispose(); - } + if (infoXref > 0) + pdf.pdf_update_object(infoXref, infoObj); - if (pdfout != null) - { - pdfout.Dispose(); - } - } + InitDoc(); } - public FzDocument ToFzDocument() - { - return _nativeDocument; - } + // ─── TOC ──────────────────────────────────────────────────────── - public static PdfDocument AsPdfDocument(FzDocument document, bool required = true) + /// + /// Get table of contents. + /// + public List<(int level, string title, int page, Dictionary link)> GetToc(bool simple = true) { - PdfDocument ret = new PdfDocument(document); - if (required) - if (ret.m_internal == null) - throw new Exception("document is Null"); - return ret; + var result = new List<(int, string, int, Dictionary)>(); + var ol = mupdf.mupdf.fz_load_outline(NativeDocument); + if (ol.m_internal == null) return result; + CollectToc(ol, result, 1, simple); + return result; } - public static PdfDocument AsPdfDocument(Document document) + private List DelTocInternal() { - if (document.IsClosed) - throw new Exception("document closed"); - if (document == null) - throw new Exception("document is Null"); - return document._nativeDocument.pdf_document_from_fz_document(); + EnsureNotClosed(); + if (IsEncrypted) + throw new ValueErrorException("document closed or encrypted"); + var xrefs = new List(); + var pdf = Helpers.AsPdfDocument(NativeDocument, required: false); + if (pdf == null || pdf.m_internal == null) + return xrefs; + + var root = mupdf.mupdf.pdf_dict_get(mupdf.mupdf.pdf_trailer(pdf), mupdf.mupdf.pdf_new_name("Root")); + var olroot = mupdf.mupdf.pdf_dict_get(root, mupdf.mupdf.pdf_new_name("Outlines")); + if (olroot.m_internal == null) + return xrefs; + + var first = mupdf.mupdf.pdf_dict_get(olroot, mupdf.mupdf.pdf_new_name("First")); + void Collect(mupdf.PdfObj item) + { + if (item == null || item.m_internal == null) + return; + xrefs.Add(mupdf.mupdf.pdf_to_num(item)); + var down = mupdf.mupdf.pdf_dict_get(item, mupdf.mupdf.pdf_new_name("First")); + if (down.m_internal != null) + Collect(down); + var next = mupdf.mupdf.pdf_dict_get(item, mupdf.mupdf.pdf_new_name("Next")); + if (next.m_internal != null) + Collect(next); + } + Collect(first); + + int olrootXref = mupdf.mupdf.pdf_to_num(olroot); + mupdf.mupdf.pdf_delete_object(pdf, olrootXref); + mupdf.mupdf.pdf_dict_del(root, mupdf.mupdf.pdf_new_name("Outlines")); + for (int i = 0; i < xrefs.Count; i++) + mupdf.mupdf.pdf_delete_object(pdf, xrefs[i]); + xrefs.Add(olrootXref); + InitDoc(); + return xrefs; } - public void InitDocument() + private int GetOLRootNumber() { + EnsureNotClosed(); if (IsEncrypted) - throw new Exception("cannot initialize - document still encrypted"); + throw new ValueErrorException("document closed or encrypted"); + var pdf = NativePdfDocument; + var root = mupdf.mupdf.pdf_dict_get(mupdf.mupdf.pdf_trailer(pdf), mupdf.mupdf.pdf_new_name("Root")); + var olroot = mupdf.mupdf.pdf_dict_get(root, mupdf.mupdf.pdf_new_name("Outlines")); + if (olroot.m_internal == null) + { + olroot = mupdf.mupdf.pdf_new_dict(pdf, 4); + mupdf.mupdf.pdf_dict_put(olroot, mupdf.mupdf.pdf_new_name("Type"), mupdf.mupdf.pdf_new_name("Outlines")); + var indObj = mupdf.mupdf.pdf_add_object(pdf, olroot); + mupdf.mupdf.pdf_dict_put(root, mupdf.mupdf.pdf_new_name("Outlines"), indObj); + olroot = mupdf.mupdf.pdf_dict_get(root, mupdf.mupdf.pdf_new_name("Outlines")); + } + return mupdf.mupdf.pdf_to_num(olroot); + } - Outline = LoadOutline(); - MetaData = new Dictionary(); + private static string FormatNum(double v) => v.ToString("g", System.Globalization.CultureInfo.InvariantCulture); - Dictionary values = new Dictionary() - { - { "format", "format" }, - }; + private static string BuildDestAction(int xref, Dictionary ddict) + { + if (ddict == null) + return ""; + int kind = ddict.ContainsKey("kind") && ddict["kind"] is int ? (int)ddict["kind"] : Constants.LINK_NONE; + if (kind == Constants.LINK_NONE) + return ""; - //if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (kind == Constants.LINK_GOTO) { - values.Add("title", "info:Title"); - values.Add("author", "info:Author"); - values.Add("subject", "info:Subject"); - values.Add("keywords", "info:Keywords"); - values.Add("creator", "info:Creator"); - values.Add("producer", "info:Producer"); - values.Add("creationDate", "info:CreationDate"); - values.Add("modDate", "info:ModDate"); - values.Add("trapped", "info:Trapped"); + double zoom = ddict.ContainsKey("zoom") ? Convert.ToDouble(ddict["zoom"], System.Globalization.CultureInfo.InvariantCulture) : 0.0; + Point to = ddict.ContainsKey("to") && ddict["to"] is Point ? new Point((Point)ddict["to"]) : new Point(0, 0); + return "/A<>"; } - - foreach (var value in values) + if (kind == Constants.LINK_URI) + { + string uri = ddict.ContainsKey("uri") ? ddict["uri"]?.ToString() ?? "" : ""; + return "/A<>"; + } + if (kind == Constants.LINK_LAUNCH) + { + string file = ddict.ContainsKey("file") ? ddict["file"]?.ToString() ?? "" : ""; + string fspec = Helpers.GetPdfStr(file); + return "/A<>>>"; + } + if (kind == Constants.LINK_GOTOR) { - MetaData.Add(value.Key, GetMetadata(value.Value)); + string file = ddict.ContainsKey("file") ? ddict["file"]?.ToString() ?? "" : ""; + string fspec = Helpers.GetPdfStr(file); + int page = ddict.ContainsKey("page") ? Convert.ToInt32(ddict["page"], System.Globalization.CultureInfo.InvariantCulture) : -1; + if (page < 0) + { + string to = ddict.ContainsKey("to") ? Helpers.GetPdfStr(ddict["to"]?.ToString() ?? "") : Helpers.GetPdfStr(""); + return "/A<>>>"; + } + Point p = ddict.ContainsKey("to") && ddict["to"] is Point ? new Point((Point)ddict["to"]) : new Point(0, 0); + double z = ddict.ContainsKey("zoom") ? Convert.ToDouble(ddict["zoom"], System.Globalization.CultureInfo.InvariantCulture) : 0.0; + return "/A<>>>"; } - string enc = GetMetadata("encryption"); - MetaData.Add("encryption", enc == "None" ? null : enc); + return ""; } - private string GetMetadata(string key) + private static List TocRowToList(object row) { - try + if (row is IList lo) return new List(lo); + if (row is object[] oa) return new List(oa); + if (row is System.Collections.IList il) { - return _nativeDocument.fz_lookup_metadata2(key); - } - catch (Exception) - { - return "None"; + var rc = new List(il.Count); + for (int i = 0; i < il.Count; i++) rc.Add(il[i]); + return rc; } + return null; } - public int GetPageXref(int pno) + /// + /// Create new outline tree (table of contents, TOC). + /// + /// Args: + /// toc: (list, tuple) each entry must contain level, title, page and + /// optionally top margin on the page. None or '()' remove the TOC. + /// collapse: (int) collapses entries beyond this level. Zero or None + /// shows all entries unfolded. + /// Returns: + /// the number of inserted items, or the number of removed items respectively. + /// + public int SetToc(IList toc, int collapse = 1) { - if (IsClosed) - throw new Exception("document closed"); - + if (IsClosed || IsEncrypted) + throw new ValueErrorException("document closed or encrypted"); + if (!IsPdf) + throw new ValueErrorException(Constants.MSG_IS_NO_PDF); + // if not toc: # remove all entries + if (toc == null || toc.Count == 0) + return DelTocInternal().Count; + + int toclen = toc.Count; int pageCount = PageCount; - int n = pno; - - while (n < 0) - n += pageCount; + var t0 = TocRowToList(toc[0]); + if (t0 == null || (t0.Count != 3 && t0.Count != 4)) + throw new ValueErrorException("items must be sequences of 3 or 4 items"); + if (Convert.ToInt32(t0[0], System.Globalization.CultureInfo.InvariantCulture) != 1) + throw new ValueErrorException("hierarchy level of item 0 must be 1"); + + for (int i = 0; i < toclen - 1; i++) + { + var t1 = TocRowToList(toc[i]); + var t2 = TocRowToList(toc[i + 1]); + int page = Convert.ToInt32(t1[2], System.Globalization.CultureInfo.InvariantCulture); + if (page < -1 || page > pageCount) + throw new ValueErrorException("row " + i.ToString(System.Globalization.CultureInfo.InvariantCulture) + ": page number out of range"); + if (t2 == null || (t2.Count != 3 && t2.Count != 4)) + throw new ValueErrorException("bad row " + (i + 1).ToString(System.Globalization.CultureInfo.InvariantCulture)); + int level2 = Convert.ToInt32(t2[0], System.Globalization.CultureInfo.InvariantCulture); + if (level2 < 1) + throw new ValueErrorException("bad hierarchy level in row " + (i + 1).ToString(System.Globalization.CultureInfo.InvariantCulture)); + int level1 = Convert.ToInt32(t1[0], System.Globalization.CultureInfo.InvariantCulture); + if (level2 > level1 + 1) + throw new ValueErrorException("bad hierarchy level in row " + (i + 1).ToString(System.Globalization.CultureInfo.InvariantCulture)); + } + + DelTocInternal(); + var xref = new List(); + xref.Add(GetOLRootNumber()); + for (int i = 0; i < toclen; i++) + xref.Add(GetNewXref()); + + var lvltab = new Dictionary { [0] = 0 }; + var olitems = new List> + { + new Dictionary + { + ["count"] = 0, ["first"] = -1, ["last"] = -1, ["xref"] = xref[0] + } + }; - PdfDocument pdf = AsPdfDocument(this); - int xref = 0; - if (n >= pageCount) + for (int i = 0; i < toclen; i++) { - pdf.Dispose(); - throw new Exception(Utils.ErrorMessages["MSG_BAD_PAGENO"]); - } - xref = pdf.pdf_lookup_page_obj(n).pdf_to_num(); + var o = TocRowToList(toc[i]); + int lvl = Convert.ToInt32(o[0], System.Globalization.CultureInfo.InvariantCulture); + string title = Helpers.GetPdfStr(o[1]?.ToString() ?? ""); + int pno = Math.Min(PageCount - 1, Math.Max(0, Convert.ToInt32(o[2], System.Globalization.CultureInfo.InvariantCulture) - 1)); + int pageXref = PageXref(pno); + double pageHeight = PageCropBox(pno).Height; + Point top = new Point(72, pageHeight - 36); + var dest = new Dictionary { ["to"] = top, ["kind"] = Constants.LINK_GOTO }; + if (Convert.ToInt32(o[2], System.Globalization.CultureInfo.InvariantCulture) < 0) + dest["kind"] = Constants.LINK_NONE; - pdf.Dispose(); + if (o.Count > 3) + { + object o3 = o[3]; + if (o3 is int || o3 is float || o3 is double || o3 is decimal) + { + double t = Convert.ToDouble(o3, System.Globalization.CultureInfo.InvariantCulture); + dest["to"] = new Point(72, pageHeight - t); + } + else if (o3 is Dictionary d) + { + // We make a copy of o[3] to avoid modifying our caller's data. + dest = new Dictionary(d); + if (!dest.ContainsKey("to")) + dest["to"] = top; + else if (dest["to"] is Point) + { + var page = this[pno]; + var point = new Point((Point)dest["to"]); + point.Y = page.CropBox.Height - point.Y; + point.Transform(page.RotationMatrix); + dest["to"] = point; + } + } + } - return xref; + var dct = new Dictionary + { + ["first"] = -1, + ["count"] = 0, + ["last"] = -1, + ["prev"] = -1, + ["next"] = -1, + ["dest"] = BuildDestAction(pageXref, dest), + ["top"] = dest["to"], + ["title"] = title, + ["parent"] = lvltab[lvl - 1], + ["xref"] = xref[i + 1], + ["color"] = dest.ContainsKey("color") ? dest["color"] : null, + ["flags"] = (dest.ContainsKey("italic") ? Convert.ToInt32(dest["italic"], System.Globalization.CultureInfo.InvariantCulture) : 0) + + 2 * (dest.ContainsKey("bold") ? Convert.ToInt32(dest["bold"], System.Globalization.CultureInfo.InvariantCulture) : 0) + }; + lvltab[lvl] = i + 1; + var parent = olitems[lvltab[lvl - 1]]; + bool suppress = (dest.ContainsKey("collapse") && Convert.ToBoolean(dest["collapse"], System.Globalization.CultureInfo.InvariantCulture)) + || (collapse != 0 && lvl > collapse); + parent["count"] = Convert.ToInt32(parent["count"], System.Globalization.CultureInfo.InvariantCulture) + (suppress ? -1 : 1); + if (Convert.ToInt32(parent["first"], System.Globalization.CultureInfo.InvariantCulture) == -1) + { + parent["first"] = i + 1; + parent["last"] = i + 1; + } + else + { + dct["prev"] = parent["last"]; + var prev = olitems[Convert.ToInt32(parent["last"], System.Globalization.CultureInfo.InvariantCulture)]; + prev["next"] = i + 1; + parent["last"] = i + 1; + } + olitems.Add(dct); + } + + int OlInt(Dictionary d, string k, int def = -1) => + d.TryGetValue(k, out var o) ? Convert.ToInt32(o, System.Globalization.CultureInfo.InvariantCulture) : def; + + for (int i = 0; i < olitems.Count; i++) + { + var ol = olitems[i]; + var txt = "<<"; + if (OlInt(ol, "count", 0) != 0) + txt += "/Count " + OlInt(ol, "count", 0).ToString(System.Globalization.CultureInfo.InvariantCulture); + if (ol.TryGetValue("dest", out var destVal) && destVal != null) + txt += destVal.ToString() ?? ""; + if (OlInt(ol, "first", -1) > -1) + txt += "/First " + xref[OlInt(ol, "first", -1)].ToString(System.Globalization.CultureInfo.InvariantCulture) + " 0 R"; + if (OlInt(ol, "last", -1) > -1) + txt += "/Last " + xref[OlInt(ol, "last", -1)].ToString(System.Globalization.CultureInfo.InvariantCulture) + " 0 R"; + if (OlInt(ol, "next", -1) > -1) + txt += "/Next " + xref[OlInt(ol, "next", -1)].ToString(System.Globalization.CultureInfo.InvariantCulture) + " 0 R"; + if (OlInt(ol, "parent", -1) > -1) + txt += "/Parent " + xref[OlInt(ol, "parent", -1)].ToString(System.Globalization.CultureInfo.InvariantCulture) + " 0 R"; + if (OlInt(ol, "prev", -1) > -1) + txt += "/Prev " + xref[OlInt(ol, "prev", -1)].ToString(System.Globalization.CultureInfo.InvariantCulture) + " 0 R"; + if (ol.TryGetValue("title", out var titleVal) && titleVal != null) + txt += "/Title" + titleVal.ToString(); + + if (ol.TryGetValue("color", out var colorVal) && colorVal is float[] f3 && f3.Length == 3) + txt += "/C[ " + Helpers.EscapePdfArray(f3).Trim('[', ']') + "]"; + if (OlInt(ol, "flags", 0) > 0) + txt += "/F " + OlInt(ol, "flags", 0).ToString(System.Globalization.CultureInfo.InvariantCulture); + if (i == 0) + txt += "/Type/Outlines"; + txt += ">>"; + UpdateObject(xref[i], txt); + } + + InitDoc(); + return toclen; } - private Outline LoadOutline() + private void RemoveTocItemByXref(int xref) { - FzDocument doc = _nativeDocument; - FzOutline ol = null; - try - { - ol = doc.fz_load_outline(); - if (ol.m_internal == null) - return null; - } - catch (Exception) { } - return new Outline(ol); + // "remove" bookmark by letting it point to nowhere + var pdf = NativePdfDocument; + var item = mupdf.mupdf.pdf_new_indirect(pdf, xref, 0); + mupdf.mupdf.pdf_dict_del(item, mupdf.mupdf.pdf_new_name("Dest")); + mupdf.mupdf.pdf_dict_del(item, mupdf.mupdf.pdf_new_name("A")); + var color = mupdf.mupdf.pdf_new_array(pdf, 3); + for (int i = 0; i < 3; i++) + mupdf.mupdf.pdf_array_push_real(color, 0.8f); + mupdf.mupdf.pdf_dict_put(item, mupdf.mupdf.pdf_new_name("C"), color); } - public static PdfObj SetObjectValue(PdfObj obj, string key, string value) + private void UpdateTocItemByXref(int xref, string action = null, string title = null, int flags = 0, bool? collapse = null, float[] color = null) { - string eyecatcher = "fiz: replace me!"; - PdfDocument pdf = obj.pdf_get_bound_document(); - - string[] list = key.Split('/'); - int len = list.Length; - int i = len - 1; - string skey = list[i]; - - list = list.Take(len - 1).ToArray(); - len = list.Length; - PdfObj testkey = obj.pdf_dict_getp(key); + // "update" bookmark by letting it point to nowhere + var pdf = NativePdfDocument; + var item = mupdf.mupdf.pdf_new_indirect(pdf, xref, 0); + if (!string.IsNullOrEmpty(title)) + mupdf.mupdf.pdf_dict_put_text_string(item, mupdf.mupdf.pdf_new_name("Title"), title); + if (!string.IsNullOrEmpty(action)) + { + mupdf.mupdf.pdf_dict_del(item, mupdf.mupdf.pdf_new_name("Dest")); + mupdf.mupdf.pdf_dict_put(item, mupdf.mupdf.pdf_new_name("A"), Helpers.JM_pdf_obj_from_str(pdf, action)); + } + mupdf.mupdf.pdf_dict_put_int(item, mupdf.mupdf.pdf_new_name("F"), flags); + if (color != null && color.Length == 3) + { + var c = mupdf.mupdf.pdf_new_array(pdf, 3); + for (int i = 0; i < 3; i++) + mupdf.mupdf.pdf_array_push_real(c, color[i]); + mupdf.mupdf.pdf_dict_put(item, mupdf.mupdf.pdf_new_name("C"), c); + } + else if (color != null) + { + mupdf.mupdf.pdf_dict_del(item, mupdf.mupdf.pdf_new_name("C")); + } - if (testkey.m_internal == null) + if (collapse.HasValue) { - while (len > 0) + var countObj = mupdf.mupdf.pdf_dict_get(item, mupdf.mupdf.pdf_new_name("Count")); + if (countObj.m_internal != null) { - string t = string.Join("/", list); - if (obj.pdf_dict_getp(t).pdf_is_indirect() != 0) - throw new Exception(string.Format("path to '{0}' has indirects", skey)); - - list = list.Take(len - 1).ToArray(); - len = list.Length; + int i = mupdf.mupdf.pdf_dict_get_int(item, mupdf.mupdf.pdf_new_name("Count")); + if ((i < 0 && collapse.Value == false) || (i > 0 && collapse.Value)) + mupdf.mupdf.pdf_dict_put_int(item, mupdf.mupdf.pdf_new_name("Count"), -i); } } - - obj.pdf_dict_putp(key, mupdf.mupdf.pdf_new_text_string(eyecatcher)); - testkey = obj.pdf_dict_getp(key); - if (testkey.pdf_is_string() == 0) - throw new Exception(string.Format("cannot insert value for '{0}'", key)); - - string temp = mupdf.mupdf.pdf_to_text_string(testkey); - if (temp != eyecatcher) - throw new Exception(string.Format("cannot insert value for '{0}'", key)); - - FzBuffer res = Object2Buffer(obj, 1, 0); - string objStr = Utils.EscapeStrFromBuffer(res); - - string nullVal = string.Format("{0}({1})", skey, eyecatcher); - string newVal = string.Format("{0} {1}", skey, value); - string newStr = objStr.Replace(nullVal, newVal); - - PdfObj newObj = Utils.PdfObjFromStr(pdf, newStr); - - return newObj; } - public static FzBuffer Object2Buffer(PdfObj what, int compress, int ascii) + /// + /// Delete TOC / bookmark item by index. + /// + public void DelTocItem(int idx) { - FzBuffer ret = new FzBuffer(512); - FzOutput output = new FzOutput(ret); - output.pdf_print_obj(what, compress, ascii); - output.fz_close_output(); - output.Dispose(); - ret.fz_terminate_buffer(); - - return ret; + int xref = GetOutlineXrefs()[idx]; + RemoveTocItemByXref(xref); } - public void SetKeyXRef(int xref, string key, string value) + /// + /// Update TOC item by index. + /// + /// It allows changing the item's title and link destination. + /// + /// Args: + /// idx: + /// (int) desired index of the TOC list, as created by get_toc. + /// dest_dict: + /// (dict) destination dictionary as created by get_toc(False). + /// Outrules all other parameters. If None, the remaining parameters + /// are used to make a dest dictionary. + /// kind: + /// (int) kind of link (pymupdf.LINK_GOTO, etc.). If None, then only + /// the title will be updated. If pymupdf.LINK_NONE, the TOC item will + /// be deleted. + /// pno: + /// (int) page number (1-based like in get_toc). Required if + /// pymupdf.LINK_GOTO. + /// uri: + /// (str) the URL, required if pymupdf.LINK_URI. + /// title: + /// (str) the new title. No change if None. + /// to: + /// (point-like) destination on the target page. If omitted, (72, 36) + /// will be used as target coordinates. + /// filename: + /// (str) destination filename, required for pymupdf.LINK_GOTOR and + /// pymupdf.LINK_LAUNCH. + /// name: + /// (str) a destination name for pymupdf.LINK_NAMED. + /// zoom: + /// (float) a zoom factor for the target location (pymupdf.LINK_GOTO). + /// + public void SetTocItem(int idx, Dictionary destDict = null, int? kind = null, int? pno = null, + string uri = null, string title = null, Point to = null, string filename = null, float zoom = 0) { - if (IsClosed) - throw new Exception("Document closed"); + int xref = GetOutlineXrefs()[idx]; + int pageXref = 0; + + if (destDict != null) + { + if (destDict.ContainsKey("kind") && Convert.ToInt32(destDict["kind"], System.Globalization.CultureInfo.InvariantCulture) == Constants.LINK_GOTO) + { + int dpno = Convert.ToInt32(destDict["page"], System.Globalization.CultureInfo.InvariantCulture); + pageXref = PageXref(dpno); + double pageHeight = PageCropBox(dpno).Height; + Point p = destDict.ContainsKey("to") && destDict["to"] is Point ? new Point((Point)destDict["to"]) : new Point(72, 36); + p.Y = pageHeight - p.Y; + destDict["to"] = p; + } + string action = BuildDestAction(pageXref, destDict); + if (!action.StartsWith("/A", StringComparison.Ordinal)) + throw new ValueErrorException("bad bookmark dest"); + + float[] color = null; + if (destDict.ContainsKey("color") && destDict["color"] != null) + { + if (destDict["color"] is float[] fc) + color = fc; + else if (destDict["color"] is double[] dc) + color = new float[] { (float)dc[0], (float)dc[1], (float)dc[2] }; + else if (destDict["color"] is IList list && list.Count == 3) + color = new float[] { Convert.ToSingle(list[0]), Convert.ToSingle(list[1]), Convert.ToSingle(list[2]) }; + if (color == null || color.Length != 3 || color[0] < 0 || color[1] < 0 || color[2] < 0 || color[0] > 1 || color[1] > 1 || color[2] > 1) + throw new ValueErrorException("bad color value"); + } + bool bold = destDict.ContainsKey("bold") && Convert.ToBoolean(destDict["bold"], System.Globalization.CultureInfo.InvariantCulture); + bool italic = destDict.ContainsKey("italic") && Convert.ToBoolean(destDict["italic"], System.Globalization.CultureInfo.InvariantCulture); + int flags = (italic ? 1 : 0) + (bold ? 2 : 0); + bool? collapseState = destDict.ContainsKey("collapse") ? (bool?)Convert.ToBoolean(destDict["collapse"], System.Globalization.CultureInfo.InvariantCulture) : null; - HashSet INVALID_NAME_CHARS = new HashSet( - new char[] { ' ', '(', ')', '<', '>', '[', ']', '{', '}', '/', '%', '\0' } - ); - var invalidChars = new HashSet(INVALID_NAME_CHARS); - var intersection = invalidChars.Intersect(key.ToArray()); + UpdateTocItemByXref(xref, action: action.Substring(2), title: title, color: color, flags: flags, collapse: collapseState); + return; + } - if (string.IsNullOrEmpty(key) || intersection.Count() != 0) + // if kind == LINK_NONE: # delete bookmark item + if (kind.HasValue && kind.Value == Constants.LINK_NONE) { - if (intersection.Count() == 1 && intersection.First() != '/') - throw new Exception("Bad Key"); + DelTocItem(idx); + return; } - if ( - !(value is string) - || string.IsNullOrEmpty(value) - || (value[0] == '/' && INVALID_NAME_CHARS.Intersect(value.Substring(1)).Any()) - ) + // if kind is None and title is None: # treat as no-op + if (!kind.HasValue && title == null) + return; + // if kind is None: # only update title text + if (!kind.HasValue) { - throw new Exception("Bad Value"); + UpdateTocItemByXref(xref, action: null, title: title); + return; } - PdfDocument pdf = AsPdfDocument(this); - int xrefLen = pdf.pdf_xref_len(); - PdfObj obj = null; - if (!Utils.INRANGE(xref, 1, xrefLen - 1) && xref != -1) + int k = kind.Value; + if (k == Constants.LINK_GOTO) { - pdf.Dispose(); - throw new Exception(Utils.ErrorMessages["MSG_BAD_XREF"]); + if (!pno.HasValue || pno.Value < 1 || pno.Value > PageCount) + throw new ValueErrorException("bad page number"); + pageXref = PageXref(pno.Value - 1); + double pageHeight = PageCropBox(pno.Value - 1).Height; + if (to == null) + to = new Point(72, pageHeight - 36); + else + { + to = new Point(to); + to.Y = pageHeight - to.Y; + } } - if (xref != -1) - obj = pdf.pdf_load_object(xref); - else - obj = pdf.pdf_trailer(); - - PdfObj nObj = SetObjectValue(obj, key, value); - if (nObj.m_internal == null) + var ddict = new Dictionary { - pdf.Dispose(); - return; - } + ["kind"] = k, + ["to"] = to, + ["uri"] = uri, + ["page"] = pno.HasValue ? pno.Value : -1, + ["file"] = filename, + ["zoom"] = zoom + }; + string action2 = BuildDestAction(pageXref, ddict); + if (action2 == "" || !action2.StartsWith("/A", StringComparison.Ordinal)) + throw new ValueErrorException("bad bookmark dest"); - if (xref != -1) - pdf.pdf_update_object(xref, nObj); - else + UpdateTocItemByXref(xref, action: action2.Substring(2), title: title); + } + + private void CollectToc(mupdf.FzOutline ol, List<(int, string, int, Dictionary)> result, int level, bool simple) + { + while (ol.m_internal != null) { - int n = nObj.pdf_dict_len(); - for (int i = 0; i < n; i++) - obj.pdf_dict_put(nObj.pdf_dict_get_key(i), nObj.pdf_dict_get_val(i)); + string title = ol.m_internal.title ?? ""; + int page = ol.m_internal.page.page; + Dictionary link = null; + if (!simple) + { + link = new Dictionary { ["kind"] = (int)LinkType.Goto, ["page"] = page, ["title"] = title }; + } + result.Add((level, title, page, link)); + var down = new mupdf.FzOutline(ol.m_internal.down); + if (down.m_internal != null) CollectToc(down, result, level + 1, simple); + ol = new mupdf.FzOutline(ol.m_internal.next); } + } - pdf.Dispose(); + /// + /// Load first outline. + /// + public Outline GetOutline() + { + var ol = mupdf.mupdf.fz_load_outline(NativeDocument); + if (ol.m_internal == null) return null; + return new Outline(ol); } - public (string, string) GetKeyXref(int xref, string key) + /// + /// Get list of outline xref numbers. + /// + public List GetOutlineXrefs() { - if (IsClosed) - throw new Exception("document closed"); + var xrefs = new List(); + var pdf = Helpers.AsPdfDocument(NativeDocument, required: false); + if (pdf == null || pdf.m_internal == null) + return xrefs; + var root = mupdf.mupdf.pdf_dict_get(mupdf.mupdf.pdf_trailer(pdf), mupdf.mupdf.pdf_new_name("Root")); + if (root.m_internal == null) + return xrefs; + var olroot = mupdf.mupdf.pdf_dict_get(root, mupdf.mupdf.pdf_new_name("Outlines")); + if (olroot.m_internal == null) + return xrefs; + var first = mupdf.mupdf.pdf_dict_get(olroot, mupdf.mupdf.pdf_new_name("First")); + if (first.m_internal == null) + return xrefs; - PdfDocument pdf = AsPdfDocument(this); - int xrefLen = pdf.pdf_xref_len(); - if (!Utils.INRANGE(xref, 1, xrefLen - 1) && xref != -1) + void Walk(mupdf.PdfObj item) { - pdf.Dispose(); - throw new Exception(Utils.ErrorMessages["MSG_BAD_XREF"]); + if (item == null || item.m_internal == null) + return; + xrefs.Add(mupdf.mupdf.pdf_to_num(item)); + var down = mupdf.mupdf.pdf_dict_get(item, mupdf.mupdf.pdf_new_name("First")); + if (down.m_internal != null) + Walk(down); + var next = mupdf.mupdf.pdf_dict_get(item, mupdf.mupdf.pdf_new_name("Next")); + if (next.m_internal != null) + Walk(next); } - PdfObj obj = null; - if (xref > 0) - obj = pdf.pdf_load_object(xref); - else - obj = pdf.pdf_trailer(); + Walk(first); + return xrefs; + } - if (obj == null) - { - pdf.Dispose(); - return ("null", "null"); - } + // ─── Page Operations ──────────────────────────────────────────── + + /// + /// Create and return a new page object. + /// + public Page NewPage(int pageNo = -1, float width = 595, float height = 842) + { + EnsurePdf(); + var pdf = NativePdfDocument; + if (pageNo < -1) + throw new ValueErrorException(Constants.MSG_BAD_PAGENO); + if (pageNo < 0 || pageNo > PageCount) pageNo = PageCount; + var mediabox = mupdf.mupdf.fz_make_rect(0, 0, width, height); + var newPage = mupdf.mupdf.pdf_add_page(pdf, mediabox, 0, new mupdf.PdfObj(), new mupdf.FzBuffer()); + mupdf.mupdf.pdf_insert_page(pdf, pageNo, newPage); + ResetPageRefsInternal(); + return LoadPage(pageNo); + } - PdfObj subObj = obj.pdf_dict_getp(key); - if (subObj == null) - return ("null", "null"); + /// + /// Delete one page from a PDF. + /// + public void DeletePage(int pno) + { + EnsurePdfOpenForDeletePages(); + pno = Helpers.ResolvePageIndex(PageCount, pno); + var pdf = NativePdfDocument; + mupdf.mupdf.pdf_delete_page(pdf, pno); + if (pdf.m_internal.rev_page_map != null) + mupdf.mupdf.ll_pdf_drop_page_tree(pdf.m_internal); + if (_suppressPageRefReset == 0) + ResetPageRefsInternal(); + } - string type = null; - string text = null; - if (subObj.pdf_is_indirect() != 0) - { - type = "xref"; - text = $"{subObj.pdf_to_num()} 0 R"; - } - else if (subObj.pdf_is_array() != 0) - type = "array"; - else if (subObj.pdf_is_dict() != 0) - type = "dict"; - else if (subObj.pdf_is_int() != 0) + /// + /// Delete pages from a PDF. + /// + public void DeletePages(params int[] pages) + { + var sorted = pages.OrderByDescending(x => x).ToArray(); + _suppressPageRefReset++; + try { - type = "int"; - text = $"{subObj.pdf_to_int()}"; + foreach (var p in sorted) + DeletePage(p); } - else if (subObj.pdf_is_real() != 0) - type = "float"; - else if (subObj.pdf_is_null() != 0) + finally { - type = "null"; - text = "null"; + _suppressPageRefReset--; } - else if (subObj.pdf_is_bool() != 0) + ResetPageRefsInternal(); + } + + /// + /// Delete pages from a PDF. + /// + public void DeletePages(int fromPage, int toPage) + { + _suppressPageRefReset++; + try { - type = "bool"; - if (subObj.pdf_to_bool() != 0) - text = "true"; - else - text = "false"; + for (int i = toPage; i >= fromPage; i--) + DeletePage(i); } - else if (subObj.pdf_is_name() != 0) + finally { - type = "name"; - text = $"/{subObj.pdf_to_name()}"; + _suppressPageRefReset--; } - else if (subObj.pdf_is_string() != 0) - { - type = "string"; - text = Utils.UnicodeFromStr(subObj.pdf_to_text_string()); + ResetPageRefsInternal(); + } + + /// + /// Page indices for a half-open [start, stop) slice with (Python range(start, stop, step) semantics). + /// + private static List GetSlicePageIndices(int start, int stop, int step, int pageCount) + { + if (step == 0) + throw new ValueErrorException(Constants.MSG_BAD_PAGENO); + int s = start; + int e = stop; + int pc = pageCount; + while (s < 0) s += pc; + if (s >= pc) + throw new ValueErrorException(Constants.MSG_BAD_PAGENO); + while (e < 0) e += pc; + if (e > pc) + throw new ValueErrorException(Constants.MSG_BAD_PAGENO); + var indices = new List(); + if (step > 0) + { + for (int i = s; i < e; i += step) + indices.Add(i); } else - type = "unknown"; - if (text is null) { - FzBuffer res = Utils.Object2Buffer(subObj, 1, 0); - text = Utils.UnicodeFromBuffer(res); + for (int i = s; i > e; i += step) + indices.Add(i); } + return indices; + } - pdf.Dispose(); - return (type, text); + /// + /// Delete pages in a half-open index interval [start, stop) with optional (Python del doc[start:stop:step], PDF only). + /// + public void DeletePagesBySlice(int start, int stop, int step = 1) + { + EnsurePdfOpenForDeletePages(); + var indices = GetSlicePageIndices(start, stop, step, PageCount); + if (indices.Count == 0) return; + DeletePages(indices.ToArray()); } - public void Save( - dynamic filename, + /// + /// Load each page in a slice (Python doc[start:stop:step] when the subscript is a slice). + /// + public List LoadPagesBySlice(int start, int stop, int step = 1) + { + EnsureNotClosed(); + var indices = GetSlicePageIndices(start, stop, step, PageCount); + var list = new List(indices.Count); + foreach (int i in indices) + list.Add(LoadPage(i)); + return list; + } + + /// + /// Insert a new page with optional text. + /// + public Page InsertPage(int pno = -1, string text = null, float fontsize = 11, float width = 595, float height = 842, string fontname = "helv", float[] color = null) + { + var page = NewPage(pno, width, height); + if (!string.IsNullOrEmpty(text)) + { + page.InsertText(new Point(72, 72), text, fontsize: fontsize, fontname: fontname, color: color); + } + return page; + } + + /// + /// Copy a page within a PDF document. + /// + /// This will only create another reference of the same page object. + /// + public void CopyPage(int pno, int to = -1) + { + EnsurePdfOpenForDeletePages(); + var pdf = NativePdfDocument; + int pc = PageCount; + pno = Helpers.ResolvePageIndex(pc, pno); + if (to < 0) to = pc; + var page = mupdf.mupdf.pdf_lookup_page_obj(pdf, pno); + var newPage = mupdf.mupdf.pdf_deep_copy_obj(page); + mupdf.mupdf.pdf_insert_page(pdf, to, newPage); + if (_suppressPageRefReset == 0) + ResetPageRefsInternal(); + } + + /// + /// Make a full page duplicate including annotations and content streams. + /// + public void FullcopyPage(int pno, int to = -1) + { + EnsurePdfOpenForDeletePages(); + var pdf = NativePdfDocument; + int pc = mupdf.mupdf.pdf_count_pages(pdf); + pno = Helpers.ResolvePageIndex(pc, pno); + if (to < -1 || to >= pc) to = pc; + + try + { + var page1 = mupdf.mupdf.pdf_resolve_indirect(mupdf.mupdf.pdf_lookup_page_obj(pdf, pno)); + var page2 = mupdf.mupdf.pdf_deep_copy_obj(page1); + + var oldAnnots = mupdf.mupdf.pdf_dict_get(page2, mupdf.mupdf.pdf_new_name("Annots")); + if (oldAnnots.m_internal != null) + { + int n = mupdf.mupdf.pdf_array_len(oldAnnots); + var newAnnots = mupdf.mupdf.pdf_new_array(pdf, n); + for (int i = 0; i < n; i++) + { + var o = mupdf.mupdf.pdf_array_get(oldAnnots, i); + var subtype = mupdf.mupdf.pdf_dict_get(o, mupdf.mupdf.pdf_new_name("Subtype")); + if (mupdf.mupdf.pdf_name_eq(subtype, mupdf.mupdf.pdf_new_name("Popup")) != 0) + continue; + if (mupdf.mupdf.pdf_dict_gets(o, "IRT").m_internal != null) + continue; + var copyO = mupdf.mupdf.pdf_deep_copy_obj(mupdf.mupdf.pdf_resolve_indirect(o)); + int xref = mupdf.mupdf.pdf_create_object(pdf); + mupdf.mupdf.pdf_update_object(pdf, xref, copyO); + copyO = mupdf.mupdf.pdf_new_indirect(pdf, xref, 0); + mupdf.mupdf.pdf_dict_del(copyO, mupdf.mupdf.pdf_new_name("Popup")); + mupdf.mupdf.pdf_dict_del(copyO, mupdf.mupdf.pdf_new_name("P")); + mupdf.mupdf.pdf_array_push(newAnnots, copyO); + } + mupdf.mupdf.pdf_dict_put(page2, mupdf.mupdf.pdf_new_name("Annots"), newAnnots); + } + + var contentsObj = mupdf.mupdf.pdf_dict_get(page1, mupdf.mupdf.pdf_new_name("Contents")); + if (contentsObj.m_internal != null) + { + mupdf.FzBuffer res; + if (mupdf.mupdf.pdf_is_array(contentsObj) != 0) + { + res = mupdf.mupdf.fz_new_buffer(1024); + int arrLen = mupdf.mupdf.pdf_array_len(contentsObj); + for (int i = 0; i < arrLen; i++) + { + var item = mupdf.mupdf.pdf_array_get(contentsObj, i); + if (mupdf.mupdf.pdf_is_stream(item) != 0) + { + var buf = mupdf.mupdf.pdf_load_stream(item); + mupdf.mupdf.fz_append_buffer(res, buf); + } + } + } + else if (mupdf.mupdf.pdf_is_stream(contentsObj) != 0) + { + res = mupdf.mupdf.pdf_load_stream(contentsObj); + } + else + { + res = null; + } + + if (res != null && res.m_internal != null) + { + var placeholder = Helpers.BufferFromBytes(System.Text.Encoding.UTF8.GetBytes(" ")); + var newContents = mupdf.mupdf.pdf_add_stream(pdf, placeholder, new mupdf.PdfObj(), 0); + mupdf.mupdf.pdf_update_stream(pdf, newContents, res, 1); + mupdf.mupdf.pdf_dict_put(page2, mupdf.mupdf.pdf_new_name("Contents"), newContents); + } + } + + int newXref = mupdf.mupdf.pdf_create_object(pdf); + mupdf.mupdf.pdf_update_object(pdf, newXref, page2); + page2 = mupdf.mupdf.pdf_new_indirect(pdf, newXref, 0); + mupdf.mupdf.pdf_insert_page(pdf, to, page2); + } + finally + { + mupdf.mupdf.ll_pdf_drop_page_tree(pdf.m_internal); + } + if (_suppressPageRefReset == 0) + ResetPageRefsInternal(); + } + + /// + /// Move a page within a PDF document. + /// + public void MovePage(int pno, int to = -1) + { + EnsurePdfOpenForDeletePages(); + int pc = PageCount; + pno = Helpers.ResolvePageIndex(pc, pno); + if (to < 0) to = pc; + if (pno == to) return; + _suppressPageRefReset++; + try + { + CopyPage(pno, to); + if (pno < to) DeletePage(pno); + else DeletePage(pno + 1); + } + finally + { + _suppressPageRefReset--; + } + ResetPageRefsInternal(); + } + + /// + /// Build sub-pdf with page numbers in the list. + /// + public void Select(int[] pages) + { + if (pages == null) + throw new ValueErrorException("sequence required"); + if (IsClosed || IsEncrypted) + throw new ValueErrorException("document closed or encrypted"); + if (!IsPdf) + throw new ValueErrorException(Constants.MSG_IS_NO_PDF); + var pdf = NativePdfDocument; + int n = pages.Length; + int pc = PageCount; + if (n == 0 || pages.Min() < 0 || pages.Max() >= pc) + throw new ValueErrorException(Constants.MSG_BAD_PAGENO); + + var pageList = new mupdf.vectori(); + foreach (var p in pages) pageList.Add(p); + mupdf.mupdf.pdf_rearrange_pages2(pdf, pageList, + mupdf.pdf_clean_options_structure.PDF_CLEAN_STRUCTURE_KEEP); + ResetPageRefsInternal(); + } + + /// + /// Make a fresh copy of a page. + /// + public Page ReloadPage(Page page) => LoadPage(page.Number); + + // ─── Save / Write ─────────────────────────────────────────────── + + /// + /// Save document to file. + /// + public void Save3(string filename, bool garbage = false, bool clean = false, bool deflate = false, + bool deflateImages = false, bool deflateFonts = false, bool incremental = false, + bool ascii = false, bool expand = false, bool linear = false, bool noNewId = false, + bool pretty = false, int encryption = 1, int permissions = 4095, + string ownerPw = null, string userPw = null, bool preserveMetadata = true, + int useObjstms = 0, int compressionEffort = 0) + { + var pdf = NativePdfDocument; + var opts = new mupdf.PdfWriteOptions(); + if (garbage) opts.do_garbage = 1; + if (clean) opts.do_clean = 1; + if (deflate) opts.do_compress = 1; + if (deflateImages) opts.do_compress_images = 1; + if (deflateFonts) opts.do_compress_fonts = 1; + if (ascii) opts.do_ascii = 1; + if (linear) opts.do_linear = 1; + if (incremental) opts.do_incremental = 1; + if (pretty) opts.do_pretty = 1; + if (useObjstms > 0) opts.do_use_objstms = 1; + mupdf.mupdf.pdf_save_document(pdf, filename, opts); + } + + public void Save1( + string filename, + Annot annot, int garbage = 0, int clean = 0, int deflate = 0, @@ -1118,13 +1653,13 @@ public void Save( if (PageCount < 1) throw new Exception("cannot save with zero pages"); - + if ((userPW != null && userPW.Length > 40) || (ownerPW != null && ownerPW.Length > 40)) throw new Exception("password length must not exceed 40"); - PdfDocument pdf = AsPdfDocument(this); - PdfWriteOptions opts = new PdfWriteOptions(); - opts.do_incremental = incremental; + var pdf = NativeDocument.pdf_document_from_fz_document(); + var opts = new mupdf.PdfWriteOptions(); + //opts.do_incremental = incremental; opts.do_ascii = ascii; opts.do_compress = deflate; opts.do_compress_images = deflateImages; @@ -1151,5098 +1686,3569 @@ public void Save( opts.compression_effort = compressionEffort; pdf.m_internal.resynth_required = 0; - Utils.EmbeddedClean(pdf); + //Utils.EmbeddedClean(pdf); - if (filename is string) - { - pdf.pdf_save_document(filename, opts); - } - else - { - FzOutput output = new FilePtrOutput(filename); - pdf.pdf_write_document(output, opts); - output.fz_close_output(); - output.Dispose(); - } + pdf.pdf_save_document(filename, opts); opts.Dispose(); pdf.Dispose(); } - public int InsertPage( - int pno, - dynamic text = null, - float fontSize = 11.0f, - float width = 595, - float height = 842, - string fontName = "helv", - string fontFile = null, - float[] color = null - ) - { - Page page = NewPage(pno, width, height); - if (text == null) - return 0; - - int rc = page.InsertText( - new Point(50, 72), - text, - fontSize: fontSize, - fontName: fontName, - fontFile: fontFile, - color: color - ); - - return rc; - } - - private void ResetPageRefs() - { - if (IsClosed) - return; - - if (PageRefs != null) - PageRefs.Clear(); - } - - public Page this[int i] + /// + /// Save document to file. + /// + /* + public void Save(Stream output, bool garbage = false, bool clean = false, bool deflate = false, + bool deflateImages = false, bool deflateFonts = false, bool incremental = false, + bool ascii = false, bool expand = false, bool linear = false, bool pretty = false, + int encryption = 1, int permissions = 4095, string ownerPw = null, string userPw = null, + bool preserveMetadata = true, int useObjstms = 0, int compressionEffort = 0) { - get - { - return LoadPage(i); - } + var data = Write(garbage: garbage, clean: clean, deflate: deflate, deflateImages: deflateImages, + deflateFonts: deflateFonts, incremental: incremental, ascii: ascii, expand: expand, + linear: linear, pretty: pretty, encryption: encryption, permissions: permissions, + ownerPw: ownerPw, userPw: userPw, preserveMetadata: preserveMetadata, + useObjstms: useObjstms, compressionEffort: compressionEffort); + output.Write(data, 0, data.Length); } + */ /// - /// PDF only: Insert an empty page. + /// Save document to file or stream. /// - /// page number in front of which the new page should be inserted. Must be in 1 < pno <= page_count. Special values -1 and doc.page_count insert after the last page. - /// page width. - /// page height. - /// the created page object. - /// - public Page NewPage(int pno = -1, float width = 595, float height = 842) - { + public void Save( + object filename, + int garbage = 0, + int clean = 0, + int deflate = 0, + int deflate_images = 0, + int deflate_fonts = 0, + int incremental = 0, + int ascii = 0, + int expand = 0, + int linear = 0, + int no_new_id = 0, + int appearance = 0, + int pretty = 0, + int encryption = 1, + int permissions = 4095, + string owner_pw = null, + string user_pw = null, + int preserve_metadata = 1, + int use_objstms = 0, + int compression_effort = 0, + bool raise_on_repair = false) + { + bool is_repaired_pre = IsRepaired; if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); + throw new ValueErrorException("document closed or encrypted"); + string fname = null; + Stream stream = null; + if (filename is string s) + fname = s; + else if (filename is Stream st) + stream = st; else + throw new ValueErrorException("filename must be str or Stream"); + if (fname == Name && incremental == 0) + throw new ValueErrorException("save to original must be incremental"); + if (linear != 0 && use_objstms != 0) + throw new ValueErrorException("'linear' and 'use_objstms' cannot both be requested"); + if (PageCount < 1) + throw new ValueErrorException("cannot save with zero pages"); + if (incremental != 0) { - PdfDocument pdf = AsPdfDocument(this); - FzRect mediaBox = new FzRect(FzRect.Fixed.Fixed_UNIT); - mediaBox.x1 = width; - mediaBox.y1 = height; - FzBuffer contents = new FzBuffer(); - - if (pno < -1) - throw new Exception(Utils.ErrorMessages["MSG_BAD_PAGENO"]); - PdfObj resources = pdf.pdf_add_new_dict(1); - PdfObj pageObj = pdf.pdf_add_page(mediaBox, 0, resources, contents); - pdf.pdf_insert_page(pno, pageObj); - - pdf.Dispose(); + if (Name != fname || StreamData != null) + throw new ValueErrorException("incremental needs original file"); } - - ResetPageRefs(); - - return this[pno]; - } - - public List GetPageFonts(int pno, bool full = false) - { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - if (!IsPDF) - return null; + if ((user_pw != null && user_pw.Length > 40) || (owner_pw != null && owner_pw.Length > 40)) + throw new ValueErrorException("password length must not exceed 40"); - List val = GetPageInfo(pno, 1); - List ret = new List(); - if (full == false) + var pdf = NativePdfDocument; + var opts = new mupdf.PdfWriteOptions(); + opts.do_incremental = incremental; + opts.do_ascii = ascii; + opts.do_compress = deflate; + opts.do_compress_images = deflate_images; + opts.do_compress_fonts = deflate_fonts; + opts.do_decompress = expand; + opts.do_garbage = garbage; + opts.do_pretty = pretty; + opts.do_linear = linear; + opts.do_clean = clean; + opts.do_sanitize = clean; + opts.dont_regenerate_id = no_new_id; + opts.do_appearance = appearance; + opts.do_encrypt = encryption; + opts.permissions = permissions; + if (owner_pw != null) + opts.opwd_utf8_set_value(owner_pw); + else if (user_pw != null) + opts.opwd_utf8_set_value(user_pw); + if (user_pw != null) + opts.upwd_utf8_set_value(user_pw); + opts.do_preserve_metadata = preserve_metadata; + opts.do_use_objstms = use_objstms; + opts.compression_effort = compression_effort; + + mupdf.FzOutput fzOut = null; + pdf.m_internal.resynth_required = 0; + Helpers.JM_embedded_clean(pdf); + if (no_new_id == 0) { - foreach (Entry v in val) - { - v.StreamXref = 0; - ret.Add(v); - } + Helpers.JM_ensure_identity(pdf); // not implemented + } + if (fname != null) + { + mupdf.mupdf.pdf_save_document(pdf, fname, opts); + } + else if (stream != null) + { + var buf = mupdf.mupdf.fz_new_buffer(8192); + fzOut = new mupdf.FzOutput(buf); + mupdf.mupdf.pdf_write_document(pdf, fzOut, opts); + fzOut.fz_close_output(); + var data = buf.fz_buffer_extract(); + stream.Write(data, 0, data.Length); + } + if (raise_on_repair) + { + if (IsRepaired && !is_repaired_pre) + throw new Exception("Document save did a repair"); } - - return ret; } /// - /// List fonts, images, XObjects used on a page. + /// Write document to bytes. /// - /// - /// - /// - /// - private List GetPageInfo(int pno, int what) + public byte[] Write(bool garbage = false, bool clean = false, bool deflate = false, + bool deflateImages = false, bool deflateFonts = false, bool incremental = false, + bool ascii = false, bool expand = false, bool linear = false, bool noNewId = false, + bool pretty = false, int encryption = 1, int permissions = 4095, + string ownerPw = null, string userPw = null, bool preserveMetadata = true, + int useObjstms = 0, int compressionEffort = 0) { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - PdfDocument pdf = AsPdfDocument(this); - int pageCount = mupdf.mupdf.fz_count_pages(_nativeDocument); - int n = pno; - - while (n < 0) - n += pageCount; - if (n >= pageCount) - { - pdf.Dispose(); - throw new Exception(Utils.ErrorMessages["MSG_BAD_PAGENO"]); - } + var pdf = NativePdfDocument; + var opts = new mupdf.PdfWriteOptions(); + if (garbage) opts.do_garbage = 1; + if (clean) opts.do_clean = 1; + if (deflate) opts.do_compress = 1; + if (deflateImages) opts.do_compress_images = 1; + if (deflateFonts) opts.do_compress_fonts = 1; + if (ascii) opts.do_ascii = 1; + if (linear) opts.do_linear = 1; + if (incremental) opts.do_incremental = 1; + if (pretty) opts.do_pretty = 1; + if (useObjstms > 0) opts.do_use_objstms = 1; + var buf = mupdf.mupdf.fz_new_buffer(8192); + var out_ = new mupdf.FzOutput(buf); + mupdf.mupdf.pdf_write_document(pdf, out_, opts); + out_.fz_close_output(); + return buf.fz_buffer_extract(); + } - PdfObj pageRef = pdf.pdf_lookup_page_obj(n); - PdfObj rsrc = pageRef.pdf_dict_get_inheritable(new PdfObj("Resources")); + /// + /// Convert document to bytes. + /// + public byte[] ToBytes(bool garbage = false, bool clean = false, bool deflate = false) => + Write(garbage: garbage, clean: clean, deflate: deflate); - List liste = new List(); - List tracer = new List(); + /// + /// Convert document to a PDF, selecting page range and optional rotation. Output bytes object. + /// + public byte[] ConvertToPdf(int fromPage = 0, int toPage = -1, int rotate = 0) + { + if (toPage < 0) toPage = PageCount - 1; + var buf = mupdf.mupdf.fz_new_buffer(8192); + var output = new mupdf.FzOutput(buf); + var writer = new mupdf.FzDocumentWriter(output, "pdf", ""); + writer.fz_write_document(NativeDocument); + writer.fz_close_document_writer(); + output.fz_close_output(); + return buf.fz_buffer_extract(); + } - if (rsrc.m_internal != null) - Utils.ScanResources(pdf, rsrc, liste, what, 0, tracer); + /// + /// Save document incrementally. + /// + public void SaveIncr() => Save(Name, incremental: 1); - pdf.Dispose(); - return liste; + /// + /// Check whether incremental saves are possible. + /// + public bool CanSaveIncrementally() + { + try { return mupdf.mupdf.pdf_can_be_saved_incrementally(NativePdfDocument) != 0; } + catch { return false; } } /// - /// Create a Page object for further processing (like rendering, text searching, etc.). + /// Save PDF using some different defaults. /// - /// Either a 0-based page number, or a tuple (chapter, pno). For an integer, any -∞ < page_id < page_count is acceptable. - /// page object - /// - public Page LoadPage(int pageId) + public void EzSave(string filename, int garbage = 1, int clean = 0, int deflate = 1, + int deflateImages = 1, int deflateFonts = 1, int pretty = 0, int linear = 0, + int ascii = 0, int encryption = 1, int noNewId = 1, int useObjstms = 1) { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - int np; - if (pageId < 0) - { - np = PageCount; - while (pageId < 0) - pageId += np; - } - - if (Utils.INRANGE(pageId, 0, PageCount - 1) == false) - throw new Exception("document page count is not enough"); - - FzPage page; - lock (Utils.MuPDFLock) - { - page = _nativeDocument.fz_load_page(pageId); - } - Page val = new Page(page, this); + Save(filename, garbage: garbage, clean: clean, deflate: deflate, deflate_images: deflateImages, + deflate_fonts: deflateFonts, pretty: pretty, linear: linear, ascii: ascii, encryption: encryption, + no_new_id: noNewId, use_objstms: useObjstms); + } - val.ThisOwn = true; - val.Parent = this; - PageRefs[val.GetHashCode()] = val; - val.AnnotRefs = new Dictionary(); - val.Number = pageId; + // ─── Xref Operations ──────────────────────────────────────────── - return val; + /// PyMuPDF _INRANGE(xref, 1, pdf_xref_len - 1) for indirect objects (not the trailer). + private void EnsureValidXrefPositiveIndirect(int xref) + { + var pdf = NativePdfDocument; + int len = mupdf.mupdf.pdf_xref_len(pdf); + if (xref < 1 || xref > len - 1) + throw new ValueErrorException(Constants.MSG_BAD_XREF); } /// - /// Create a Page object for further processing (like rendering, text searching, etc.). + /// PyMuPDF allows in [1, pdf_xref_len - 1] or -1 (trailer). + /// Same rule as xref_get_key, xref_object, xref_stream, etc. /// - /// - /// - /// - /// - public Page LoadPage(int chapter, int pagenum) + private void EnsureValidXrefDict(int xref) { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - FzPage page; - lock (Utils.MuPDFLock) - { - page = _nativeDocument.fz_load_chapter_page(chapter, pagenum); - } - Page val = new Page(page, this); - - val.ThisOwn = true; - val.Parent = this; - PageRefs[val.GetHashCode()] = val; - val.AnnotRefs = new Dictionary(); - val.Number = 0; + if (xref == -1) return; + EnsureValidXrefPositiveIndirect(xref); + } - return val; + /// PyMuPDF update_stream: xref < 1 or xref > pdf_xref_len is invalid. + private void EnsureValidXrefForUpdateStream(int xref) + { + var pdf = NativePdfDocument; + int len = mupdf.mupdf.pdf_xref_len(pdf); + if (xref < 1 || xref > len) + throw new ValueErrorException(Constants.MSG_BAD_XREF); } /// - /// PDF only: Provide a new copy of a page after finishing and updating all pending changes. + /// Get xref of page number. /// - /// page object. - /// a new copy of the same page. All pending updates (e.g. to annotations or widgets) will be finalized and a fresh copy of the page will be loaded. - public Page ReloadPage(Page page) + public int PageXref(int pno) { - Dictionary oldAnnots = new Dictionary(); - int pno = page.Number; - foreach (var annot in page.AnnotRefs) - oldAnnots.Add(annot.Key, annot.Value); + pno = Helpers.ResolvePageIndex(PageCount, pno); + return mupdf.mupdf.pdf_to_num(mupdf.mupdf.pdf_lookup_page_obj(NativePdfDocument, pno)); + } - int old_ref = page.GetPdfPage().super().m_internal.refs; - long m_internal_old = page.GetPdfPage().super().m_internal_value(); + /// + /// Get string representation of a PDF object (PyMuPDF xref_object: pdf_print_obj into a buffer). + /// + public string XrefObject(int xref, bool compressed = false, bool ascii = false) + { + EnsureNotClosed(); + EnsureValidXrefDict(xref); + var pdf = NativePdfDocument; + var obj = xref > 0 ? mupdf.mupdf.pdf_load_object(pdf, xref) : mupdf.mupdf.pdf_trailer(pdf); + var resolved = mupdf.mupdf.pdf_resolve_indirect(obj); + return PdfObjPrintToString(resolved, compressed ? 1 : 0, ascii ? 1 : 0); + } - page.Erase(); - page = null; - Utils.StoreShrink(100); + /// + /// Check if xref is a stream object. + /// + public bool XrefIsStream(int xref = 0) + { + try { return mupdf.mupdf.pdf_obj_num_is_stream(NativePdfDocument, xref) != 0; } + catch { return false; } + } - page = LoadPage(pno); + /// + /// Check if xref is a font. + /// + public bool XrefIsFont(int xref) => XrefGetKey(xref, "Type").value == "/Font"; + /// + /// Check if xref is an image. + /// + public bool XrefIsImage(int xref) => XrefGetKey(xref, "Subtype").value == "/Image"; + /// + /// Check if xref is a Form XObject. + /// + public bool XrefIsXobject(int xref) => XrefGetKey(xref, "Subtype").value == "/Form"; - foreach (var oldAnnot in oldAnnots) + /// + /// Get decompressed stream of a PDF object. + /// + public byte[] XrefStream(int xref) + { + EnsureNotClosed(); + if (IsEncrypted) + throw new ValueErrorException("document closed or encrypted"); + EnsureValidXrefDict(xref); + var pdf = NativePdfDocument; + var obj = xref >= 0 ? mupdf.mupdf.pdf_new_indirect(pdf, xref, 0) : mupdf.mupdf.pdf_trailer(pdf); + if (mupdf.mupdf.pdf_is_stream(obj) != 0) { - dynamic annot = oldAnnots[oldAnnot.Key]; - page.AnnotRefs[oldAnnot.Key] = annot; + var res = mupdf.mupdf.pdf_load_stream_number(pdf, xref); + return res.fz_buffer_extract(); } + return null; + } - if (old_ref == 1) - { - // pass - } - else + /// + /// Get raw (compressed) stream of a PDF object. + /// + public byte[] XrefStreamRaw(int xref) + { + EnsureNotClosed(); + if (IsEncrypted) + throw new ValueErrorException("document closed or encrypted"); + EnsureValidXrefDict(xref); + var pdf = NativePdfDocument; + var obj = xref >= 0 ? mupdf.mupdf.pdf_new_indirect(pdf, xref, 0) : mupdf.mupdf.pdf_trailer(pdf); + if (mupdf.mupdf.pdf_is_stream(obj) != 0) { - long m_internal_new = page.GetPdfPage().super().m_internal_value(); + var res = mupdf.mupdf.pdf_load_raw_stream_number(pdf, xref); + return res.fz_buffer_extract(); } - - return page; + return null; } /// - /// PDF Only: Return an embedded font file’s data and appropriate file extension. - ///
- /// This can be used to store the font as an external file. The method does not throw exceptions (other than via checking for PDF and valid xref). + /// Same as PyMuPDF JM_object_to_buffer + UTF-8 decode (Python uses raw-unicode-escape; PDF syntax is ASCII-safe here). ///
- /// PDF object number of the font to extract. - /// only return font information, not the buffer. To be used for information-only purposes, avoids allocation of large buffer areas. - /// If true, a dictionary with the following keys is returned: ‘name’ (font base name), ‘ext’ (font file extension), ‘type’ (font type), ‘content’ (font file content). - /// Font object, where ext is a 3-byte suggested file extension (str), basename is the font’s name (str), type is the font’s type (e.g. “Type1”) and content is a bytes object containing the font file’s content (or b””). - public FontInfo ExtractFont(int xref = 0, int infoOnly = 0, string named = null) + private static string PdfObjPrintToString(mupdf.PdfObj obj, int compress, int ascii) { - PdfDocument pdf = AsPdfDocument(this); - PdfObj obj = pdf.pdf_load_object(xref); - PdfObj type = obj.pdf_dict_get(new PdfObj("Type")); - PdfObj subType = obj.pdf_dict_get(new PdfObj("Subtype")); - - if ( - type.pdf_name_eq(new PdfObj("Font")) != 0 - && !subType.pdf_to_name().StartsWith("CIDFontType") - ) // matched + try { - PdfObj bName = null; - PdfObj baseFont = obj.pdf_dict_get(new PdfObj("BaseFont")); - if (baseFont == null || baseFont.pdf_is_null() != 0) - { - bName = obj.pdf_dict_get(new PdfObj("Name")); - } - else - { - bName = baseFont; - } - string ext = Utils.GetFontExtension(pdf, xref); - byte[] bytes = null; - if (ext != "n/a" && infoOnly == 0) + if (obj?.m_internal == null) return ""; + using (var buf = mupdf.mupdf.fz_new_buffer(512)) + using (var output = new mupdf.FzOutput(buf)) { - FzBuffer buf = Utils.GetFontBuffer(pdf, xref); - bytes = Utils.BinFromBuffer(buf); + output.pdf_print_obj(obj, compress, ascii); + output.fz_close_output(); + buf.fz_terminate_buffer(); + return System.Text.Encoding.UTF8.GetString(Helpers.BufferToBytes(buf)); } - else - bytes = Encoding.UTF8.GetBytes(""); + } + catch + { + return ""; + } + } - subType.Dispose(); - type.Dispose(); - obj.Dispose(); - pdf.Dispose(); - return new FontInfo() - { - Name = Utils.EscapeStrFromStr(bName.pdf_to_name()), - Ext = Utils.UnicodeFromStr(ext), - Type = Utils.UnicodeFromStr(subType.pdf_to_name()), - Content = bytes - }; + /// + /// Serialize a for when pdf_to_str_buf is empty + /// (observed for some arrays/dicts in the C# binding). Fallback matches PyMuPDF JM_object_to_buffer(sub, 1, 0). + /// + private static string PdfObjToKeyValueString(mupdf.PdfObj sub) + { + try + { + if (sub?.m_internal == null) return ""; + var s = sub.pdf_to_str_buf(); + if (!string.IsNullOrEmpty(s)) + return s; + return PdfObjPrintToString(sub, 1, 0); } - else + catch { - subType.Dispose(); - type.Dispose(); - obj.Dispose(); - pdf.Dispose(); - return new FontInfo() - { - Name = "", - Ext = "", - Type = "", - Content = Encoding.UTF8.GetBytes("") - }; + return ""; } } /// - /// Get list of glyph information of a font. + /// Get type and value of a PDF dictionary key. /// - /// - /// - /// - /// - /// - /// - /// - public List<(int, double)> GetCharWidths( - int xref, - int limit = 256, - int idx = 0, - FontInfo fontDict = null - ) + public (string type, string value) XrefGetKey(int xref, string key) { - FontInfo fontStruct = Utils.CheckFontInfo(this, xref); - string name = ""; - string ext = ""; - string stype = ""; - float asc = 0.0f; - float dsc = 0.0f; - bool simple = false; - int ordering = 0; - List<(int, double)> glyphs = null; + EnsureNotClosed(); + EnsureValidXrefDict(xref); + var pdf = NativePdfDocument; + var obj = xref > 0 ? mupdf.mupdf.pdf_load_object(pdf, xref) : mupdf.mupdf.pdf_trailer(pdf); + if (obj.m_internal == null) return ("null", "null"); + // Prefer path lookup; fall back to direct name (PyMuPDF often uses plain keys like "CropBox"). + var sub = mupdf.mupdf.pdf_dict_getp(obj, key); + if (sub.m_internal == null && !string.IsNullOrEmpty(key) && key[0] != '/') + sub = mupdf.mupdf.pdf_dict_get(obj, mupdf.mupdf.pdf_new_name(key)); + if (sub.m_internal == null) return ("null", "null"); - if (fontStruct == null) - { - if (fontDict == null) - { - (name, ext, stype, asc, dsc) = Utils.GetFontProperties(this, xref); - fontStruct.Name = name; - fontStruct.Ext = ext; - fontStruct.Type = stype; - fontStruct.Ascender = asc; - fontStruct.Descender = dsc; - } - else - { - name = fontDict.Name; - ext = fontDict.Ext; - stype = fontDict.Type; - ordering = fontDict.Ordering; - simple = fontDict.Simple; - } - - if (string.IsNullOrEmpty(ext)) - throw new Exception("xref is not a font"); - - if (stype == "Type1" || stype == "MMType1" || stype == "TrueType") - simple = true; - else - simple = false; - - if (name == "Fangti" || name == "Ming") - ordering = 0; - else if (name == "Heiti" || name == "Song") - ordering = 1; - else if (name == "Gothic" || name == "Mincho") - ordering = 2; - else if (name == "Dotum" || name == "Batang") - ordering = 3; - else - ordering = -1; - - fontDict.Simple = simple; - - if (name == "ZapfDingbats") - glyphs = new List<(int, double)>(Utils.zapf_glyphs); - else if (name == "Symbol") - glyphs = new List<(int, double)>(Utils.symbol_glyphs); - else - glyphs = null; - - fontDict.Glyphs = glyphs; - fontDict.Ordering = ordering; - fontDict.Xref = xref; - FontInfos.Add(fontDict); - } - else - { - fontDict = fontStruct; - glyphs = new List<(int, double)>(fontDict.Glyphs); - simple = fontDict.Simple; - ordering = fontDict.Ordering; - } - - int oldLimit = 0; - if (glyphs != null) - oldLimit = glyphs.Count; - int myLimit = Math.Max(256, limit); - if (myLimit <= oldLimit) - return glyphs; - - if (ordering < 0) - glyphs = _GetCharWidths( - xref, - fontDict.Name, - fontDict.Ext, - fontDict.Ordering, - myLimit, - idx - ); - else - glyphs = null; - - fontDict.Glyphs = glyphs; - Utils.UpdateFontInfo(this, fontDict); - - return glyphs; - } - - /// - /// Return a list of character glyphs and their widths for a font that is present in the document. - /// - /// cross reference number of a font embedded in the PDF - /// - /// - /// - /// limits the number of returned entries - /// - /// - /// - internal List<(int, double)> _GetCharWidths( - int xref, - string bfName, - string ext, - int ordering, - int limit, - int idx = 0 - ) - { - PdfDocument pdf = AsPdfDocument(this); - int myLimit = limit; - FzFont font = null; - - if (myLimit < 256) - myLimit = 256; - if (ordering >= 0) - { - ll_fz_lookup_cjk_font_outparams cjk = new ll_fz_lookup_cjk_font_outparams(); - SWIGTYPE_p_unsigned_char data = mupdf.mupdf.ll_fz_lookup_cjk_font_outparams_fn( - ordering, - cjk - ); - - font = mupdf.mupdf.fz_new_font_from_memory(null, data, cjk.len, cjk.index, 0); - } - else - { - ll_fz_lookup_base14_font_outparams base14 = - new ll_fz_lookup_base14_font_outparams(); - SWIGTYPE_p_unsigned_char data = mupdf.mupdf.ll_fz_lookup_base14_font_outparams_fn( - bfName, - base14 - ); - if (data != null) - font = mupdf.mupdf.fz_new_font_from_memory(bfName, data, base14.len, 0, 0); - else - { - FzBuffer buf = Utils.GetFontBuffer(pdf, xref); - if (buf == null) - throw new Exception($"font at xref {xref} is not supported"); - font = mupdf.mupdf.fz_new_font_from_buffer(null, buf, idx, 0); - buf.Dispose(); - } - } - List<(int, double)> wList = new List<(int, double)>(); - for (int i = 0; i < myLimit; i++) - { - int glyph = font.fz_encode_character(i); - float adv = font.fz_advance_glyph(glyph, 0); - if (ordering >= 0) - glyph = i; - if (glyph > 0) - wList.Add((glyph, adv)); - else - wList.Add((glyph, 0.0f)); - } - - if (font != null) - font.Dispose(); - pdf.Dispose(); - return wList; - } - - /// - /// PDF only: Return the definition source of a PDF object. - /// - /// the object’s xref. - /// whether to generate a compact output with no line breaks or spaces. - /// whether to ASCII-encode binary data. - /// whether to ASCII-encode binary data. - public string GetXrefObject(int xref, int compressed = 0, int ascii = 0) - { - if (IsClosed) - throw new Exception("document closed"); - - PdfDocument pdf = AsPdfDocument(this); - int xrefLen = pdf.pdf_xref_len(); - PdfObj obj = null; - - if (!Utils.INRANGE(xref, 1, xrefLen - 1) && xref != -1) - { - pdf.Dispose(); - throw new Exception(Utils.ErrorMessages["MSG_BAD_XREF"]); - } - if (xref > 0) - obj = pdf.pdf_load_object(xref); - else - obj = pdf.pdf_trailer(); - - FzBuffer res = Utils.Object2Buffer(obj.pdf_resolve_indirect(), compressed, ascii); - string text = Utils.EscapeStrFromBuffer(res); - - res.Dispose(); - obj.Dispose(); - pdf.Dispose(); - - return text; - } - - /// - /// PDF only: Return a list of all XObjects referenced by a page. - /// - /// page number, 0-based, -∞ < pno < PageCount - /// a list of (non-image) XObjects. These objects typically represent pages embedded (not copied) from other PDFs. - /// - public List GetPageXObjects(int pno) - { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - if (!IsPDF) - return new List(); - - List val = GetPageInfo(pno, 3); - - return val; - } - - /// - /// Retrieve a list of images used on a page. - /// - /// page number, 0-based, -∞ < pno < PageCount - /// whether to also include the referencer’s xref (which is zero if this is the page). - /// a list of images referenced by this page. - /// - public List GetPageImages(int pno, bool full = false) - { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - if (!IsPDF) - return new List(); - - List val = GetPageInfo(pno, 2); - if (full == false) - { - List ret = new List(); - foreach (Entry v in val) - { - v.StreamXref = 0; - ret.Add(v); - } - return ret; - } - - return val; - } - - private void _DeletePage(int pno) - { - PdfDocument pdf = AsPdfDocument(this); - pdf.pdf_delete_page(pno); - if (pdf.m_internal.rev_page_map != null) - mupdf.mupdf.ll_pdf_drop_page_tree(pdf.m_internal); - pdf.Dispose(); + if (mupdf.mupdf.pdf_is_indirect(sub) != 0) return ("xref", $"{mupdf.mupdf.pdf_to_num(sub)} 0 R"); + if (mupdf.mupdf.pdf_is_int(sub) != 0) return ("int", $"{mupdf.mupdf.pdf_to_int(sub)}"); + if (mupdf.mupdf.pdf_is_real(sub) != 0) return ("float", PdfObjToKeyValueString(sub)); + if (mupdf.mupdf.pdf_is_null(sub) != 0) return ("null", "null"); + if (mupdf.mupdf.pdf_is_bool(sub) != 0) return ("bool", mupdf.mupdf.pdf_to_bool(sub) != 0 ? "true" : "false"); + if (mupdf.mupdf.pdf_is_name(sub) != 0) return ("name", $"/{mupdf.mupdf.pdf_to_name(sub)}"); + if (mupdf.mupdf.pdf_is_string(sub) != 0) return ("string", mupdf.mupdf.pdf_to_text_string(sub)); + if (mupdf.mupdf.pdf_is_array(sub) != 0) return ("array", PdfObjToKeyValueString(sub)); + if (mupdf.mupdf.pdf_is_dict(sub) != 0) return ("dict", PdfObjToKeyValueString(sub)); + return ("unknown", PdfObjToKeyValueString(sub)); } /// - /// Create a table of contents. + /// Get list of PDF dictionary keys. /// - /// a bool to control output. - /// Returns a list, where each entry consists of outline level, title, page number and link destination (if simple = False). For details see MuPDF's documentation. - /// - public List GetToc(bool simple = true) + public List XrefGetKeys(int xref) { - List Recurse(Outline _olItem, List list, int _lvl) - { - while (_olItem != null && _olItem.ToFzOutline().m_internal != null) - { - string title = ""; - int page = -1; - if (_olItem.Title != null) - title = _olItem.Title; - - if (!_olItem.IsExternal) - { - if (_olItem.Uri != null) - { - if (_olItem.Page == -1) - { - (List, float, float) resolve = ResolveLink(_olItem.Uri); - page = resolve.Item1[0] + 1; - } - else - page = _olItem.Page + 1; - } - else - page = -1; - } - else - page = -1; - - if (!simple) - { - LinkInfo link = Utils.GetLinkDict(_olItem, this); - list.Add( - new Toc() - { - Level = _lvl, - Title = title, - Page = page, - Link = link - } - ); - } - else - list.Add( - new Toc() - { - Level = _lvl, - Title = title, - Page = page - } - ); - - if (_olItem.Down != null) - list = Recurse(_olItem.Down, list, _lvl + 1); - _olItem = _olItem.Next; - } - - return list; - } - - if (IsClosed) - throw new Exception("document closed"); - - InitDocument(); - Outline olItem = Outline; - if (olItem == null) - return new List(); - - int lvl = 1; - List liste = new List(); - List toc = Recurse(olItem, liste, lvl); - - if (IsPDF && simple == false) - ExtendTocItems(toc); - - return toc; - } - - /// - /// Convert the PDF's destination names into a dict. - /// - /// - /// - /// - internal (List, float, float) ResolveLink(string uri = null, int chapters = 0) - { - fz_location loc = null; - float xp = 0.0f; - float yp = 0.0f; - - if (string.IsNullOrEmpty(uri)) - { - if (chapters != 0) - return (new List() { -1, -1 }, 0, 0); - return (new List() { -1 }, 0, 0); - } - try - { - ll_fz_resolve_link_outparams outparams = new ll_fz_resolve_link_outparams(); - loc = mupdf.mupdf.ll_fz_resolve_link_outparams_fn( - _nativeDocument.m_internal, - uri, - outparams - ); - xp = outparams.xp; - yp = outparams.yp; - } - catch (Exception) - { - if (chapters != 0) - return (new List() { -1, -1 }, 0, 0); - return (new List() { -1 }, 0, 0); - } - - if (chapters != 0) - return (new List() { loc.chapter, loc.page }, xp, yp); - int pno = _nativeDocument.fz_page_number_from_location(new FzLocation(loc)); - - return (new List() { pno }, xp, yp); - } - - /// - /// Rewrite images in a PDF document. - /// The typical use case is to reduce the size of the PDF by recompressing - /// images.Default parameters will convert all images to JPEG where - /// possible, using the specified resolutions and quality.Exclude - /// undesired images by setting parameters to False. - /// - /// look at images with a larger DPI only. - /// change eligible images to this DPI. - /// Quality of the recompressed images (0-100). - /// process lossy image types (e.g. JPEG). - /// process lossless image types (e.g. PNG). - /// process black-and-white images (e.g. FAX) - /// process colored images. - /// process gray images. - /// whether to change the PDF to gray at process start. - /// Custom options for image rewriting(optional). - /// Expert use only.If provided, other parameters are ignored, except set_to_gray. - /// - public void RewriteImage( - int dpiThreshold = -1, - int dpiTarget = 0, - int quality = 0, - bool lossy = true, - bool lossless = true, - bool bitonal = true, - bool color = true, - bool gray = true, - bool setToGray = false, - PdfImageRewriterOptions options = null - ) - { - string qualityStr = quality.ToString(); - if (dpiTarget < 0) - { - dpiThreshold = 0; - dpiTarget = 0; - } - if (dpiTarget > 0 && dpiTarget >= dpiThreshold) - { - throw new Exception($"dpi_target={dpiTarget} must be less than dpi_threshold={dpiThreshold}"); - } - - var templateOpts = new PdfImageRewriterOptions(); - HashSet dir1 = new HashSet(templateOpts.GetType().GetMembers().Select(m => m.Name)); - - PdfImageRewriterOptions opts; - if (options == null) - { - opts = new PdfImageRewriterOptions(); - if (bitonal == true) - { - opts.bitonal_image_recompress_method = mupdf.mupdf.FZ_RECOMPRESS_FAX; - opts.bitonal_image_subsample_method = mupdf.mupdf.FZ_SUBSAMPLE_AVERAGE; - opts.bitonal_image_subsample_to = dpiTarget; - opts.bitonal_image_recompress_quality = qualityStr; - opts.bitonal_image_subsample_threshold = dpiThreshold; - } - if (color == true) - { - if (lossless == true) - { - opts.color_lossless_image_recompress_method = mupdf.mupdf.FZ_RECOMPRESS_JPEG; - opts.color_lossless_image_subsample_method = mupdf.mupdf.FZ_SUBSAMPLE_AVERAGE; - opts.color_lossless_image_subsample_to = dpiTarget; - opts.color_lossless_image_subsample_threshold = dpiThreshold; - opts.color_lossless_image_recompress_quality = qualityStr; - } - if (lossy == true) - { - opts.color_lossy_image_recompress_method = mupdf.mupdf.FZ_RECOMPRESS_JPEG; - opts.color_lossy_image_subsample_method = mupdf.mupdf.FZ_SUBSAMPLE_AVERAGE; - opts.color_lossy_image_subsample_threshold = dpiThreshold; - opts.color_lossy_image_subsample_to = dpiTarget; - opts.color_lossy_image_recompress_quality = qualityStr; - } - } - if (gray == true) - { - if (lossless == true) - { - opts.gray_lossless_image_recompress_method = mupdf.mupdf.FZ_RECOMPRESS_JPEG; - opts.gray_lossless_image_subsample_method = mupdf.mupdf.FZ_SUBSAMPLE_AVERAGE; - opts.gray_lossless_image_subsample_to = dpiTarget; - opts.gray_lossless_image_subsample_threshold = dpiThreshold; - opts.gray_lossless_image_recompress_quality = qualityStr; - } - if (lossy == true) - { - opts.gray_lossy_image_recompress_method = mupdf.mupdf.FZ_RECOMPRESS_JPEG; - opts.gray_lossy_image_subsample_method = mupdf.mupdf.FZ_SUBSAMPLE_AVERAGE; - opts.gray_lossy_image_subsample_threshold = dpiThreshold; - opts.gray_lossy_image_subsample_to = dpiTarget; - opts.gray_lossy_image_recompress_quality = qualityStr; - } - } - } - else - opts = options; - - var dir2 = new HashSet(opts.GetType().GetMembers().Select(m => m.Name)); - var invalidOptions = dir2.Except(dir1).ToList(); - if (invalidOptions.Any()) - { - throw new ArgumentException($"Invalid options: {string.Join(", ", invalidOptions)}"); - } - - if (setToGray == true) - this.Recolor(1); - - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - mupdf.mupdf.pdf_rewrite_images(pdf, opts); - pdf.Dispose(); - } - - /// - /// Change the color component count on all pages. - /// - /// (int) desired color component count, one of 1, 3, 4. - /// - public void Recolor(int components=1) - { - if (!IsPDF) - throw new ArgumentException("is no PDF"); - for (int i = 0; i < this.PageCount; i++) - { - this.LoadPage(i).Recolor(components); - } - } - - /// - /// Return string version of a PDF object definition. - /// - /// PdfObj - /// - private string ObjString(PdfObj obj) - { - FzBuffer buffer = mupdf.mupdf.fz_new_buffer(512); - FzOutput output = new FzOutput(buffer); - output.pdf_print_obj(obj, 1, 0); - output.fz_close_output(); - output.Dispose(); - - return Utils.UnicodeFromBuffer(buffer); - } - - /// - /// Generate value of one item of the names dictionary. - /// - /// - /// - /// - private DestName GetArray(PdfObj val, Dictionary page_refs) - { - DestName template = new DestName() { Page = -1, Dest = "" }; - - string array = ""; - if (val.pdf_is_indirect() != 0) - val = val.pdf_resolve_indirect(); - if (val.pdf_is_array() != 0) - array = ObjString(val); - else if (val.pdf_is_dict() != 0) - array = ObjString(val.pdf_dict_gets("D")); - else - return template; - - // replace PDF "null" by zero, omit the square brackets - array = array.Replace("null", "0"); - if (array.Length >= 2 && array[0] == '[' && array[array.Length - 1] == ']') - array = array.Substring(1, array.Length - 2); - - // find stuff before first / - int idx = array.IndexOf("/"); - if (idx < 1) - { - template.Dest = array; - return template; - } - - string subval = array.Substring(0, idx); - array = array.Substring(idx); - template.Dest = array; - - // Parse destination type and parameters - string[] arr_t = array.Replace("null", "0").Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); - if (arr_t.Length == 0) - return template; - - string destType = arr_t[0]; - string[] params_t = arr_t.Skip(1).ToArray(); - - if (destType == "/XYZ") - { - template.Dest = destType; - if (params_t.Length >= 3) - { - float x = float.Parse(params_t[0], System.Globalization.CultureInfo.InvariantCulture); - float y = float.Parse(params_t[1], System.Globalization.CultureInfo.InvariantCulture); - float z = float.Parse(params_t[2], System.Globalization.CultureInfo.InvariantCulture); - template.To = new Point(x, y); - template.Zoom = z; - } - } - else if (destType == "/FitH" || destType == "/FitBH") - { - template.Dest = destType; - if (params_t.Length >= 1) - { - float top = float.Parse(params_t[0], System.Globalization.CultureInfo.InvariantCulture); - template.To = new Point(0, top); - } - } - else if (destType == "/FitV" || destType == "/FitBV") - { - template.Dest = destType; - if (params_t.Length >= 1) - { - float left = float.Parse(params_t[0], System.Globalization.CultureInfo.InvariantCulture); - template.To = new Point(left, 0); - } - } - else if (destType == "/FitR") - { - template.Dest = destType; - if (params_t.Length >= 4) - { - float left = float.Parse(params_t[0], System.Globalization.CultureInfo.InvariantCulture); - float bottom = float.Parse(params_t[1], System.Globalization.CultureInfo.InvariantCulture); - float right = float.Parse(params_t[2], System.Globalization.CultureInfo.InvariantCulture); - float top = float.Parse(params_t[3], System.Globalization.CultureInfo.InvariantCulture); - template.Rect = new Rect(left, top, right, bottom); - } - } - else if (destType == "/Fit" || destType == "/FitB") - { - // Fit and FitB have no parameters - template.Dest = destType; - } - - // extract page number - if (subval.Contains(" R")) - { - string[] parts = subval.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); - if (parts.Length > 0 && int.TryParse(parts[0], out int pageRef)) - template.Page = page_refs.ContainsKey(pageRef) ? page_refs[pageRef] : -1; - } - else - { - if (int.TryParse(subval.Trim(), out int pageNum)) - template.Page = pageNum; - } - - return template; - } - - private void FillDict( - Dictionary destDict, - PdfObj pdfDict, - Dictionary page_refs - ) - { - int nameCount = pdfDict.pdf_dict_len(); - - for (int i = 0; i < nameCount; i++) - { - PdfObj key = pdfDict.pdf_dict_get_key(i); - PdfObj val = pdfDict.pdf_dict_get_val(i); - string dictKey = ""; - if (key.pdf_is_name() != 0) - dictKey = key.pdf_to_name(); - else - { - Console.WriteLine($"key {i} is no /Name"); - dictKey = null; - } - - if (dictKey != null) - { - destDict[dictKey] = GetArray(val, page_refs); - } - } - } - - /// - /// PDF only: Convert destination names into a dict. - /// - /// PDF only: Convert destination names into a dict. - public Dictionary ResolveNames() - { - Dictionary page_refs = new Dictionary(); - for (int i = 0; i < PageCount; i++) - page_refs[GetPageXref(i)] = i; - - PdfDocument pdf = Document.AsPdfDocument(this); - - // access PDF catalog - PdfObj catalog = pdf.pdf_trailer().pdf_dict_gets("Root"); - Dictionary destDict = new Dictionary(); - PdfObj dests = mupdf.mupdf.pdf_new_name("Dests"); - - if (catalog.m_internal != null) - { - PdfObj oldDests = catalog.pdf_dict_get(dests); - if (oldDests.m_internal != null && oldDests.pdf_is_dict() != 0) - FillDict(destDict, oldDests, page_refs); - } - - PdfObj tree = pdf.pdf_load_name_tree(dests); - if (tree.m_internal != null && tree.pdf_is_dict() != 0) - FillDict(destDict, tree, page_refs); - - dests.Dispose(); - - Dictionary ret = new Dictionary(); - foreach (var dict in destDict) - ret[dict.Key] = dict.Value; - - pdf.Dispose(); - - return ret; - } - - /// - /// PDF only: Return whether the document contains signature fields. This is an optional PDF property: if not present (return value -1), no conclusions can be drawn – the PDF creator may just not have bothered using it. - /// - /// int - public int GetSigFlags() - { - PdfDocument pdf = AsPdfDocument(this); - if (pdf.m_internal == null) - return -1; - PdfObj sigflags = Utils.pdf_dict_getl( - pdf.pdf_trailer(), - new string[] { "Root", "AcroForm", "SigFlags" } - ); - int sigflag = -1; - if (sigflags != null) - sigflag = sigflags.pdf_to_int(); - - pdf.Dispose(); - - return sigflag; - } - - /// - /// PDF only: Get the document XML metadata. - /// - /// XML metadata of the document. Empty string if not present or not a PDF. - public string GetXmlMetadata() - { - PdfObj xml = null; - PdfDocument pdf = AsPdfDocument(this); - if (pdf.m_internal != null) - { - xml = Utils.pdf_dict_getl(pdf.pdf_trailer(), new string[] { "Root", "Metadata" }); - } - - string rc = ""; - if (xml.m_internal != null) - { - FzBuffer buff = xml.pdf_load_stream(); - rc = Utils.UnicodeFromBuffer(buff); - } - - pdf.Dispose(); - - return rc; - } - - /// - /// PDF only: Add an arbitrary supported document to the current PDF. Opens “infile” as a document, converts it to a PDF and then invokes Document.insert_pdf(). Parameters are the same as for that method. Among other things, this features an easy way to append images as full pages to an output PDF. - /// - /// the input document to insert. May be a filename specification as is valid for creating a Document or a Pixmap. - /// - /// - /// - /// - /// - /// - /// - /// - /// - public void InsertFile( - Document infile, - int fromPage = -1, - int toPage = -1, - int startAt = -1, - int rotate = -1, - bool links = true, - bool annots = true, - int showProgress = 0, - int final = 1 - ) - { - Document src = infile; - if (src == null) - throw new Exception("bad infile parameter"); - - if (!src.IsPDF) - { - byte[] pdfBytes = src.Convert2Pdf(); - src = new Document("pdf", pdfBytes); - } - - InsertPdf(src, fromPage, toPage, startAt, rotate, links, annots, showProgress, final); - } - - /// - /// Insert a page range from another PDF. - /// - /// PDF to copy from. Must be different object, but may be same file. - /// first source page to copy, 0-based, default 0. - /// last source page to copy, 0-based, default last page. - /// from_page will become this page number in target. - /// rotate copied pages, default -1 is no change. - /// whether to also copy links. - /// whether to also copy annotations. - /// progress message interval, 0 is no messages. - /// - /// internal use only - /// - public void InsertPdf( - Document docSrc, - int fromPage = -1, - int toPage = -1, - int startAt = -1, - int rotate = -1, - bool links = true, - bool annots = true, - int showProgress = 0, - int final = 1, - GraftMap gmap = null - ) - { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - if (GraftID == docSrc.GraftID) - throw new Exception("source and target cannot be same object"); - - int sa = startAt; - if (sa < 0) - sa = PageCount; - - if (docSrc.PageCount > showProgress && showProgress > 0) - { - string inname = Path.GetFileName(docSrc.Name); - if (inname == null) - inname = "memory PDF"; - string outname = Path.GetFileName(Name); - if (outname == null) - outname = "memory PDF"; - Console.WriteLine(string.Format("Inserting {0} at {1}", inname, outname)); - } - - int isrt = docSrc.GraftID; - Dictionary t = new Dictionary(); - - //gmap = GraftMaps.GetValueOrDefault(isrt, null); - if (!GraftMaps.TryGetValue(isrt, out gmap)) - { - gmap = null; // Default value if the key is missing - } - if (gmap == null) - { - gmap = new GraftMap(this); - GraftMaps[isrt] = gmap; - } - - PdfDocument pdfout = AsPdfDocument(this); - PdfDocument pdfsrc = AsPdfDocument(docSrc); - int outCount = _nativeDocument.fz_count_pages(); - int srcCount = docSrc.ToFzDocument().fz_count_pages(); - - int fp = fromPage; - int tp = toPage; - sa = startAt; - - fp = Math.Max(fp, 0); - fp = Math.Min(fp, srcCount - 1); - - if (tp < 0) - tp = srcCount - 1; - - tp = Math.Min(tp, srcCount - 1); - - if (sa < 0) - sa = outCount; - sa = Math.Min(sa, outCount); - - if (pdfout.m_internal == null || pdfsrc.m_internal == null) - throw new Exception("source or target not a PDF"); - - Utils.MergeRange( - new Document(pdfout), - new Document(pdfsrc), - fp, - tp, - sa, - rotate, - links, - annots, - showProgress, - gmap - ); - - ResetPageRefs(); - if (links) - Utils.DoLinks(this, docSrc, fromPage, toPage, sa); - - if (final == 1) - GraftMaps[isrt] = null; - - pdfout.Dispose(); - pdfsrc.Dispose(); - } - - /// - /// Show if undo and / or redo are possible. - /// - /// - /// - public (bool, bool) JournalCanDo() - { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - int undo = 0; - int redo = 0; - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - undo = pdf.pdf_can_undo(); - redo = pdf.pdf_can_redo(); - - pdf.Dispose(); - - return (undo != 0, redo != 0); + EnsureNotClosed(); + EnsureValidXrefDict(xref); + var pdf = NativePdfDocument; + var obj = xref > 0 ? mupdf.mupdf.pdf_load_object(pdf, xref) : mupdf.mupdf.pdf_trailer(pdf); + int n = mupdf.mupdf.pdf_dict_len(obj); + var rc = new List(n); + for (int i = 0; i < n; i++) + rc.Add(mupdf.mupdf.pdf_to_name(mupdf.mupdf.pdf_dict_get_key(obj, i))); + return rc; } /// - /// Activate document journalling. + /// Set a PDF dictionary key. /// - /// - public void JournalEnable() + public void XrefSetKey(int xref, string key, string value) { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - pdf.pdf_enable_journal(); - pdf.Dispose(); + EnsureNotClosed(); + EnsureValidXrefDict(xref); + var pdf = NativePdfDocument; + var obj = xref > 0 ? mupdf.mupdf.pdf_load_object(pdf, xref) : mupdf.mupdf.pdf_trailer(pdf); + // PyMuPDF xref_set_key(..., "null"): JM_pdf_obj_from_str("null") yields a real PDF null there; this binding's + // pdf_parse_stm_obj path can fall back to a text string. Remove the entry instead (xref_copy / scrubber intent). + if (value != null && string.Equals(value.Trim(), "null", StringComparison.Ordinal)) + { + mupdf.mupdf.pdf_dict_dels(obj, key); + if (xref != -1) mupdf.mupdf.pdf_update_object(pdf, xref, obj); + return; + } + var newObj = Helpers.JM_pdf_obj_from_str(pdf, value); + mupdf.mupdf.pdf_dict_puts(obj, key, newObj); + if (xref != -1) mupdf.mupdf.pdf_update_object(pdf, xref, obj); } /// - /// Move forward in the journal. + /// Get xref of XML metadata. /// - /// true if success - /// - public bool JournalRedo() + public int XrefXmlMetadata { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - PdfDocument pdf = Document.AsPdfDocument(this); - pdf.pdf_redo(); - - pdf.Dispose(); - - return true; + get + { + try + { + var pdf = NativePdfDocument; + var root = mupdf.mupdf.pdf_dict_get(mupdf.mupdf.pdf_trailer(pdf), mupdf.mupdf.pdf_new_name("Root")); + var xml = mupdf.mupdf.pdf_dict_gets(root, "Metadata"); + return xml.m_internal != null ? mupdf.mupdf.pdf_to_num(xml) : 0; + } + catch { return 0; } + } } /// - /// Check if journalling is enabled. + /// Get document XML metadata. /// - /// - /// - public bool IsEnabledJournal() + public string GetXmlMetadata() { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - bool enabled = (pdf != null) && (pdf.m_internal.journal != null); - - pdf.Dispose(); - - return enabled; + int xref = XrefXmlMetadata; + if (xref == 0) return ""; + var data = XrefStream(xref); + return data != null ? System.Text.Encoding.UTF8.GetString(data) : ""; } /// - /// Load a journal from a file. + /// Set document XML metadata. /// - /// File name for loading journal - /// - public void JournalLoad(string filename) + public void SetXmlMetadata(string metadata) { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - - IntPtr utf8Ptr = Utils.Utf16_Utf8Ptr(filename); - try + EnsurePdf(); + var pdf = NativePdfDocument; + var root = mupdf.mupdf.pdf_dict_get(mupdf.mupdf.pdf_trailer(pdf), mupdf.mupdf.pdf_new_name("Root")); + var buf = Helpers.BufferFromBytes(System.Text.Encoding.UTF8.GetBytes(metadata)); + var xml = mupdf.mupdf.pdf_dict_gets(root, "Metadata"); + if (xml.m_internal != null) { - pdf.pdf_load_journal(filename); + mupdf.mupdf.pdf_update_stream(pdf, xml, buf, 0); } - catch (Exception) + else { - Marshal.FreeHGlobal(utf8Ptr); + xml = mupdf.mupdf.pdf_add_stream(pdf, buf, new mupdf.PdfObj(), 0); + mupdf.mupdf.pdf_dict_puts(xml, "Type", mupdf.mupdf.pdf_new_name("Metadata")); + mupdf.mupdf.pdf_dict_puts(xml, "Subtype", mupdf.mupdf.pdf_new_name("XML")); + mupdf.mupdf.pdf_dict_puts(root, "Metadata", xml); } - - if (pdf.m_internal.journal == null) - throw new Exception("Journal and document do not match"); - - pdf.Dispose(); } /// - /// Load a journal from a file. + /// Delete XML metadata. /// - /// Journal bytes - /// - public void JournalLoad(byte[] journal) + public void DelXmlMetadata() { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - FzBuffer res = Utils.BufferFromBytes(journal); - FzStream stream = res.fz_open_buffer(); - pdf.pdf_deserialise_journal(stream); - - if (pdf.m_internal.journal == null) - throw new Exception("Journal and document do not match"); + EnsurePdf(); + var pdf = NativePdfDocument; + var root = mupdf.mupdf.pdf_dict_get(mupdf.mupdf.pdf_trailer(pdf), mupdf.mupdf.pdf_new_name("Root")); + mupdf.mupdf.pdf_dict_dels(root, "Metadata"); + } - pdf.Dispose(); + /// + /// Update a PDF object. + /// + public void UpdateObject(int xref, string text) + { + EnsureNotClosed(); + if (IsEncrypted) + throw new ValueErrorException("document closed or encrypted"); + EnsureValidXrefPositiveIndirect(xref); + var pdf = NativePdfDocument; + var newObj = Helpers.JM_pdf_obj_from_str(pdf, text); + mupdf.mupdf.pdf_update_object(pdf, xref, newObj); } /// - /// Show operation name for given step. + /// Replace object definition source; when is not null, refresh that page's link list (Python Document.update_object(xref, text, page=page)). /// - /// Steps to redo or undo - /// - /// - public string JournalOpName(int step) + public void UpdateObject(int xref, string text, Page page) { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - string name = pdf.pdf_undoredo_step(step); + UpdateObject(xref, text); + if (page == null) return; + var pdf = NativePdfDocument; + Helpers.JM_refresh_links(pdf, page.NativePdfPage); + page.SyncLinkWrapperCache(); + } - pdf.Dispose(); + /// + /// Update the stream of a PDF object. + /// + /// PyMuPDF Document.update_stream rejects non-dictionary xrefs with MSG_IS_NO_DICT; the new parameter there is unused. + public void UpdateStream(int xref, byte[] stream, bool compress = true) + { + EnsureNotClosed(); + if (IsEncrypted) + throw new ValueErrorException("document closed or encrypted"); + EnsureValidXrefForUpdateStream(xref); + var pdf = NativePdfDocument; + var obj = mupdf.mupdf.pdf_new_indirect(pdf, xref, 0); + if (mupdf.mupdf.pdf_is_dict(obj) == 0) + throw new ValueErrorException(Constants.MSG_IS_NO_DICT); + var buf = Helpers.BufferFromBytes(stream); + mupdf.mupdf.pdf_update_stream(pdf, obj, buf, compress ? 1 : 0); + } - return name; + /// + /// PyMuPDF Document.xref_copy(doc, source, target, keep=...) (staticmethod in Python). + /// + public static void XrefCopy(Document document, int source, int target, IReadOnlyCollection keepKeys = null) + { + if (document == null) throw new ArgumentNullException(nameof(document)); + document.XrefCopyImpl(source, target, keepKeys); } /// - /// Show journalling state. + /// Copy one PDF dictionary (and optionally raw stream bytes) from xref to xref. /// - /// - public (int, int) JournalPosition() + public void XrefCopy(int source, int target, IReadOnlyCollection keepKeys = null) + => XrefCopyImpl(source, target, keepKeys); + + private void XrefCopyImpl(int source, int target, IReadOnlyCollection keepKeys) { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - (int, int) rc = pdf.pdf_undoredo_state(); + EnsurePdf(); + EnsureValidXrefPositiveIndirect(source); + EnsureValidXrefPositiveIndirect(target); - pdf.Dispose(); + if (XrefIsStream(source)) + { + var raw = XrefStreamRaw(source); + if (raw != null) + UpdateStream(target, raw, compress: false); + } - return rc; + var keep = keepKeys != null && keepKeys.Count > 0 + ? new HashSet(keepKeys, StringComparer.Ordinal) + : new HashSet(StringComparer.Ordinal); + + foreach (var key in new List(XrefGetKeys(target))) + { + if (keep.Contains(key)) continue; + XrefSetKey(target, key, "null"); + } + + foreach (var key in XrefGetKeys(source)) + { + var (_, value) = XrefGetKey(source, key); + XrefSetKey(target, key, value); + } } /// - /// Save journal to a file + /// Make new xref. /// - /// - /// - public void JournalSave(string filename) + public int GetNewXref() { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - IntPtr utf8Ptr = Utils.Utf16_Utf8Ptr(filename); - pdf.pdf_save_journal(filename); - pdf.Dispose(); + var pdf = NativePdfDocument; + return mupdf.mupdf.pdf_create_object(pdf); } /// - /// Save journal to a file + /// Return optional content object xref for an image or form xobject. /// - /// - /// - public void JournalSave(byte[] journal) + public int GetOc(int xref) { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - MemoryStream memoryStream = new MemoryStream(journal); - FilePtrOutput output = new FilePtrOutput(memoryStream); - pdf.pdf_write_journal(output); - pdf.Dispose(); + var subtype = XrefGetKey(xref, "Subtype"); + if (subtype.type != "name" || (subtype.value != "/Image" && subtype.value != "/Form")) + throw new ValueErrorException($"bad object type at xref {xref}"); + + var oc = XrefGetKey(xref, "OC"); + if (oc.type != "xref") + return 0; + + return int.Parse(oc.value.Replace("0 R", "").Trim()); } /// - /// Begin a journaling operation. + /// Attach optional content object to image or form xobject. /// - /// - /// - public void JournalStartOp(string name) + public void SetOc(int xref, int oc) { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - if (pdf.m_internal.journal == null) - throw new Exception("Journalling not enabled"); - - if (!string.IsNullOrEmpty(name)) - pdf.pdf_begin_operation(name); - else - pdf.pdf_begin_implicit_operation(); + var subtype = XrefGetKey(xref, "Subtype"); + if (subtype.type != "name" || (subtype.value != "/Image" && subtype.value != "/Form")) + throw new ValueErrorException($"bad object type at xref {xref}"); - pdf.Dispose(); + if (oc > 0) + { + var ocType = XrefGetKey(oc, "Type"); + if (ocType.type != "name" || (ocType.value != "/OCG" && ocType.value != "/OCMD")) + throw new ValueErrorException($"bad object type at xref {oc}"); + } + + if (oc == 0 && XrefGetKeys(xref).Contains("OC")) + { + XrefSetKey(xref, "OC", "null"); + return; + } + + XrefSetKey(xref, "OC", $"{oc} 0 R"); } /// - /// End a journalling operation. + /// Return the definition of an OCMD (optional content membership dictionary). /// - public void JournalStopOp() + public Dictionary GetOcmd(int xref) { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); + if (xref < 0 || xref >= XrefLength) + throw new ValueErrorException("bad xref"); - PdfDocument pdf = AsPdfDocument(_nativeDocument); - pdf.pdf_end_operation(); - pdf.Dispose(); + string text = XrefObject(xref, compressed: true); + if (!text.Contains("/Type/OCMD")) + throw new ValueErrorException("bad object type"); + + int textlen = text.Length; + + int p0 = text.IndexOf("/OCGs[", StringComparison.Ordinal); + int p1 = p0 >= 0 ? text.IndexOf("]", p0, StringComparison.Ordinal) : -1; + List ocgs = null; + if (p0 >= 0 && p1 >= 0) + { + ocgs = new List(); + string[] parts = text.Substring(p0 + 6, p1 - (p0 + 6)) + .Replace("0 R", " ") + .Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + foreach (var part in parts) + ocgs.Add(int.Parse(part)); + } + + p0 = text.IndexOf("/P/", StringComparison.Ordinal); + string policy = null; + if (p0 >= 0) + { + p1 = text.IndexOf("ff", p0, StringComparison.Ordinal); + if (p1 < 0) p1 = text.IndexOf("on", p0, StringComparison.Ordinal); + if (p1 < 0) throw new ValueErrorException("bad object at xref"); + policy = text.Substring(p0 + 3, p1 + 2 - (p0 + 3)); + } + + p0 = text.IndexOf("/VE[", StringComparison.Ordinal); + object ve = null; + if (p0 >= 0) + { + int lp = 0, rp = 0; + p1 = p0; + while (lp < 1 || lp != rp) + { + p1++; + if (!(p1 < textlen)) + throw new ValueErrorException("bad object at xref"); + if (text[p1] == '[') lp++; + if (text[p1] == ']') rp++; + } + string veText = text.Substring(p0 + 3, p1 + 1 - (p0 + 3)); + veText = veText + .Replace("/And", "\"and\",") + .Replace("/Not", "\"not\",") + .Replace("/Or", "\"or\","); + veText = veText.Replace(" 0 R]", "]").Replace(" 0 R", ",").Replace("][", "],["); + ve = System.Text.Json.JsonSerializer.Deserialize(veText); + } + + return new Dictionary + { + ["xref"] = xref, + ["ocgs"] = ocgs, + ["policy"] = policy, + ["ve"] = ve + }; } /// - /// Move backwards in the journal. + /// Create or update an OCMD object in a PDF document. /// - /// true - /// document closed or encrypted - public bool JournalUndo() + public int SetOcmd(int xref = 0, List ocgs = null, string policy = null, object ve = null) { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - PdfDocument pdf = AsPdfDocument(_nativeDocument); - pdf.pdf_undo(); + var allOcgs = new HashSet(GetOcgs().Keys); - pdf.Dispose(); - return true; + string VeMaker(object veObject) + { + if (!(veObject is IList list) || list.Count < 2) + throw new ValueErrorException($"bad 've' format: {veObject}"); + + string op = list[0]?.ToString() ?? ""; + string opLower = op.ToLowerInvariant(); + if (opLower != "and" && opLower != "or" && opLower != "not") + throw new ValueErrorException($"bad operand: {op}"); + if (opLower == "not" && list.Count != 2) + throw new ValueErrorException($"bad 've' format: {veObject}"); + + string item = $"[/{char.ToUpperInvariant(opLower[0])}{opLower.Substring(1)}"; + for (int i = 1; i < list.Count; i++) + { + object x = list[i]; + if (x is int xi) + { + if (!allOcgs.Contains(xi)) + throw new ValueErrorException($"bad OCG {xi}"); + item += $" {xi} 0 R"; + } + else if (x is long xl) + { + int xli = (int)xl; + if (!allOcgs.Contains(xli)) + throw new ValueErrorException($"bad OCG {xli}"); + item += $" {xli} 0 R"; + } + else + { + item += $" {VeMaker(x)}"; + } + } + item += "]"; + return item; + } + + string text = "< 0) + { + var bad = new HashSet(ocgs); + bad.ExceptWith(allOcgs); + if (bad.Count != 0) + { + string inner = string.Join(", ", bad.OrderBy(x => x).Select(x => x.ToString(System.Globalization.CultureInfo.InvariantCulture))); + throw new ValueErrorException($"bad OCGs: {{{inner}}}"); + } + text += "/OCGs[" + string.Join(" ", ocgs.ConvertAll(x => $"{x} 0 R")) + "]"; + } + + if (!string.IsNullOrEmpty(policy)) + { + string p = policy.ToLowerInvariant(); + var pols = new Dictionary + { + ["anyon"] = "AnyOn", + ["allon"] = "AllOn", + ["anyoff"] = "AnyOff", + ["alloff"] = "AllOff", + }; + if (!pols.ContainsKey(p)) + throw new ValueErrorException($"bad policy: {policy}"); + text += $"/P/{pols[p]}"; + } + + if (ve != null) + text += $"/VE{VeMaker(ve)}"; + + text += ">>"; + + if (xref == 0) + xref = GetNewXref(); + else if (!XrefObject(xref, compressed: true).Contains("/Type/OCMD")) + throw new ValueErrorException("bad xref or not an OCMD"); + + UpdateObject(xref, text); + return xref; } /// - /// Show OC visibility status modifiable by user. + /// Convert the PDF's destination names into a dictionary. /// - /// - public List LayerUIConfigs() + public Dictionary> ResolveNames() { - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - PdfLayerConfigUi info = new PdfLayerConfigUi(); - int n = pdf.pdf_count_layer_config_ui(); - string type; + if (_resolvedNames != null) + return _resolvedNames; - List rc = new List(); - for (int i = 0; i < n; i++) + var pageXrefs = new Dictionary(); + for (int i = 0; i < PageCount; i++) + pageXrefs[PageXref(i)] = i; + + Dictionary GetArray(mupdf.PdfObj val) { - pdf.pdf_layer_config_ui_info(i, info); - switch ((int)info.type) + var templ = new Dictionary { - case 1: - type = "checkbox"; - break; - case 2: - type = "radiobox"; - break; - default: - type = "label"; - break; + ["page"] = -1, + ["dest"] = "" + }; + + if (mupdf.mupdf.pdf_is_indirect(val) != 0) + val = mupdf.mupdf.pdf_resolve_indirect(val); + + string array; + if (mupdf.mupdf.pdf_is_array(val) != 0) + array = mupdf.mupdf.pdf_to_str_buf(val); + else if (mupdf.mupdf.pdf_is_dict(val) != 0) + array = mupdf.mupdf.pdf_to_str_buf(mupdf.mupdf.pdf_dict_gets(val, "D")); + else + return templ; + + array = array.Replace("null", "0"); + if (array.Length >= 2 && array[0] == '[' && array[array.Length - 1] == ']') + array = array.Substring(1, array.Length - 2); + + int idx = array.IndexOf("/", StringComparison.Ordinal); + if (idx < 1) + { + templ["dest"] = array; + return templ; } - LayerConfigUI item = new LayerConfigUI() + string subval = array.Substring(0, idx).Trim(); + array = array.Substring(idx); + templ["dest"] = array; + + if (array.StartsWith("/XYZ", StringComparison.Ordinal)) { - Number = i, - Text = info.text, - Depth = info.depth, - Type = type, - On = info.selected != 0, - IsLocked = info.locked != 0 - }; - rc.Add(item); + templ.Remove("dest"); + var arrayList = new List(array.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)); + if (arrayList.Count > 0) + arrayList.RemoveAt(0); // omit /XYZ + while (arrayList.Count < 3) + arrayList.Add("0"); + float x = float.Parse(arrayList[0], System.Globalization.CultureInfo.InvariantCulture); + float y = float.Parse(arrayList[1], System.Globalization.CultureInfo.InvariantCulture); + float z = float.Parse(arrayList[2], System.Globalization.CultureInfo.InvariantCulture); + templ["to"] = (x, y); + templ["zoom"] = z; + } + + if (subval.EndsWith("0 R", StringComparison.Ordinal)) + { + int px = int.Parse(subval.Split(' ')[0], System.Globalization.CultureInfo.InvariantCulture); + templ["page"] = pageXrefs.ContainsKey(px) ? pageXrefs[px] : -1; + } + else + { + templ["page"] = int.Parse(subval, System.Globalization.CultureInfo.InvariantCulture); + } + return templ; } - pdf.Dispose(); - return rc; + void FillDict(Dictionary> destDict, mupdf.PdfObj pdfDict) + { + int nameCount = mupdf.mupdf.pdf_dict_len(pdfDict); + for (int i = 0; i < nameCount; i++) + { + var key = mupdf.mupdf.pdf_dict_get_key(pdfDict, i); + var val = mupdf.mupdf.pdf_dict_get_val(pdfDict, i); + string dictKey = null; + if (mupdf.mupdf.pdf_is_name(key) != 0) + dictKey = mupdf.mupdf.pdf_to_name(key); + if (!string.IsNullOrEmpty(dictKey)) + destDict[dictKey] = GetArray(val); + } + } + + var pdf = NativePdfDocument; + var catalog = mupdf.mupdf.pdf_dict_gets(mupdf.mupdf.pdf_trailer(pdf), "Root"); + + var destDictResult = new Dictionary>(); + var dests = mupdf.mupdf.pdf_new_name("Dests"); + + var oldDests = mupdf.mupdf.pdf_dict_get(catalog, dests); + if (mupdf.mupdf.pdf_is_dict(oldDests) != 0) + FillDict(destDictResult, oldDests); + + var tree = mupdf.mupdf.pdf_load_name_tree(pdf, dests); + if (mupdf.mupdf.pdf_is_dict(tree) != 0) + FillDict(destDictResult, tree); + + _resolvedNames = destDictResult; + return destDictResult; } /// - /// Re-layout a reflowable document. + /// Get xref of PDF catalog. /// - /// - /// - /// - /// - /// - public void SetLayout( - Rect rect = null, - float width = 0, - float height = 0, - int fontSize = 11 - ) + public int PdfCatalog { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - FzDocument doc = _nativeDocument; - if (doc.fz_is_document_reflowable() == 0) - return; - - float w = width; - float h = height; - FzRect r = rect.ToFzRect(); - if (r.fz_is_infinite_rect() == 0) + get { - w = r.x1 - r.x0; - h = r.y1 - r.y0; + try + { + var pdf = NativePdfDocument; + var root = mupdf.mupdf.pdf_dict_get(mupdf.mupdf.pdf_trailer(pdf), mupdf.mupdf.pdf_new_name("Root")); + return mupdf.mupdf.pdf_to_num(root); + } + catch { return 0; } } - - if (w <= 0.0f || h <= 0.0f) - throw new Exception("bad page size"); - - doc.fz_layout_document(w, h, fontSize); - ResetPageRefs(); - InitDocument(); } /// - /// Convert pno to (chapter, page) + /// Get PDF trailer as a string. /// - /// - /// - /// - public (int, int) GetLocationFromPageNumber(int pno) + public string PdfTrailer(bool compressed = false, bool ascii = false) { - if (IsClosed) + return XrefObject(-1, compressed, ascii); + } + + // ─── Embedded Files ───────────────────────────────────────────── + + /// + /// Get number of EmbeddedFiles. + /// + public int EmbfileCount + { + get { - throw new Exception("document is closed"); + if (!IsPdf) return 0; + var names = GetEmbeddedFilesNamesArray(); + return names.m_internal != null ? mupdf.mupdf.pdf_array_len(names) / 2 : 0; } - - FzDocument doc = _nativeDocument; - FzLocation loc = mupdf.mupdf.fz_make_location(-1, -1); - int pageCount = doc.fz_count_pages(); - while (pno < 0) - pno += pageCount; - - if (pno >= pageCount) - throw new Exception(Utils.ErrorMessages["MSG_BAD_PAGENO"]); - - loc = doc.fz_location_from_page_number(pno); - - return (loc.chapter, loc.page); } /// - /// Make a page pointer before layouting document. + /// Get list of names of EmbeddedFiles. /// - /// Contains chapter and page numbers - /// - /// - public ulong MakeBookmark((int, int) locNumbers) + public List EmbfileNames() { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - FzLocation loc = new FzLocation(locNumbers.Item1, locNumbers.Item2); - ulong mark = mupdf.mupdf.ll_fz_make_bookmark2( - _nativeDocument.m_internal, - loc.internal_() - ); - - return mark; + var result = new List(); + if (!IsPdf) return result; + var names = GetEmbeddedFilesNamesArray(); + if (names.m_internal == null) return result; + int count = mupdf.mupdf.pdf_array_len(names) / 2; + for (int i = 0; i < count; i++) + { + var key = mupdf.mupdf.pdf_array_get(names, i * 2); + result.Add(key.m_internal != null ? mupdf.mupdf.pdf_to_text_string(key) ?? "" : ""); + } + return result; } /// - /// Get xref of PDF catalog. + /// Get the content of an item in the EmbeddedFiles array by name. /// - /// - public int GetPdfCatalog() + public byte[] EmbfileGet(string name) { - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument, false); - int xref = 0; - if (pdf.m_internal == null) - return xref; - - PdfObj root = pdf.pdf_trailer().pdf_dict_get(new PdfObj("Root")); - xref = root.pdf_to_num(); - - pdf.Dispose(); - return xref; + int idx = EmbfileIndex(name); + return EmbfileGetByIndex(idx); } /// - /// PDF only: Return the trailer source of the PDF, which is usually located at the PDF file’s end. + /// Get the content of an item in the EmbeddedFiles array by index. /// - /// - /// - /// - public string GetPdfTrailer(int compressed = 0, int ascii = 0) + public byte[] EmbfileGetByIndex(int idx) { - return GetXrefObject(-1, compressed, ascii); + var names = GetEmbeddedFilesNamesArray(); + if (names.m_internal == null) throw new ValueErrorException($"'{idx}' not in EmbeddedFiles array."); + int count = mupdf.mupdf.pdf_array_len(names) / 2; + if (idx < 0 || idx >= count) throw new ValueErrorException($"'{idx}' not in EmbeddedFiles array."); + var fs = mupdf.mupdf.pdf_array_get(names, idx * 2 + 1); + var buf = mupdf.mupdf.pdf_load_embedded_file_contents(fs); + return buf.fz_buffer_extract(); } /// - /// Move a page within a PDF document. + /// Delete an entry from EmbeddedFiles. + /// + /// Physical deletion of data will happen on save to a new file with appropriate garbage option. /// - /// source page number. - /// put before this page, '-1' means after last page. - /// - public void MovePage(int pno, int to = -1) + public void EmbfileDel(string name) { - if (IsClosed) - throw new Exception("document closed"); - int pageCount = PageCount; - if (pno >= pageCount || (to < -1 && to >= pageCount)) - throw new Exception("bad page numbers(s)"); - - bool before = true; - bool copy = false; - if (to == -1) - { - to = pageCount - 1; - before = false; - } - - MoveCopyPage(pno, to, before, copy); + int idx = EmbfileIndex(name); + var pdf = NativePdfDocument; + var names = Helpers.PdfDictGetl(mupdf.mupdf.pdf_trailer(pdf), + mupdf.mupdf.pdf_new_name("Root"), mupdf.mupdf.pdf_new_name("Names"), + mupdf.mupdf.pdf_new_name("EmbeddedFiles"), mupdf.mupdf.pdf_new_name("Names")); + int keyIndex = idx * 2; + mupdf.mupdf.pdf_array_delete(names, keyIndex + 1); + mupdf.mupdf.pdf_array_delete(names, keyIndex); + } + + /// + /// Delete an entry from EmbeddedFiles by index. + /// + public void EmbfileDel(int idx) + { + var names = EmbfileNames(); + if (idx < 0 || idx >= names.Count) + throw new ValueErrorException($"'{idx}' not in EmbeddedFiles array."); + EmbfileDel(names[idx]); } - private void MoveCopyPage(int pno, int nb, bool before, bool copy) + /// + /// Add an item to the EmbeddedFiles array. + /// + public int EmbfileAdd(string name, byte[] buffer, string filename = null, string ufilename = null, string desc = null) { - PdfDocument pdf = AsPdfDocument(_nativeDocument); - PdfObj parent; - bool same; - int pos; - (PdfObj page1, PdfObj parent1, int i1) = pdf.pdf_lookup_page_loc(pno); - PdfObj kids1 = parent1.pdf_dict_get(new PdfObj("Kids")); + if (string.IsNullOrEmpty(name)) throw new ArgumentException("name must not be empty"); + if (buffer == null) throw new ArgumentException("buffer must not be null"); + var filenames = EmbfileNames(); + if (filenames.Contains(name)) + throw new ValueErrorException($"Name '{name}' already exists."); - (PdfObj page2, PdfObj parent2, int i2) = pdf.pdf_lookup_page_loc(nb); - PdfObj kids2 = parent2.pdf_dict_get(new PdfObj("Kids")); + filename ??= name; + ufilename ??= filename; + desc ??= name; - if (before) - pos = i2; - else - pos = i2 + 1; + var pdf = NativePdfDocument; + long now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + var fs = mupdf.mupdf.pdf_add_embedded_file(pdf, filename, null, Helpers.BufferFromBytes(buffer), now, now, 1); + if (fs.m_internal == null) + throw new InvalidOperationException("failed to add embedded file"); - same = mupdf.mupdf.pdf_objcmp(kids1, kids2) == 0; // if same, true else false - if (!copy && !same) - page1.pdf_dict_put(new PdfObj("Parent"), parent2); - - kids2.pdf_array_insert(page1, pos); + mupdf.mupdf.pdf_dict_put_text_string(fs, mupdf.mupdf.pdf_new_name("UF"), ufilename); + mupdf.mupdf.pdf_dict_put_text_string(fs, mupdf.mupdf.pdf_new_name("Desc"), desc); - if (!same) // not same - { - parent = parent2; - while (parent.m_internal != null) - { - int count = parent.pdf_dict_get_int(new PdfObj("Count")); - parent.pdf_dict_put_int(new PdfObj("Count"), count + 1); - parent = parent.pdf_dict_get(new PdfObj("Parent")); - } - if (!copy) - { - kids1.pdf_array_delete(i1); - parent = parent1; - while (parent.m_internal != null) - { - int count = parent.pdf_dict_get_int(new PdfObj("Count")); - parent.pdf_dict_put_int(new PdfObj("Count"), count - 1); - parent = parent.pdf_dict_get(new PdfObj("Parent")); - } - } - } - else + var names = EnsureEmbeddedFilesNamesArray(); + mupdf.mupdf.pdf_array_push(names, mupdf.mupdf.pdf_new_text_string(name)); + mupdf.mupdf.pdf_array_push(names, fs); + + var ef = mupdf.mupdf.pdf_dict_get(fs, mupdf.mupdf.pdf_new_name("EF")); + var stream = ef.m_internal != null ? mupdf.mupdf.pdf_dict_get(ef, mupdf.mupdf.pdf_new_name("F")) : new mupdf.PdfObj(); + if (stream.m_internal != null) { - if (copy) - { - parent = parent2; - while (parent.m_internal != null) - { - int count = parent.pdf_dict_get_int(new PdfObj("Count")); - parent.pdf_dict_put_int(new PdfObj("Count"), count + 1); - parent = parent.pdf_dict_get(new PdfObj("Parent")); - } - } - else + mupdf.mupdf.pdf_dict_put(stream, mupdf.mupdf.pdf_new_name("Type"), mupdf.mupdf.pdf_new_name("EmbeddedFile")); + var date = Helpers.GetPdfNow(); + var parms = mupdf.mupdf.pdf_dict_get(stream, mupdf.mupdf.pdf_new_name("Params")); + if (parms.m_internal == null) { - if (i1 < pos) - kids1.pdf_array_delete(i1); - else - kids1.pdf_array_delete(i1 + 1); + parms = mupdf.mupdf.pdf_new_dict(pdf, 2); + mupdf.mupdf.pdf_dict_put(stream, mupdf.mupdf.pdf_new_name("Params"), parms); } + mupdf.mupdf.pdf_dict_put_text_string(parms, mupdf.mupdf.pdf_new_name("CreationDate"), date); + mupdf.mupdf.pdf_dict_put_text_string(parms, mupdf.mupdf.pdf_new_name("ModDate"), date); + return mupdf.mupdf.pdf_to_num(stream); } - if (pdf.m_internal.rev_page_map != null) - mupdf.mupdf.ll_pdf_drop_page_tree(pdf.m_internal); - - ResetPageRefs(); - pdf.Dispose(); + return mupdf.mupdf.pdf_to_num(fs); } /// - /// Get/set the NeedAppearances value. + /// Get information of an item in the EmbeddedFiles array by index. /// - /// - /// - public int NeedAppearances(int value = 0) + public Dictionary EmbfileInfo(int idx) { - if (IsFormPDF == 0) - return 0; + var names = GetEmbeddedFilesNamesArray(); + if (names.m_internal == null) throw new ValueErrorException($"'{idx}' not in EmbeddedFiles array."); + int count = mupdf.mupdf.pdf_array_len(names) / 2; + if (idx < 0 || idx >= count) throw new ValueErrorException($"'{idx}' not in EmbeddedFiles array."); + + var keyObj = mupdf.mupdf.pdf_array_get(names, idx * 2); + var fs = mupdf.mupdf.pdf_array_get(names, idx * 2 + 1); - using (PdfDocument pdf = Document.AsPdfDocument(_nativeDocument)) + var info = new Dictionary(); + info["name"] = keyObj.m_internal != null ? mupdf.mupdf.pdf_to_text_string(keyObj) ?? "" : ""; + + var fObj = mupdf.mupdf.pdf_dict_get(fs, mupdf.mupdf.pdf_new_name("F")); + var ufObj = mupdf.mupdf.pdf_dict_get(fs, mupdf.mupdf.pdf_new_name("UF")); + var descObj = mupdf.mupdf.pdf_dict_get(fs, mupdf.mupdf.pdf_new_name("Desc")); + info["filename"] = fObj.m_internal != null ? mupdf.mupdf.pdf_to_text_string(fObj) ?? "" : ""; + info["ufilename"] = ufObj.m_internal != null ? mupdf.mupdf.pdf_to_text_string(ufObj) ?? "" : ""; + info["desc"] = descObj.m_internal != null ? mupdf.mupdf.pdf_to_text_string(descObj) : null; + + var ef = mupdf.mupdf.pdf_dict_get(fs, mupdf.mupdf.pdf_new_name("EF")); + var stream = ef.m_internal != null ? mupdf.mupdf.pdf_dict_get(ef, mupdf.mupdf.pdf_new_name("F")) : new mupdf.PdfObj(); + if (stream.m_internal != null) { - int oldVal = -1; - string appkey = "NeedAppearances"; + var lengthObj = mupdf.mupdf.pdf_dict_get(stream, mupdf.mupdf.pdf_new_name("Length")); + info["length"] = lengthObj.m_internal != null ? mupdf.mupdf.pdf_to_int(lengthObj) : -1; - PdfObj form = Utils.pdf_dict_getl(pdf.pdf_trailer(), new string[] { "Root/AcroForm" }); - PdfObj app = form.pdf_dict_gets(appkey); - if (app.pdf_is_bool() == 1) - oldVal = app.pdf_to_bool(); - if (value != 0) - form.pdf_dict_puts(appkey, new PdfObj(mupdf.mupdf.PDF_ENUM_TRUE)); - else - form.pdf_dict_puts(appkey, new PdfObj(mupdf.mupdf.PDF_ENUM_FALSE)); + var paramsObj = mupdf.mupdf.pdf_dict_get(stream, mupdf.mupdf.pdf_new_name("Params")); + if (paramsObj.m_internal != null) + { + var sizeObj = mupdf.mupdf.pdf_dict_get(paramsObj, mupdf.mupdf.pdf_new_name("Size")); + if (sizeObj.m_internal != null) info["size"] = mupdf.mupdf.pdf_to_int(sizeObj); - if (value == 0) - return Convert.ToInt32(oldVal >= 0); + var cObj = mupdf.mupdf.pdf_dict_get(paramsObj, mupdf.mupdf.pdf_new_name("CreationDate")); + if (cObj.m_internal != null) info["creationDate"] = mupdf.mupdf.pdf_to_text_string(cObj); - return value; + var mObj = mupdf.mupdf.pdf_dict_get(paramsObj, mupdf.mupdf.pdf_new_name("ModDate")); + if (mObj.m_internal != null) info["modDate"] = mupdf.mupdf.pdf_to_text_string(mObj); + + var csObj = mupdf.mupdf.pdf_dict_get(paramsObj, mupdf.mupdf.pdf_new_name("CheckSum")); + if (csObj.m_internal != null) + { + string checksum = mupdf.mupdf.pdf_to_text_string(csObj) ?? ""; + info["checksum"] = BitConverter.ToString(System.Text.Encoding.UTF8.GetBytes(checksum)).Replace("-", "").ToLowerInvariant(); + } + } } + else + { + info["length"] = -1; + } + + return info; } /// - /// Get (chapter, page) of next page. + /// Get information of an item in the EmbeddedFiles array by name. /// - /// the current page id. This must be a tuple (chapter, pno) identifying an existing page. - /// The tuple of the following page, i.e. either (chapter, pno + 1) or (chapter + 1, 0), or the empty tuple () if the argument was the last page. Relevant only for document types with chapter support (EPUB currently). - /// - public (int, int) NextLocation(int pageId) - { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - (int, int) _pageId; - _pageId = (0, pageId); - - if (!Contains(_pageId)) - throw new Exception("page id not in document"); - - if (_pageId.Item1 == LastLocation.Item1 && _pageId.Item2 == LastLocation.Item2) - return (-1, -1); - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - int val = _pageId.Item1; - int chapter = val; - val = _pageId.Item2; - int pno = val; - FzLocation loc = mupdf.mupdf.fz_make_location(chapter, pno); - FzLocation nextLoc = mupdf.mupdf.fz_next_page(_nativeDocument, loc); - - pdf.Dispose(); - return (nextLoc.chapter, nextLoc.page); - } + public Dictionary EmbfileInfo(string name) => EmbfileInfo(EmbfileIndex(name)); /// - /// Get (chapter, page) of next page. + /// Change an item of the EmbeddedFiles array by index. /// - /// - /// - public (int, int) NextLocation((int, int) pageId) + public int EmbfileUpd(int idx, byte[] buffer = null, string filename = null, string ufilename = null, string desc = null) { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); - - if (pageId.Item1 == LastLocation.Item1 && pageId.Item2 == LastLocation.Item2) - return (-1, -1); - if (!Contains(pageId)) - throw new Exception("page id not in document"); - - PdfDocument pdf = Document.AsPdfDocument(_nativeDocument); - int val = pageId.Item1; - int chapter = val; - val = pageId.Item2; - int pno = val; - FzLocation loc = mupdf.mupdf.fz_make_location(chapter, pno); - FzLocation nextLoc = mupdf.mupdf.fz_next_page(_nativeDocument, loc); + var names = GetEmbeddedFilesNamesArray(); + if (names.m_internal == null) throw new ValueErrorException($"'{idx}' not in EmbeddedFiles array."); + int count = mupdf.mupdf.pdf_array_len(names) / 2; + if (idx < 0 || idx >= count) throw new ValueErrorException($"'{idx}' not in EmbeddedFiles array."); - pdf.Dispose(); - return (nextLoc.chapter, nextLoc.page); - } + var pdf = NativePdfDocument; + var fs = mupdf.mupdf.pdf_array_get(names, idx * 2 + 1); + var ef = mupdf.mupdf.pdf_dict_get(fs, mupdf.mupdf.pdf_new_name("EF")); + var stream = ef.m_internal != null ? mupdf.mupdf.pdf_dict_get(ef, mupdf.mupdf.pdf_new_name("F")) : new mupdf.PdfObj(); + if (stream.m_internal == null) + throw new InvalidOperationException("bad PDF: no embedded file stream"); - /// - /// - /// - /// - /// - /// - public List PageAnnotXrefs(int n) - { - using (PdfDocument pdf = AsPdfDocument(_nativeDocument)) + if (buffer != null) { - int pageCount = pdf.pdf_count_pages(); - while (n < 0) + mupdf.mupdf.pdf_update_stream(pdf, stream, Helpers.BufferFromBytes(buffer), 1); + var parms = mupdf.mupdf.pdf_dict_get(stream, mupdf.mupdf.pdf_new_name("Params")); + if (parms.m_internal == null) { - n += pageCount; + parms = mupdf.mupdf.pdf_new_dict(pdf, 1); + mupdf.mupdf.pdf_dict_put(stream, mupdf.mupdf.pdf_new_name("Params"), parms); } + mupdf.mupdf.pdf_dict_put(parms, mupdf.mupdf.pdf_new_name("Size"), mupdf.mupdf.pdf_new_int(buffer.Length)); + mupdf.mupdf.pdf_dict_put(stream, mupdf.mupdf.pdf_new_name("DL"), mupdf.mupdf.pdf_new_int(buffer.Length)); + } - if (n > pageCount) - throw new Exception(Utils.ErrorMessages["MSG_BAD_PAGENO"]); - PdfObj pageObj = pdf.pdf_lookup_page_obj(n); - - return Utils.GetAnnotXrefList(pageObj); + if (!string.IsNullOrEmpty(filename)) + { + mupdf.mupdf.pdf_dict_put_text_string(fs, mupdf.mupdf.pdf_new_name("F"), filename); + mupdf.mupdf.pdf_dict_put_text_string(fs, mupdf.mupdf.pdf_new_name("UF"), filename); + mupdf.mupdf.pdf_dict_put_text_string(stream, mupdf.mupdf.pdf_new_name("F"), filename); + mupdf.mupdf.pdf_dict_put_text_string(stream, mupdf.mupdf.pdf_new_name("UF"), filename); + } + if (!string.IsNullOrEmpty(ufilename)) + { + mupdf.mupdf.pdf_dict_put_text_string(fs, mupdf.mupdf.pdf_new_name("UF"), ufilename); + mupdf.mupdf.pdf_dict_put_text_string(stream, mupdf.mupdf.pdf_new_name("UF"), ufilename); + } + if (!string.IsNullOrEmpty(desc)) + { + mupdf.mupdf.pdf_dict_put_text_string(fs, mupdf.mupdf.pdf_new_name("Desc"), desc); + mupdf.mupdf.pdf_dict_put_text_string(stream, mupdf.mupdf.pdf_new_name("Desc"), desc); } - } - /// - /// PDF only: Return the unrotated page rectangle – without loading the page - /// - /// 0-based page number. - /// Rect of the page - /// - public Rect PageCropBox(int pno) - { - if (IsClosed) - throw new Exception("document closed"); - - FzDocument doc = _nativeDocument; - int pageCount = doc.fz_count_pages(); - int n = pno; - while (n < 0) - n += pageCount; - - PdfDocument pdf = AsPdfDocument(doc); - if (n >= pageCount) - throw new Exception(Utils.ErrorMessages["MSG_BAD_PAGENO"]); - - PdfObj pageRef = pdf.pdf_lookup_page_obj(n); - Rect cropbox = Utils.GetCropBox(pageRef); + var dateNow = Helpers.GetPdfNow(); + var paramsObj = mupdf.mupdf.pdf_dict_get(stream, mupdf.mupdf.pdf_new_name("Params")); + if (paramsObj.m_internal == null) + { + paramsObj = mupdf.mupdf.pdf_new_dict(pdf, 1); + mupdf.mupdf.pdf_dict_put(stream, mupdf.mupdf.pdf_new_name("Params"), paramsObj); + } + mupdf.mupdf.pdf_dict_put_text_string(paramsObj, mupdf.mupdf.pdf_new_name("ModDate"), dateNow); - pdf.Dispose(); - return cropbox; + return mupdf.mupdf.pdf_to_num(stream); } /// - /// Convert (chapter, pno) to page number. + /// Change an item of the EmbeddedFiles array by name. /// - /// page id - /// chapter and pno - public int GetPageNumberFromLocation(int pageId) - { - int pageN = PageCount; - while (pageId < 0) - pageId += pageN; - - (int, int) _pageId = (0, pageId); - if (!Contains(_pageId)) - throw new Exception("page id not in document"); + public int EmbfileUpd(string name, byte[] buffer = null, string filename = null, string ufilename = null, string desc = null) + => EmbfileUpd(EmbfileIndex(name), buffer, filename, ufilename, desc); - (int chapter, int pno) = _pageId; - FzLocation loc = mupdf.mupdf.fz_make_location(chapter, pno); - pageN = _nativeDocument.fz_page_number_from_location(loc); - - return pageN; + private int EmbfileIndex(string name) + { + var names = EmbfileNames(); + int idx = names.IndexOf(name); + if (idx < 0) throw new ValueErrorException($"'{name}' not in EmbeddedFiles array."); + return idx; } + // ─── Font / Image extraction ──────────────────────────────────── + /// - /// Convert (chapter, pno) to page number. + /// List fonts used on a page. /// - /// page id - /// chapter and pno - public int GetPageNumberFromLocation(int chapter, int pno) + public List<(int xref, string ext, string type, string baseName, string name, string encoding)> GetPageFonts(int pno, bool full = false) { - int pageN = PageCount; - while (pno < 0) - pno += pageN; - - FzLocation loc = mupdf.mupdf.fz_make_location(chapter, pno); - pageN = _nativeDocument.fz_page_number_from_location(loc); - - return pageN; + // Keep Python structure: get_page_fonts() -> _getPageInfo(..., 1) + var pageInfo = _getPageInfo(pno, 1); + var result = new List<(int xref, string ext, string type, string baseName, string name, string encoding)>(pageInfo.Count); + foreach (var item in pageInfo) + result.Add(((int xref, string ext, string type, string baseName, string name, string encoding))item); + return result; } /// - /// PDF only: Return the xref of the page – without loading the page + /// List images used on a page. /// - /// 0-based page number - /// xref of the page - /// - public int PageXref(int pno) + public List<(int xref, string smask, int width, int height, int bpc, string colorspace, string altCs, string name, string filter)> GetPageImages(int pno, bool full = false) { - if (IsClosed) - throw new Exception("document closed"); - - int pageCount = _nativeDocument.fz_count_pages(); - int n = pno; - while (n < 0) - n += pageCount; - - PdfDocument pdf = AsPdfDocument(_nativeDocument); - int xref = 0; - if (n >= pageCount) - throw new Exception(Utils.ErrorMessages["MSG_BAD_PAGENO"]); - - xref = pdf.pdf_lookup_page_obj(n).pdf_to_num(); - - pdf.Dispose(); - - return xref; + // Keep Python structure: get_page_images() -> _getPageInfo(..., 2) + var pageInfo = _getPageInfo(pno, 2); + var result = new List<(int xref, string smask, int width, int height, int bpc, string colorspace, string altCs, string name, string filter)>(pageInfo.Count); + foreach (var item in pageInfo) + result.Add(((int xref, string smask, int width, int height, int bpc, string colorspace, string altCs, string name, string filter))item); + return result; } - /// - /// A generator for a range of pages. - /// - /// start iteration with this page number - /// stop iteration at this page number. - /// stop iteration at this page number. - /// a generator iterator over the document’s pages. - /// - public List GetPages(int start, int stop, int step) + internal List<(int xref, string ext, string type, string baseName, string name, string encoding)> GetPageFontsCore(int pno, bool full = false) { - while (start < 0) - start += PageCount; - - if (!(PageCount > start && start >= 0)) - throw new Exception("bad start page number"); - - stop = (stop <= PageCount) ? stop : PageCount; - if (step == 0) - throw new Exception("arg 3 must not be zero"); + var result = new List<(int xref, string ext, string type, string baseName, string name, string encoding)>(); + if (!IsPdf) + return result; - if ((start > stop && step > 0) || (start < stop && step < 0)) - throw new Exception("bad step, pick right direction"); + using var page = LoadPage(pno); + var pageObj = page.NativePdfPage.obj(); + var resources = mupdf.mupdf.pdf_dict_get_inheritable(pageObj, mupdf.mupdf.pdf_new_name("Resources")); + if (resources.m_internal == null) + return result; - List ret = new List(); - for (int i = start; i < stop; i += step) + var fonts = mupdf.mupdf.pdf_dict_get(resources, mupdf.mupdf.pdf_new_name("Font")); + if (fonts.m_internal == null || mupdf.mupdf.pdf_is_dict(fonts) == 0) + return result; + + int n = mupdf.mupdf.pdf_dict_len(fonts); + for (int i = 0; i < n; i++) { - ret.Add(LoadPage(i)); - } + var key = mupdf.mupdf.pdf_dict_get_key(fonts, i); + var val = mupdf.mupdf.pdf_dict_get_val(fonts, i); + var resolved = mupdf.mupdf.pdf_resolve_indirect(val); - return ret; - } + int xref = mupdf.mupdf.pdf_to_num(val); + string ext = ""; + string type = ""; + string baseName = ""; + string name = mupdf.mupdf.pdf_to_name(key) ?? ""; + string encoding = ""; - /// - /// Add new form font. - /// - /// - /// - /// - private void AddFormFont(string name, string font) - { - if (IsClosed || IsEncrypted) - throw new Exception("document closed or encrypted"); + var subtypeObj = mupdf.mupdf.pdf_dict_get(resolved, mupdf.mupdf.pdf_new_name("Subtype")); + if (subtypeObj.m_internal != null && mupdf.mupdf.pdf_is_name(subtypeObj) != 0) + type = mupdf.mupdf.pdf_to_name(subtypeObj); - using (PdfDocument pdf = AsPdfDocument(this)) - { - if (pdf.m_internal == null) - return; + var basefontObj = mupdf.mupdf.pdf_dict_get(resolved, mupdf.mupdf.pdf_new_name("BaseFont")); + if (basefontObj.m_internal != null && mupdf.mupdf.pdf_is_name(basefontObj) != 0) + baseName = mupdf.mupdf.pdf_to_name(basefontObj); - PdfObj fonts = Utils.pdf_dict_getl( - pdf.pdf_trailer(), - new string[] { "Root", "AcroFrom", "DR", "Font" } - ); + var encodingObj = mupdf.mupdf.pdf_dict_get(resolved, mupdf.mupdf.pdf_new_name("Encoding")); + if (encodingObj.m_internal != null) + { + if (mupdf.mupdf.pdf_is_name(encodingObj) != 0) + encoding = mupdf.mupdf.pdf_to_name(encodingObj); + else if (mupdf.mupdf.pdf_is_dict(encodingObj) != 0) + { + var baseEncObj = mupdf.mupdf.pdf_dict_get(encodingObj, mupdf.mupdf.pdf_new_name("BaseEncoding")); + if (baseEncObj.m_internal != null && mupdf.mupdf.pdf_is_name(baseEncObj) != 0) + encoding = mupdf.mupdf.pdf_to_name(baseEncObj); + } + } - if (fonts.m_internal == null || fonts.pdf_is_dict() == 0) - throw new Exception("PDF has no form fonts yet"); + if (xref > 0) + { + try { ext = ExtractFont(xref).ext ?? ""; } + catch { ext = ""; } + } - PdfObj k = mupdf.mupdf.pdf_new_name(name); - PdfObj v = Utils.PdfObjFromStr(pdf, font); - fonts.pdf_dict_put(k, v); + result.Add((xref, ext, type ?? "", baseName ?? "", name ?? "", encoding ?? "")); } + + return result; } - /// - /// Add color info to all items of an extended TOC list. - /// - /// - /// - public void ExtendTocItems(List items) + internal List<(int xref, string smask, int width, int height, int bpc, string colorspace, string altCs, string name, string filter)> GetPageImagesCore(int pno, bool full = false) { - if (IsClosed) - throw new Exception("document closed"); - - using (PdfDocument pdf = AsPdfDocument(this)) - { - //string zoom = "zoom"; - //string bold = "bold"; - //string italic = "italic"; - //string collapse = "collapse"; - float[] color = null; - float z = 0; - - PdfObj root = pdf.pdf_trailer().pdf_dict_get(new PdfObj("Root")); - if (root.m_internal == null) - return; + var result = new List<(int xref, string smask, int width, int height, int bpc, string colorspace, string altCs, string name, string filter)>(); + if (!IsPdf) + return result; - PdfObj olRoot = root.pdf_dict_get(new PdfObj("Outlines")); - if (olRoot.m_internal == null) - return; + using var page = LoadPage(pno); + var pageObj = page.NativePdfPage.obj(); + var resources = mupdf.mupdf.pdf_dict_get_inheritable(pageObj, mupdf.mupdf.pdf_new_name("Resources")); + if (resources.m_internal == null) + return result; - PdfObj first = olRoot.pdf_dict_get(new PdfObj("First")); - if (first.m_internal == null) - return; + var xobjects = mupdf.mupdf.pdf_dict_get(resources, mupdf.mupdf.pdf_new_name("XObject")); + if (xobjects.m_internal == null || mupdf.mupdf.pdf_is_dict(xobjects) == 0) + return result; - List xrefs = new List(); - xrefs = Utils.GetOutlineXrefs(first, xrefs); - int n = xrefs.Count; - int m = items.Count; + int n = mupdf.mupdf.pdf_dict_len(xobjects); + for (int i = 0; i < n; i++) + { + var key = mupdf.mupdf.pdf_dict_get_key(xobjects, i); + var val = mupdf.mupdf.pdf_dict_get_val(xobjects, i); + var resolved = mupdf.mupdf.pdf_resolve_indirect(val); - if (n == 0) - return; - if (n != m) - throw new Exception("internal error finding outline xrefs"); + var subtypeObj = mupdf.mupdf.pdf_dict_get(resolved, mupdf.mupdf.pdf_new_name("Subtype")); + if (subtypeObj.m_internal == null || mupdf.mupdf.pdf_is_name(subtypeObj) == 0) + continue; + if (!string.Equals(mupdf.mupdf.pdf_to_name(subtypeObj), "Image", StringComparison.Ordinal)) + continue; - for (int i = 0; i < n; i++) + int xref = mupdf.mupdf.pdf_to_num(val); + if (xref <= 0 || !XrefIsStream(xref)) + continue; + string name = mupdf.mupdf.pdf_to_name(key) ?? ""; + + int width = 0; + int height = 0; + int bpc = 0; + string colorspace = ""; + string altCs = ""; + string filter = ""; + string smask = "0"; + + var wObj = mupdf.mupdf.pdf_dict_get(resolved, mupdf.mupdf.pdf_new_name("Width")); + if (wObj.m_internal != null) width = mupdf.mupdf.pdf_to_int(wObj); + var hObj = mupdf.mupdf.pdf_dict_get(resolved, mupdf.mupdf.pdf_new_name("Height")); + if (hObj.m_internal != null) height = mupdf.mupdf.pdf_to_int(hObj); + var bpcObj = mupdf.mupdf.pdf_dict_get(resolved, mupdf.mupdf.pdf_new_name("BitsPerComponent")); + if (bpcObj.m_internal != null) bpc = mupdf.mupdf.pdf_to_int(bpcObj); + + var csObj = mupdf.mupdf.pdf_dict_get(resolved, mupdf.mupdf.pdf_new_name("ColorSpace")); + if (csObj.m_internal != null) { - int xref = xrefs[i]; - Toc item = items[i]; - LinkInfo link; - if (item.Link != null) - link = item.Link; - else - throw new Exception("need non-simple TOC format"); - - link.Xref = xrefs[i]; - PdfObj bm = pdf.pdf_load_object(xref); - int flags = bm.pdf_dict_get(new PdfObj("F")).pdf_to_int(); - if (flags == 1) - link.Italic = true; - else if (flags == 2) - link.Bold = true; - else if (flags == 3) + if (mupdf.mupdf.pdf_is_name(csObj) != 0) { - link.Italic = true; - link.Bold = true; + colorspace = mupdf.mupdf.pdf_to_name(csObj); } - int count = bm.pdf_dict_get(new PdfObj("F")).pdf_to_int(); - if (count < 0) - link.Collapse = true; - else if (count > 0) - link.Collapse = false; - PdfObj col = bm.pdf_dict_get(new PdfObj("C")); - if (col.pdf_is_array() != 0 && col.pdf_array_len() == 3) + else if (mupdf.mupdf.pdf_is_array(csObj) != 0 && mupdf.mupdf.pdf_array_len(csObj) > 0) { - color = new float[3] + var cs0 = mupdf.mupdf.pdf_array_get(csObj, 0); + if (cs0.m_internal != null && mupdf.mupdf.pdf_is_name(cs0) != 0) + colorspace = mupdf.mupdf.pdf_to_name(cs0); + if (mupdf.mupdf.pdf_array_len(csObj) > 1) { - col.pdf_array_get(0).pdf_to_real(), - col.pdf_array_get(1).pdf_to_real(), - col.pdf_array_get(2).pdf_to_real(), - }; - link.Color = color; + var cs1 = mupdf.mupdf.pdf_array_get(csObj, 1); + if (cs1.m_internal != null && mupdf.mupdf.pdf_is_name(cs1) != 0) + altCs = mupdf.mupdf.pdf_to_name(cs1); + } } + } - PdfObj obj = bm.pdf_dict_get(new PdfObj("Dest")); - if (obj.m_internal == null || obj.pdf_is_array() == 0) + var filterObj = mupdf.mupdf.pdf_dict_get(resolved, mupdf.mupdf.pdf_new_name("Filter")); + if (filterObj.m_internal != null) + { + if (mupdf.mupdf.pdf_is_name(filterObj) != 0) + filter = mupdf.mupdf.pdf_to_name(filterObj); + else if (mupdf.mupdf.pdf_is_array(filterObj) != 0 && mupdf.mupdf.pdf_array_len(filterObj) > 0) { - obj = Utils.pdf_dict_getl(bm, new string[] { "A", "D" }); + var f0 = mupdf.mupdf.pdf_array_get(filterObj, 0); + if (f0.m_internal != null && mupdf.mupdf.pdf_is_name(f0) != 0) + filter = mupdf.mupdf.pdf_to_name(f0); } + } - if (obj.pdf_is_array() != 0 && obj.pdf_array_len() == 5) - { - z = obj.pdf_array_get(4).pdf_to_real(); - } + var smaskObj = mupdf.mupdf.pdf_dict_get(resolved, mupdf.mupdf.pdf_new_name("SMask")); + if (smaskObj.m_internal != null) + smask = mupdf.mupdf.pdf_to_num(smaskObj).ToString(System.Globalization.CultureInfo.InvariantCulture); - link.Zoom = z; - item.Link = link; - items[i] = item; - } + result.Add((xref, smask, width, height, bpc, colorspace ?? "", altCs ?? "", name, filter ?? "")); } + + return result; } /// - /// Remove a page from document page dict. + /// Retrieve annotation xref/type/id triples for a page. /// - /// - public void ForgetPage(Page page) + public List<(int xref, AnnotationType type, string id)> PageAnnotXrefs(int n) { - int pid = page.GetHashCode(); - if (PageRefs.ContainsKey(pid)) - { - PageRefs.Remove(pid); - } + int pageCount = PageCount; + while (n < 0) + n += pageCount; + if (n < 0 || n >= pageCount) + throw new ValueErrorException(Constants.MSG_BAD_PAGENO); + using var page = LoadPage(n); + return page.AnnotXrefs(); } - internal List<(int, string)> _getPageLabels() + /// + /// Extract a font by xref. Returns (name, ext, type, content). + /// + public (string name, string ext, string type, byte[] content) ExtractFont(int xref) { - using (PdfDocument pdf = AsPdfDocument(this)) - { - List<(int, string)> rc = new List<(int, string)>(); + var pdf = NativePdfDocument; + var obj = mupdf.mupdf.pdf_load_object(pdf, xref); + string name = "", ext = "", type = ""; + byte[] content = Array.Empty(); - PdfObj obj = Utils.pdf_dict_getl( - pdf.pdf_trailer(), - new string[] { "Root", "PageLabels" } - ); - if (obj.m_internal == null) - return rc; + var basefont = mupdf.mupdf.pdf_dict_gets(obj, "BaseFont"); + if (basefont.m_internal != null) name = mupdf.mupdf.pdf_to_name(basefont); - PdfObj nums = obj.pdf_dict_get(new PdfObj("Nums")).pdf_resolve_indirect(); - if (nums.m_internal != null) - { - Utils.GetPageLabels(rc, nums); - return rc; - } + var subtype = mupdf.mupdf.pdf_dict_gets(obj, "Subtype"); + if (subtype.m_internal != null) type = mupdf.mupdf.pdf_to_name(subtype); - nums = Utils.pdf_dict_getl(obj, new string[] { "Kids", "Nums" }).pdf_resolve_indirect(); - if (nums.m_internal != null) + var desc = mupdf.mupdf.pdf_dict_gets(obj, "FontDescriptor"); + if (desc.m_internal != null) + { + var ff = mupdf.mupdf.pdf_dict_gets(desc, "FontFile"); + if (ff.m_internal == null) ff = mupdf.mupdf.pdf_dict_gets(desc, "FontFile2"); + if (ff.m_internal == null) ff = mupdf.mupdf.pdf_dict_gets(desc, "FontFile3"); + if (ff.m_internal != null) { - Utils.GetPageLabels(rc, nums); - return rc; + var buf = mupdf.mupdf.pdf_load_stream(ff); + content = buf.fz_buffer_extract(); + ext = "n/a"; + var sub2 = mupdf.mupdf.pdf_dict_gets(ff, "Subtype"); + if (sub2.m_internal != null) + { + string sn = mupdf.mupdf.pdf_to_name(sub2); + if (sn.Contains("Type1C")) ext = "cff"; + else if (sn.Contains("CIDFontType0C")) ext = "cff"; + else if (sn.Contains("OpenType")) ext = "otf"; + else ext = "ttf"; + } + else ext = type == "Type1" ? "pfa" : "ttf"; } + } + return (name, ext, type, content); + } - PdfObj kids = obj.pdf_dict_get(new PdfObj("Kids")).pdf_resolve_indirect(); - if (kids.m_internal == null || kids.pdf_is_array() == 0) - { - return rc; - } + private object[] CheckFontInfo(int xref) + { + foreach (var fi in FontInfos) + { + if (fi == null || fi.Length < 2) + continue; + if (!(fi[0] is int)) + continue; + if ((int)fi[0] == xref) + return fi; + } + return null; + } - int n = kids.pdf_array_len(); - for (int i = 0; i < n; i++) + private void UpdateFontInfo(object[] fontInfo) + { + if (fontInfo == null || fontInfo.Length < 2 || !(fontInfo[0] is int)) + return; + int xref = (int)fontInfo[0]; + for (int i = 0; i < FontInfos.Count; i++) + { + var fi = FontInfos[i]; + if (fi != null && fi.Length > 0 && fi[0] is int && (int)fi[0] == xref) { - nums = kids.pdf_array_get(i) - .pdf_dict_get(new PdfObj("Nums")) - .pdf_resolve_indirect(); - Utils.GetPageLabels(rc, nums); + FontInfos[i] = fontInfo; + return; } - - return rc; } + FontInfos.Add(fontInfo); } - /// - /// Return page label definitions in PDF document. - /// - /// A list of dictionaries with the following format - public List
GetTables( - Rect clip = null, - string vertical_strategy = "lines", - string horizontal_strategy = "lines", - List vertical_lines = null, - List horizontal_lines = null, - float snap_tolerance = TableFlags.TABLE_DEFAULT_SNAP_TOLERANCE, - float snap_x_tolerance = 0.0f, - float snap_y_tolerance = 0.0f, - float join_tolerance = TableFlags.TABLE_DEFAULT_JOIN_TOLERANCE, - float join_x_tolerance = 0.0f, - float join_y_tolerance = 0.0f, - float edge_min_length = 3.0f, - float min_words_vertical = TableFlags.TABLE_DEFAULT_MIN_WORDS_VERTICAL, - float min_words_horizontal = TableFlags.TABLE_DEFAULT_MIN_WORDS_HORIZONTAL, - float intersection_tolerance = 3.0f, - float intersection_x_tolerance = 0.0f, - float intersection_y_tolerance = 0.0f, - float text_tolerance = 3.0f, - float text_x_tolerance = 3.0f, - float text_y_tolerance = 3.0f, - string strategy = null, // offer abbreviation - List add_lines = null - ) - { - return Utils.GetTables(this, clip, - vertical_strategy, horizontal_strategy, vertical_lines, horizontal_lines, - snap_tolerance, snap_x_tolerance, snap_y_tolerance, - join_tolerance, join_x_tolerance, join_y_tolerance, - edge_min_length, min_words_vertical, min_words_horizontal, - intersection_tolerance, intersection_x_tolerance, intersection_y_tolerance, - text_tolerance, text_x_tolerance, text_y_tolerance, strategy, add_lines); - } - - /// - /// Read barcodes from page. - /// - /// - /// Decode barcodes only from embedded images in the PDF resources. - /// Barcode format to decode. - /// Spend more time to try to find a barcode; optimize for accuracy, not speed. - /// Try to decode as inverted image. - /// Image is a pure monochrome image of a barcode. - /// Try to read multi barcodes on page. - /// Indicate whether the image should be automatically rotated. - /// Rotation is supported for 90, 180 and 270 degrees. - public List ReadBarcodes( - Rect clip = null, - bool decodeEmbeddedOnly = false, - BarcodeFormat barcodeFormat = BarcodeFormat.ALL, - bool tryHarder = true, - bool tryInverted = false, - bool pureBarcode = false, - bool multi = true, - bool autoRotate = true - ) - { - return Utils.ReadBarcodes(this, clip, decodeEmbeddedOnly, barcodeFormat, tryHarder, tryInverted, pureBarcode, multi, autoRotate); - } - - /// - /// Write barcode to page. - /// - /// Rect area on page to write - /// Contents to write - /// Format to encode; Supported formats: QR_CODE, EAN_8, EAN_13, UPC_A, CODE_39, CODE_128, ITF, PDF_417, CODABAR - /// Use a specific character set for binary encoding (if supported by the selected barcode format) - /// don't generate ECI segment if non-default character set is used - /// Resize output barcode image width/height with params, Avoid enabling this parameter, as it can reduce barcode recognition accuracy. - /// Don't put the content string into the output image - /// Specifies margin left, in pixels, to use when generating the barcode - /// Specifies margin top, in pixels, to use when generating the barcode - /// Specifies margin right, in pixels, to use when generating the barcode - /// Specifies margin bottom, in pixels, to use when generating the barcode - /// The width of the narrow bar in pixels - public void WriteBarcode( - Rect clip, - string text, - BarcodeFormat barcodeFormat, - string characterSet = null, - bool disableEci = false, - bool forceFitToRect = false, - bool pureBarcode = false, - int marginLeft = 0, - int marginTop = 0, - int marginRight = 0, - int marginBottom = 0, - int narrowBarWidth = 0 - ) - { - Utils.WriteBarcode(this, clip, - text, barcodeFormat, characterSet, disableEci, forceFitToRect, pureBarcode, marginLeft, marginTop, marginRight, marginBottom, narrowBarWidth); + return false; + var xobj = mupdf.mupdf.pdf_dict_get(resources, mupdf.mupdf.pdf_new_name("XObject")); + if (xobj.m_internal == null) + return false; + var existing = mupdf.mupdf.pdf_dict_gets(xobj, name); + return existing.m_internal != null; } + // ─── Fonts and Images ─────────────────────────────────────────── + /// - /// Run page through a device. + /// List of fonts defined in the page object. /// - /// - /// Transformation to apply to the page. - public void Run(DeviceWrapper dw, Matrix m) + public List<(int xref, string ext, string type, string baseName, string name, string encoding)> GetFonts(bool full = false) { - _nativePage.fz_run_page(dw._nativeDevice, m.ToFzMatrix(), new FzCookie()); + return RequireParent().GetPageFonts(Number, full); } /// - /// Return text selected between p1 and p2 + /// List of images defined in the page object. /// - /// - /// - /// - /// - /// - public string GetTextSelection( - Point p1, - Point p2, - Rect clip = null, - TextPage textPage = null - ) + public List<(int xref, string smask, int width, int height, int bpc, string colorspace, string altCs, string name, string filter)> GetImages(bool full = false) { - return Utils.GetTextSelection(this, p1, p2, clip, textPage); + return RequireParent().GetPageImages(Number, full); } /// - /// + /// Insert an image for display in a rectangle. /// - /// - /// - /// - /// - /// - /// - public List GetTextWords( - Rect clip = null, - int flags = 0, - TextPage textPage = null, - bool sort = false, - char[] delimiters = null - ) + public int InsertImage(Rect rect, string filename = null, byte[] stream = null, Pixmap pixmap = null, + string mask = null, int rotate = 0, int xref = 0, int oc = 0, bool keepProportion = true, + int alpha = -1, string overlay = "true") { - return Utils.GetTextWords(this, clip, flags, textPage, sort, delimiters); - } + var pdf = RequireParent().NativePdfDocument; + var pdfPage = NativePdfPage; - public string GetTextbox(Rect rect = null, TextPage textPage = null) - { - return Utils.GetTextbox(this, rect, textPage); + mupdf.FzImage fzImg; + if (xref > 0) + { + var obj = mupdf.mupdf.pdf_new_indirect(pdf, xref, 0); + fzImg = mupdf.mupdf.pdf_load_image(pdf, obj); + } + else if (!string.IsNullOrEmpty(filename)) + { + fzImg = mupdf.mupdf.fz_new_image_from_file(filename); + } + else if (stream != null && stream.Length > 0) + { + var buf = Helpers.BufferFromBytes(stream); + fzImg = mupdf.mupdf.fz_new_image_from_buffer(buf); + } + else if (pixmap != null) + { + fzImg = mupdf.mupdf.fz_new_image_from_pixmap(pixmap.NativePixmap, new mupdf.FzImage()); + } + else + throw new ArgumentException("need one of filename, stream, pixmap or xref"); + + var imgObj = mupdf.mupdf.pdf_add_image(pdf, fzImg); + int imgXref = mupdf.mupdf.pdf_to_num(imgObj); + + float w = (float)rect.Width, h = (float)rect.Height; + if (keepProportion) + { + int imgW = fzImg.w(); + int imgH = fzImg.h(); + float scaleW = w / imgW, scaleH = h / imgH; + float scale = Math.Min(scaleW, scaleH); + w = imgW * scale; + h = imgH * scale; + } + + string name = $"Img{imgXref}"; + var resources = mupdf.mupdf.pdf_dict_get(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Resources")); + if (resources.m_internal == null) + { + resources = mupdf.mupdf.pdf_new_dict(pdf, 2); + mupdf.mupdf.pdf_dict_put(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Resources"), resources); + } + var xObject = mupdf.mupdf.pdf_dict_get(resources, mupdf.mupdf.pdf_new_name("XObject")); + if (xObject.m_internal == null) + { + xObject = mupdf.mupdf.pdf_new_dict(pdf, 1); + mupdf.mupdf.pdf_dict_put(resources, mupdf.mupdf.pdf_new_name("XObject"), xObject); + } + mupdf.mupdf.pdf_dict_puts(xObject, name, imgObj); + + string cmd = $"q {w:G} 0 0 {h:G} {rect.X0:G} {rect.Y1 - h:G} cm /{name} Do Q\n"; + var buf2 = Helpers.BufferFromBytes(System.Text.Encoding.ASCII.GetBytes(cmd)); + var contents = mupdf.mupdf.pdf_dict_get(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Contents")); + if (contents.m_internal == null) + { + var stream2 = mupdf.mupdf.pdf_add_stream(pdf, buf2, new mupdf.PdfObj(), 0); + mupdf.mupdf.pdf_dict_put(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Contents"), stream2); + } + else + { + var newStream = mupdf.mupdf.pdf_add_stream(pdf, buf2, new mupdf.PdfObj(), 0); + if (mupdf.mupdf.pdf_is_array(contents) != 0) + mupdf.mupdf.pdf_array_push(contents, newStream); + else + { + var arr = mupdf.mupdf.pdf_new_array(pdf, 2); + mupdf.mupdf.pdf_array_push(arr, contents); + mupdf.mupdf.pdf_array_push(arr, newStream); + mupdf.mupdf.pdf_dict_put(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Contents"), arr); + } + } + + return imgXref; } /// - /// Create a Textpage from combined results of normal and OCR text parsing + /// Insert a font for use on the page. /// - /// control content becoming part of the result - /// specify expected language(s) - /// resolution in dpi, default 72 - /// whether to OCR the full page image, or only its images (default) - /// - /// Optional preprocessing filters applied to raster images before OCR. - /// - /// Maximum size of the full-page pixmap sample buffer before OCR is split vertically into strips. - /// Default 120,000,000 bytes (~114 MiB). Set lower on machines with limited RAM. - /// - /// - public TextPage GetTextPageOcr( - int flags = 0, - string language = "eng", - int dpi = 72, - bool full = false, - string tessdata = null, - ImageFilterPipeline imageFilters = null, - long maxOcrPixmapBytes = 3000000 - ) - { - if (string.IsNullOrEmpty(Utils.TESSDATA_PREFIX) && string.IsNullOrEmpty(tessdata)) - throw new Exception("No OCR support: TESSDATA_PREFIX not set"); - if (maxOcrPixmapBytes <= 0) - throw new ArgumentOutOfRangeException(nameof(maxOcrPixmapBytes), "maxOcrPixmapBytes must be greater than zero."); - - TextPage FullOcr(Page page, int _dpi, string _language, int _flags, ImageFilterPipeline filters) + public int InsertFont(string fontname = "helv", string fontfile = null, byte[] fontbuffer = null, + bool setSimple = false, int encoding = 0) + { + lock (s_insertFontLock) { - if (_dpi <= 0) - throw new ArgumentOutOfRangeException(nameof(_dpi), "dpi must be greater than zero."); + var pdf = RequireParent().NativePdfDocument; + var pdfPage = NativePdfPage; - long GetPixmapBytes(Pixmap p) + mupdf.PdfObj fontObj; + if (!string.IsNullOrEmpty(fontfile)) { - byte[] samples = p.SAMPLES; - if (samples != null && samples.Length > 0) - return samples.Length; - return (long)p.Stride * p.H; + var buf = mupdf.mupdf.fz_read_file(fontfile); + var fzFont = mupdf.mupdf.fz_new_font_from_buffer(fontname, buf, 0, 0); + fontObj = mupdf.mupdf.pdf_add_cid_font(pdf, fzFont); } - - int effectiveDpi = _dpi; - for (int i = 0; i < 3; i++) + else if (fontbuffer != null && fontbuffer.Length > 0) { - float probeZoom = effectiveDpi / 72.0f; - using (Pixmap probe = page.GetPixmap(matrix: new Matrix(probeZoom, probeZoom))) - { - long probeBytes = GetPixmapBytes(probe); - if (probeBytes <= maxOcrPixmapBytes) - break; - - double scale = Math.Sqrt((double)maxOcrPixmapBytes / probeBytes); - int nextDpi = Math.Max(1, (int)Math.Floor(effectiveDpi * scale)); - if (nextDpi >= effectiveDpi) - nextDpi = effectiveDpi - 1; - if (nextDpi < 1) - nextDpi = 1; - effectiveDpi = nextDpi; - } + var buf = Helpers.BufferFromBytes(fontbuffer); + var fzFont = mupdf.mupdf.fz_new_font_from_buffer(fontname, buf, 0, 0); + fontObj = mupdf.mupdf.pdf_add_cid_font(pdf, fzFont); } - - float zoom = effectiveDpi / 72.0f; - Matrix mat = new Matrix(zoom, zoom); - using (Pixmap basePix = page.GetPixmap(matrix: mat)) + else { - Pixmap workingPix = basePix; - Pixmap filteredPix = null; - try - { - if (filters != null) - { - filteredPix = Pixmap.ApplyImageFilters(basePix, filters); - if (filteredPix != null) - workingPix = filteredPix; - } - - using (Document ocrPdf = new Document("pdf", workingPix.PdfOCR2Bytes(true, _language, tessdata))) - using (Page ocrPage = ocrPdf.LoadPage(0)) - { - float unZoom = page.Rect.Width / ocrPage.Rect.Width; - Matrix ctm = new Matrix(unZoom, unZoom) * page.DerotationMatrix; - TextPage ocrTp = ocrPage.GetTextPage(flags: _flags, matrix: ctm); - ocrTp.Parent = this; - return ocrTp; - } - } - finally - { - if (filteredPix != null && !object.ReferenceEquals(filteredPix, basePix)) - filteredPix.Dispose(); - } + string base14 = Font.NormalizeBase14FontName(fontname); + var fzFont = mupdf.mupdf.fz_new_base14_font(base14); + if (fzFont?.m_internal == null) + throw new ValueErrorException($"cannot create base-14 font: {fontname}"); + fontObj = mupdf.mupdf.pdf_add_simple_font(pdf, fzFont, encoding); } - } - if (full) - return FullOcr(this, dpi, language, flags, imageFilters); - TextPage tp = GetTextPage(flags: flags); - foreach ( - Block block in ( - GetText("dict", flags: (int)TextFlags.TEXT_PRESERVE_IMAGES) as PageInfo - ).Blocks - ) - { - if (block.Type != 1) - continue; - Rect bbox = new Rect(block.Bbox); - if (bbox.Width <= 3 || bbox.Height <= 3) - continue; - try + int xref = mupdf.mupdf.pdf_to_num(fontObj); + + var resources = mupdf.mupdf.pdf_dict_get(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Resources")); + if (resources.m_internal == null) { - Pixmap pix = new Pixmap(block.Image); - if (imageFilters != null) - pix = Pixmap.ApplyImageFilters(pix, imageFilters); - if (pix.N - pix.Alpha != 3) - pix = new Pixmap(new ColorSpace(Utils.CS_RGB), pix); - if (pix.Alpha != 0) - pix = new Pixmap(pix, 0); - Document imgDoc = new Document( - "pdf", - pix.PdfOCR2Bytes(language: language, tessdata: tessdata) - ); - Page imgPage = imgDoc.LoadPage(0); - pix = null; - Rect imgRect = imgPage.Rect; - Matrix shrink = new Matrix(1 / imgRect.Width, 1 / imgRect.Height); - Matrix mat = shrink; //* block.; - - imgPage.ExtendTextPage(tp, flags: 0, m: mat); - imgDoc.Close(); + resources = mupdf.mupdf.pdf_new_dict(pdf, 2); + mupdf.mupdf.pdf_dict_put(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Resources"), resources); } - catch (Exception) + var fonts = mupdf.mupdf.pdf_dict_get(resources, mupdf.mupdf.pdf_new_name("Font")); + if (fonts.m_internal == null) { - tp = null; - Console.WriteLine("Falling back to full page OCR"); - return FullOcr(this, dpi, language, flags, imageFilters); + fonts = mupdf.mupdf.pdf_new_dict(pdf, 1); + mupdf.mupdf.pdf_dict_put(resources, mupdf.mupdf.pdf_new_name("Font"), fonts); } - } + string resName = $"F{xref}"; + mupdf.mupdf.pdf_dict_puts(fonts, resName, fontObj); - return tp; + return xref; + } } /// - /// PDF only: Insert text into the specified rect. + /// Extract image information from the page. /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public float InsertTextbox( - Rect rect, - dynamic text, - string fontName = "helv", - string fontFile = null, - float fontSize = 11, - float lineHeight = 0, - int setSimple = 0, - int encoding = 0, - float[] color = null, - float[] fill = null, - int expandTabs = 1, - int align = 0, - float borderWidth = 0.05f, - int renderMode = 0, - int rotate = 0, - Morph morph = null, - bool overlay = true, - float strokeOpacity = 1, - float fillOpacity = 1, - int oc = 0 - ) - { - if (string.IsNullOrEmpty(fontName) && string.IsNullOrEmpty(fontFile)) - throw new Exception("should include fontName and fontFile."); - Shape img = new Shape(this); - float ret = img.InsertTextbox( - rect, - text, - fontFile, - fontName, - fontSize, - lineHeight, - setSimple != 0, - encoding, - color, - fill, - expandTabs, - align, - renderMode, - borderWidth, - rotate, - morph, - strokeOpacity, - fillOpacity, - oc - ); - if (ret >= 0) - img.Commit(overlay); - - return ret; + public List> GetImageInfo(bool hashes = false, bool xrefs = false) + { + var doc = RequireParent(); + if (xrefs && doc.IsPdf) + hashes = true; + if (!doc.IsPdf) + xrefs = false; + + var imginfo = _imageInfo; + if (imginfo != null && !xrefs) + return imginfo; + + if (imginfo == null) + { + using (var tp = GetTextPage(flags: mupdf.mupdf.FZ_STEXT_PRESERVE_IMAGES)) + { + imginfo = tp.ExtractImgInfo(hashes); + } + if (hashes) + _imageInfo = imginfo; + } + + if (!xrefs || !doc.IsPdf) + return imginfo; + + var imglist = GetImages(); + var digests = new Dictionary(); + foreach (var item in imglist) + { + int xref = item.xref; + var obj = mupdf.mupdf.pdf_load_object(doc.NativePdfDocument, xref); + var image = mupdf.mupdf.pdf_load_image(doc.NativePdfDocument, obj); + var pix = image.fz_get_pixmap_from_image(new mupdf.FzIrect(mupdf.mupdf.fz_infinite_irect), new mupdf.FzMatrix(image.w(), 0, 0, image.h(), 0, 0), null, null); + var md5 = pix.fz_md5_pixmap2(); + byte[] digestBytes = new byte[md5.Count]; + for (int i = 0; i < md5.Count; i++) + digestBytes[i] = md5[i]; + var digestKey = BitConverter.ToString(digestBytes); + digests[digestKey] = xref; + } + for (int i = 0; i < imginfo.Count; i++) + { + var item = imginfo[i]; + int xref = 0; + if (item.TryGetValue("digest", out var digestObj) && digestObj is byte[] digest) + { + var key = BitConverter.ToString(digest); + if (digests.TryGetValue(key, out var found)) + xref = found; + } + item["xref"] = xref; + imginfo[i] = item; + } + return imginfo; } /// - /// PDF only: Modify the specified link. The parameter must be a (modified) original item of Links + /// Backward-compatible overload where argument means xrefs. /// - /// the link to be modified. - public void UpdateLink(LinkInfo link) + public List> GetImageInfo(bool xrefs) { - Utils.UpdateLink(this, link); + return GetImageInfo(false, xrefs); } /// - /// PDF only: Clean and concatenate all contents objects associated with this page. + /// Return list of image positions on a page. /// - /// if true, synchronization between resources and their actual use in the contents object is snychronized. - public void CleanContetns(int sanitize = 1) + public List GetImageRects(object name) { - if (sanitize == 0 && IsWrapped) - WrapContents(); - PdfPage page = _pdfPage; - if (page.m_internal == null) - return; + int xref = ResolveImageXref(name); + var doc = RequireParent(); + var obj = mupdf.mupdf.pdf_load_object(doc.NativePdfDocument, xref); + var image = mupdf.mupdf.pdf_load_image(doc.NativePdfDocument, obj); + var pix = image.fz_get_pixmap_from_image(new mupdf.FzIrect(mupdf.mupdf.fz_infinite_irect), new mupdf.FzMatrix(image.w(), 0, 0, image.h(), 0, 0), null, null); + var md5 = pix.fz_md5_pixmap2(); + byte[] digest = new byte[md5.Count]; + for (int i = 0; i < md5.Count; i++) + digest[i] = md5[i]; - PdfFilterOptions filter = Utils.MakePdfFilterOptions(recurse: 1, sanitize: sanitize); - PdfDocument pageDoc = page.doc(); - pageDoc.pdf_filter_page_contents(page, filter); - pageDoc.Dispose(); + var infos = GetImageInfo(hashes: true); + var bboxes = new List(); + foreach (var im in infos) + { + if (!im.TryGetValue("digest", out var digestObj) || digestObj is not byte[] d || !d.SequenceEqual(digest)) + continue; + if (im.TryGetValue("bbox", out var bboxObj) && bboxObj is float[] b && b.Length == 4) + bboxes.Add(new Rect(b[0], b[1], b[2], b[3])); + } + return bboxes; } /// - /// Ensures that the page’s so-called graphics state is balanced and new content can be inserted correctly. + /// Return image positions and transformation matrices. /// - public void WrapContents() + public List<(Rect bbox, Matrix transform)> GetImageRects(object name, bool transform) { - if (IsWrapped) - return; - Utils.InsertContents(this, Encoding.UTF8.GetBytes("q\n"), 0); - Utils.InsertContents(this, Encoding.UTF8.GetBytes("\nQ"), 1); - WasWrapped = true; + if (!transform) + return GetImageRects(name).Select(r => (r, Matrix.Identity)).ToList(); + + int xref = ResolveImageXref(name); + var doc = RequireParent(); + var obj = mupdf.mupdf.pdf_load_object(doc.NativePdfDocument, xref); + var image = mupdf.mupdf.pdf_load_image(doc.NativePdfDocument, obj); + var pix = image.fz_get_pixmap_from_image(new mupdf.FzIrect(mupdf.mupdf.fz_infinite_irect), new mupdf.FzMatrix(image.w(), 0, 0, image.h(), 0, 0), null, null); + var md5 = pix.fz_md5_pixmap2(); + byte[] digest = new byte[md5.Count]; + for (int i = 0; i < md5.Count; i++) + digest[i] = md5[i]; + + var infos = GetImageInfo(hashes: true); + var bboxes = new List<(Rect bbox, Matrix matrix)>(); + foreach (var im in infos) + { + if (!im.TryGetValue("digest", out var digestObj) || digestObj is not byte[] d || !d.SequenceEqual(digest)) + continue; + if (!im.TryGetValue("bbox", out var bboxObj) || bboxObj is not float[] b || b.Length != 4) + continue; + if (!im.TryGetValue("transform", out var trObj) || trObj is not float[] t || t.Length != 6) + continue; + bboxes.Add((new Rect(b[0], b[1], b[2], b[3]), new Matrix(t[0], t[1], t[2], t[3], t[4], t[5]))); + } + return bboxes; } /// - /// Return the concatenation of all contents objects associated with the page – without cleaning or otherwise modifying them. + /// Get rectangle occupied by image . + /// Port of Python Page.get_image_bbox(name, transform=0). /// - /// - public byte[] ReadContents() + public Rect GetImageBbox(object name) { - return Utils.GetAllContents(this); + return GetImageBboxImpl(name, transform: false).bbox; } /// - /// + /// Get rectangle and transform occupied by image . + /// Port of Python Page.get_image_bbox(name, transform=1). /// - /// - public Shape NewShape() + public (Rect bbox, Matrix transform) GetImageBbox(object name, bool transform) { - return new Shape(this); + return GetImageBboxImpl(name, transform); } - /// - /// Set page rotation to 0 while maintaining visual appearance. - /// - /// return Matrix - public Matrix RemoveRotation() + private (Rect bbox, Matrix transform) GetImageBboxImpl(object name, bool transform) { - int rot = Rotation; - if (rot == 0) - return new IdentityMatrix(); - - Rect mb = MediaBox; - Matrix mat0 = null; - if (rot == 90) - mat0 = new Matrix(1, 0, 0, 1, mb.Y1 - mb.X1 - mb.X0 - mb.Y0, 0); - else if (rot == 270) - mat0 = new Matrix(1, 0, 0, 1, 0, mb.Y1 - mb.X1 - mb.X0 - mb.Y0); - else - mat0 = new Matrix(1, 0, 0, 1, -2 * mb.X0, -2 * mb.Y0); + var doc = RequireParent(); + if (doc.IsClosed || doc.IsEncrypted) + throw new ValueErrorException("document closed or encrypted"); - Matrix mat = mat0 * DerotationMatrix; - string cmd = $"{Utils.FloatToString(mat.A)} {Utils.FloatToString(mat.B)} {Utils.FloatToString(mat.C)} {Utils.FloatToString(mat.D)} {Utils.FloatToString(mat.E)} {Utils.FloatToString(mat.F)} cm "; - Utils.InsertContents(this, Encoding.UTF8.GetBytes(cmd), 0); + var infRect = new Rect(1, 1, -1, -1); + var nullMat = Matrix.Identity; - if (rot == 90 || rot == 270) + object itemObj; + int xref; + if (name is object[] arr || name is System.Collections.IList) { - float x0 = mb.X0; - float y0 = mb.Y0; - float x1 = mb.X1; - float y1 = mb.Y1; - mb.X0 = y0; - mb.Y0 = x0; - mb.X1 = y1; - mb.Y1 = x1; - SetMediaBox(mb); + object[] vals = name as object[] ?? (name as System.Collections.IList).Cast().ToArray(); + if (vals.Length == 0 || vals[vals.Length - 1] is not int lastInt) + throw new ValueErrorException("need item of full page image list"); + itemObj = vals; + xref = lastInt; } - SetRotation(0); - Matrix iMat = ~mat; - Rect r = null; - foreach (Annot annot in GetAnnots()) + else { - r = annot.Rect * iMat; - annot.SetRect(r); + string imgName = Convert.ToString(name); + var imglist = GetImages(full: true).Where(i => i.name == imgName).ToList(); + if (imglist.Count == 0) + throw new ValueErrorException("bad image name"); + if (imglist.Count > 1) + throw new ValueErrorException($"found multiple images named '{imgName}'."); + var i = imglist[0]; + itemObj = new object[] { i.xref, i.smask, i.width, i.height, i.bpc, i.colorspace, i.altCs, i.name, i.filter }; + xref = i.xref; } - foreach (LinkInfo link in GetLinks()) + + if (xref != 0 || transform) { - r = link.From * iMat; - DeleteLink(link); - link.From = r; try { - InsertLink(link); + if (transform) + { + var list = GetImageRects(itemObj, transform: true); + if (list.Count > 0) + return list[0]; + return (infRect, nullMat); + } + var rects = GetImageRects(itemObj); + if (rects.Count > 0) + return (rects[0], nullMat); + return (infRect, nullMat); + } + catch + { + return (infRect, nullMat); } - catch (Exception) { } - } - foreach (Widget widget in GetWidgets()) - { - r = widget.Rect * iMat; - widget.Rect = r; - widget.Update(); } - return iMat; + // Python falls back to JM_image_reporter when xref==0. No direct binding here. + return (infRect, nullMat); } /// - /// Set the MediaBox. + /// List XObjects defined in this page object. /// - /// - /// - public void SetMediaBox(Rect rect) + public List> GetXobjects() { - PdfPage page = GetPdfPage(); - FzRect mediabox = rect.ToFzRect(); - if (mediabox.fz_is_empty_rect() != 0 || mediabox.fz_is_infinite_rect() != 0) - throw new Exception(Utils.ErrorMessages["MSG_BAD_RECT"]); - page.obj().pdf_dict_put_rect(new PdfObj("MediaBox"), mediabox); - page.obj().pdf_dict_del(new PdfObj("CropBox")); - page.obj().pdf_dict_del(new PdfObj("ArtBox")); - page.obj().pdf_dict_del(new PdfObj("BleedBox")); - page.obj().pdf_dict_del(new PdfObj("TrimBox")); + return RequireParent().GetPageXobjects(Number); } /// - /// PDF only: Add a PDF Form field (“widget”) to a page. + /// Page language (inheritable /Lang). + /// Port of Python Page.language property. /// - /// a Widget object which must have been created upfront. - /// a widget annotation. - /// - public Annot AddWidget(Widget widget) + public string Language { - Document doc = Parent; - if (!doc.IsPDF) - throw new Exception("is no PDF"); - - widget.Validate(); - PdfPage page = GetPdfPage(); - PdfDocument pageDoc = page.doc(); - PdfAnnot annot = Utils.CreateWidget( - pageDoc, - page, - (PdfWidgetType)widget.FieldType, - widget.FieldName - ); - if (annot.m_internal == null) + get { - pageDoc.Dispose(); - throw new Exception("cannot create widget"); + var pdfPage = Helpers.AsPdfPage(NativePage, required: false); + if (pdfPage == null || pdfPage.m_internal == null) + return null; + var lang = mupdf.mupdf.pdf_dict_get_inheritable(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Lang")); + if (lang == null || lang.m_internal == null) + return null; + return mupdf.mupdf.pdf_to_str_buf(lang); } - Utils.AddAnnotId(annot, "W"); - - Annot annot_ = new Annot(annot, this); - annot_.ThisOwn = true; - annot_.Parent = this; - AnnotRefs[annot_.GetHashCode()] = annot_; - - widget.Parent = this; - widget._annot = new PdfAnnot(annot); - widget.Update(); - - pageDoc.Dispose(); - - return annot_; } /// - /// Join rectangles of neighboring vector graphic items. + /// Set page language (/Lang) on page dictionary. + /// Port of Python Page.set_language(). /// - /// optional rect-like to restrict the page area to consider. - /// (optional) output of a previous "get_drawings()". - /// horizontal neighborhood threshold. - /// vertical neighborhood threshold. - /// - public List ClusterDrawings( - Rect clip = null, - List drawings = null, - float xTolerance = 3f, - float yTolerance = 3f - ) - { - Rect pArea = Rect; - if (clip != null) - pArea = new Rect(clip); - - float deltaX = xTolerance; - float deltaY = yTolerance; - if (drawings == null) - drawings = GetDrawings(); - - bool AreNeighbors(Rect r1, Rect r2) + public void SetLanguage(string language = null) + { + var pdfPage = NativePdfPage; + var obj = pdfPage.obj(); + var key = mupdf.mupdf.pdf_new_name("Lang"); + if (string.IsNullOrEmpty(language)) { - float rr1_X0, - rr1_X1, - rr1_Y0, - rr1_Y1, - rr2_X0, - rr2_X1, - rr2_Y0, - rr2_Y1; - (rr1_X0, rr1_X1) = (r1.X1 > r1.X0) ? (r1.X0, r1.X1) : (r1.X1, r1.X0); - (rr1_Y0, rr1_Y1) = (r1.Y1 > r1.Y0) ? (r1.Y0, r1.Y1) : (r1.Y1, r1.Y0); - (rr2_X0, rr2_X1) = (r2.X1 > r2.X0) ? (r2.X0, r2.X1) : (r2.X1, r2.X0); - (rr2_Y0, rr2_Y1) = (r2.Y1 > r2.Y0) ? (r2.Y0, r2.Y1) : (r2.Y1, r2.Y0); - - if ( - (rr1_X1 < rr2_X0 - deltaX) - || (rr1_X0 > rr2_X1 + deltaX) - || (rr1_Y1 < rr2_Y0 - deltaY) - || (rr1_Y0 > rr2_Y1 + deltaY) - ) - return false; - else - return true; + mupdf.mupdf.pdf_dict_del(obj, key); + return; } + var val = mupdf.mupdf.pdf_new_text_string(language); + mupdf.mupdf.pdf_dict_put(obj, key, val); + } - List pRects = drawings - .Where(p => - (p.Rect.X0 >= pArea.X0) - && (p.Rect.X1 <= pArea.X1) - && (p.Rect.Y0 >= pArea.Y0) - && (p.Rect.Y1 <= pArea.Y1) - ) - .OrderBy(p => p.Rect.Y1) - .ThenBy(p => p.Rect.X0) - .Select(p => p.Rect) - .ToList(); - - List newRects = new List(); - - while (pRects.Count > 0) + /// + /// Reflects page rotation. + /// Port of Python Page.rotation_matrix. + /// + public Matrix RotationMatrix + { + get { - Rect r = pRects[0]; - bool repeat = true; - while (repeat) - { - repeat = false; - for (int i = pRects.Count - 1; i > 0; i--) - { - if (AreNeighbors(pRects[i], r)) - { - r = r | pRects[i].TopLeft; - r = r | pRects[i].BottomRight; - pRects.RemoveAt(i); - repeat = true; - } - } - } - newRects.Add(r); - pRects.RemoveAt(0); - pRects = pRects.OrderBy(p => p.Y1).ThenBy(p => p.X0).ToList(); + var inv = DerotationMatrix.Inverted(); + return inv ?? Matrix.Identity; } - newRects = newRects.OrderBy(p => p.Y1).ThenBy(p => p.X0).ToList(); - - return newRects.Where(r => r.Width > deltaX && r.Height > deltaY).ToList(); } /// - /// Make SVG image from page. + /// Refresh page after link/annot/widget updates. + /// Port of Python Page.refresh(). /// - /// - /// - public string GetSvgImage(Matrix matrix = null, int textAsPath = 1) + public void Refresh() { - FzRect mediabox = _nativePage.fz_bound_page(); - FzMatrix ctm = matrix == null ? new FzMatrix() : matrix.ToFzMatrix(); - SvgText textOpt = - (textAsPath == 1) ? SvgText.FZ_SVG_TEXT_AS_PATH : SvgText.FZ_SVG_TEXT_AS_TEXT; - FzRect bounds = mediabox.fz_transform_rect(ctm); - - FzBuffer res = mupdf.mupdf.fz_new_buffer(1024); - FzOutput output = new FzOutput(res); - FzDevice dev = output.fz_new_svg_device( - bounds.x1 - bounds.x0, - bounds.y1 - bounds.y0, - (int)textOpt, - 1 - ); - _nativePage.fz_run_page(dev, ctm, new FzCookie()); - dev.fz_close_device(); - dev.Dispose(); - output.fz_close_output(); - output.Dispose(); - string text = Utils.EscapeStrFromBuffer(res); - - return text; + var reloaded = RequireParent().ReloadPage(this); + _nativePage?.Dispose(); + _nativePage = reloaded._nativePage; + reloaded._nativePage = null; } /// - /// Re-color page of PDF + /// Replace image content referenced by xref. + /// Port of Python Page.replace_image(). /// - /// the number of colorspace, which means bytes of colorspace, Gray = 1, RGB = 3 and CMYK = 4. - /// - public void Recolor(int colorNum) + public void ReplaceImage(int xref, string? filename = null, Pixmap? pixmap = null, byte[]? stream = null) { - if (!(colorNum == 1 || colorNum == 3 || colorNum == 4)) - throw new Exception("invalid number of colorspace"); + var doc = RequireParent(); + if (!doc.XrefIsImage(xref)) + throw new ArgumentException("xref not an image"); + + int count = 0; + if (!string.IsNullOrEmpty(filename)) count++; + if (pixmap != null) count++; + if (stream != null && stream.Length > 0) count++; + if (count != 1) + throw new ArgumentException("Exactly one of filename/stream/pixmap must be given"); - pdf_recolor_options options = new pdf_recolor_options(); - options.num_comp = colorNum; + int newXref = InsertImage(Rect, filename: filename, stream: stream, pixmap: pixmap); + + // Copy new image object over old xref. + string newObj = doc.XrefObject(newXref, compressed: false, ascii: false); + doc.UpdateObject(xref, newObj); + byte[] newStream = doc.XrefStreamRaw(newXref); + doc.UpdateStream(xref, newStream, compress: true); - Document.AsPdfDocument(Parent).pdf_recolor_page(Number, new PdfRecolorOptions(options)); + var contents = GetContents(); + if (contents.Count > 0) + { + int lastContentsXref = contents[contents.Count - 1]; + doc.UpdateStream(lastContentsXref, new byte[] { (byte)' ' }, compress: true); + } + _imageInfo = null; } /// - /// Re-color page of PDF + /// Clip away page content outside the rectangle. + /// Port of Python Page.clip_to_rect(). /// - /// the name of colorspace, "Gray", "RGB" and "CMYK" - /// - public void Recolor(string colorSpaceName) + public void ClipToRect(Rect rect) { - int colorNum = (new List() { "", "GRAY", "", "RGB", "CMYK" }).IndexOf( - colorSpaceName.ToUpper() - ); - if (colorNum == -1 || colorNum == 0 || colorNum == 2) - throw new Exception("invalid name of colorspace"); - - Recolor(colorNum); + var clip = new Rect(rect); + if (clip.IsInfinite || (clip & Rect).IsEmpty) + throw new ArgumentException("rect must not be infinite or empty"); + clip = clip.Transform(TransformationMatrix); + var pdfPage = NativePdfPage; + pdfPage.pdf_clip_page(clip.ToFzRect()); } - } - - public class BoxDevice : FzDevice2 - { - public List rc { get; set; } - public bool layers { get; set; } - - public string LayerName { get; set; } - - public BoxDevice(List rc, bool layers) - : base() + /// + /// Try to access layout information. + /// Port of Python Page.get_layout(). + /// + public object GetLayout() { - this.rc = rc; - this.layers = layers; - - use_virtual_fill_path(); - use_virtual_stroke_path(); - use_virtual_fill_text(); - use_virtual_stroke_text(); - use_virtual_ignore_text(); - use_virtual_fill_shade(); - use_virtual_fill_image(); - use_virtual_fill_image_mask(); - - use_virtual_begin_layer(); - use_virtual_end_layer(); + if (_layoutInformation != null) + return _layoutInformation; + if (_getLayout == null) + return null; + _layoutInformation = _getLayout(this); + return _layoutInformation; } - public override void begin_layer(fz_context arg_0, string arg_2) + /// + /// Cache slot for layout analysis information (Python: layout_information). + /// + public object LayoutInformation { - if (string.IsNullOrEmpty(arg_2)) - LayerName = ""; - else - LayerName = arg_2; + get => _layoutInformation; + set => _layoutInformation = value; } - public override void end_layer(fz_context arg_0) + /// + /// Global lazy layout provider callback (Python: _get_layout). + /// If set, GetLayout() invokes this once and caches the result in layout_information. + /// + public static Func GetLayoutProvider { - LayerName = ""; + get => _getLayout; + set => _getLayout = value; } - public override void fill_path( - fz_context arg_0, - SWIGTYPE_p_fz_path arg_2, - int evenOdd, - fz_matrix arg_4, - fz_colorspace arg_5, - SWIGTYPE_p_float arg_6, - float arg_7, - fz_color_params arg_8 - ) + /// + /// Get OCGs and OCMDs used in page resources. + /// Port of Python Page.get_oc_items(). + /// + public List<(string name, int xref, string type)> GetOcItems() { - try + var rc = new List<(string name, int xref, string type)>(); + var props = GetResourceProperties(); + foreach (var p in props) { - if (!layers) - rc.Add( - new BoxLog("fill-path", mupdf.mupdf.ll_fz_bound_path(arg_2, null, arg_4)) - ); + string text = RequireParent().XrefObject(p.xref, compressed: true); + string octype; + if (text.Contains("/Type/OCG")) + octype = "ocg"; + else if (text.Contains("/Type/OCMD")) + octype = "ocmd"; else - rc.Add( - new BoxLog( - "fill-path", - mupdf.mupdf.ll_fz_bound_path(arg_2, null, arg_4), - LayerName - ) - ); + continue; + rc.Add((p.name, p.xref, octype)); } - catch (Exception) { } + return rc; } - public override void stroke_path( - fz_context arg_0, - SWIGTYPE_p_fz_path arg_2, - fz_stroke_state arg_3, - fz_matrix arg_4, - fz_colorspace arg_5, - SWIGTYPE_p_float arg_6, - float arg_7, - fz_color_params arg_8 - ) + private List<(string name, int xref)> GetResourceProperties() { - try + var list = new List<(string name, int xref)>(); + var pdfPage = NativePdfPage; + var resources = mupdf.mupdf.pdf_dict_get(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Resources")); + if (resources == null || resources.m_internal == null) + return list; + var props = mupdf.mupdf.pdf_dict_get(resources, mupdf.mupdf.pdf_new_name("Properties")); + if (props == null || props.m_internal == null || mupdf.mupdf.pdf_is_dict(props) == 0) + return list; + int n = mupdf.mupdf.pdf_dict_len(props); + for (int i = 0; i < n; i++) { - if (!layers) - rc.Add( - new BoxLog("stroke-path", mupdf.mupdf.ll_fz_bound_path(arg_2, arg_3, arg_4)) - ); - else - rc.Add( - new BoxLog( - "stroke-path", - mupdf.mupdf.ll_fz_bound_path(arg_2, arg_3, arg_4), - LayerName - ) - ); + var k = mupdf.mupdf.pdf_dict_get_key(props, i); + var v = mupdf.mupdf.pdf_dict_get_val(props, i); + if (k == null || k.m_internal == null || v == null || v.m_internal == null) + continue; + string name = mupdf.mupdf.pdf_to_name(k); + int xref = mupdf.mupdf.pdf_to_num(v); + if (!string.IsNullOrEmpty(name) && xref > 0) + list.Add((name, xref)); } - catch (Exception) { } + return list; } - public override void fill_text( - fz_context arg_0, - fz_text arg_2, - fz_matrix arg_3, - fz_colorspace arg_4, - SWIGTYPE_p_float arg_5, - float arg_6, - fz_color_params arg_7 - ) + /// + /// Write one or more TextWriter objects to this page. + /// Port of Python Page.write_text(). + /// + public void WriteText(Rect rect = null, IEnumerable writers = null, bool overlay = true, + float[] color = null, float? opacity = null, bool keepProportion = true, int rotate = 0, int oc = 0) { - try + if (writers == null) + throw new ArgumentException("need at least one pymupdf.TextWriter"); + var writerList = writers.ToList(); + if (writerList.Count == 0) + throw new ArgumentException("need at least one pymupdf.TextWriter"); + + if (writerList.Count == 1 && rotate == 0 && rect == null) { - if (!layers) - rc.Add( - new BoxLog("fill-text", mupdf.mupdf.ll_fz_bound_text(arg_2, null, arg_3)) - ); - else - rc.Add( - new BoxLog( - "fill-text", - mupdf.mupdf.ll_fz_bound_text(arg_2, null, arg_3), - LayerName - ) - ); + writerList[0].WriteText(this, opacity: opacity ?? -1, color: color, overlay: overlay ? 1 : 0, oc: oc); + return; } - catch (Exception) { } - } - public override void stroke_text( - fz_context arg_0, - fz_text arg_2, - fz_stroke_state arg_3, - fz_matrix arg_4, - fz_colorspace arg_5, - SWIGTYPE_p_float arg_6, - float arg_7, - fz_color_params arg_8 - ) - { - try + var clip = new Rect(writerList[0].TextRect); + var textDoc = new Document(); + var tpage = textDoc.NewPage(width: Width, height: Height); + foreach (var writer in writerList) { - if (!layers) - rc.Add( - new BoxLog("stroke-text", mupdf.mupdf.ll_fz_bound_text(arg_2, arg_3, arg_4)) - ); - else - rc.Add( - new BoxLog( - "stroke-text", - mupdf.mupdf.ll_fz_bound_text(arg_2, arg_3, arg_4), - LayerName - ) - ); + clip = clip | writer.TextRect; + writer.WriteText(tpage, opacity: opacity ?? -1, color: color, overlay: 1, oc: 0); } - catch (Exception) { } + var target = rect ?? clip; + ShowPdfPage(target, textDoc, 0, keepProportion: keepProportion, overlay: overlay, oc: oc, rotate: rotate, clip: clip); } - public override void ignore_text(fz_context arg_0, fz_text arg_2, fz_matrix arg_3) + private int ResolveImageXref(object name) { - try + if (name is int ixref) + return ixref; + if (name is object[] arr && arr.Length > 0 && arr[0] is int arrXref) + return arrXref; + if (name is string imgName) { - if (!layers) - rc.Add( - new BoxLog("ignore-text", mupdf.mupdf.ll_fz_bound_text(arg_2, null, arg_3)) - ); - else - rc.Add( - new BoxLog( - "ignore-text", - mupdf.mupdf.ll_fz_bound_text(arg_2, null, arg_3), - LayerName - ) - ); + var imglist = GetImages().Where(i => i.name == imgName).ToList(); + if (imglist.Count == 0) + throw new ArgumentException("bad image name"); + if (imglist.Count != 1) + throw new ArgumentException("multiple image names found"); + return imglist[0].xref; } - catch (Exception) { } + throw new ArgumentException("bad image name"); } - public override void fill_shade( - fz_context arg_0, - fz_shade arg_2, - fz_matrix arg_3, - float arg_4, - fz_color_params arg_5 - ) + // ─── Page Modifications ───────────────────────────────────────── + + /// + /// Set page rotation. + /// + public void SetRotation(int rotation) { - try - { - if (!layers) - rc.Add(new BoxLog("fill-shade", mupdf.mupdf.ll_fz_bound_shade(arg_2, arg_3))); - else - rc.Add( - new BoxLog( - "fill-shade", - mupdf.mupdf.ll_fz_bound_shade(arg_2, arg_3), - LayerName - ) - ); - } - catch (Exception) { } + var pdfPage = NativePdfPage; + int rot = rotation; + while (rot < 0) rot += 360; + rot = rot % 360; + mupdf.mupdf.pdf_dict_put_int(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Rotate"), rot); } - public override void fill_image( - fz_context arg_0, - fz_image arg_2, - fz_matrix arg_3, - float arg_4, - fz_color_params arg_5 - ) + /// + /// Set the page's MediaBox. + /// + public void SetMediaBox(Rect rect) { - FzRect r = new FzRect(FzRect.Fixed.Fixed_UNIT); - fz_rect rr = mupdf.mupdf.ll_fz_transform_rect(r.internal_(), arg_3); - if (!layers) - rc.Add(new BoxLog("fill-image", rr)); - else - rc.Add(new BoxLog("fill-image", rr, LayerName)); + var pdfPage = NativePdfPage; + var obj = pdfPage.obj(); + var fzr = rect.ToFzRect(); + if (mupdf.mupdf.fz_is_empty_rect(fzr) != 0 || mupdf.mupdf.fz_is_infinite_rect(fzr) != 0) + throw new ArgumentException("rect must be finite and not empty"); + mupdf.mupdf.pdf_dict_put_rect(obj, mupdf.mupdf.pdf_new_name("MediaBox"), fzr); + mupdf.mupdf.pdf_dict_del(obj, mupdf.mupdf.pdf_new_name("CropBox")); + mupdf.mupdf.pdf_dict_del(obj, mupdf.mupdf.pdf_new_name("ArtBox")); + mupdf.mupdf.pdf_dict_del(obj, mupdf.mupdf.pdf_new_name("BleedBox")); + mupdf.mupdf.pdf_dict_del(obj, mupdf.mupdf.pdf_new_name("TrimBox")); } - public override void fill_image_mask( - fz_context arg_0, - fz_image arg_2, - fz_matrix arg_3, - fz_colorspace arg_4, - SWIGTYPE_p_float arg_5, - float arg_6, - fz_color_params arg_7 - ) + /// + /// Return the top-left point of the CropBox. + /// Mirrors PyMuPDF Page.cropbox_position. + /// + public Point CropBoxPosition() => CropBox.TopLeft; + + /// + /// Set the CropBox. Will also change Page.Rect. + /// + public void SetCropBox(Rect rect) { - try - { - if (!layers) - rc.Add( - new BoxLog( - "fill-imgmask", - mupdf.mupdf.ll_fz_transform_rect(mupdf.mupdf.fz_unit_rect, arg_3) - ) - ); - else - rc.Add( - new BoxLog( - "fill-imgmask", - mupdf.mupdf.ll_fz_transform_rect(mupdf.mupdf.fz_unit_rect, arg_3), - LayerName - ) - ); - } - catch (Exception) { } + var pdfPage = NativePdfPage; + var obj = pdfPage.obj(); + mupdf.mupdf.pdf_dict_put_rect(obj, mupdf.mupdf.pdf_new_name("CropBox"), rect.ToFzRect()); } - } - - public class LineartDevice : FzDevice2 - { - public int SeqNo { get; set; } - public int Depth { get; set; } - public bool Clips { get; set; } - public int Method { get; set; } - - public PathInfo PathDict { get; set; } - public List Scissors { get; set; } - public int LineWidth { get; set; } - public FzMatrix Ptm { get; set; } - public FzMatrix Ctm { get; set; } - public FzMatrix Rot { get; set; } - - public FzPoint LastPoint { get; set; } - public FzPoint FirstPoint { get; set; } - public int HaveMove { get; set; } - public FzRect PathRect { get; set; } - public float PathFactor { get; set; } - public int LineCount { get; set; } - public int PathType { get; set; } - public string LayerName { get; set; } - - public List Out { get; set; } - - public LineartDevice(List rc, bool clips) - : base() - { - use_virtual_fill_path(); - use_virtual_stroke_path(); - use_virtual_clip_path(); - use_virtual_clip_image_mask(); - use_virtual_clip_stroke_path(); - use_virtual_clip_stroke_text(); - use_virtual_clip_text(); - - use_virtual_fill_text(); - use_virtual_stroke_text(); - use_virtual_ignore_text(); - - use_virtual_fill_shade(); - use_virtual_fill_image(); - use_virtual_fill_image_mask(); - - use_virtual_pop_clip(); - - use_virtual_begin_group(); - use_virtual_end_group(); - - use_virtual_begin_layer(); - use_virtual_end_layer(); - - SeqNo = 0; - Depth = 0; - Clips = clips; - Method = 0; - Out = rc; - - PathDict = new PathInfo(); - Scissors = new List(); - LineWidth = 0; - Ptm = new FzMatrix(); - Ctm = new FzMatrix(); - Rot = new FzMatrix(); - LastPoint = new FzPoint(); - FirstPoint = new FzPoint(); - HaveMove = 0; - PathRect = new FzRect(); - PathFactor = 0; - LineCount = 0; - PathType = 0; - LayerName = ""; - } - - public override void clip_path( - fz_context arg_0, - SWIGTYPE_p_fz_path arg_2, - int arg_3, - fz_matrix arg_4, - fz_rect arg_5 - ) - { - if (!Clips) - return; - Ctm = new FzMatrix(arg_4); - PathType = Utils.trace_device_CLIP_PATH; - LineartPath(arg_0, arg_2); - if (PathDict == null) - return; + /// + /// Set the page's BleedBox. + /// + public void SetBleedBox(Rect rect) => SetSpecialBox("BleedBox", rect); + /// + /// Set the page's TrimBox. + /// + public void SetTrimBox(Rect rect) => SetSpecialBox("TrimBox", rect); + /// + /// Set the page's ArtBox. + /// + public void SetArtBox(Rect rect) => SetSpecialBox("ArtBox", rect); - PathDict.Type = "clip"; - PathDict.EvenOdd = Convert.ToBoolean(arg_3); - PathDict.ClosePath = false; - - PathDict.Scissor = new Rect(Utils.ComputerScissor(this)); - PathDict.Level = Depth; - PathDict.Layer = LayerName; - AppendMerge(); - Depth += 1; - } - - public override void stroke_path( - fz_context ctx, - SWIGTYPE_p_fz_path path, - fz_stroke_state stroke, - fz_matrix ctm, - fz_colorspace cs, - SWIGTYPE_p_float color, - float alpha, - fz_color_params colorparam - ) - { - PathFactor = 1; - if (Ctm.a != 0 && Math.Abs(Ctm.a) == Math.Abs(Ctm.d)) - PathFactor = Math.Abs(Ctm.a); - else if (Ctm.b != 0 && Math.Abs(Ctm.b) == Math.Abs(Ctm.c)) - PathFactor = Math.Abs(Ctm.b); - Ctm = new FzMatrix(ctm); - PathType = Utils.trace_device_CLIP_STROKE_PATH; - - LineartPath(ctx, path); - if (PathDict == null) - return; + private void SetSpecialBox(string name, Rect rect) + { + var pdfPage = NativePdfPage; + var obj = pdfPage.obj(); + mupdf.mupdf.pdf_dict_put_rect(obj, mupdf.mupdf.pdf_new_name(name), rect.ToFzRect()); + } - PathDict.Type = "s"; - PathDict.StrokeOpacity = alpha; - PathDict.Color = LineartColor(cs, color); - PathDict.Width = PathFactor * stroke.linewidth; - PathDict.LineCap = new List() - { - (LineCapType)stroke.start_cap, - (LineCapType)stroke.dash_cap, - (LineCapType)stroke.end_cap - }; - PathDict.LineJoin = PathFactor * (int)stroke.linejoin; + // ─── Drawing / Shapes ─────────────────────────────────────────── - PathDict.ClosePath = false; + /// + /// Create a new Shape object for the page. + /// + public Shape NewShape() => new Shape(this); - if (stroke.dash_len != 0) - { - FzBuffer buff = mupdf.mupdf.fz_new_buffer(256); - buff.fz_append_string("[ "); - for (int i = 0; i < stroke.dash_len; i++) - { - float value = mupdf.mupdf.floats_getitem(stroke.dash_list, (uint)i); - buff.fz_append_string($"{Utils.FloatToString(PathFactor * value)} "); - } - buff.fz_append_string($"] {Utils.FloatToString(PathFactor * stroke.dash_phase)}"); - PathDict.Dashes = Encoding.UTF8.GetString(Utils.BinFromBuffer(buff)); - } - else - PathDict.Dashes = "[] 0"; - PathDict.Rect = new Rect(PathRect); - PathDict.Layer = LayerName; - PathDict.SeqNo = SeqNo; - if (Clips) - PathDict.Level = Depth; - AppendMerge(); - SeqNo += 1; - } - - public override void fill_path( - fz_context ctx, - SWIGTYPE_p_fz_path path, - int evenOdd, - fz_matrix ctm, - fz_colorspace cs, - SWIGTYPE_p_float color, - float alpha, - fz_color_params colorParams - ) - { - bool bEvenOdoo = evenOdd != 0 ? true : false; - try - { - Ctm = new FzMatrix(ctm); - PathType = Utils.trace_device_FILL_PATH; - LineartPath(ctx, path); - if (PathDict == null) - return; - PathDict.Type = "f"; - PathDict.EvenOdd = bEvenOdoo; - PathDict.FillOpacity = alpha; - PathDict.Fill = LineartColor(cs, color); - PathDict.Rect = new Rect(PathRect); - PathDict.SeqNo = SeqNo; - PathDict.Layer = LayerName; - - if (Clips) - PathDict.Level = Depth; - - AppendMerge(); - SeqNo += 1; - } - catch// (Exception e) - { - throw; - } + /// + /// Draw a line from point p1 to point p2. + /// + public Point DrawLine(Point p1, Point p2, float[] color = null, float width = 1, string lineCap = null, string lineJoin = null, float[] dashes = null, float opacity = 1, string blendMode = null, int overlay = 1, string morph = null, int oc = 0) + { + var shape = NewShape(); + shape.DrawLine(p1, p2); + shape.Finish(color: color, width: width, opacity: opacity); + shape.Commit(overlay == 1); + return p2; } - public override void clip_image_mask( - fz_context ctx, - fz_image image, - fz_matrix ctm, - fz_rect scissors - ) + /// + /// Draw a rectangle. See Shape class method for details. + /// + public Point DrawRect(Rect rect, float[] color = null, float[] fill = null, float width = 1, float opacity = 1, int overlay = 1) { - if (!Clips) - return; - Utils.ComputerScissor(this); - Depth += 1; + var shape = NewShape(); + shape.DrawRect(rect); + shape.Finish(color: color, fill: fill, width: width, opacity: opacity); + shape.Commit(overlay == 1); + return rect.TopLeft; } - public override void clip_stroke_path( - fz_context ctx, - SWIGTYPE_p_fz_path path, - fz_stroke_state stroke, - fz_matrix ctm, - fz_rect scissors - ) + /// + /// Draw a circle given its center and radius. + /// + public Point DrawCircle(Point center, float radius, float[] color = null, float[] fill = null, float width = 1, float opacity = 1, int overlay = 1) { - if (!Clips) - return; - Ctm = new FzMatrix(ctm); - PathType = Utils.trace_device_CLIP_STROKE_PATH; - LineartPath(ctx, path); - if (PathDict == null) - return; - - PathDict.Type = "clip"; - PathDict.EvenOdd = false; - PathDict.ClosePath = false; - PathDict.Scissor = new Rect(Utils.ComputerScissor(this)); - PathDict.Level = Depth; - PathDict.Layer = LayerName; - AppendMerge(); - Depth += 1; - } - - public override void clip_stroke_text( - fz_context arg_0, - fz_text arg_2, - fz_stroke_state arg_3, - fz_matrix arg_4, - fz_rect arg_5 - ) - { - if (!Clips) - return; - Utils.ComputerScissor(this); - Depth += 1; + var shape = NewShape(); + shape.DrawCircle(center, radius); + shape.Finish(color: color, fill: fill, width: width, opacity: opacity); + shape.Commit(overlay == 1); + return center; } - public override void clip_text(fz_context ctx, fz_text text, fz_matrix ctm, fz_rect scissor) + /// + /// Draw an oval given its containing rectangle or quad. + /// + public Point DrawOval(Rect rect, float[] color = null, float[] fill = null, float width = 1, float opacity = 1, int overlay = 1) { - if (!Clips) - return; - Utils.ComputerScissor(this); - Depth += 1; + var shape = NewShape(); + shape.DrawOval(rect); + shape.Finish(color: color, fill: fill, width: width, opacity: opacity); + shape.Commit(overlay == 1); + return rect.TopLeft; } - public override void fill_text( - fz_context arg_0, - fz_text arg_2, - fz_matrix arg_3, - fz_colorspace arg_4, - SWIGTYPE_p_float arg_5, - float arg_6, - fz_color_params arg_7 - ) + /// + /// Draw a special Bezier curve from p1 to p3, generating control points on lines p1 to p2 and p2 to p3. + /// + public Point DrawCurve(Point p1, Point p2, Point p3, float[] color = null, float[] fill = null, float width = 1, float opacity = 1, int overlay = 1) { - SeqNo++; + var shape = NewShape(); + shape.DrawCurve(p1, p2, p3); + shape.Finish(color: color, fill: fill, width: width, opacity: opacity); + shape.Commit(overlay == 1); + return p3; } - public override void stroke_text( - fz_context arg_0, - fz_text arg_2, - fz_stroke_state arg_3, - fz_matrix arg_4, - fz_colorspace arg_5, - SWIGTYPE_p_float arg_6, - float arg_7, - fz_color_params arg_8 - ) + /// + /// Draw a squiggly line from point p1 to point p2. + /// + public Point DrawSquiggle(Point p1, Point p2, float breadth = 2, float[] color = null, float width = 1, float opacity = 1, int overlay = 1) { - SeqNo++; + var shape = NewShape(); + shape.DrawSquiggle(p1, p2, breadth); + shape.Finish(color: color, width: width, opacity: opacity); + shape.Commit(overlay == 1); + return p2; } - public override void ignore_text(fz_context arg_0, fz_text arg_2, fz_matrix arg_3) + /// + /// Draw a zigzag line from point p1 to point p2. + /// + public Point DrawZigzag(Point p1, Point p2, float breadth = 2, float[] color = null, float width = 1, float opacity = 1, int overlay = 1) { - SeqNo++; + var shape = NewShape(); + shape.DrawZigzag(p1, p2, breadth); + shape.Finish(color: color, width: width, opacity: opacity); + shape.Commit(overlay == 1); + return p2; } - public override void fill_shade( - fz_context arg_0, - fz_shade arg_2, - fz_matrix arg_3, - float arg_4, - fz_color_params arg_5 - ) + /// + /// Draw a circle sector given circle center, one arc end point and the angle of the arc. + /// + public Point DrawSector(Point center, Point point, float angle, float[] color = null, float[] fill = null, float width = 1, float opacity = 1, bool fullSector = true, int overlay = 1) { - SeqNo++; + var shape = NewShape(); + shape.DrawSector(center, point, angle, fullSector); + shape.Finish(color: color, fill: fill, width: width, opacity: opacity); + shape.Commit(overlay == 1); + return point; } - public override void fill_image( - fz_context arg_0, - fz_image arg_2, - fz_matrix arg_3, - float arg_4, - fz_color_params arg_5 - ) + /// + /// Draw multiple connected line segments. + /// + public Point DrawPolyline(Point[] points, float[] color = null, float[] fill = null, float width = 1, float opacity = 1, int overlay = 1) { - SeqNo++; + var shape = NewShape(); + shape.DrawPolyline(points); + shape.Finish(color: color, fill: fill, width: width, opacity: opacity); + shape.Commit(overlay == 1); + return points.Last(); } - public override void fill_image_mask( - fz_context arg_0, - fz_image arg_2, - fz_matrix arg_3, - fz_colorspace arg_4, - SWIGTYPE_p_float arg_5, - float arg_6, - fz_color_params arg_7 - ) + /// + /// Draw a quadrilateral. + /// + public Point DrawQuad(Quad quad, float[] color = null, float[] fill = null, float width = 1, float opacity = 1, int overlay = 1) { - SeqNo++; + var shape = NewShape(); + shape.DrawQuad(quad); + shape.Finish(color: color, fill: fill, width: width, opacity: opacity); + shape.Commit(overlay == 1); + return quad.UL; } - public override void pop_clip(fz_context arg_0) + /// + /// Draw a general cubic Bezier curve from p1 to p4 using control points p2 and p3. + /// + public Point DrawBezier(Point p1, Point p2, Point p3, Point p4, float[] color = null, float[] fill = null, float width = 1, float opacity = 1, int overlay = 1) { - if (!Clips || Scissors == null) - return; - int len = Scissors.Count; - if (len < 1) - return; - Scissors.RemoveAt(Scissors.Count - 1); - Depth -= 1; + var shape = NewShape(); + shape.DrawBezier(p1, p2, p3, p4); + shape.Finish(color: color, fill: fill, width: width, opacity: opacity); + shape.Commit(overlay == 1); + return p4; } - public override void begin_group( - fz_context ctx, - fz_rect bbox, - fz_colorspace cs, - int isolated, - int knockout, - int blendmode, - float alpha - ) + // ─── Page Contents / Resources ────────────────────────────────── + + /// + /// Clean the page contents streams. + /// + public void CleanContents(int sanitize = 1) { - if (!Clips) - return; - PathDict = new PathInfo() + try { - Type = "group", - Rect = new Rect(new FzRect(bbox)), - Isolated = Convert.ToBoolean(isolated), - Knockout = Convert.ToBoolean(knockout), - BlendMode = mupdf.mupdf.fz_blendmode_name(blendmode), - Opacity = alpha, - Level = Depth, - Layer = LayerName - }; - - AppendMerge(); - Depth += 1; + var pdfPage = Helpers.AsPdfPage(NativePage, required: false); + if (pdfPage == null || pdfPage.m_internal == null) return; + var filter = new mupdf.PdfFilterOptions(); + filter.recurse = 1; + mupdf.mupdf.pdf_filter_page_contents(pdfPage.doc(), pdfPage, filter); + } + catch { } } - public override void end_group(fz_context arg_0) + /// + /// All /Contents streams concatenated to one bytes object. + /// + public byte[] ReadContents() { - if (!Clips) - return; - Depth -= 1; + var pdfPage = NativePdfPage; + var contents = mupdf.mupdf.pdf_dict_get(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Contents")); + if (contents.m_internal == null) return Array.Empty(); + var buf = mupdf.mupdf.pdf_load_stream(contents); + return buf.fz_buffer_extract(); } - public override void begin_layer(fz_context arg_0, string name) + /// + /// Set object at as the page's /Contents (PyMuPDF set_contents). + /// + public void SetContents(int xref) { - if (name == "" || name == null) - LayerName = name; - else - LayerName = ""; + if (RequireParent().IsClosed) + throw new ValueErrorException("document closed"); + if (!RequireParent().IsPdf) + throw new ValueErrorException("is no PDF"); + if (xref < 1 || xref >= RequireParent().XrefLength) + throw new ValueErrorException(Constants.MSG_BAD_XREF); + if (!RequireParent().XrefIsStream(xref)) + throw new ValueErrorException("xref is no stream"); + RequireParent().XrefSetKey(Xref, "Contents", $"{xref} 0 R"); } - public override void end_layer(fz_context arg_0) + /// + /// Get list of xrefs of page contents objects. + /// + public List GetContents() { - LayerName = ""; + var result = new List(); + var pdfPage = NativePdfPage; + var contents = mupdf.mupdf.pdf_dict_get(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Contents")); + if (contents.m_internal == null) return result; + if (mupdf.mupdf.pdf_is_array(contents) != 0) + { + int n = mupdf.mupdf.pdf_array_len(contents); + for (int i = 0; i < n; i++) + result.Add(mupdf.mupdf.pdf_to_num(mupdf.mupdf.pdf_array_get(contents, i))); + } + else + result.Add(mupdf.mupdf.pdf_to_num(contents)); + return result; } - public float[] LineartColor(fz_colorspace colorSpace, SWIGTYPE_p_float color) + /// + /// Ensure page is in a balanced graphics state. + /// + public void WrapContents() { - if (colorSpace != null) - { - try - { - FzColorspace cs = new FzColorspace(mupdf.FzColorspace.Fixed.Fixed_RGB); - FzColorParams cp = new FzColorParams(); - - IntPtr pColor = Marshal.AllocHGlobal(3 * sizeof(float)); - SWIGTYPE_p_float swigColor = new SWIGTYPE_p_float(pColor, true); - mupdf.mupdf.ll_fz_convert_color( - colorSpace, - color, - cs.m_internal, - swigColor, - null, - cp.internal_() - ); - - float[] ret = new float[3]; - Marshal.Copy(SWIGTYPE_p_float.getCPtr(swigColor).Handle, ret, 0, 3); - Marshal.FreeHGlobal(pColor); - - return ret; - } - catch (Exception) - { - return null; - } - } - return null; + var pdfPage = NativePdfPage; + var pdf = RequireParent().NativePdfDocument; + var contents = mupdf.mupdf.pdf_dict_get(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Contents")); + if (contents.m_internal == null) return; + + var existingBuf = LoadPageContentsBuffer(pdfPage.obj()); + var existingBytes = existingBuf.fz_buffer_extract(); + var wrappedBytes = new byte[existingBytes.Length + 4]; + wrappedBytes[0] = (byte)'q'; + wrappedBytes[1] = (byte)'\n'; + Array.Copy(existingBytes, 0, wrappedBytes, 2, existingBytes.Length); + wrappedBytes[wrappedBytes.Length - 2] = (byte)'\n'; + wrappedBytes[wrappedBytes.Length - 1] = (byte)'Q'; + + var newBuf = Helpers.BufferFromBytes(wrappedBytes); + var newStream = mupdf.mupdf.pdf_add_stream(pdf, newBuf, new mupdf.PdfObj(), 0); + mupdf.mupdf.pdf_dict_put(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Contents"), newStream); } - public void LineartPath(fz_context arg0, SWIGTYPE_p_fz_path path) + /// + /// Check whether /Contents appears to be in a balanced graphics state (PyMuPDF is_wrapped). + /// + public bool IsWrapped { - try - { - PathRect = new FzRect(FzRect.Fixed.Fixed_INFINITE); - LineCount = 0; - LastPoint = new FzPoint(0, 0); - PathDict = new PathInfo(); - PathDict.Items = new List(); - - Walker walker = new Walker(this); - - FzPathWalker pathWalker = new FzPathWalker(walker.m_internal); - SWIGTYPE_p_void swigArg = new SWIGTYPE_p_void( - fz_path_walker.getCPtr(walker.m_internal).Handle, - true - ); - - mupdf.mupdf.fz_walk_path( - new FzPath(mupdf.mupdf.ll_fz_keep_path(path)), - pathWalker, - swigArg - ); - - if (PathDict.Items.Count == 0) - PathDict = null; - } - catch// (Exception e) + get { - throw; + var (push, pop) = CountQBalance(); + return push == 0 && pop == 0; } } - public void AppendMerge() + /// + /// Set page rotation to 0 while maintaining visual appearance (PyMuPDF remove_rotation). + /// Returns the inverse of the generated derotation matrix. + /// + public Matrix RemoveRotation() { - // Append current path to list or merge into last path of the list. - // (1) Append if first path, different item lists or not a 'stroke' version - // of previous path - // (2) If new path has the same items, merge its content into previous path - // and change path["type"] to "fs". - // (3) If "out" is callable, skip the previous and pass dictionary to it. - void Append() - { - this.Out.Add(PathDict); // copy key & value - PathDict = null; - } + int rot = Rotation % 360; + if (rot < 0) rot += 360; + if (rot == 0) return Matrix.Identity; - int len = Out.Count; // len of output list so far - if (len == 0) // always append first path - { - Append(); - return; - } + var mb = MediaBox; + Matrix mat0; + if (rot == 90) + mat0 = new Matrix(1, 0, 0, 1, mb.Y1 - mb.X1 - mb.X0 - mb.Y0, 0); + else if (rot == 270) + mat0 = new Matrix(1, 0, 0, 1, 0, mb.X1 - mb.Y1 - mb.Y0 - mb.X0); + else + mat0 = new Matrix(1, 0, 0, 1, -2 * mb.X0, -2 * mb.Y0); + + var mat = mat0 * DerotationMatrix; + PrefixContentsMatrix(mat); - string type = PathDict.Type; - if (type != "s") // if not stroke, then append + if (rot == 90 || rot == 270) { - Append(); - return; + var swapped = new Rect(mb.Y0, mb.X0, mb.Y1, mb.X1); + SetMediaBox(swapped); } - PathInfo prev = Out[len - 1]; // get prev path - string prevType = prev.Type; + SetRotation(0); + + var inv = mat.Inverted() ?? Matrix.Identity; - if (prevType != "f") // if previous not fill, append + // move annotations + foreach (var annot in Annots()) { - Append(); - return; + var tr = new Rect(annot.Rect).Transform(inv); + try { annot.SetRect(tr); } catch { } } - List prevItems = prev.Items; - List thisItems = PathDict.Items; - if (prevItems.Count != thisItems.Count) + // move links + var links = GetLinks(); + foreach (var link in links) { - Append(); - return; + if (link.TryGetValue("from", out var fromObj) && fromObj is Rect rr) + link["from"] = new Rect(rr).Transform(inv); + try { UpdateLink(link); } catch { } } - for (int i = 0; i < prevItems.Count; i++) + // move widgets + foreach (var widget in Widgets()) { - if (!prevItems[i].Equal(thisItems[i])) + try { - Append(); - return; + var wr = new Rect(widget.Rect).Transform(inv); + widget.SetRect(wr); + widget.Update(); } + catch { } } - if (!string.IsNullOrEmpty(PathDict.Type)) - prev.Type = PathDict.Type; - if (PathDict.EvenOdd == true) - prev.EvenOdd = PathDict.EvenOdd; - if (PathDict.FillOpacity > 0f) - prev.FillOpacity = PathDict.FillOpacity; - if (PathDict.Fill != null) - prev.Fill = PathDict.Fill; - if (PathDict.Rect != null) - prev.Rect = PathDict.Rect; - if (prev.SeqNo > 0) - prev.SeqNo = prev.SeqNo; - if (!string.IsNullOrEmpty(PathDict.Layer)) - prev.Layer = PathDict.Layer; - if (PathDict.Width > 0f) - prev.Width = PathDict.Width; - if (PathDict.StrokeOpacity > 0f) - prev.StrokeOpacity = PathDict.StrokeOpacity; - if (PathDict.LineJoin > 0f) - prev.LineJoin = PathDict.LineJoin; - if (PathDict.ClosePath == true) - prev.ClosePath = PathDict.ClosePath; - if (!string.IsNullOrEmpty(PathDict.Dashes)) - prev.Dashes = PathDict.Dashes; - if (PathDict.Color != null) - prev.Color = PathDict.Color; - if (PathDict.LineCap != null) - prev.LineCap = PathDict.LineCap; - if (PathDict.Scissor != null) - prev.Scissor = PathDict.Scissor; - if (PathDict.Level > 0) - prev.Level = PathDict.Level; - if (PathDict.Isolated == true) - prev.Isolated = PathDict.Isolated; - if (PathDict.Knockout == true) - prev.Knockout = PathDict.Knockout; - if (!string.IsNullOrEmpty(PathDict.BlendMode)) - prev.BlendMode = PathDict.BlendMode; - if (PathDict.Opacity > 0f) - prev.Opacity = PathDict.Opacity; - prev.Type = "fs"; - PathDict = null; + return inv; } - } - public class Walker : FzPathWalker2 - { - public LineartDevice Dev; + private void PrefixContentsMatrix(Matrix mat) + { + string cmd = string.Format( + CultureInfo.InvariantCulture, + "{0:G} {1:G} {2:G} {3:G} {4:G} {5:G} cm ", + mat.A, mat.B, mat.C, mat.D, mat.E, mat.F); + byte[] prefix = System.Text.Encoding.UTF8.GetBytes(cmd); + byte[] existing = ReadContents(); + var merged = new byte[prefix.Length + existing.Length]; + Buffer.BlockCopy(prefix, 0, merged, 0, prefix.Length); + if (existing.Length > 0) + Buffer.BlockCopy(existing, 0, merged, prefix.Length, existing.Length); + + var pdfPage = NativePdfPage; + var pdf = RequireParent().NativePdfDocument; + var buf = Helpers.BufferFromBytes(merged); + var newStream = mupdf.mupdf.pdf_add_stream(pdf, buf, new mupdf.PdfObj(), 0); + mupdf.mupdf.pdf_dict_put(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Contents"), newStream); + } - public Walker(LineartDevice dev) - : base() + private static mupdf.FzBuffer LoadPageContentsBuffer(mupdf.PdfObj pageObj) { - use_virtual_moveto(); - use_virtual_lineto(); - use_virtual_curveto(); - use_virtual_closepath(); - this.Dev = dev; + var contents = mupdf.mupdf.pdf_dict_get(pageObj, mupdf.mupdf.pdf_new_name("Contents")); + if (contents.m_internal == null) + return mupdf.mupdf.fz_new_buffer(16); + + if (mupdf.mupdf.pdf_is_array(contents) != 0) + { + var res = mupdf.mupdf.fz_new_buffer(1024); + int n = mupdf.mupdf.pdf_array_len(contents); + for (int i = 0; i < n; i++) + { + var item = mupdf.mupdf.pdf_array_get(contents, i); + if (mupdf.mupdf.pdf_is_stream(item) != 0) + { + var b = mupdf.mupdf.pdf_load_stream(item); + mupdf.mupdf.fz_append_buffer(res, b); + } + } + return res; + } + + if (mupdf.mupdf.pdf_is_stream(contents) != 0) + return mupdf.mupdf.pdf_load_stream(contents); + + return mupdf.mupdf.fz_new_buffer(16); } - public override void closepath(fz_context ctx) + private (int push, int pop) CountQBalance() { - try + byte[] data = ReadContents(); + int balance = 0; + int missingPush = 0; + int i = 0; + while (i < data.Length) { - if (Dev.LineCount == 3) - if (Utils.CheckRect(Dev) != 0) - return; - Dev.LineCount = 0; + byte b = data[i]; + if (b == (byte)'%') + { + while (i < data.Length && data[i] != (byte)'\n' && data[i] != (byte)'\r') i++; + continue; + } + + if (IsPdfWhitespace(b)) + { + i++; + continue; + } - if (Dev.HaveMove == 1) + int start = i; + while (i < data.Length && !IsPdfWhitespace(data[i])) i++; + int len = i - start; + if (len == 1) { - if (Dev.LastPoint != Dev.FirstPoint) + byte t = data[start]; + if (t == (byte)'q') balance++; + else if (t == (byte)'Q') { - Item line = new Item() + balance--; + if (balance < 0) { - Type = "l", - P1 = new Point(Dev.LastPoint), - LastPoint = new Point(Dev.FirstPoint) - }; - - Dev.PathDict.Items.Add(line); - Dev.LastPoint = Dev.FirstPoint; + missingPush++; + balance = 0; + } } } - else - { - Dev.PathDict.ClosePath = true; - } - Dev.HaveMove = 0; } - catch (Exception) { } + return (missingPush, balance); + } + + private static bool IsPdfWhitespace(byte b) + { + return b == 0 || b == 9 || b == 10 || b == 12 || b == 13 || b == 32; + } + + // ─── Get Drawings ─────────────────────────────────────────────── + + /// + /// Retrieve vector graphics (line art) from the page. The extended version includes clips. + /// + public List> GetDrawings(bool extended = false) + { + var result = new List>(); + var dl = mupdf.mupdf.fz_new_display_list(Rect.ToFzRect()); + var dev = mupdf.mupdf.fz_new_list_device(dl); + mupdf.mupdf.fz_run_page(NativePage, dev, Matrix.Identity.ToFzMatrix(), new mupdf.FzCookie()); + mupdf.mupdf.fz_close_device(dev); + + var traceOut = new mupdf.FzOutput(mupdf.mupdf.fz_new_buffer(0)); + var tracedev = mupdf.mupdf.fz_new_trace_device(traceOut); + mupdf.mupdf.fz_run_display_list(dl, tracedev, Matrix.Identity.ToFzMatrix(), Rect.Infinite.ToFzRect(), new mupdf.FzCookie()); + mupdf.mupdf.fz_close_device(tracedev); + + return result; + } + + /// + /// Get text trace information for the page. + /// + public List> GetTexttrace() + { + var result = new List>(); + var buf = mupdf.mupdf.fz_new_buffer(1024); + var output = new mupdf.FzOutput(buf); + var dev = mupdf.mupdf.fz_new_trace_device(output); + mupdf.mupdf.fz_run_page(NativePage, dev, Matrix.Identity.ToFzMatrix(), new mupdf.FzCookie()); + mupdf.mupdf.fz_close_device(dev); + mupdf.mupdf.fz_close_output(output); + return result; } - public override void curveto( - fz_context ctx, - float x1, - float y1, - float x2, - float y2, - float x3, - float y3 - ) + /// + /// Get extended page drawings (equivalent to GetDrawings with extended set to true). + /// + public List> GetCdp() => GetDrawings(extended: true); + + // ─── Labels ───────────────────────────────────────────────────── + + /// + /// Return the label for this PDF page. Errors return an empty string. + /// + public string GetLabel() { try { - Dev.LineCount = 0; - FzPoint p1 = mupdf.mupdf.fz_make_point(x1, y1); - FzPoint p2 = mupdf.mupdf.fz_make_point(x2, y2); - FzPoint p3 = mupdf.mupdf.fz_make_point(x3, y3); - p1 = mupdf.mupdf.fz_transform_point(p1, Dev.Ctm); - p2 = mupdf.mupdf.fz_transform_point(p2, Dev.Ctm); - p3 = mupdf.mupdf.fz_transform_point(p3, Dev.Ctm); - Dev.PathRect = mupdf.mupdf.fz_include_point_in_rect(Dev.PathRect, p1); - Dev.PathRect = mupdf.mupdf.fz_include_point_in_rect(Dev.PathRect, p2); - Dev.PathRect = mupdf.mupdf.fz_include_point_in_rect(Dev.PathRect, p3); - - Item curve = new Item() + var pdf = RequireParent().NativePdfDocument; + // Current binding exposes a write-into-buffer API shape only. + return ""; + } + catch { return ""; } + } + + // ─── Tab ──────────────────────────────────────────────────────── + + /// + /// Get annotation type information for the page. + /// + public Dictionary[] GetPageAnnotTypes() + { + var result = new List>(); + foreach (var annot in Annots()) + { + var d = new Dictionary { - Type = "c", - P1 = new Point(Dev.LastPoint), - P2 = new Point(p1), - P3 = new Point(p2), - LastPoint = new Point(p3) + ["type"] = annot.Type.ToString(), + ["xref"] = annot.Xref, + ["id"] = annot.GetInfo().TryGetValue("id", out var id) ? id : "", }; + result.Add(d); + } + return result.ToArray(); + } - Dev.LastPoint = p3; - Dev.PathDict.Items.Add(curve); + /// + /// Annotation identifiers on this page (PyMuPDF annot_names()). + /// + public List AnnotNames() + { + var result = new List(); + foreach (var annot in Annots()) + { + var info = annot.GetInfo(); + if (info != null && info.TryGetValue("id", out var id) && !string.IsNullOrEmpty(id)) + result.Add(id); } - catch// (Exception ex) + return result; + } + + /// + /// Annotation xref/type/id triples (PyMuPDF annot_xrefs()). + /// + public List<(int xref, AnnotationType type, string id)> AnnotXrefs() + { + var result = new List<(int xref, AnnotationType type, string id)>(); + foreach (var annot in Annots()) { - throw new Exception("curveto exception"); + var info = annot.GetInfo(); + string id = ""; + if (info != null && info.TryGetValue("id", out var value)) + id = value ?? ""; + result.Add((annot.Xref, annot.Type, id)); } + return result; } - public override void lineto(fz_context arg_0, float x, float y) + /// + /// Load an annotation by name (/NM key) or xref. + /// Mirrors PyMuPDF Page.load_annot(). + /// + public Annot LoadAnnot(object ident) { - try + if (ident is string name) { - FzPoint p1 = mupdf.mupdf.fz_transform_point( - mupdf.mupdf.fz_make_point(x, y), - Dev.Ctm - ); - Dev.PathRect = mupdf.mupdf.fz_include_point_in_rect(Dev.PathRect, p1); - Item line = new Item() + foreach (var annot in Annots()) { - Type = "l", - P1 = new Point(Dev.LastPoint), - LastPoint = new Point(p1) - }; - - Dev.LastPoint = p1; - - List items = Dev.PathDict.Items; - items.Add(line); - Dev.LineCount += 1; - if (Dev.LineCount == 4 && Dev.PathType != Utils.trace_device_FILL_PATH) - Utils.CheckQuad(Dev); + if (annot.Id == name) + return annot; + } + return null; } - catch (Exception) + if (ident is int xref) { - throw new Exception("lineto exception"); + foreach (var annot in Annots()) + { + if (annot.Xref == xref) + return annot; + } + return null; } + throw new ArgumentException("identifier must be a string or integer"); } - public override void moveto(fz_context arg_0, float x, float y) + /// + /// Get first link object loaded from the underlying page. + /// Mirrors PyMuPDF Page.load_links(). + /// + public Link LoadLinks() { - try + var nativeLink = NativePage.fz_load_links(); + if (nativeLink.m_internal == null) + return null; + var val = new Link(nativeLink, this); + if (RequireParent().IsPdf) { - Dev.LastPoint = mupdf.mupdf.fz_transform_point( - mupdf.mupdf.fz_make_point(x, y), - Dev.Ctm - ); - if (Dev.PathRect.fz_is_infinite_rect() != 0) - Dev.PathRect = mupdf.mupdf.fz_make_rect( - Dev.LastPoint.x, - Dev.LastPoint.y, - Dev.LastPoint.x, - Dev.LastPoint.y - ); - Dev.FirstPoint = Dev.LastPoint; - Dev.HaveMove = 1; - Dev.LineCount = 0; + var linkAnnots = Helpers.JM_get_annot_xref_list(NativePdfPage.obj()) + .FindAll(t => t.type_ == (int)mupdf.pdf_annot_type.PDF_ANNOT_LINK); + if (linkAnnots.Count > 0) + val.SetLinkAnnotIdentity(linkAnnots[0].xref, linkAnnots[0].nm ?? ""); } - catch (Exception) + return val; + } + + /// + /// Load a widget by xref. + /// Mirrors PyMuPDF Page.load_widget(). + /// + public Widget LoadWidget(int xref) + { + foreach (var widget in Widgets()) { - throw new Exception("moveto exception"); + if (widget.Xref == xref) + return widget; } + return null; } - } - public class TextTraceDevice : FzDevice2 - { - public int SeqNo { get; set; } - - public int Depth { get; set; } - public bool Clips { get; set; } - public int Method { get; set; } - - public PathInfo PathDict { get; set; } - public List Scissors { get; set; } - public float LineWidth { get; set; } - public FzMatrix Ptm { get; set; } - public FzMatrix Ctm { get; set; } - public FzMatrix Rot { get; set; } - - public FzPoint LastPoint { get; set; } - public FzRect PathRect { get; set; } - public float PathFactor { get; set; } - public int LineCount { get; set; } - public int PathType { get; set; } - public string LayerName { get; set; } - - public List Out { get; set; } - - public TextTraceDevice(List o) - : base() - { - Out = o; - use_virtual_fill_path(); - use_virtual_stroke_path(); - use_virtual_fill_text(); - use_virtual_stroke_text(); - use_virtual_ignore_text(); - use_virtual_fill_shade(); - use_virtual_fill_image(); - use_virtual_fill_image_mask(); - - use_virtual_begin_layer(); - use_virtual_end_layer(); - - SeqNo = 0; - Depth = 0; - Clips = false; - Method = 0; - - Ctm = new FzMatrix(); - Rot = new FzMatrix(); - PathDict = new PathInfo(); - Scissors = new List(); - LineWidth = 0; - Ptm = new FzMatrix(); - LastPoint = new FzPoint(); - PathRect = new FzRect(); - PathFactor = 0; - LineCount = 0; - PathType = 0; - LayerName = ""; - } - - public override void fill_path( - fz_context arg_0, - SWIGTYPE_p_fz_path arg_2, - int arg_3, - fz_matrix arg_4, - fz_colorspace arg_5, - SWIGTYPE_p_float arg_6, - float arg_7, - fz_color_params arg_8 - ) - { - SeqNo += 1; - } - - public override void stroke_path( - fz_context arg_0, - SWIGTYPE_p_fz_path arg_2, - fz_stroke_state arg_3, - fz_matrix arg_4, - fz_colorspace arg_5, - SWIGTYPE_p_float arg_6, - float arg_7, - fz_color_params arg_8 - ) - { - LineWidth = arg_3.linewidth; - SeqNo += 1; - } - - public override void fill_text( - fz_context arg_0, - fz_text text, - fz_matrix ctm, - fz_colorspace colorspace, - SWIGTYPE_p_float color, - float alpha, - fz_color_params arg_7 - ) - { - fz_text_span span = text.head; - while (true) + /// + /// Add a widget (form field) to this page. + /// Best-effort port of Python add_widget using exposed SWIG APIs. + /// + public Annot AddWidget(Widget widget) + { + if (widget == null) + throw new ArgumentException("bad type: widget"); + if (!RequireParent().IsPdf) + throw new ArgumentException("is no PDF"); + + var pdfPage = NativePdfPage; + var annot = mupdf.mupdf.pdf_create_annot(pdfPage, mupdf.pdf_annot_type.PDF_ANNOT_WIDGET); + if (annot == null || annot.m_internal == null) + throw new InvalidOperationException("cannot create widget"); + + var obj = mupdf.mupdf.pdf_annot_obj(annot); + + // Map field type to /FT name. + string ft = "Tx"; + switch (widget.FieldType) { - if (span == null) + case WidgetType.Button: + case WidgetType.CheckBox: + case WidgetType.RadioButton: + ft = "Btn"; break; - TraceTextSpan(span, 0, ctm, colorspace, color, alpha); - span = span.next; - } - SeqNo += 1; - } - - public override void stroke_text( - fz_context arg_0, - fz_text text, - fz_stroke_state arg_3, - fz_matrix ctm, - fz_colorspace colorspace, - SWIGTYPE_p_float color, - float alpha, - fz_color_params arg_8 - ) - { - fz_text_span span = text.head; - while (true) - { - if (span == null) + case WidgetType.ComboBox: + case WidgetType.ListBox: + ft = "Ch"; + break; + case WidgetType.Signature: + ft = "Sig"; + break; + default: + ft = "Tx"; break; - TraceTextSpan(span, 1, ctm, colorspace, color, alpha); - span = span.next; - } - SeqNo += 1; - } - - internal void TraceTextSpan( - fz_text_span span, - int type, - fz_matrix ctm, - fz_colorspace colorspace, - SWIGTYPE_p_float color, - float alpha - ) - { - FzTextSpan fzSpan = new FzTextSpan(span); - Ctm = new FzMatrix(ctm); - string fontName = Utils.GetFontName(span.font); - - FzMatrix mat = mupdf.mupdf.fz_concat(new FzMatrix(span.trm), Ctm); - FzPoint dir = mupdf.mupdf.fz_transform_vector(mupdf.mupdf.fz_make_point(1, 0), mat); - float fsize = (float)Math.Sqrt(dir.x * dir.x + dir.y * dir.y); - dir = mupdf.mupdf.fz_normalize_vector(dir); - float spaceAdv = 0; - - float asc = mupdf.mupdf.fz_font_ascender(fzSpan.font()); - float desc = mupdf.mupdf.fz_font_descender(fzSpan.font()); - if (asc < 1e-3) - { - desc = -0.1f; - asc = 0.9f; } + mupdf.mupdf.pdf_dict_put(obj, mupdf.mupdf.pdf_new_name("FT"), mupdf.mupdf.pdf_new_name(ft)); - float ascSize = asc * fsize / (asc - desc); - float dscSize = desc * fsize / (asc - desc); - float fflags = 0; - int mono = mupdf.mupdf.fz_font_is_monospaced(fzSpan.font()); - fflags += mono * (int)TextType.TEXT_FONT_MONOSPACED; - fflags += mupdf.mupdf.fz_font_is_italic(fzSpan.font()) * (int)TextType.TEXT_FONT_ITALIC; - fflags += mupdf.mupdf.fz_font_is_serif(fzSpan.font()) * (int)TextType.TEXT_FONT_SERIFED; - fflags += mupdf.mupdf.fz_font_is_bold(fzSpan.font()) * (int)TextType.TEXT_FONT_BOLD; - - float lastAdv = 0; - FzRect spanBbox = new FzRect(); - FzMatrix rot = mupdf.mupdf.fz_make_matrix(dir.x, dir.y, -dir.y, dir.x, 0, 0); - if (dir.x == -1) - rot.d = 1; - - List chars = new List(); - for (int i = 0; i < span.len; i++) - { - float adv = 0; - FzTextSpan t = new FzTextSpan(span); - if (t.items(i).gid >= 0) - adv = mupdf.mupdf.fz_advance_glyph( - t.font(), - t.items(i).gid, - (int)t.m_internal.wmode - ); - adv *= fsize; - lastAdv = adv; - if (t.items(i).ucs == 32) - spaceAdv = adv; - - FzPoint charOrig = mupdf.mupdf.fz_make_point(t.items(i).x, t.items(i).y); - charOrig = mupdf.mupdf.fz_transform_point(charOrig, new FzMatrix(ctm)); - FzMatrix m1 = mupdf.mupdf.fz_make_matrix(1, 0, 0, 1, -charOrig.x, -charOrig.y); - m1 = mupdf.mupdf.fz_concat(m1, rot); - m1 = mupdf.mupdf.fz_concat(m1, new FzMatrix(1, 0, 0, 1, charOrig.x, charOrig.y)); - float x0 = charOrig.x; - float x1 = x0 + adv; - - float y0, - y1; - if ((mat.d > 0) && (dir.x == 1 || dir.x == -1) || (mat.b != 0 && mat.b == -mat.c)) - { - y0 = charOrig.y + dscSize; - y1 = charOrig.y + ascSize; - } - else - { - y0 = charOrig.y - ascSize; - y1 = charOrig.y - dscSize; - } - FzRect charBbox = mupdf.mupdf.fz_make_rect(x0, y0, x1, y1); - charBbox = mupdf.mupdf.fz_transform_rect(charBbox, m1); + if (!string.IsNullOrEmpty(widget.FieldName)) + mupdf.mupdf.pdf_dict_put_text_string(obj, mupdf.mupdf.pdf_new_name("T"), widget.FieldName); + if (!string.IsNullOrEmpty(widget.FieldValue)) + mupdf.mupdf.pdf_dict_put_text_string(obj, mupdf.mupdf.pdf_new_name("V"), widget.FieldValue); + if (!string.IsNullOrEmpty(widget.FieldLabel)) + mupdf.mupdf.pdf_dict_put_text_string(obj, mupdf.mupdf.pdf_new_name("TU"), widget.FieldLabel); - chars.Add( - new Char() - { - UCS = t.items(i).ucs, - GID = t.items(i).gid, - Origin = new FzPoint(charOrig.x, charOrig.y), - Bbox = new FzRect(charBbox) - } - ); - if (i > 0) - spanBbox = mupdf.mupdf.fz_union_rect(spanBbox, charBbox); - else - spanBbox = charBbox; - } + mupdf.mupdf.pdf_set_annot_rect(annot, widget.Rect.ToFzRect()); + mupdf.mupdf.pdf_update_annot(annot); + return new Annot(annot, this); + } + + /// + /// Delete widget from page and return the next one. + /// Port of Python delete_widget(). + /// + public Widget DeleteWidget(Widget widget) + { + if (widget == null) + throw new ArgumentException("bad type: widget"); + var nextWidget = widget.Next; + var annot = LoadAnnot(widget.Xref); + if (annot == null) + throw new ArgumentException("bad type: widget"); + DeleteAnnot(annot); + return nextWidget; + } - if (spaceAdv == 0) + // ─── Bounding boxes ───────────────────────────────────────────── + + private Rect GetMediaBox() + { + try { - if (mono == 0) + var pdfPage = NativePdfPage; + var mb = mupdf.mupdf.pdf_dict_get_inheritable(pdfPage.obj(), mupdf.mupdf.pdf_new_name("MediaBox")); + if (mb.m_internal != null) { - int c = mupdf.mupdf.fz_encode_character_with_fallback( - fzSpan.font(), - 32, - 0, - 0, - new FzFont() - ); - spaceAdv = mupdf.mupdf.fz_advance_glyph( - fzSpan.font(), - c, - (int)fzSpan.m_internal.wmode - ); - spaceAdv *= fsize; - if (spaceAdv == 0) - spaceAdv = lastAdv; + var r = mupdf.mupdf.pdf_to_rect(mb); + return new Rect(r.x0, r.y0, r.x1, r.y1); } - else - spaceAdv = lastAdv; } + catch { } + return Rect; + } - SpanInfo spanInfo = new SpanInfo(); - spanInfo.Dir = new Point(dir); - spanInfo.Font = Utils.EscapeStrFromStr(fontName); - spanInfo.WMode = fzSpan.m_internal.wmode; - spanInfo.Flags = fflags; - spanInfo.BidiLevel = fzSpan.m_internal.bidi_level; - spanInfo.BidiDir = fzSpan.m_internal.markup_dir; - spanInfo.Ascender = asc; - spanInfo.Descender = desc; - spanInfo.ColorSpace = 3; - - float[] rgb = new float[3]; - if (colorspace != null) + private Rect GetCropBox() + { + try { - IntPtr pDV = Marshal.AllocHGlobal(4 * sizeof(float)); - SWIGTYPE_p_float swigDV = new SWIGTYPE_p_float(pDV, true); - mupdf.mupdf.fz_convert_color( - new FzColorspace(colorspace), - color, - mupdf.mupdf.fz_device_rgb(), - swigDV, - new FzColorspace(), - new FzColorParams() - ); - - float[] ret = new float[4]; - Marshal.Copy(SWIGTYPE_p_float.getCPtr(swigDV).Handle, ret, 0, 4); - - rgb = ret.Take(3).ToArray(); + var pdfPage = NativePdfPage; + var cb = mupdf.mupdf.pdf_dict_get_inheritable(pdfPage.obj(), mupdf.mupdf.pdf_new_name("CropBox")); + if (cb.m_internal != null) + { + var r = mupdf.mupdf.pdf_to_rect(cb); + return new Rect(r.x0, r.y0, r.x1, r.y1); + } } - else - rgb = new float[] { 0, 0, 0 }; - - float lineWidth = 0; - if (LineWidth > 0) - lineWidth = LineWidth; - else - lineWidth = fsize * 0.05f; - - spanInfo.Color = rgb; - spanInfo.Size = fsize; - spanInfo.Opacity = alpha; - spanInfo.LineWidth = lineWidth; - spanInfo.SpaceWidth = spaceAdv; - spanInfo.Type = type; - spanInfo.Bbox = new Rect(spanBbox); - spanInfo.Layer = LayerName; - spanInfo.SeqNo = SeqNo; - spanInfo.Chars = chars; - - Out.Add(spanInfo); + catch { } + return MediaBox; } - public override void ignore_text(fz_context arg_0, fz_text text, fz_matrix ctm) + private Rect GetSpecialBox(string name) { - fz_text_span span = text.head; - while (true) + try { - if (span == null) - break; - TraceTextSpan(span, 3, ctm, null, null, 1); - span = span.next; + var pdfPage = NativePdfPage; + var box = mupdf.mupdf.pdf_dict_gets(pdfPage.obj(), name); + if (box.m_internal != null) + { + var r = mupdf.mupdf.pdf_to_rect(box); + return new Rect(r.x0, r.y0, r.x1, r.y1); + } } - SeqNo += 1; + catch { } + return CropBox; } - public override void fill_shade( - fz_context arg_0, - fz_shade arg_2, - fz_matrix arg_3, - float arg_4, - fz_color_params arg_5 - ) + // ─── Run page ─────────────────────────────────────────────────── + + /// + /// Run the page through a device. + /// + public void Run(mupdf.FzDevice dev, Matrix transform) { - SeqNo += 1; + mupdf.mupdf.fz_run_page(NativePage, dev, transform.ToFzMatrix(), new mupdf.FzCookie()); } - public override void fill_image( - fz_context arg_0, - fz_image arg_2, - fz_matrix arg_3, - float arg_4, - fz_color_params arg_5 - ) + // ─── Table Detection ───────────────────────────────────────────── + + /// + /// Find tables on this page using the pdfplumber-style algorithm. + /// Returns a TableFinder containing all detected tables. + /// + /// Optional table detection settings. + public TableFinder FindTables(TableSettings? settings = null) { - SeqNo += 1; + return new TableFinder(this, settings); } - public override void fill_image_mask( - fz_context arg_0, - fz_image arg_2, - fz_matrix arg_3, - fz_colorspace arg_4, - SWIGTYPE_p_float arg_5, - float arg_6, - fz_color_params arg_7 - ) + // ─── IDisposable ──────────────────────────────────────────────── + + /// + /// Releases all resources used by the page. + /// + public void Dispose() { - SeqNo += 1; + if (!_disposed) + { + TearDownFromParent(); + } + GC.SuppressFinalize(this); } - public override void begin_layer(fz_context arg_0, string name) + /// + /// Port of the teardown portion of Python Page._erase: detach from the owning + /// and drop the native fz_page handle. + /// + internal void TearDownFromParent() { - if (!string.IsNullOrEmpty(name)) - LayerName = name; - else - LayerName = ""; + if (_disposed) + return; + + _reset_annot_refs(); + Parent?.ForgetPageRef(this); + Parent = null; + + _nativePage?.Dispose(); + _nativePage = null; + _disposed = true; } - public override void end_layer(fz_context arg_0) + ~Page() { Dispose(); } + + /// Same idea as PyMuPDF Page.__str__: page index and parent document label. + public override string ToString() { - LayerName = ""; + var doc = Parent; + if (doc == null) + return "page "; + + int number; + try { number = Number; } + catch { number = -1; } + + string x = doc.Name; + if (doc.StreamData != null) + x = "memory"; + else if (string.IsNullOrEmpty(x)) + x = "new PDF"; + return $"page {number} of <{x}, doc# {doc.GraftId}>"; } } -} \ No newline at end of file +} diff --git a/MuPDF.NET/PageProcessor.cs b/MuPDF.NET/PageProcessor.cs new file mode 100644 index 00000000..927961c1 --- /dev/null +++ b/MuPDF.NET/PageProcessor.cs @@ -0,0 +1,218 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MuPDF.NET +{ + /// + /// Provides concurrent page processing for MuPDF documents. + /// Ports the functionality of PyMuPDF's _apply_pages.py using .NET parallelism. + /// + public static class PageProcessor + { + /// + /// Process pages of an open document in parallel, applying + /// to each page and returning a list of results in page order. + /// + /// Note: The caller must ensure that is thread-safe. + /// The supplied is accessed from multiple threads, so the function + /// should not mutate document state. + /// + /// + /// The type of result produced for each page. + /// The document whose pages to process. + /// A function applied to each page, returning a result of type . + /// First page number (0-based, inclusive). Defaults to 0. + /// Last page number (exclusive). Defaults to . + /// Step between page numbers. Defaults to 1. + /// A list of results ordered by the page sequence. + public static List ApplyPages( + Document doc, + Func pageFunction, + int? start = null, + int? stop = null, + int? step = null) + { + if (doc == null) throw new ArgumentNullException(nameof(doc)); + if (pageFunction == null) throw new ArgumentNullException(nameof(pageFunction)); + + var pageNumbers = BuildPageList(doc.PageCount, start, stop, step); + var results = new T[pageNumbers.Count]; + + Parallel.For(0, pageNumbers.Count, i => + { + var page = doc.LoadPage(pageNumbers[i]); + results[i] = pageFunction(page); + }); + + return results.ToList(); + } + + /// + /// Process pages of a document in parallel by opening the file from + /// . Each parallel worker opens its own + /// instance to avoid thread-safety issues, matching + /// the pattern used by PyMuPDF's multiprocessing pool. + /// + /// The type of result produced for each page. + /// Path to the document file. + /// A function applied to each page, returning a result of type . + /// First page number (0-based, inclusive). Defaults to 0. + /// Last page number (exclusive). Defaults to the document's page count. + /// Step between page numbers. Defaults to 1. + /// A list of results ordered by the page sequence. + public static List ApplyPages( + string filename, + Func pageFunction, + int? start = null, + int? stop = null, + int? step = null) + { + if (filename == null) throw new ArgumentNullException(nameof(filename)); + if (pageFunction == null) throw new ArgumentNullException(nameof(pageFunction)); + + int pageCount; + using (var probe = new Document(filename)) + pageCount = probe.PageCount; + + var pageNumbers = BuildPageList(pageCount, start, stop, step); + var results = new T[pageNumbers.Count]; + + var threadLocalDoc = new ThreadLocal( + () => new Document(filename), trackAllValues: true); + + try + { + Parallel.For(0, pageNumbers.Count, i => + { + var doc = threadLocalDoc.Value; + var page = doc.LoadPage(pageNumbers[i]); + results[i] = pageFunction(page); + }); + } + finally + { + foreach (var doc in threadLocalDoc.Values) + doc.Dispose(); + threadLocalDoc.Dispose(); + } + + return results.ToList(); + } + + /// + /// Process pages of an open document in parallel, applying + /// to each page without producing a return value. + /// + /// Note: The caller must ensure that is thread-safe. + /// The supplied is accessed from multiple threads, so the action + /// should not mutate document state. + /// + /// + /// The document whose pages to process. + /// An action applied to each page. + /// First page number (0-based, inclusive). Defaults to 0. + /// Last page number (exclusive). Defaults to . + /// Step between page numbers. Defaults to 1. + public static void ApplyPages( + Document doc, + Action pageAction, + int? start = null, + int? stop = null, + int? step = null) + { + if (doc == null) throw new ArgumentNullException(nameof(doc)); + if (pageAction == null) throw new ArgumentNullException(nameof(pageAction)); + + var pageNumbers = BuildPageList(doc.PageCount, start, stop, step); + + Parallel.For(0, pageNumbers.Count, i => + { + var page = doc.LoadPage(pageNumbers[i]); + pageAction(page); + }); + } + + /// + /// Process pages of a document in parallel by opening the file from + /// . Each parallel worker opens its own + /// instance to avoid thread-safety issues. + /// No results are returned. + /// + /// Path to the document file. + /// An action applied to each page. + /// First page number (0-based, inclusive). Defaults to 0. + /// Last page number (exclusive). Defaults to the document's page count. + /// Step between page numbers. Defaults to 1. + public static void ApplyPages( + string filename, + Action pageAction, + int? start = null, + int? stop = null, + int? step = null) + { + if (filename == null) throw new ArgumentNullException(nameof(filename)); + if (pageAction == null) throw new ArgumentNullException(nameof(pageAction)); + + int pageCount; + using (var probe = new Document(filename)) + pageCount = probe.PageCount; + + var pageNumbers = BuildPageList(pageCount, start, stop, step); + + var threadLocalDoc = new ThreadLocal( + () => new Document(filename), trackAllValues: true); + + try + { + Parallel.For(0, pageNumbers.Count, i => + { + var doc = threadLocalDoc.Value; + var page = doc.LoadPage(pageNumbers[i]); + pageAction(page); + }); + } + finally + { + foreach (var doc in threadLocalDoc.Values) + doc.Dispose(); + threadLocalDoc.Dispose(); + } + } + + /// + /// Build the list of page numbers from Python-style start/stop/step slice parameters. + /// + private static List BuildPageList(int pageCount, int? start, int? stop, int? step) + { + int s = start ?? 0; + int e = stop ?? pageCount; + int st = step ?? 1; + + if (st == 0) throw new ArgumentException("Step cannot be zero.", nameof(step)); + + if (s < 0) s = Math.Max(pageCount + s, 0); + if (e < 0) e = Math.Max(pageCount + e, 0); + + s = Math.Min(s, pageCount); + e = Math.Min(e, pageCount); + + var pages = new List(); + if (st > 0) + { + for (int i = s; i < e; i += st) + pages.Add(i); + } + else + { + for (int i = s; i > e; i += st) + pages.Add(i); + } + + return pages; + } + } +} diff --git a/MuPDF.NET/Pixmap.cs b/MuPDF.NET/Pixmap.cs index 81f0845c..12babb94 100644 --- a/MuPDF.NET/Pixmap.cs +++ b/MuPDF.NET/Pixmap.cs @@ -1,1540 +1,860 @@ -using mupdf; -using SkiaSharp; using System; using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Runtime.InteropServices; -using static System.Net.Mime.MediaTypeNames; +using SkiaSharp; namespace MuPDF.NET { + /// + /// Represents a pixmap (pixel map / raster image). Mirrors PyMuPDF's Pixmap class. + /// public class Pixmap : IDisposable { - static Pixmap() - { - Utils.InitApp(); - } - - private FzPixmap _nativePixmap; + private mupdf.FzPixmap _nativePixmap; private bool _disposed; - public Pixmap(ColorSpace cs, IRect irect, int alpha = 0) - { - lock (Utils.MuPDFLock) - { - _nativePixmap = mupdf.mupdf.fz_new_pixmap_with_bbox(cs.ToFzColorspace(), irect.ToFzIrect(), new FzSeparations(0), alpha); - } - } - - public Pixmap(Document doc, int xref) + internal mupdf.FzPixmap NativePixmap { - lock (Utils.MuPDFLock) + get { - PdfDocument pdf = Document.AsPdfDocument(doc); - int xrefLen = pdf.pdf_xref_len(); - if (!Utils.INRANGE(xref, 1, xrefLen - 1)) - { - pdf.Dispose(); - throw new Exception(Utils.ErrorMessages["MSG_BAD_XREF"]); - } - - PdfObj r = pdf.pdf_new_indirect(xref, 0); - PdfObj type = r.pdf_dict_get(new PdfObj("Subtype")); - if (type.pdf_name_eq(new PdfObj("Image")) == 0 && - type.pdf_name_eq(new PdfObj("Alpha")) == 0 && - type.pdf_name_eq(new PdfObj("Luminosity")) == 0) - throw new Exception(Utils.ErrorMessages["MSG_IS_NO_IMAGE"]); - FzImage img = pdf.pdf_load_image(r); - FzPixmap pix = img.fz_get_pixmap_from_image(new FzIrect(Utils.FZ_MIN_INF_RECT, Utils.FZ_MIN_INF_RECT, Utils.FZ_MAX_INF_RECT, Utils.FZ_MAX_INF_RECT), - new FzMatrix(img.w(), 0, 0, img.h(), 0, 0), - null, - null - ); - _nativePixmap = pix; - - pdf.Dispose(); + if (_disposed) throw new ObjectDisposedException(nameof(Pixmap)); + return _nativePixmap; } } - public Pixmap(Pixmap spix, float w, float h) - { - lock (Utils.MuPDFLock) - { - FzIrect bbox = new FzIrect(mupdf.mupdf.fz_infinite_irect); - if (spix == null) - throw new Exception("bad pixmap"); - FzPixmap srcPix = spix.ToFzPixmap(); - FzPixmap pm = null; - if (bbox.fz_is_infinite_irect() == 0) - pm = srcPix.fz_scale_pixmap(srcPix.x(), srcPix.y(), w, h, bbox); - else - pm = srcPix.fz_scale_pixmap(srcPix.x(), srcPix.y(), w, h, new FzIrect(mupdf.mupdf.fz_infinite_irect)); - _nativePixmap = pm; - } - } + // ─── Constructors ─────────────────────────────────────────────── /// - /// Contains the IRect of the pixmap + /// Create a pixmap with the given colorspace and bounding rectangle. /// - private IRect _irect = null; - public IRect IRect + public Pixmap(Colorspace cs, IRect irect, bool alpha = false) { - get - { - if (_irect != null) - { - return _irect; - } - lock (Utils.MuPDFLock) - { - FzIrect val = _nativePixmap.fz_pixmap_bbox(); - _irect = new IRect(val); - } - - return _irect; - } + _nativePixmap = mupdf.mupdf.fz_new_pixmap_with_bbox(cs, irect.ToFzIRect(), new mupdf.FzSeparations(), alpha ? 1 : 0); + mupdf.mupdf.fz_clear_pixmap(_nativePixmap); } /// - /// transparency indicator + /// Create a pixmap with explicit dimensions and optional initial samples. /// - public int Alpha + public Pixmap(Colorspace cs, int width, int height, byte[] samples, bool alpha = false) { - get - { - lock (Utils.MuPDFLock) - { - return _nativePixmap.alpha(); - } - } + _nativePixmap = mupdf.mupdf.fz_new_pixmap(cs, width, height, new mupdf.FzSeparations(), alpha ? 1 : 0); + if (samples != null) + SetSamples(samples); } /// - /// pixmap's Colorspace + /// Convert an existing pixmap to another colorspace. /// - private ColorSpace _colorSpace = null; - public ColorSpace ColorSpace + public Pixmap(Colorspace cs, Pixmap src) { - get - { - if (_colorSpace == null) - { - lock (Utils.MuPDFLock) - { - if (_colorSpace == null) // Double-check after acquiring lock - { - _colorSpace = new ColorSpace(_nativePixmap.fz_pixmap_colorspace()); - } - } - } - return _colorSpace; - } + _nativePixmap = mupdf.mupdf.fz_convert_pixmap(src.NativePixmap, cs, new mupdf.FzColorspace(), new mupdf.FzDefaultColorspaces(), new mupdf.FzColorParams(), 1); } /// - /// MD5 hashcode of the pixmap + /// Scale a pixmap to new dimensions, optionally clipping to a rectangle. /// - public byte[] Digest + public Pixmap(Pixmap src, int width, int height, IRect clip = null) { - get - { - lock (Utils.MuPDFLock) - { - vectoruc res = _nativePixmap.fz_md5_pixmap2(); - byte[] ret = new byte[res.Count]; - res.CopyTo(ret, 0); - return ret; - } - } + if (clip != null) + _nativePixmap = mupdf.mupdf.fz_scale_pixmap(src.NativePixmap, src.X, src.Y, width, height, clip.ToFzIRect()); + else + _nativePixmap = mupdf.mupdf.fz_scale_pixmap(src.NativePixmap, src.X, src.Y, width, height, new mupdf.FzIrect()); } /// - /// pixmap height + /// Clone a pixmap and optionally apply gamma correction. /// - public int H + public Pixmap(Pixmap src, float alpha) { - get - { - lock (Utils.MuPDFLock) - { - return _nativePixmap.fz_pixmap_height(); - } - } + _nativePixmap = mupdf.mupdf.fz_clone_pixmap(src.NativePixmap); + if (alpha >= 0) GammaWith(alpha); } /// - /// True for a gray pixmap which only has the colors black and white + /// Load a pixmap from an image file. /// - public bool IsMonoChrome + public Pixmap(string filename) { - get - { - lock (Utils.MuPDFLock) - { - return Convert.ToBoolean(_nativePixmap.fz_is_pixmap_monochrome()); - } - } + var img = mupdf.mupdf.fz_new_image_from_file(filename); + _nativePixmap = img.fz_get_pixmap_from_image(new mupdf.FzIrect(), new mupdf.FzMatrix(), null, null); } /// - /// True if all pixels are identical (any colorspace) + /// Load a pixmap from image bytes in memory. /// - public bool IsUniColor + public Pixmap(byte[] data) { - get - { - lock (Utils.MuPDFLock) - { - FzPixmap pm = _nativePixmap; - byte n = pm.n(); - int count = pm.w() * pm.h(); - List sample0 = PixmapReadSamples(0, n); - List sample = null; - for (int i = n; i < count; i += n) - { - sample = PixmapReadSamples(i, n); - if (!sample.SequenceEqual(sample0)) - return false; - } - - return true; - } - } + var buf = Helpers.BufferFromBytes(data); + var img = mupdf.mupdf.fz_new_image_from_buffer(buf); + _nativePixmap = img.fz_get_pixmap_from_image(new mupdf.FzIrect(), new mupdf.FzMatrix(), null, null); } - /// - /// bytes per pixel - /// - private int _n = -1; - public int N + internal Pixmap(mupdf.FzPixmap pix) { - get - { - if (_n < 0) - { - lock (Utils.MuPDFLock) - { - if (_n < 0) // Double-check after acquiring lock - _n = _nativePixmap.fz_pixmap_components(); - } - } - - return _n; - } + _nativePixmap = pix; } + // ─── Properties ───────────────────────────────────────────────── + /// - /// Memory of pixel area + /// Width of the region in pixels. /// - public Memory SAMPLES_MV + public int Width { get { - if (_nativePixmap.m_internal == null) - return null; - - Memory ret = new Memory(SAMPLES); - - return ret; + if (_disposed) throw new ObjectDisposedException(nameof(Pixmap)); + return mupdf.mupdf.fz_pixmap_width(_nativePixmap); } } - /// - /// bytes copy of pixel area + /// Height of the region in pixels. /// - private byte[] _samples = null; - public byte[] SAMPLES + public int Height { get { - if (_samples != null) - return _samples; - - //if (_nativePixmap.m_internal == null) - // return null; - - int size = (ColorSpace.N + Alpha) * _nativePixmap.w() * _nativePixmap.h(); - _samples = new byte[size]; - SWIGTYPE_p_unsigned_char pData = _nativePixmap.samples(); - Marshal.Copy(SWIGTYPE_p_unsigned_char.getCPtr(pData).Handle, _samples, 0, size); - - return _samples; + if (_disposed) throw new ObjectDisposedException(nameof(Pixmap)); + return mupdf.mupdf.fz_pixmap_height(_nativePixmap); } } - /// - /// pointer to pixel area + /// x component of Pixmap origin. /// - public long SamplesPtr - { - get - { - lock (Utils.MuPDFLock) - { - return _nativePixmap.fz_pixmap_samples_int(); - } - } - } + public int X => mupdf.mupdf.fz_pixmap_x(_nativePixmap); + /// + /// y component of Pixmap origin. + /// + public int Y => mupdf.mupdf.fz_pixmap_y(_nativePixmap); + /// + /// The size of one pixel (number of components). + /// + public int N => mupdf.mupdf.fz_pixmap_components(_nativePixmap); + /// + /// Indicates presence of alpha channel. + /// + public int Alpha => mupdf.mupdf.fz_pixmap_alpha(_nativePixmap); + /// + /// Length of one image line (width * n). + /// + public int Stride => mupdf.mupdf.fz_pixmap_stride(_nativePixmap); + /// + /// Resolution in x direction. + /// + public float XRes => _nativePixmap.m_internal.xres; + /// + /// Resolution in y direction. + /// + public float YRes => _nativePixmap.m_internal.yres; + /// + /// Pixmap size in bytes. + /// + public int Size => (int)mupdf.mupdf.fz_pixmap_size(_nativePixmap); /// - /// pixmap's total length + /// Check if pixmap is monochrome. /// - public int Size + public bool IsMonochrome { get { - lock (Utils.MuPDFLock) - { - return _nativePixmap.n() * _nativePixmap.w() * _nativePixmap.h(); - } + if (N != 1 + Alpha) return false; + return mupdf.mupdf.fz_is_pixmap_monochrome(_nativePixmap) != 0; } } /// - /// size of one image row + /// Check if pixmap has only one color. /// - private int _stride = -1; - public int Stride + public bool IsUnicolor { get { - if (_stride < 0) + int n = N, w = Width, h = Height; + int count = w * h * n; + byte[] samples = Samples; + byte[] sample0 = new byte[n]; + Array.Copy(samples, 0, sample0, 0, n); + for (int offset = n; offset < count; offset += n) { - lock (Utils.MuPDFLock) + for (int i = 0; i < n; i++) { - if (_stride < 0) // Double-check after acquiring lock - _stride = _nativePixmap.fz_pixmap_stride(); + if (samples[offset + i] != sample0[i]) + return false; } } - return _stride; + return true; } } /// - /// pixmap width + /// Pixmap bbox - an IRect object. /// - public int W - { - get - { - lock (Utils.MuPDFLock) - { - return _nativePixmap.fz_pixmap_width(); - } - } - } - + public IRect IRect => new IRect(X, Y, X + Width, Y + Height); /// - /// X-coordinate of top-left corner + /// Pixmap Colorspace. /// - private int _x = -1; - public int X + public Colorspace Colorspace { get { - if (_x < 0) - { - lock (Utils.MuPDFLock) - { - if (_x < 0) // Double-check after acquiring lock - { - _x = _nativePixmap.fz_pixmap_x(); - } - } - } - return _x; + var cs = mupdf.mupdf.fz_pixmap_colorspace(_nativePixmap); + return cs.m_internal != null ? new Colorspace(cs) : null; } } /// - /// resolution in X-direction + /// The width. /// - public int Xres - { - get - { - lock (Utils.MuPDFLock) - { - return _nativePixmap.xres(); - } - } - } + public int W => Width; + /// + /// The height. + /// + public int H => Height; /// - /// Y-coordinate of top-left corner + /// Copy of the pixel area as bytes. /// - private int _y = -1; - public int Y + public byte[] Samples { get { - if (_y < 0) - { - lock (Utils.MuPDFLock) - { - if (_y < 0) // Double-check after acquiring lock - _y = _nativePixmap.fz_pixmap_y(); - } - } - return _y; + int len = Width * Height * N; + var ptr = mupdf.mupdf.fz_pixmap_samples(_nativePixmap); + var basePtr = mupdf.SWIGTYPE_p_unsigned_char.getCPtr(ptr).Handle; + byte[] result = new byte[len]; + Marshal.Copy(basePtr, result, 0, len); + return result; } } /// - /// resolution in Y-direction + /// Pixmap samples pointer (same data as Samples). /// - public int Yres - { - get - { - lock (Utils.MuPDFLock) - { - return _nativePixmap.yres(); - } - } - } + public byte[] SamplesPtr => Samples; - private List PixmapReadSamples(int offset, int n) - { - List ret = new List(); - for (int i = 0; i < n; i++) - { - ret.Add(Convert.ToByte(_nativePixmap.fz_samples_get(offset + i))); - } - - return ret; - } + /// + /// Pixmap samples memoryview length (Width * Height * N). + /// + public int SamplesMv => Width * Height * N; - public Pixmap(ColorSpace cs, Pixmap src) + /// + /// MD5 digest of pixmap. + /// + public string Digest { - ColorSpace cs_ = cs; - FzPixmap pix = src.ToFzPixmap(); - if (pix.fz_pixmap_colorspace().m_internal == null) - throw new Exception("source colorspace must not be None"); - - if (cs_ != null) - { - _nativePixmap = pix.fz_convert_pixmap(cs_.ToFzColorspace(), new FzColorspace(0), new FzDefaultColorspaces(), new FzColorParams(), 1); - } - else + get { - _nativePixmap = pix.fz_new_pixmap_from_alpha_channel(); - if (_nativePixmap == null) - throw new Exception(Utils.ErrorMessages["MSG_PIX_NOALPHA"]); + var md5 = mupdf.mupdf.fz_md5_pixmap2(_nativePixmap); + var bytes = new byte[md5.Count]; + for (int i = 0; i < md5.Count; i++) + bytes[i] = md5[i]; + return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant(); } } - public Pixmap(Pixmap src, float width, float height, Rect clip = null) - { - FzIrect bBox = new FzIrect(mupdf.mupdf.fz_infinite_irect); - if (clip != null) - bBox = new FzIrect(clip.ToFzRect()); - - FzPixmap srcPix = src.ToFzPixmap(); - FzPixmap pm; - if (bBox.fz_is_infinite_irect() == 0) - pm = srcPix.fz_scale_pixmap(srcPix.x(), srcPix.y(), width, height, bBox); - else - pm = srcPix.fz_scale_pixmap(srcPix.x(), srcPix.y(), width, height, new FzIrect(mupdf.mupdf.fz_infinite_irect)); - _nativePixmap = pm; - } - - public Pixmap(FzPixmap fzPix) - { - _nativePixmap = fzPix; - } - - public Pixmap(string filename) - { - FzImage img = mupdf.mupdf.fz_new_image_from_file(filename); - FzPixmap pix = img.fz_get_pixmap_from_image( - new FzIrect(Utils.FZ_MIN_INF_RECT, Utils.FZ_MIN_INF_RECT, Utils.FZ_MAX_INF_RECT, Utils.FZ_MAX_INF_RECT), - new FzMatrix(img.w(), 0, 0, img.h(), 0, 0), - new SWIGTYPE_p_int(IntPtr.Zero, false), - new SWIGTYPE_p_int(IntPtr.Zero, false) - ); - (int xres, int yres) = img.fz_image_resolution(); - pix.m_internal.xres = xres; - pix.m_internal.yres = yres; - _nativePixmap = pix; - img.Dispose(); - } + // ─── Methods ──────────────────────────────────────────────────── - public Pixmap(byte[] image) + /// + /// Fill all color components with same value. + /// + public void ClearWith(int value = 0) { - FzBuffer buffer = Utils.BufferFromBytes(image); - FzImage img = mupdf.mupdf.fz_new_image_from_buffer(buffer); - FzPixmap pix = img.fz_get_pixmap_from_image( - new FzIrect(Utils.FZ_MIN_INF_RECT, Utils.FZ_MIN_INF_RECT, Utils.FZ_MAX_INF_RECT, Utils.FZ_MAX_INF_RECT), - new FzMatrix(img.w(), 0, 0, img.h(), 0, 0), - new SWIGTYPE_p_int(IntPtr.Zero, false), - new SWIGTYPE_p_int(IntPtr.Zero, false) - ); - (int xres, int yres) = img.fz_image_resolution(); - pix.m_internal.xres = xres; - pix.m_internal.yres = yres; - _nativePixmap = pix; + mupdf.mupdf.fz_clear_pixmap_with_value(_nativePixmap, value); } - public Pixmap(ColorSpace cs, int w, int h, byte[] samples, int alpha) + /// + /// Fill all color components within a bbox with same value. + /// + public void ClearWith(int value, IRect irect) { - int n = cs.N; - int stride = (n + alpha) * w; - FzSeparations seps = new FzSeparations(); - FzPixmap pixmap = mupdf.mupdf.fz_new_pixmap(cs.ToFzColorspace(), w, h, seps, alpha); - int size = 0; - - size = samples.Length; - - if (stride * h != size) - { - throw new Exception($"bad samples length {w} {h} {alpha} {n} {stride} {size}"); - } - Marshal.Copy(samples, 0, SWIGTYPE_p_unsigned_char.getCPtr(pixmap.samples()).Handle, size); - _nativePixmap = pixmap; + mupdf.mupdf.fz_clear_pixmap_rect_with_value(_nativePixmap, value, irect.ToFzIRect()); } - public Pixmap(string arg0, Pixmap arg1) + /// + /// Tint colors with modifiers for black and white. + /// + public void TintWith(int black, int white) { - if (arg0 == "raw" && arg1 != null) - { - _nativePixmap = arg1.ToFzPixmap(); - GC.SuppressFinalize(arg1); - lock (Utils.MuPDFLock) - { - int n = N; - byte[] samples = SAMPLES; - ColorSpace colorSpace = ColorSpace; - IRect irect = IRect; - int stride = Stride; - int x = X; - int y = Y; - } - } - else - throw new Exception("arg0 must be `raw` or arg1 must be not null."); + if (Colorspace == null || Colorspace.N > 3) + throw new InvalidOperationException("colorspace invalid for function"); + mupdf.mupdf.fz_tint_pixmap(NativePixmap, black, white); } - public static Pixmap ApplyImageFilters(Pixmap pixmap, ImageFilterPipeline pipeline) + /// + /// Apply gamma correction. gamma=1 is a no-op. + /// + public void GammaWith(float gamma) { - if (pipeline == null || pixmap == null) - return pixmap; - - var filters = pipeline.Filters; - if (filters == null || filters.Count == 0) - return pixmap; - - byte[] sourceBytes = pixmap.ToBytes("png", 95); - if (sourceBytes == null || sourceBytes.Length == 0) - return pixmap; - - SKBitmap workingBitmap = SKBitmap.Decode(sourceBytes); - if (workingBitmap == null) - return pixmap; - - try - { - pipeline.Apply(ref workingBitmap); - ImageFilter.EnsureColorType(ref workingBitmap, SKColorType.Rgb888x, SKAlphaType.Opaque); - - using (var image = SKImage.FromBitmap(workingBitmap)) - using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) - { - var bytes = data.ToArray(); - if (bytes == null || bytes.Length == 0) - return pixmap; - - pixmap.Dispose(); - return new Pixmap(bytes); - } - } - finally - { - workingBitmap.Dispose(); - } + if (Colorspace == null) + throw new InvalidOperationException("colorspace invalid for function"); + mupdf.mupdf.fz_gamma_pixmap(NativePixmap, gamma); } /// - /// Extract text from this Pixmap using OCR (Optical Character Recognition) with optional image preprocessing filters. + /// Apply sharpening to the pixmap. /// - /// Optional image preprocessing filters to apply before OCR. If null, no filters are applied. - /// OCR language code (e.g., "eng" for English). Default is "eng". - /// Path to Tesseract tessdata directory. If null, uses TESSDATA_PREFIX environment variable. - /// Whether to compress the intermediate OCR PDF. Default is true. - /// Text extraction flags. Default is 0 (no flags). - /// The extracted text as a string. - /// Thrown if OCR support is not available or if OCR processing fails. - /// - /// - /// // Extract text with image filters - /// var pipeline = new ImageFilterPipeline(); - /// pipeline.AddGrayscale(); - /// pipeline.AddDeskew(); - /// string text = pixmap.GetTextFromOcr(pipeline, language: "eng"); - /// - /// // Extract text without filters - /// string text = pixmap.GetTextFromOcr(); - /// - /// - public string GetTextFromOcr( - ImageFilterPipeline imageFilters = null, - string language = "eng", - string tessdata = null, - bool compress = true, - int flags = 0 - ) + public void SharpenWith(int radius = 2) { - if (string.IsNullOrEmpty(Utils.TESSDATA_PREFIX) && string.IsNullOrEmpty(tessdata)) - throw new Exception("No OCR support: TESSDATA_PREFIX not set"); - - // Create a working copy to avoid modifying/disposing the original pixmap - Pixmap processedPixmap = new Pixmap(this.ToBytes("png", 95)); - bool disposeProcessedPixmap = true; - - try - { - // Apply image filters if provided - if (imageFilters != null) - { - var filtered = ApplyImageFilters(processedPixmap, imageFilters); - // ApplyImageFilters already disposes the input, so processedPixmap is already disposed - processedPixmap = filtered; - } - - // Ensure pixmap is in RGB format for OCR - if (processedPixmap.N - processedPixmap.Alpha != 3) - { - var tempPixmap = processedPixmap; - processedPixmap = new Pixmap(new ColorSpace(Utils.CS_RGB), processedPixmap); - tempPixmap.Dispose(); - } - - // Remove alpha channel if present - if (processedPixmap.Alpha != 0) - { - var tempPixmap = processedPixmap; - processedPixmap = new Pixmap(processedPixmap, 0); - tempPixmap.Dispose(); - } - - // Perform OCR - byte[] ocrPdfBytes = processedPixmap.PdfOCR2Bytes(compress, language, tessdata); - if (ocrPdfBytes == null || ocrPdfBytes.Length == 0) - throw new Exception("Failed to generate OCR PDF"); - - // Create document from OCR PDF bytes - Document ocrDoc = null; - Page ocrPage = null; - TextPage textPage = null; + if (radius < 1) return; + int w = Width, h = Height, n = N, stride = Stride; + byte[] src = Samples; + byte[] dst = new byte[src.Length]; - try - { - ocrDoc = new Document("pdf", ocrPdfBytes); - ocrPage = ocrDoc.LoadPage(0); - textPage = ocrPage.GetTextPage(flags: flags); - return textPage.ExtractText(); - } - finally - { - textPage?.Dispose(); - ocrPage?.Dispose(); - ocrDoc?.Close(); - } - } - finally + for (int y = 0; y < h; y++) { - // Dispose processed pixmap if we created it - if (disposeProcessedPixmap && processedPixmap != null) + for (int x = 0; x < w; x++) { - processedPixmap.Dispose(); + for (int c = 0; c < n; c++) + { + int idx = y * stride + x * n + c; + int sum = 0, count = 0; + for (int dy = -radius; dy <= radius; dy++) + { + for (int dx = -radius; dx <= radius; dx++) + { + int ny = y + dy, nx = x + dx; + if (ny >= 0 && ny < h && nx >= 0 && nx < w) + { + sum += src[ny * stride + nx * n + c]; + count++; + } + } + } + int blur = sum / count; + int sharp = src[idx] + (src[idx] - blur); + dst[idx] = (byte)(sharp < 0 ? 0 : (sharp > 255 ? 255 : sharp)); + } } } + SetSamples(dst); } - public Pixmap(PdfDocument doc, int xref) - { - int xrefLen = doc.pdf_xref_len(); - if (!Utils.INRANGE(xref, 1, xrefLen - 1)) - throw new Exception(Utils.ErrorMessages["MSG_BAD_XREF"]); - PdfObj refObj = doc.pdf_new_indirect(xref, 0); - PdfObj type = refObj.pdf_dict_get(new PdfObj("Subtype")); - - if ((type.pdf_name_eq(new PdfObj("Image")) == 0 && type.pdf_name_eq(new PdfObj("Alpha")) == 0) - && type.pdf_name_eq(new PdfObj("Luminosity")) == 0) - { - throw new Exception(Utils.ErrorMessages["MSG_IS_NO_IMAGE"]); - } - FzImage img = doc.pdf_load_image(refObj); - - FzPixmap pix = img.fz_get_pixmap_from_image( - new FzIrect(Utils.FZ_MIN_INF_RECT, Utils.FZ_MIN_INF_RECT, Utils.FZ_MAX_INF_RECT, Utils.FZ_MAX_INF_RECT), - new FzMatrix(img.w(), 0, 0, img.h(), 0, 0), - new SWIGTYPE_p_int(IntPtr.Zero, false), - new SWIGTYPE_p_int(IntPtr.Zero, false) - ); - _nativePixmap = pix; - } - - public Pixmap(Pixmap pix, int alpha) - { - FzPixmap srcPix = pix.ToFzPixmap(); - if (!Utils.INRANGE(alpha, 0, 1)) - throw new Exception("bad alpha value"); - FzColorspace cs = mupdf.mupdf.fz_pixmap_colorspace(srcPix); - if (cs.m_internal == null && alpha == 0) - throw new Exception("cannot drop alpha for 'Null' colorspace"); - FzSeparations seps = new FzSeparations(); - int n = srcPix.fz_pixmap_colorants(); - int w = srcPix.fz_pixmap_width(); - int h = srcPix.fz_pixmap_height(); - FzPixmap pm = mupdf.mupdf.fz_new_pixmap(cs, w, h, seps, alpha); - pm.m_internal.x = srcPix.m_internal.x; - pm.m_internal.y = srcPix.m_internal.y; - pm.m_internal.xres = srcPix.m_internal.xres; - pm.m_internal.yres = srcPix.m_internal.yres; - - } - - public FzPixmap ToFzPixmap() { return _nativePixmap; } - /// - /// Convert to binary image stream of desired type. + /// Invert the colors inside a bbox. /// - /// - /// - /// - internal byte[] ToBytes(int format, int jpgQuality) - { - lock (Utils.MuPDFLock) - { - FzPixmap pixmap = _nativePixmap; - int size = pixmap.fz_pixmap_stride() * pixmap.h(); - FzBuffer res = new FzBuffer((uint)size); - FzOutput output = new FzOutput(res); - - if (format == 1) mupdf.mupdf.fz_write_pixmap_as_png(output, pixmap); - else if (format == 2) mupdf.mupdf.fz_write_pixmap_as_pnm(output, pixmap); - else if (format == 3) mupdf.mupdf.fz_write_pixmap_as_pam(output, pixmap); - else if (format == 5) mupdf.mupdf.fz_write_pixmap_as_psd(output, pixmap); - else if (format == 6) mupdf.mupdf.fz_write_pixmap_as_ps(output, pixmap); - else if (format == 7) output.fz_write_pixmap_as_jpeg(pixmap, jpgQuality, 0); // v1.24 later - else mupdf.mupdf.fz_write_pixmap_as_png(output, pixmap); - - byte[] barray = Utils.BinFromBuffer(res); - output.fz_close_output(); - output.Dispose(); - res.Dispose(); - return barray; - } - } - - private void WriteImage(string filename, int format, int jpgQuality) + public bool InvertIRect(IRect irect = null) { - lock (Utils.MuPDFLock) - { - FzPixmap pixmap = _nativePixmap; - - if (format == 1) mupdf.mupdf.fz_save_pixmap_as_png(pixmap, filename); - else if (format == 2) mupdf.mupdf.fz_save_pixmap_as_pnm(pixmap, filename); - else if (format == 3) mupdf.mupdf.fz_save_pixmap_as_pam(pixmap, filename); - else if (format == 5) mupdf.mupdf.fz_save_pixmap_as_psd(pixmap, filename); - else if (format == 6) mupdf.mupdf.fz_save_pixmap_as_ps(pixmap, filename, 0); - else if (format == 7) mupdf.mupdf.fz_save_pixmap_as_jpeg(pixmap, filename, jpgQuality); - else mupdf.mupdf.fz_save_pixmap_as_png(pixmap, filename); - } + if (Colorspace == null) + return false; + if (irect == null) + mupdf.mupdf.fz_invert_pixmap(NativePixmap); + else + mupdf.mupdf.fz_invert_pixmap_rect(NativePixmap, irect.ToFzIRect()); + return true; } - public static int ClearPixmap_RectWithValue(Pixmap pixmap, int v = 0, FzIrect bbox = null) + /// + /// Set alpha channel to values contained in a byte array. + /// If omitted, set all alpha values to 255. + /// + public void SetAlpha(byte[] alphaValues = null, int premultiply = 1, float[] opaque = null) { - FzPixmap dest = pixmap.ToFzPixmap(); - FzIrect b = bbox.fz_intersect_irect(dest.fz_pixmap_bbox()); - float w = b.x1 - b.x0; - float y = b.y1 - b.y0; - if (w <= 0 || y <= 0) - return 0; - int destSpan = dest.fz_pixmap_stride(); - float destP = destSpan * (b.y0 - dest.y()) + dest.n() * (b.x0 - dest.x()); - int v_ = 0; - - if (dest.colorspace().fz_colorspace_n() == 4) + if (Alpha == 0) throw new InvalidOperationException("pixmap has no alpha channel"); + int w = Width, h = Height, n = N, stride = Stride; + int alphaPos = n - 1; + var ptr = mupdf.mupdf.fz_pixmap_samples(NativePixmap); + var basePtr = mupdf.SWIGTYPE_p_unsigned_char.getCPtr(ptr).Handle; + if (alphaValues != null) { - v_ = 255 - v; - while (true) + if (alphaValues.Length != w * h) + throw new ArgumentException($"alpha values length must be {w * h}, got {alphaValues.Length}"); + int ai = 0; + for (int y = 0; y < h; y++) { - int s = (int)destP; - for (int i = 0; i < w; i++) + for (int x = 0; x < w; x++) { - dest.fz_samples_set(s, 0); - s += 1; - dest.fz_samples_set(s, 0); - s += 1; - dest.fz_samples_set(s, 0); - s += 1; - dest.fz_samples_set(s, v_); - s += 1; - if (dest.alpha() != 0) - { - dest.fz_samples_set(s, 255); - s += 1; - } + int offset = y * stride + x * n + alphaPos; + Marshal.WriteByte(IntPtr.Add(basePtr, offset), alphaValues[ai++]); } - destP += destSpan; - if (y == 0) - break; - y -= 1; } - return 1; } - while (true) + else { - int s = (int)destP; - for (int i = 0; i < w; i++) + for (int y = 0; y < h; y++) { - for (int j = 0; j < dest.n() - 1; j++) - { - dest.fz_samples_set(s, v); - s += 1; - } - if (dest.alpha() != 0) - { - dest.fz_samples_set(s, 255); - s += 1; - } - else + for (int x = 0; x < w; x++) { - dest.fz_samples_set(s, v); - s += 1; + int offset = y * stride + x * n + alphaPos; + Marshal.WriteByte(IntPtr.Add(basePtr, offset), 255); } } - destP += destSpan; - if (y == 0) - break; - y -= 1; } - - return 1; + // MuPDF C# bindings currently do not expose fz_premultiply_pixmap. } /// - /// Fill all color components with same value. + /// Set top-left coordinates. /// - /// - /// - public void ClearWith(int v = 0, IRect bbox = null) + public void SetOrigin(int x, int y) { - if (v == 0) - _nativePixmap.fz_clear_pixmap(); - else if (bbox is null) - _nativePixmap.fz_clear_pixmap_with_value(v); - else Pixmap.ClearPixmap_RectWithValue(this, v, bbox.ToFzIrect()); + NativePixmap.m_internal.x = x; + NativePixmap.m_internal.y = y; } /// - /// Return count of each color. + /// Set resolution in both dimensions. /// - /// - /// - /// Dcitionary, int> or int - /// - public dynamic ColorCount(bool colors = false, dynamic clip = null) + public void SetDpi(int xres, int yres) { - if (_disposed || _nativePixmap == null) - throw new ObjectDisposedException("Pixmap has been disposed."); - - try - { - FzPixmap pm = _nativePixmap; - IRect irect = IRect; - int stride = Stride; - int n = N; - int x = X; - int y = Y; - byte[] samples = SAMPLES; - - Dictionary rc = Utils.ColorCount(pm, irect, stride, n, x, y, samples, clip); - if (rc == null) - throw new Exception(Utils.ErrorMessages["MSG_COLOR_COUNT_FAILED"]); - if (!colors) - return rc.Count; - - return rc; - } - catch (AccessViolationException) - { - _disposed = true; - throw new ObjectDisposedException("Pixmap has been disposed."); - } + NativePixmap.m_internal.xres = xres; + NativePixmap.m_internal.yres = yres; } /// - /// Return most frequent color and its usage ratio. + /// Set color of all pixels in bbox. /// - /// Return most frequent color and its usage ratio. - /// - public (float, byte[]) ColorTopUsage(dynamic clip = null) + public void SetRect(IRect irect, float[] color) { - int allPixels = 0; - int count = 0; - string maxPixel = null; - if (clip != null) - clip = this.IRect; + int n = N; + if (color.Length != n) + throw new ArgumentException($"color length {color.Length} must match {n}"); + byte[] colorBytes = new byte[n]; + for (int i = 0; i < n; i++) + { + int component = (int)(color[i] * 255 + 0.5f); + colorBytes[i] = (byte)(component < 0 ? 0 : (component > 255 ? 255 : component)); + } + + int stride = Stride; + var ptr = mupdf.mupdf.fz_pixmap_samples(NativePixmap); + var basePtr = mupdf.SWIGTYPE_p_unsigned_char.getCPtr(ptr).Handle; + int x0 = Math.Max(irect.X0, X), y0 = Math.Max(irect.Y0, Y); + int x1 = Math.Min(irect.X1, X + Width), y1 = Math.Min(irect.Y1, Y + Height); - Dictionary colorCount = (Dictionary)ColorCount(true, clip); - foreach (string pixel in colorCount.Keys) + for (int y = y0; y < y1; y++) { - int c = colorCount[pixel]; - allPixels += c; - if (c > count) + for (int x = x0; x < x1; x++) { - count = c; - maxPixel = pixel; + int offset = (y - Y) * stride + (x - X) * n; + for (int c = 0; c < n; c++) + Marshal.WriteByte(IntPtr.Add(basePtr, offset + c), colorBytes[c]); } } - - if (allPixels == 0) - return (1, Enumerable.Repeat((byte)255, N).ToArray()); - - return (count / (float)allPixels, maxPixel.Split(',').Select(b => byte.Parse(b)).ToArray()); - } - - /// - /// Copy bbox from another Pixmap. - /// - /// source pixmap - /// The area to be copied - /// - public void Copy(Pixmap src, IRect bbox) - { - FzPixmap pm = _nativePixmap; - FzPixmap srcPm = src.ToFzPixmap(); - if (srcPm.fz_pixmap_colorspace() == null) - throw new Exception("cannot copy pixmap with NULL colorspace"); - - if (pm.alpha() != srcPm.alpha()) - throw new Exception("source and target alpha must be equal"); - - pm.fz_copy_pixmap_rect(srcPm, bbox.ToFzIrect(), new FzDefaultColorspaces()); } /// - /// Apply correction with some float + /// Set color of a pixel. /// - /// - /// - public void GammaWith(float gamma) + public void SetPixel(int x, int y, float[] color) { - if (_nativePixmap.fz_pixmap_colorspace() == null) + int n = N; + if (color.Length != n) throw new ArgumentException($"color length {color.Length} must match {n}"); + if (x < 0 || x >= Width || y < 0 || y >= Height) + throw new ArgumentOutOfRangeException($"pixel ({x},{y}) out of range"); + int stride = Stride; + var ptr = mupdf.mupdf.fz_pixmap_samples(NativePixmap); + var basePtr = mupdf.SWIGTYPE_p_unsigned_char.getCPtr(ptr).Handle; + int offset = y * stride + x * n; + for (int c = 0; c < n; c++) { - throw new Exception("colorspace invalid for function"); + int component = (int)(color[c] * 255 + 0.5f); + Marshal.WriteByte(IntPtr.Add(basePtr, offset + c), (byte)(component < 0 ? 0 : (component > 255 ? 255 : component))); } - _nativePixmap.fz_gamma_pixmap(gamma); } - private int InvertPixmapRect(FzPixmap dest, FzIrect bb) + /// + /// Get color tuple of pixel (x, y). + /// Last item is the alpha if Pixmap.Alpha is true. + /// + public float[] GetPixel(int x, int y) { - FzIrect b = bb.fz_intersect_irect(dest.fz_pixmap_bbox()); - int w = b.x1 - b.x0; - int y = b.y1 - b.y0; - if (w <= 0 || y <= 0) - return 0; - int destSpan = dest.fz_pixmap_stride(); - int destp = destSpan * (b.y0 - dest.y()) + dest.n() * (b.x0 - dest.x()); - int n0 = dest.n() - dest.alpha(); - int alpha = dest.alpha(); - - while (true) - { - int s = destp; - for (int x = 0; x < w; x++) - { - for (int i = 0; i < n0; i++) - { - int ss = dest.fz_samples_get(s); - ss = 255 - ss; - dest.fz_samples_set(s, ss); - s += 1; - } - if (alpha != 0) - { - int ss = dest.fz_samples_get(s); - ss += 1; - dest.fz_samples_set(s, ss); - } - } - destp += destSpan; - y -= 1; - if (y == 0) - break; - } - - return 1; + if (x < 0 || x >= Width || y < 0 || y >= Height) + throw new ArgumentOutOfRangeException($"pixel ({x},{y}) out of range"); + int n = N, stride = Stride; + var ptr = mupdf.mupdf.fz_pixmap_samples(NativePixmap); + var basePtr = mupdf.SWIGTYPE_p_unsigned_char.getCPtr(ptr).Handle; + int offset = y * stride + x * n; + float[] result = new float[n]; + for (int c = 0; c < n; c++) + result[c] = Marshal.ReadByte(IntPtr.Add(basePtr, offset + c)) / 255.0f; + return result; } /// - /// Invert the colors inside a bbox. + /// Copy bbox from another Pixmap. /// - /// The area to be inverted - /// - public bool InvertIrect(IRect bbox = null) + public void CopyPixmap(Pixmap src, IRect irect) { - FzPixmap pm = _nativePixmap; - if (_nativePixmap.fz_pixmap_colorspace() == null) - { - return false; - } - FzIrect r; - if (bbox is null) - { - r = pm.fz_pixmap_bbox(); - } - else - { - r = bbox.ToFzIrect(); - if (r.fz_is_infinite_irect() != 0) - r = pm.fz_pixmap_bbox(); - } - - return Convert.ToBoolean(InvertPixmapRect(pm, r)); + mupdf.mupdf.fz_copy_pixmap_rect(NativePixmap, src.NativePixmap, irect.ToFzIRect(), new mupdf.FzDefaultColorspaces()); } + // ─── Color conversions ────────────────────────────────────────── + /// - /// Save pixmap as an OCR-ed PDF page + /// Convert pixmap to another colorspace. /// - /// File name - /// (bool) compress, default 1 (True) - /// language(s) occurring on page, default "eng" - /// folder name of Tesseract's language support. Must be given if environment variable TESSDATA_PREFIX is not set - /// - public void SavePdfOCR(string filename, int compress = 1, string language = null, string tessdata = null) + public Pixmap ToColorspace(Colorspace cs, bool alpha = true) { - if (Utils.TESSDATA_PREFIX == null && tessdata == null) - throw new Exception("No OCR support: TESSDATA_PREFIX not set"); - FzPdfocrOptions opts = new FzPdfocrOptions(); - opts.compress = compress; - FzPixmap pix = _nativePixmap; - - if (language != null) - { - opts.language_set2(language); - } - if (tessdata != null) - { - opts.datadir_set2(tessdata); - } - - pix.fz_save_pixmap_as_pdfocr(filename, 0, opts); + var newPix = mupdf.mupdf.fz_convert_pixmap(NativePixmap, cs, new mupdf.FzColorspace(), new mupdf.FzDefaultColorspaces(), new mupdf.FzColorParams(), 1); + return new Pixmap(newPix); } + // ─── Save ─────────────────────────────────────────────────────── + /// - /// Save pixmap as an OCR-ed PDF page + /// Output as image in format determined by filename extension. /// - /// Buffer to store page data - /// (bool) compress, default 1 (True) - /// language(s) occurring on page, default "eng" - /// folder name of Tesseract's language support. Must be given if environment variable TESSDATA_PREFIX is not set - /// - public void SavePdfOCR(MemoryStream filename, int compress = 1, string language = null, string tessdata = null) + public void Save(string filename, string output = null, int quality = 95) { - if (Utils.TESSDATA_PREFIX == null && tessdata == null) - throw new Exception("No OCR support: TESSDATA_PREFIX not set"); - FzPdfocrOptions opts = new FzPdfocrOptions(); - opts.compress = compress; - FzPixmap pix = _nativePixmap; + string ext = output ?? System.IO.Path.GetExtension(filename).TrimStart('.'); + ext = ext.ToLower(); - if (language != null) - { - opts.language_set2(language); - } - if (tessdata != null) - { - opts.datadir_set2(tessdata); - } + if (Alpha != 0 && (ext == "pnm" || ext == "pgm" || ext == "ppm" || ext == "pbm" || ext == "ps" || ext == "jpg" || ext == "jpeg")) + throw new ArgumentException($"'{ext}' cannot have alpha"); + if (Colorspace != null && Colorspace.N > 3 && (ext == "png" || ext == "pnm" || ext == "pgm" || ext == "ppm" || ext == "pbm")) + throw new ArgumentException($"unsupported colorspace for '{ext}'"); - FilePtrOutput output = null; - try - { - output = new FilePtrOutput(filename); - output.fz_write_pixmap_as_pdfocr(pix, opts); - output.fz_close_output(); - } - finally + switch (ext) { - output?.Dispose(); + case "png": mupdf.mupdf.fz_save_pixmap_as_png(NativePixmap, filename); break; + case "pnm": + case "pgm": + case "ppm": + case "pbm": mupdf.mupdf.fz_save_pixmap_as_pnm(NativePixmap, filename); break; + case "pam": mupdf.mupdf.fz_save_pixmap_as_pam(NativePixmap, filename); break; + case "psd": mupdf.mupdf.fz_save_pixmap_as_psd(NativePixmap, filename); break; + case "ps": mupdf.mupdf.fz_save_pixmap_as_ps(NativePixmap, filename, 0); break; + case "jpg": + case "jpeg": + SetDpi((int)XRes, (int)YRes); + mupdf.mupdf.fz_save_pixmap_as_jpeg(NativePixmap, filename, quality); + break; + default: + mupdf.mupdf.fz_save_pixmap_as_png(NativePixmap, filename); + break; } } /// - /// Return the value of the pixel at location (x, y) (column, line) + /// Convert to binary image stream of desired type. /// - /// the column number of the pixel. Must be in range(pix.width) - /// the line number of the pixel, Must be in range(pix.height) - /// - public byte[] GetPixel(int x, int y) + public byte[] ToBytes(string output = "png", int quality = 96) { - if (false || x < 0 || x >= _nativePixmap.m_internal.w - || y < 0 || y >= _nativePixmap.m_internal.h - ) + mupdf.FzBuffer buf; + switch (output?.ToLower()) { - throw new Exception(Utils.ErrorMessages["MSG_PIXEL_OUTSIDE"]); + case "pnm": + case "pgm": + case "ppm": + case "pbm": + buf = mupdf.mupdf.fz_new_buffer_from_pixmap_as_pnm(NativePixmap, new mupdf.FzColorParams()); + break; + case "pam": + buf = mupdf.mupdf.fz_new_buffer_from_pixmap_as_pam(NativePixmap, new mupdf.FzColorParams()); + break; + case "psd": + buf = mupdf.mupdf.fz_new_buffer_from_pixmap_as_psd(NativePixmap, new mupdf.FzColorParams()); + break; + case "jpg": + case "jpeg": + buf = mupdf.mupdf.fz_new_buffer_from_pixmap_as_jpeg(NativePixmap, new mupdf.FzColorParams(), quality, 0); + break; + case "png": + default: + buf = mupdf.mupdf.fz_new_buffer_from_pixmap_as_png(NativePixmap, new mupdf.FzColorParams()); + break; } - - int n = _nativePixmap.m_internal.n; - int stride = _nativePixmap.fz_pixmap_stride(); - int i = stride * y + n * x; - - byte[] pixel = SAMPLES.Skip(i).Take(n).ToArray(); - - return pixel; + return buf.fz_buffer_extract(); } /// - /// Output as image in format determined by filename extension + /// Save pixmap as a PDF page. /// - /// The file to save to. May be provided as a string - /// only use to overrule filename extension. Default is PNG. Others are JPEG, JPG, PNM, PGM, PPM, PBM, PAM, PSD, PS. - /// The desired image quality, default 95. Only applies to JPEG images, else ignored - /// - public void Save(string filename, string output = null, int jpgQuality = 95) - { - Dictionary validFormats = new Dictionary() - { - {"png", 1 }, - {"pnm", 2 }, - {"pgm", 2 }, - {"ppm", 2 }, - {"pbm", 2 }, - {"pam", 3 }, - {"psd", 5 }, - {"ps", 6 }, - {"jpg", 7 }, - {"jpeg", 7 } - }; - - string filename_ = filename; - string output_ = output; - - if (string.IsNullOrEmpty(filename)) - throw new Exception("filename must be set"); - - if (string.IsNullOrEmpty(output_)) - output_ = Path.GetExtension(filename_).Substring(1); - - int idx = validFormats[output_.ToLower()]; - if (idx is -1) - throw new Exception($"Image format {output_} not in {validFormats.Keys}"); - if (Alpha != 0 && (new List() { 2, 6, 7 }).Contains(idx)) - { - throw new Exception(string.Format("'{0}' cannot have alpha", output_)); - } - if (ColorSpace != null && ColorSpace.N > 3 && (new List() { 1, 2, 4 }).Contains(idx)) - { - throw new Exception(string.Format("unsupported colorspace for '{0}'", output_)); - } - if (idx == 7) - SetDpi(Xres, Yres); - WriteImage(filename_, idx, jpgQuality); - } + public byte[] ToPdfBytes(bool compressImages = true) => + PdfOCRSave(compress: compressImages); - private int fz_mul255(int a, int b) + /// + /// Save pixmap as an OCR-ed PDF page. + /// + public byte[] PdfOCRSave(bool compress = true) { - int x = a * b + 128; - x += x / 256; - - return x / 256; + var buf = mupdf.mupdf.fz_new_buffer(1024); + var output = new mupdf.FzOutput(buf); + output.fz_write_pixmap_as_pdfocr(NativePixmap, new mupdf.FzPdfocrOptions()); + mupdf.mupdf.fz_close_output(output); + return buf.fz_buffer_extract(); } + // ─── SkiaSharp interop ─────────────────────────────────────────── + /// - /// Set alpha channel to values contained in a byte array + /// Convert pixmap to an SkiaSharp SKBitmap. + /// Works cross-platform (Windows, Linux, macOS). /// - /// with length (width * height) or 'None' - /// premultiply colors with alpha values - /// this color receives opacity 0 - /// preblending background color - /// - public void SetAlpha(dynamic alphaValues = null, int premultiply = 1, dynamic opaque = null, dynamic matte = null) + public SKBitmap ToSKBitmap() { - FzPixmap pixmap = _nativePixmap; - int alpha = 0; - int m = 0; - if (pixmap.alpha() == 0) - throw new Exception(Utils.ErrorMessages["MSG_PIX_NOALPHA"]); - - int n = pixmap.fz_pixmap_colorants(); - int w = pixmap.fz_pixmap_width(); - int h = pixmap.fz_pixmap_height(); - int balen = w * h * (n + 1); - int[] colors = new int[4] { 0, 0, 0, 0 }; - int[] bgColor = new int[4] { 0, 0, 0, 0 }; - int zeroOut = 0; - int bground = 0; - if (opaque != null && (opaque is List || opaque is Tuple) && opaque.Count == n) - { - foreach (int i in opaque) - { - colors[i] = opaque[i]; - } - zeroOut = 1; - } - if (matte != null && (matte is Tuple || matte is List) && matte.Count == n) + int w = Width, h = Height, n = N; + bool hasAlpha = Alpha != 0; + var cs = Colorspace; + + SKColorType colorType; + if (cs != null && cs.N == 3 && hasAlpha) + colorType = SKColorType.Rgba8888; + else if (cs != null && cs.N == 3 && !hasAlpha) + colorType = SKColorType.Rgb888x; + else if (cs != null && cs.N == 1 && !hasAlpha) + colorType = SKColorType.Gray8; + else { - foreach (int i in matte) - bgColor[i] = matte[i]; - bground = 1; + var png = ToPng(); + return SKBitmap.Decode(png); } - List data = null; - int dataLen = 0; - if (alphaValues != null) + + var info = new SKImageInfo(w, h, colorType, hasAlpha ? SKAlphaType.Unpremul : SKAlphaType.Opaque); + var bmp = new SKBitmap(info); + var samples = Samples; + + if (colorType == SKColorType.Rgba8888 || colorType == SKColorType.Rgb888x) { - if (alphaValues is List || alphaValues is byte[]) + if (n == 4) { - data = new List(alphaValues); - dataLen = alphaValues.Count; + var ptr = bmp.GetPixels(); + Marshal.Copy(samples, 0, ptr, samples.Length); } else - throw new Exception($"unexpected type for alphavalues: {alphaValues.GetType()}"); - if (dataLen < w * h) - throw new Exception("bad alpha values"); - } - - if (true) - { - IntPtr dataPtr = Marshal.AllocHGlobal(dataLen * sizeof(byte)); - Marshal.Copy(data.ToArray(), 0, dataPtr, dataLen); - SWIGTYPE_p_unsigned_char swigData = new SWIGTYPE_p_unsigned_char(dataPtr, false); - - IEnumerable iColors = colors; - IEnumerable iBgColor = bgColor; - - mupdf.mupdf.Pixmap_set_alpha_helper( - balen, - n, - dataLen, - zeroOut, - swigData, - pixmap.m_internal, - premultiply, - bground, - new vectori(iColors), - new vectori(iBgColor) - ); - } - else - { - int i = 0; int j = 0; int k = 0; - int dataFix = 255; - while (i < balen) { - alpha = data[k]; - if (zeroOut != 0) + var ptr = bmp.GetPixels(); + unsafe { - for (j = i; j < i + n; j++) + byte* dst = (byte*)ptr.ToPointer(); + int si = 0; + for (int y = 0; y < h; y++) { - if (pixmap.fz_samples_get(j) != colors[j - 1]) + for (int x = 0; x < w; x++) { - dataFix = 255; - break; + dst[0] = samples[si]; + dst[1] = samples[si + 1]; + dst[2] = samples[si + 2]; + dst[3] = 255; + dst += 4; + si += 3; } - else - dataFix = 0; } } - if (dataLen != 0) - { - if (dataFix == 0) - pixmap.fz_samples_set(i + n, 0); - else - pixmap.fz_samples_set(i + n, alpha); - if (premultiply != 0 && bground == 0) - { - for (j = i; j < i + n; j++) - pixmap.fz_samples_set(j, fz_mul255(pixmap.fz_samples_get(j), alpha)); - } - else if (bground != 0) - for (j = i; j < i + n; j++) - { - m = bgColor[j - 1]; - pixmap.fz_samples_set(j, fz_mul255(pixmap.fz_samples_get(j) - m, alpha)); - } - } - else - pixmap.fz_samples_set(i + n, dataFix); - i += n + 1; - k += 1; } } + else + { + var ptr = bmp.GetPixels(); + Marshal.Copy(samples, 0, ptr, samples.Length); + } + + return bmp; } /// - /// Convert to binary image stream of desired type + /// Create a Pixmap from an SkiaSharp SKBitmap. /// - /// The desired image format. The default is "png" - /// The desired image quality, default 95. Only applies to JPEG images, else ignored. This parameter trades quality against file size. A value of 98 is close to lossless. Higher values should not lead to better quality - /// Returns bytes - /// - public byte[] ToBytes(string output = "png", int jpgQuality = 95) + public static Pixmap FromSKBitmap(SKBitmap bitmap) { - Dictionary validFormats = new Dictionary() - { - {"png", 1 }, - {"pnm", 2 }, - {"pgm", 2 }, - {"ppm", 2 }, - {"pbm", 2 }, - {"pam", 3 }, - {"psd", 5 }, - {"ps", 6 }, - {"jpg", 7 }, - {"jpeg", 7 } - }; - - //int idx = validFormats.GetValueOrDefault(output.ToLower(), 0); - int idx; - if (!validFormats.TryGetValue(output.ToLower(), out idx)) - { - idx = 0; // Default value if key does not exist - } - if (idx == 0) - { - throw new Exception($"Image format {output} not in {string.Join(", ", validFormats.Keys)}"); - } - if (Alpha != 0 && (new List() { 2, 6, 7 }).Contains(idx)) - throw new Exception($"'{output}' cannot have alpha"); - if (ColorSpace != null && ColorSpace.N > 3 && (new List() { 1, 2, 4 }).Contains(idx)) - throw new Exception($"unsupported colorspace for '{output}'"); + if (bitmap == null) throw new ArgumentNullException(nameof(bitmap)); - if (idx == 7) - SetDpi(Xres, Yres); + using var image = SKImage.FromBitmap(bitmap); + using var data = image.Encode(SKEncodedImageFormat.Png, 100); + var pngBytes = data.ToArray(); - return ToBytes(idx, jpgQuality); + var buf = Helpers.BufferFromBytes(pngBytes); + var img = mupdf.mupdf.fz_new_image_from_buffer(buf); + var nativePix = img.fz_get_pixmap_from_image(new mupdf.FzIrect(), new mupdf.FzMatrix(), null, null); + return new Pixmap(nativePix); } - public void SetDpi(int xres, int yres) - { - _nativePixmap.m_internal.xres = xres; - _nativePixmap.m_internal.yres = yres; - } + /// + /// Convert pixmap to PNG bytes. + /// + public byte[] ToPng() => ToBytes("png"); /// - /// Set the x and y values of the pixmap’s top-left point + /// Set the pixel area from bytes. /// - /// x coordinate - /// y coordinate - public void SetOrigin(int x, int y) + public void SetSamples(byte[] data) { - _nativePixmap.m_internal.x = x; - _nativePixmap.m_internal.y = y; + int expected = Width * Height * N; + if (data.Length != expected) + throw new ArgumentException($"samples length {data.Length} must be {expected}"); + var ptr = mupdf.mupdf.fz_pixmap_samples(_nativePixmap); + var basePtr = mupdf.SWIGTYPE_p_unsigned_char.getCPtr(ptr).Handle; + Marshal.Copy(data, 0, basePtr, data.Length); } /// - /// Set color of pixel (x, y) + /// Apply an affine transform to the pixmap. /// - /// the column number of the pixel - /// the line number of the pixel - /// - /// - public void SetPixel(int x, int y, byte[] color) + public Pixmap WarpAffine(Matrix matrix, IRect irect = null) { - FzPixmap pm = _nativePixmap; - if (!Utils.INRANGE(x, 0, pm.w() - 1) || !Utils.INRANGE(y, 0, pm.h() - 1)) - { - throw new Exception(Utils.ErrorMessages["MSG_PIXEL_OUTSIDE"]); - } - int n = pm.n(); - List c = new List(); + int w = Width, h = Height, n = N; + bool hasAlpha = Alpha != 0; + var src = Samples; + int srcStride = Stride; - for (int j = 0; j < n; j++) - { - byte t = color[j]; - if (!Utils.INRANGE(t, 0, 255)) - throw new Exception(Utils.ErrorMessages["MSG_BAD_COLOR_SEQ"]); - c.Add(Convert.ToInt32(t)); - } - int stride = pm.fz_pixmap_stride(); - int i = stride * y + n * x; - for (int j = 0; j < n; j++) - pm.fz_samples_set(i + j, c[j]); - } + int dstW = irect?.Width ?? w; + int dstH = irect?.Height ?? h; - private int FillPixmap_RectWithColor(FzPixmap dest, byte[] col, FzIrect b) - { - FzIrect b_ = b.fz_intersect_irect(dest.fz_pixmap_bbox()); - int w = b.x1 - b.x0; - int y = b.y1 - b.y0; - if (w <= 0 && y <= 0) - return 0; - - int destSpan = dest.fz_pixmap_stride(); - int destP = destSpan * (b.y0 - dest.y()) + dest.n() * (b.x0 - dest.x()); - while (true) + var result = new Pixmap(Colorspace, new IRect(0, 0, dstW, dstH), hasAlpha); + var dst = new byte[dstW * dstH * n]; + int dstStride = dstW * n; + + float det = (float)(matrix.A * matrix.D - matrix.B * matrix.C); + if (Math.Abs(det) < 1e-10f) + return result; + + float invA = (float)(matrix.D / det), invB = (float)(-matrix.B / det); + float invC = (float)(-matrix.C / det), invD = (float)(matrix.A / det); + float invE = (float)((matrix.C * matrix.F - matrix.D * matrix.E) / det); + float invF = (float)((matrix.B * matrix.E - matrix.A * matrix.F) / det); + + for (int dy = 0; dy < dstH; dy++) { - int s = destP; - for (int x = 0; x < w; x++) - for (int i = 0; i < dest.n(); i++) + for (int dx = 0; dx < dstW; dx++) + { + float sx = invA * dx + invC * dy + invE; + float sy = invB * dx + invD * dy + invF; + int isx = (int)sx, isy = (int)sy; + if (isx >= 0 && isx < w && isy >= 0 && isy < h) { - dest.fz_samples_set(s, col[i]); - s += 1; + int srcOff = isy * srcStride + isx * n; + int dstOff = dy * dstStride + dx * n; + for (int c = 0; c < n; c++) + dst[dstOff + c] = src[srcOff + c]; } - destP += destSpan; - y -= 1; - - if (y == 0) break; + } } - - return 1; + + result.SetSamples(dst); + return result; } /// - /// Set color of all pixels in bbox. + /// Return pixmap from a warped quad. /// - /// the rectangle to be filled with the value - /// the desired value, given as a sequence of integers in range(256) - /// False if the rectangle was invalid or had an empty intersection with Pixmap.irect, else True - /// - public bool SetRect(IRect bbox, byte[] color) + public Pixmap Warp(Quad quad, int width, int height) { - FzPixmap pm = _nativePixmap; - int n = pm.n(); - List c = new List(); - int i = 0; - for (int j = 0; j < n; j++) - { - i = color[j]; - if (!Utils.INRANGE(i, 0, 255)) - throw new Exception(Utils.ErrorMessages["MSG_BAD_COLOR_SEQ"]); - c.Add((byte)i); - } - i = FillPixmap_RectWithColor(pm, c.ToArray(), bbox.ToFzIrect()); - - return Convert.ToBoolean(i); + if (!quad.IsConvex) throw new ArgumentException("quad must be convex"); + var dst = NativePixmap.fz_warp_pixmap(quad.ToFzQuad(), width, height); + return new Pixmap(dst); } /// - /// Divide width and height by 2**factor + /// Divide width and height by 2**factor. + /// E.g. factor=1 shrinks to 25% of original size (in place). /// - /// determines the new pixmap (samples) size. For example, a value of 2 divides width and height by 4 and thus results in a size of one 16th of the original. Values less than 1 are ignored with a warning. - public void Shrink(int factor) + public Pixmap Shrink(int factor) { - if (factor < 1) - { - Console.WriteLine("ignoring shrink factor < 1"); - return; - } - _nativePixmap.fz_subsample_pixmap(factor); + mupdf.mupdf.fz_subsample_pixmap(NativePixmap, factor); + return this; } /// - /// Tint colors with modifiers for black and white. + /// Return count of each color, or just the number of distinct colors. /// - /// replace black with this value. Specifying 0x000000 makes no changes - /// replace white with this value. Specifying 0xFFFFFF makes no changes - public void TintWith(int black, int white) + public object Color_Count(bool colors = false, IRect clip = null) { - if (ColorSpace == null || ColorSpace.N > 3) + var counts = new Dictionary(); + int n = N, w = Width, h = Height, stride = Stride; + byte[] samples = Samples; + + int x0 = clip != null ? Math.Max(clip.X0 - X, 0) : 0; + int y0 = clip != null ? Math.Max(clip.Y0 - Y, 0) : 0; + int x1 = clip != null ? Math.Min(clip.X1 - X, w) : w; + int y1 = clip != null ? Math.Min(clip.Y1 - Y, h) : h; + + for (int y = y0; y < y1; y++) { - Console.WriteLine("warning: colorspace invalid for function"); - return; + for (int x = x0; x < x1; x++) + { + int offset = y * stride + x * n; + byte[] pixel = new byte[n]; + Array.Copy(samples, offset, pixel, 0, n); + string key = BitConverter.ToString(pixel); + if (counts.ContainsKey(key)) + counts[key] = (counts[key].color, counts[key].count + 1); + else + counts[key] = (pixel, 1); + } } - - _nativePixmap.fz_tint_pixmap(black, white); + if (!colors) + return counts.Count; + var result = new Dictionary(); + foreach (var kv in counts) + result[kv.Value.color] = kv.Value.count; + return result; } /// - /// Return a new pixmap by “warping” the quad such that the quad corners become the new pixmap’s corners. The target pixmap’s IRect will be (0, 0, width, height) + /// Return most frequent color and its usage ratio. /// - /// a convex quad with coordinates inside Pixmap.irect (including the border points) - /// desired resulting width - /// desired resulting height - /// - /// - public Pixmap Warp(Quad quad, float width, float height) + public (float ratio, byte[] color) Color_TopUsage(IRect clip = null) { - if (!quad.IsConvex) - throw new Exception("quad must be convex"); - - FzPoint[] points = new FzPoint[4] { - quad.UpperLeft.ToFzPoint(), - quad.UpperRight.ToFzPoint(), - quad.LowerRight.ToFzPoint(), - quad.UpperLeft.ToFzPoint() - }; - - FzPixmap fpxmp = mupdf.mupdf.fz_warp_pixmap(_nativePixmap, quad.ToFzQuad(), (int)width, (int)height); - - return new Pixmap(fpxmp); - } + int n = N; + var colorCounts = Color_Count(colors: true, clip: clip) as Dictionary; + if (colorCounts == null || colorCounts.Count == 0) + return (1f, new byte[n]); - public byte[] PdfOCR2Bytes(bool compress = true, string language = "eng", string tessdata = null) - { - if (Utils.TESSDATA_PREFIX == null && tessdata == null) - throw new Exception("No OCR support: TESSDATA_PREFIX not set"); - MemoryStream byteStream = new MemoryStream(); - SavePdfOCR(byteStream, compress ? 1 : 0, language, tessdata); - - return byteStream.ToArray(); + int totalPixels = 0; + byte[] topColor = null; + int topCount = 0; + foreach (var kv in colorCounts) + { + totalPixels += kv.Value; + if (kv.Value > topCount) + { + topCount = kv.Value; + topColor = kv.Key; + } + } + float ratio = totalPixels > 0 ? (float)topCount / totalPixels : 0f; + return (ratio, topColor ?? new byte[n]); } - ~Pixmap() - { - Dispose(false); - } + // Python/legacy compatibility aliases (mirrors _alias(Pixmap, ...)). + public void clear_with(int value = 0) => ClearWith(value); + public Pixmap copy(float alpha = -1) => new Pixmap(this, alpha); + public Pixmap copyPixmap(float alpha = -1) => copy(alpha); + public void gamma_with(float gamma) => GammaWith(gamma); + public bool invert_irect(IRect irect = null) => InvertIRect(irect); + public bool invertIRect(IRect irect = null) => invert_irect(irect); + public void pil_save(string filename, string output = null, int quality = 95) => Save(filename, output, quality); + public void pillowWrite(string filename, string output = null, int quality = 95) => pil_save(filename, output, quality); + public byte[] pil_tobytes(string output = "png", int quality = 96) => ToBytes(output, quality); + public byte[] pillowData(string output = "png", int quality = 96) => pil_tobytes(output, quality); + public void save(string filename, string output = null, int quality = 95) => Save(filename, output, quality); + public void writeImage(string filename, string output = null, int quality = 95) => save(filename, output, quality); + public void writePNG(string filename, int quality = 95) => save(filename, "png", quality); + public void set_alpha(byte[] alphaValues = null, int premultiply = 1, float[] opaque = null) => SetAlpha(alphaValues, premultiply, opaque); + public void set_dpi(int xres, int yres) => SetDpi(xres, yres); + public void setResolution(int xres, int yres) => set_dpi(xres, yres); + public void set_origin(int x, int y) => SetOrigin(x, y); + public void set_pixel(int x, int y, float[] color) => SetPixel(x, y, color); + public void set_rect(IRect irect, float[] color) => SetRect(irect, color); + public void tint_with(int black, int white) => TintWith(black, white); + public byte[] tobytes(string output = "png", int quality = 96) => ToBytes(output, quality); + public byte[] getImageData(string output = "png", int quality = 96) => tobytes(output, quality); + public byte[] getPNGData() => tobytes("png"); + public byte[] getPNGdata() => getPNGData(); - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + // ─── IDisposable ──────────────────────────────────────────────── - protected virtual void Dispose(bool disposing) + /// + /// Releases the native pixmap resources. + /// + public void Dispose() { - if (_disposed) - return; - - if (_nativePixmap != null) + if (!_disposed) { - _nativePixmap.Dispose(); + _nativePixmap?.Dispose(); _nativePixmap = null; + _disposed = true; } - - _disposed = true; - } - } - - public class FilePtrOutput : FzOutput2 - { - public MemoryStream data { get; set; } - - public FilePtrOutput(MemoryStream src) : base() - { - this.data = src; - this.use_virtual_write(); - this.use_virtual_seek(); - this.use_virtual_tell(); - this.use_virtual_truncate(); - } - - public override void seek(fz_context arg_0, long arg_2, int arg_3) - { - data.Seek(arg_2, (SeekOrigin)arg_3); - } - - public override long tell(fz_context arg_0) - { - return data.Position; - } - - public override void truncate(fz_context arg_0) - { - data.SetLength(0); + GC.SuppressFinalize(this); } - public override void write(fz_context arg_0, SWIGTYPE_p_void arg_2, ulong arg_3) - { - byte[] data = new byte[(int)arg_3]; - Marshal.Copy(SWIGTYPE_p_void.getCPtr(arg_2).Handle, data, 0, data.Length); + ~Pixmap() { Dispose(); } - this.data.Write(data, 0, data.Length); - } + /// + /// Returns a string describing colorspace, bounds, and alpha. + /// + public override string ToString() => $"Pixmap({Colorspace?.Name}, {IRect}, {(Alpha > 0 ? "alpha" : "no alpha")})"; } } - \ No newline at end of file diff --git a/MuPDF.NET/Platforms/Android/PlatformClass1.cs b/MuPDF.NET/Platforms/Android/PlatformClass1.cs deleted file mode 100644 index 833e4f47..00000000 --- a/MuPDF.NET/Platforms/Android/PlatformClass1.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MuPDF.NET -{ -#if ANDROID - // All the code in this file is only included on Android. - public class PlatformClass1 - { - } -#endif -} \ No newline at end of file diff --git a/MuPDF.NET/Platforms/MacCatalyst/PlatformClass1.cs b/MuPDF.NET/Platforms/MacCatalyst/PlatformClass1.cs deleted file mode 100644 index 61851d04..00000000 --- a/MuPDF.NET/Platforms/MacCatalyst/PlatformClass1.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MuPDF.NET -{ -#if MACCATALYST - // All the code in this file is only included on Mac Catalyst. - public class PlatformClass1 - { - } -#endif -} \ No newline at end of file diff --git a/MuPDF.NET/Platforms/Tizen/PlatformClass1.cs b/MuPDF.NET/Platforms/Tizen/PlatformClass1.cs deleted file mode 100644 index f61eedf7..00000000 --- a/MuPDF.NET/Platforms/Tizen/PlatformClass1.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace MuPDF.NET -{ -#if TVOS - // All the code in this file is only included on Tizen. - public class PlatformClass1 - { - } -#endif -} \ No newline at end of file diff --git a/MuPDF.NET/Platforms/Windows/PlatformClass1.cs b/MuPDF.NET/Platforms/Windows/PlatformClass1.cs deleted file mode 100644 index 0cd676d6..00000000 --- a/MuPDF.NET/Platforms/Windows/PlatformClass1.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MuPDF.NET -{ -#if WINDOWS - // All the code in this file is only included on Windows. - public class PlatformClass1 - { - } -#endif -} \ No newline at end of file diff --git a/MuPDF.NET/Platforms/iOS/PlatformClass1.cs b/MuPDF.NET/Platforms/iOS/PlatformClass1.cs deleted file mode 100644 index 8a7ccb16..00000000 --- a/MuPDF.NET/Platforms/iOS/PlatformClass1.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MuPDF.NET -{ -#if IOS - // All the code in this file is only included on iOS. - public class PlatformClass1 - { - } -#endif -} \ No newline at end of file diff --git a/MuPDF.NET/Properties/Resources.Designer.cs b/MuPDF.NET/Properties/Resources.Designer.cs deleted file mode 100644 index 8bf5fdc5..00000000 --- a/MuPDF.NET/Properties/Resources.Designer.cs +++ /dev/null @@ -1,63 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace MuPDF.NET.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MuPDF.NET.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - } -} diff --git a/MuPDF.NET/Properties/Resources.resx b/MuPDF.NET/Properties/Resources.resx deleted file mode 100644 index 4fdb1b6a..00000000 --- a/MuPDF.NET/Properties/Resources.resx +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 1.3 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/MuPDF.NET/PythonParity.AllModules.md b/MuPDF.NET/PythonParity.AllModules.md new file mode 100644 index 00000000..17dbe326 --- /dev/null +++ b/MuPDF.NET/PythonParity.AllModules.md @@ -0,0 +1,68 @@ +# Python -> C# Parity Matrix (All Modules) + +This matrix extends the conversion traceability beyond `Page`/`Document`. + +## Module Mapping + +- `src/__init__.py` + - `Annot` -> `MuPDF.NET/Annot.cs` + - `Archive` -> `MuPDF.NET/Archive.cs` + - `Colorspace` -> `MuPDF.NET/Colorspace.cs` + - `DisplayList` -> `MuPDF.NET/DisplayList.cs` + - `Document` -> `MuPDF.NET/Document.cs` + `MuPDF.NET/Document.PythonCompat.cs` + - `DocumentWriter` -> `MuPDF.NET/DocumentWriter.cs` + - `Font` -> `MuPDF.NET/Font.cs` + - `Graftmap` -> `MuPDF.NET/Graftmap.cs` + - `Link` -> `MuPDF.NET/Link.cs` (snake_case mirrors: `is_external`, `id`, `xref`, `next`, `uri`, `page`, `rect`, `dest`, `to_dict`, `set_border` / `set_colors` / `set_flags`, etc.; use `Flags` for the flags getter; `Dest` / `dest` -> `LinkDest`) + - `linkDest` -> `MuPDF.NET/LinkDest.cs`; `Outline` also exposes `LinkDest` / `dest` / `Destination(Document)` (native x/y rect remains `Outline.Dest`) + - `Matrix` -> `MuPDF.NET/Geometry/Matrix.cs` + - `Outline` -> `MuPDF.NET/Outline.cs` + - `Page` -> `MuPDF.NET/Page.cs` + `MuPDF.NET/Page.PythonCompat.cs` + - `Pixmap` -> `MuPDF.NET/Pixmap.cs` + - `Point` -> `MuPDF.NET/Geometry/Point.cs` + - `Quad` -> `MuPDF.NET/Geometry/Quad.cs` + - `Rect` -> `MuPDF.NET/Geometry/Rect.cs` + - `IRect` -> `MuPDF.NET/Geometry/IRect.cs` + - `Shape` -> `MuPDF.NET/Shape.cs` + - `Story` -> `MuPDF.NET/Story.cs` + - `TextPage` -> `MuPDF.NET/TextPage.cs` + - `TextWriter` -> `MuPDF.NET/TextWriter.cs` + - `Widget` -> `MuPDF.NET/Widget.cs` + - Python exception names (`FileDataError`, `FileNotFoundError`, `EmptyFileError`) -> aliases added in `MuPDF.NET/Exceptions.cs` + +- `src/table.py` + - `Table`, `TableSettings`, `TableFinder`, related row/header/group classes -> `MuPDF.NET/Table.cs` + +- `src/utils.py`, `src/fitz_utils.py` + - Utility/helper behavior -> `MuPDF.NET/Utils.cs`, `MuPDF.NET/Helpers.cs` + +- `src/_wxcolors.py` + - Color map helpers -> `MuPDF.NET/WxColors.cs` + +## `extra.i` Alignment Summary + +- Directly represented helper names: + - `JM_add_oc_object` exists in `Helpers.cs` + - `JM_insert_contents` exists in `Helpers.cs` + - `JM_set_resource_property` exists in `Helpers.cs` + - `JM_xobject_from_page` exists in `Helpers.cs` + - `JM_refresh_links`, `JM_get_annot_xref_list` (annot xref / type / `/NM` list) exist in `Helpers.cs` for PDF link lifecycle parity with `Page.get_links` / `Page.delete_link` flows +- Behavior represented via C# APIs: + - annotation id/xref listing (`AnnotNames`, `AnnotXrefs`) + - stream extraction helpers (`fz_buffer_extract` paths) +- Known outstanding strict-parity internals: + - exact `_show_pdf_page` Form XObject chain parameter/typing parity still differs in public surface shape + +## Current Compatibility Additions + +- `MuPDF.NET/Page.PythonCompat.cs` + - Python snake_case wrappers for major `Page` APIs, including `add_*`, `get_*`, `insert_*`, `show_pdf_page`, `set_*`, and drawing/content helpers. +- `MuPDF.NET/Document.PythonCompat.cs` + - Python snake_case wrappers for major `Document` APIs, including `load_page`, `save/write`, xref/object functions, metadata, journaling, and cleanup helpers. +- `MuPDF.NET/Exceptions.cs` + - Python exception aliases added. + +## Validation Notes + +- `dotnet build MuPDF.NET/MuPDF.NET.csproj` succeeds for **`net8.0`**, **`net472`**, **`net48`**, and **`netstandard2.0`** (many pre-existing XML-doc warnings from generated SWIG sources). +- Added compatibility wrappers integrate in the current code structure and provide side-by-side traceability anchors for Python method names. diff --git a/MuPDF.NET/PythonParity.Phase1.md b/MuPDF.NET/PythonParity.Phase1.md new file mode 100644 index 00000000..41065632 --- /dev/null +++ b/MuPDF.NET/PythonParity.Phase1.md @@ -0,0 +1,56 @@ +# Python -> C# Parity Matrix (Phase 1) + +This document tracks strict traceability for `src/__init__.py` class conversion into `MuPDF.NET`. + +## Scope + +- Python `Document` (`src/__init__.py`) -> C# `Document` (`MuPDF.NET/Document.cs`, `MuPDF.NET/Document.PythonCompat.cs`) +- Python `Page` (`src/__init__.py`) -> C# `Page` (`MuPDF.NET/Page.cs`, `MuPDF.NET/Page.PythonCompat.cs`) +- SWIG helper surface review for `src/extra.i` -> `MuPDF.NET/Helpers.cs` and C# wrappers + +## Method Inventory Snapshot + +- `Document` + - Python methods discovered: `180` + - C# public methods pre-compat: `93` + - Name-normalized matches pre-compat: `83` + - Action: added Python-name wrappers in `Document.PythonCompat.cs` for major public API paths (loading, save/write, xref, metadata, embfile, journalling, insert_pdf, scrub/rewrite, page helpers). +- `Page` + - Python methods discovered: `148` + - C# public methods pre-compat: `87` + - Name-normalized matches pre-compat: `88` + - Action: added Python-name wrappers in `Page.PythonCompat.cs` for major public API paths (annots/links/widgets, add_* annot methods, text extraction, insert_* methods, box setters, content/graphics helpers, show_pdf_page, drawing/table helpers). + +## Comment/Traceability Mapping + +- Added dedicated compatibility partial classes with explicit top-level comments: + - `Page.PythonCompat.cs`: "preserve naming traceability to src/__init__.py:class Page" + - `Document.PythonCompat.cs`: "preserve naming traceability to src/__init__.py:class Document" +- Existing method-order in `Page.cs` / `Document.cs` remains unchanged; wrappers provide line-by-line comparison anchors for Python names while retaining existing .NET API. + +## `extra.i` Dependency Notes + +Observed helper families in `src/extra.i` that are relevant to strict parity: + +- Implemented or partially represented in C#: + - `JM_add_oc_object` -> `Helpers.JM_add_oc_object` + - Annotation id/xref list behavior -> represented by `Page.AnnotNames()` and `Page.AnnotXrefs()` + - Buffer extraction semantics -> represented with `fz_buffer_extract` extension usage + - Link table refresh / listing parity -> `Helpers.JM_refresh_links`, `Helpers.JM_get_annot_xref_list`, `Helpers.PdfAnnotNmForXref`; `Page.DeleteLink(Link)`, `Page.DeleteLink(Dictionary<...>)` (dict path mirrors `delete_link` + `finished()`), `InsertLink(..., mark: bool = true)` exposes Python’s unused `mark` flag; `InsertLink` / `SetLinks` refresh paths unchanged. `Page._addAnnot_FromString` delegates each string to `Helpers.AppendPdfAnnotFromObjectString` then runs `JM_refresh_links` + `SyncLinkWrapperCache` once per batch (C# keeps the MuPDF link list in sync; PyMuPDF’s `extra.i` path does not call `JM_refresh_links`). Dicts with `kind` use `Helpers.TryBuildInsertLinkAnnotObjectString` (port of `utils.getLinkText` + `/NM` allocation) and `AppendPdfAnnotFromObjectString`; legacy dicts without `kind` still use the small URI/Dest builder. `Page.FirstLink` delegates to `LoadLinks()` (Python `first_link` -> `load_links()`). `Page.LoadLinks`, `InsertLink` (return value), and `Link.Next` use `Link.SetLinkAnnotIdentity` so xref/`/NM` follow the `JM_get_annot_xref_list` link-annot order (Python `load_links` first row and `Link.next` when prior `xref > 0`); otherwise `Link.Xref` / `Link.Id` fall back to rect match + `PdfAnnotNmForXref`. `Link.ToDictionary` / `GetLinks` call `Helpers.EnrichLinkDictFromPdfAnnot` when `xref > 0` to fill `kind`, `uri`, `page`, `to`, `zoom`, `file`, `name` from `/A` and `/Dest` like `utils.getLinkDict`; `GetLinks` still merges `xref` / `id` when annot xref counts match link count (same gate as Python `get_links`). `Link.IsExternal` uses `mupdf.fz_is_external_link` on the URI (Python `Link.is_external`), with a narrow `http`/`mailto`/`ftp` fallback if the native call throws. `Link.Dest` / `link.dest` builds `LinkDest` using `Document.ResolveLink` when not external and not `#...` (Python `Link.dest`); `Outline.LinkDest` / `outline.dest` and `Outline.Destination(Document)` mirror `Outline.dest` / `Outline.destination`; `Outline.IsExternal` uses `fz_is_external_link` like Python. `LinkDest` ports `linkDest` (`#page=` / `#nameddest=` / `file:` / URI vs launch). `Link._erase` and per-page weak caches (`_linkRefsByXref`, `_linkRefsByNm` via `TryGetCachedLinkByAnnotNm`) support wrapper teardown; Python `finished()` indexes `_annot_refs` with `linkdict["id"]` (PDF `/NM`) while `load_links` / `Link.next` register under `id(link)` — C# keys wrappers by xref and by non-empty `/NM` so dict-based deletion can detach live `Link` instances when those caches are warm. +- Still missing direct helper equivalents (or only behaviorally approximated): + - `_show_pdf_page` Form XObject pipeline is now helper-backed, but still differs in API typing shape from Python internals + - Several low-level TOC/outline and merge internals that exist in `extra.i` but not as direct C# helper names + +## Phase 1 Remaining Gaps + +- `show_pdf_page` internals now call explicit helper ports (`JM_xobject_from_page`, `JM_insert_contents`) but can still be tightened for exact Python private-API signatures. +- Python-private helper names coverage improved (`_set_resource_property`, `_show_pdf_page`, `_pdf_page`, `_count_q_balance`, `_get_resource_properties`, `_get_optional_content`, `_set_pagebox`, `_other_box`, `_reset_annot_refs`, `_erase`, `_get_textpage`, `_set_opacity`, `_apply_redactions`, `_load_annot`, `_insertFont`, `_makePixmap`, `_insert_image`, `_addWidget`, `_addAnnot_FromString`, `_add_*` annot helper family added). `Page._annot_refs` is now represented by a per-page weak wrapper map in `Page.cs` (register on `Annot` construction, clear on `_reset_annot_refs`); other private names may still need explicit ports. +- `Document` private helper-name coverage has been extended in `Document.PythonCompat.cs` (`_delToC`, `_delete_page`, `_deleteObject`, `_embeddedFileGet`, `_embeddedFileIndex`, `_embfile_*`, `_get_page_labels`, `_set_page_labels`, `_getMetadata`, `_getOLRootNumber`, `_getPDFfileid`, `_getPageInfo`, `_loadOutline`, `_newPage`, `_remove_toc_item`, `_update_toc_item`, `_addFormFont`, `_insert_font`, `_remove_links_to`, `_forget_page`, `_reset_page_refs`). Page-ref tracking is implemented in `Document.cs` to mirror Python `Document._page_refs` + `_reset_page_refs` invalidation semantics (weak-value-dictionary analogue + `Page` teardown). +- Added parser-backed helper `Helpers.JM_pdf_obj_from_str`, and wired it into `Document.XrefSetKey`, `Document.UpdateObject`, and TOC action update paths for closer Python object-string parity. +- Python comment bodies are not yet copied verbatim for every mapped method. + +## Validation Status + +- `dotnet build MuPDF.NET/MuPDF.NET.csproj` succeeds for **`net8.0`**, **`net472`**, **`net48`**, and **`netstandard2.0`** (large pre-existing XML-doc warning volume from generated SWIG sources). +- Older TFMs use small shims in-repo: **`MemberNotNullPolyfill.cs`** (`MemberNotNullAttribute`), **`Helpers.PythonTupleLikeCount` / `PythonTupleLikeItem`** (reflection over `ITuple` for `_addAnnot_FromString`), and BCL-safe **`IndexOf` / `Replace`** patterns where newer overloads are unavailable. +- Phase-1 touched sources (`Document.cs`, `Page.cs`, `*.PythonCompat.cs`, `Helpers.cs`, polyfills) compile cleanly aside from those repo-wide warnings. diff --git a/MuPDF.NET/Shape.cs b/MuPDF.NET/Shape.cs index ec299886..c544b1bd 100644 --- a/MuPDF.NET/Shape.cs +++ b/MuPDF.NET/Shape.cs @@ -1,4 +1,3 @@ -using mupdf; using System; using System.Collections.Generic; using System.Linq; @@ -6,1386 +5,450 @@ namespace MuPDF.NET { - public class Shape + /// + /// Create a new shape for drawing on a page. + /// + public class Shape : IDisposable { - static Shape() - { - Utils.InitApp(); - } + private bool _disposed; + private Page _page; + private Document _doc; + private float _width; + private float _height; + private StringBuilder _contents; + private StringBuilder _totalContents; + private Rect _rect; + private Point _lastPoint; + private Point _firstPoint; + private int _drawCount; + private int _pathCount; /// - /// the owning page + /// Rectangle surrounding all drawings. /// - public Page Page { get; set; } - + public Rect Rect => _rect; /// - /// the page's document + /// Last point of the current drawing. /// - public Document Doc { get; set; } - + public Point LastPoint => _lastPoint; /// - /// The page's height + /// Number of unfinished drawings. /// - public float Height { get; set; } + public int DrawCount => _drawCount; /// - /// The page's width + /// Initializes a new instance of the class for the specified page. /// - public float Width { get; set; } - - public float X { get; set; } - - public float Y { get; set; } - - public Matrix Pctm { get; set; } - - public Matrix IPctm { get; set; } - - /// - /// Accumulated command buffer for draw methods since last finish - /// - public string DrawCont { get; set; } + public Shape(Page page) + { + _page = page; + _doc = page.Parent; + _width = page.Width; + _height = page.Height; + _contents = new StringBuilder(); + _totalContents = new StringBuilder(); + _rect = new Rect(); + _lastPoint = null; + _firstPoint = null; + } - /// - /// Accumulated text buffer. All text insertions go here - /// - public string TextCont { get; set; } + // ─── Drawing Primitives ───────────────────────────────────────── /// - /// Total accumulated command buffer for draws and text insertions + /// Draw a line from p1 to p2. /// - public string TotalCont { get; set; } + public Point DrawLine(Point p1, Point p2) + { + if (_lastPoint == null || _lastPoint.X != p1.X || _lastPoint.Y != p1.Y) + _contents.AppendLine($"{p1.X:F4} {_height - p1.Y:F4} m"); + _contents.AppendLine($"{p2.X:F4} {_height - p2.Y:F4} l"); + UpdateRect(p1); + UpdateRect(p2); + _lastPoint = p2; + if (_firstPoint == null) _firstPoint = p1; + _drawCount++; + return p2; + } /// - /// For reference only: the current point of the drawing path + /// Draw a rectangle. /// - public Point LastPoint { get; set; } + public Point DrawRect(Rect rect) + { + _contents.AppendLine($"{rect.X0:F4} {_height - rect.Y1:F4} {rect.Width:F4} {rect.Height:F4} re"); + UpdateRect(rect.TopLeft); + UpdateRect(rect.BottomRight); + _lastPoint = rect.TopLeft; + if (_firstPoint == null) _firstPoint = rect.TopLeft; + _drawCount++; + return rect.TopLeft; + } /// - /// Rectangle surrounding drawings + /// Draw a circle given center and radius. /// - public Rect Rect { get; set; } - - public Shape(Page page) + public Point DrawCircle(Point center, float radius) { - this.Page = page; - this.Doc = page.Parent; - - if (!Doc.IsPDF) - throw new Exception("is no PDF"); - Height = page.MediaBoxSize.Y; - Width = page.MediaBoxSize.X; - X = page.CropBoxPosition.X; - Y = page.CropBoxPosition.Y; - Pctm = page.TransformationMatrix; - IPctm = ~page.TransformationMatrix; - - DrawCont = ""; - TextCont = ""; - TotalCont = ""; - LastPoint = null; - Rect = null; + return DrawOval(new Rect( + center.X - radius, center.Y - radius, + center.X + radius, center.Y + radius)); } /// - /// Insert text lines + /// Draw an oval (ellipse) within a given rectangle. /// - /// the bottom-left position of the first character of text in pixels - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// set transparency for stroke colors (the border line of a character). Only 0 <= value <= 1 will be considered. Default is 1 - /// set transparency for fill colors. Default is 1 - /// the xref number of an OCG or OCMD to make this text conditionally displayable - /// number of lines inserted. - public int InsertText( - Point point, - List buffer, - string fontName, - string fontFile, - float fontSize = 11, - float lineHeight = 0, - bool setSimple = false, - int encoding = 0, - float[] color = null, - float[] fill = null, - int renderMode = 0, - float borderWidth = 0.05f, - int rotate = 0, - Morph morph = null, - float strokeOpacity = 1, - float fillOpacity = 1, - int oc = 0 - ) + public Point DrawOval(Rect rect) { - if (string.IsNullOrEmpty(fontName) && string.IsNullOrEmpty(fontFile)) - throw new Exception("should include fontName and fontFile."); - - return _InsertText(point, buffer, fontSize, lineHeight, fontName, fontFile, setSimple, encoding, - color, fill, renderMode, borderWidth, rotate, morph, strokeOpacity, fillOpacity, oc); + float mx = (float)((rect.X0 + rect.X1) / 2); + float my = (float)((rect.Y0 + rect.Y1) / 2); + float w2 = (float)(rect.Width / 2); + float h2 = (float)(rect.Height / 2); + float kappa = 0.5522848f; + float ox = w2 * kappa; + float oy = h2 * kappa; + + float top = (float)rect.Y0, bottom = (float)rect.Y1; + float left = (float)rect.X0, right = (float)rect.X1; + + _contents.AppendLine($"{right:F4} {_height - my:F4} m"); + _contents.AppendLine($"{right:F4} {_height - (my - oy):F4} {mx + ox:F4} {_height - top:F4} {mx:F4} {_height - top:F4} c"); + _contents.AppendLine($"{mx - ox:F4} {_height - top:F4} {left:F4} {_height - (my - oy):F4} {left:F4} {_height - my:F4} c"); + _contents.AppendLine($"{left:F4} {_height - (my + oy):F4} {mx - ox:F4} {_height - bottom:F4} {mx:F4} {_height - bottom:F4} c"); + _contents.AppendLine($"{mx + ox:F4} {_height - bottom:F4} {right:F4} {_height - (my + oy):F4} {right:F4} {_height - my:F4} c"); + + UpdateRect(rect.TopLeft); + UpdateRect(rect.BottomRight); + _lastPoint = new Point(right, my); + if (_firstPoint == null) _firstPoint = new Point(right, my); + _drawCount++; + return new Point(right, my); } /// - /// Insert text lines + /// Draw a curve from current point through p2 to p3. /// - /// the bottom-left position of the first character of text in pixels - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// set transparency for stroke colors (the border line of a character). Only 0 <= value <= 1 will be considered. Default is 1 - /// set transparency for fill colors. Default is 1 - /// the xref number of an OCG or OCMD to make this text conditionally displayable - /// number of lines inserted. - public int InsertText( - Point point, - string buffer, - string fontName, - string fontFile, - float fontSize = 11, - float lineHeight = 0, - bool setSimple = false, - int encoding = 0, - float[] color = null, - float[] fill = null, - int renderMode = 0, - float borderWidth = 0.05f, - int rotate = 0, - Morph morph = null, - float strokeOpacity = 1, - float fillOpacity = 1, - int oc = 0 - ) + public Point DrawCurve(Point p1, Point p2, Point p3) { - string[] list = buffer.Split('\n'); - if (string.IsNullOrEmpty(fontName) && string.IsNullOrEmpty(fontFile)) - throw new Exception("should include fontName or fontFile."); - - return _InsertText(point, new List(list), fontSize, lineHeight, fontName, fontFile, setSimple, encoding, - color, fill, renderMode, borderWidth, rotate, morph, strokeOpacity, fillOpacity, oc); - } - - internal int _InsertText( - Point point, - List buffer, - float fontSize = 11, - float lineHeight = 0, - string fontName = "helv", - string fontFile = null, - bool setSimple = false, - int encoding = 0, - float[] color = null, - float[] fill = null, - int renderMode = 0, - float borderWidth = 0.05f, - int rotate = 0, - Morph morph = null, - float strokeOpacity = 1, - float fillOpacity = 1, - int oc = 0 - ) - { - List text = buffer; - if (text.Count == 0) - return 0; - - int maxCode = 0; - try - { - foreach (char c in String.Join(" ", text)) - { - if (maxCode < Convert.ToInt32(c)) - maxCode = Convert.ToInt32(c); - } - } - catch (Exception) - { - return 0; - } - - string fName = fontName; - if (fName.StartsWith("/")) - fName = fName.Substring(1); - - int xref = Page.InsertFont( - fontName: fName, - fontFile: fontFile, - encoding: encoding, - setSimple: setSimple - ); - FontInfo fontInfo = Utils.CheckFontInfo(Doc, xref); - - int ordering = fontInfo.Ordering; - bool simple = fontInfo.Simple; - string bfName = fontInfo.Name; - float ascender = fontInfo.Ascender; - float descender = fontInfo.Descender; - float lheight = 0; - - if (lineHeight != 0) - lheight = fontSize * lineHeight; - else if (ascender - descender <= 1) - lheight = fontSize * 1.2f; - else - lheight = fontSize * (ascender - descender); - - List<(int, double)> glyphs = new List<(int, double)>(); - if (maxCode > 255) - glyphs = Doc.GetCharWidths(xref: xref, limit: maxCode + 1); - else - glyphs = fontInfo.Glyphs; - - List tab = new List(); - List<(int, double)> g = null; - foreach (string t in text) - { - if (simple && !(bfName == "Symbol" || bfName == "ZapfDingbats")) - g = null; - else - g = new List<(int, double)>(glyphs); - tab.Add(Utils.GetTJstr(t, g, simple, ordering)); - } - - text = tab; - - string colorStr = Utils.GetColorCode(color, "c"); - string fillStr = Utils.GetColorCode(fill, "f"); - if (fill == null && renderMode == 0) - { - fill = color; - fillStr = Utils.GetColorCode(color, "f"); - } - - bool morphing = (morph != null); - int rot = rotate; - if (rot % 90 != 0) - throw new Exception("bad rotate value"); - - while (rot < 0) - rot += 360; - rot = rot % 360; - - string cmp90 = "0 1 -1 0 0 0 cm\n";// rotates 90 deg counter-clockwise - string cmm90 = "0 -1 1 0 0 0 cm\n";// rotates 90 deg clockwise - string cm180 = "-1 0 0 -1 0 0 cm\n";// rotates by 180 deg. - float height = Height; - float width = Width; - string cm; - - if (morphing) - { - Matrix m1 = new Matrix(1, 0, 0, 1, morph.P.X + X, height - morph.P.Y - Y); - Matrix mat = ~m1 * morph.M * m1; - cm = $"{Utils.FloatToString(mat.A)} {Utils.FloatToString(mat.B)} {Utils.FloatToString(mat.C)} {Utils.FloatToString(mat.D)} {Utils.FloatToString(mat.E)} {Utils.FloatToString(mat.F)} cm\n"; - } - else cm = ""; - - float top = height - point.Y - Y; - float left = point.X + X; - float space = top; - if (rot == 90) - { - left = height - point.Y - Y; - top = -point.X - X; - cm += cmp90; - space = width - Math.Abs(top); - } - else if (rot == 270) - { - left = -height + point.Y + Y; - top = point.X + X; - cm += cmm90; - space = Math.Abs(top); - } - else if (rot == 180) - { - left = -point.X - X; - top = -height + point.Y + Y; - cm += cm180; - space = Math.Abs(point.Y + Y); - } - - string optCont = Page.GetOptionalContent(oc); - string bdc; - string emc; - if (optCont != null) - { - bdc = $"/OC /{optCont} BDC\n"; - emc = "EMC\n"; - } - else bdc = emc = ""; - - string alpha = Page.SetOpacity(CA: strokeOpacity, ca: fillOpacity); - if (alpha == null) - alpha = ""; - else - alpha = $"/{alpha} gs\n"; - - string nres = $"\nq\n{bdc}{alpha}BT\n{cm}1 0 0 1 {Utils.FloatToString(left)} {Utils.FloatToString(top)} Tm\n/{fName} {Utils.FloatToString(fontSize)} Tf "; - if (renderMode > 0) - nres += $"{renderMode} Tr "; - if (borderWidth != 1) - nres += $"{Utils.FloatToString(borderWidth)} w "; - if (color != null) - nres += colorStr; - if (fill != null) - nres += fillStr; - - /// start text insertion - nres += text[0]; - int nLines = 1; - string template = "TJ\n0 -{0} TD\n"; - if (text.Count > 1) - nres += string.Format(System.Globalization.CultureInfo.InvariantCulture, template, Utils.FloatToString(lheight)); - else nres += "TJ"; - - for (int i = 1; i < text.Count; i++) - { - if (space < lheight) - break; // no space left on page - if (i > 1) - nres += "\nT* "; - nres += text[i] + "TJ"; - space -= lheight; - nLines += 1; - } - - nres += $"\nET\n{emc}Q\n"; - - // end of text insertion - TextCont += nres; - return nLines; + if (_lastPoint == null || _lastPoint.X != p1.X || _lastPoint.Y != p1.Y) + _contents.AppendLine($"{p1.X:F4} {_height - p1.Y:F4} m"); + float kx1 = (float)(p1.X + 2.0 / 3.0 * (p2.X - p1.X)); + float ky1 = (float)(p1.Y + 2.0 / 3.0 * (p2.Y - p1.Y)); + float kx2 = (float)(p3.X + 2.0 / 3.0 * (p2.X - p3.X)); + float ky2 = (float)(p3.Y + 2.0 / 3.0 * (p2.Y - p3.Y)); + _contents.AppendLine($"{kx1:F4} {_height - ky1:F4} {kx2:F4} {_height - ky2:F4} {p3.X:F4} {_height - p3.Y:F4} c"); + UpdateRect(p1); UpdateRect(p2); UpdateRect(p3); + _lastPoint = p3; + if (_firstPoint == null) _firstPoint = p1; + _drawCount++; + return p3; } /// - /// Update the page's contents with the accumulated drawings, followed by any text insertions + /// Draw a cubic Bezier curve. /// - /// determine whether to put content in foreground (default) or background - public void Commit(bool overlay = true) + public Point DrawBezier(Point p1, Point p2, Point p3, Point p4) { - TotalCont += this.TextCont; - byte[] bTotal = Encoding.UTF8.GetBytes(TotalCont); - if (TotalCont != "") - { - int xref = Utils.InsertContents(Page, Encoding.UTF8.GetBytes(" "), overlay ? 1 : 0); - //Doc.UpdateStream(xref, bTotal); - PdfDocument doc = Document.AsPdfDocument(Doc); - doc.pdf_update_stream(doc.pdf_load_object(xref), Utils.BufferFromBytes(bTotal), 1); - doc.Dispose(); - } - - LastPoint = null; - Rect = null; - DrawCont = ""; - TextCont = ""; - TotalCont = ""; - - return; + if (_lastPoint == null || _lastPoint.X != p1.X || _lastPoint.Y != p1.Y) + _contents.AppendLine($"{p1.X:F4} {_height - p1.Y:F4} m"); + _contents.AppendLine($"{p2.X:F4} {_height - p2.Y:F4} {p3.X:F4} {_height - p3.Y:F4} {p4.X:F4} {_height - p4.Y:F4} c"); + UpdateRect(p1); UpdateRect(p2); UpdateRect(p3); UpdateRect(p4); + _lastPoint = p4; + if (_firstPoint == null) _firstPoint = p1; + _drawCount++; + return p4; } /// - /// Draw a standard cubic Bezier curve. + /// Draw a polyline connecting the given points. /// - /// - /// - /// - /// - /// - public Point DrawBezier(Point p1, Point p2, Point p3, Point p4) + public Point DrawPolyline(Point[] points) { - if (LastPoint == null || !LastPoint.EqualTo(p1)) - { - Point t = p1 * IPctm; - DrawCont += $"{Utils.FloatToString(t.X)} {Utils.FloatToString(t.Y)} m\n"; - } - - Point t2 = p2 * IPctm; - Point t3 = p3 * IPctm; - Point t4 = p4 * IPctm; - DrawCont += $"{Utils.FloatToString(t2.X)} {Utils.FloatToString(t2.Y)} {Utils.FloatToString(t3.X)} {Utils.FloatToString(t3.Y)} {Utils.FloatToString(t4.X)} {Utils.FloatToString(t4.Y)} c\n"; - - UpdateRect(p1); - UpdateRect(p2); - UpdateRect(p3); - UpdateRect(p4); - LastPoint = p4; - return LastPoint; + if (points == null || points.Length < 2) throw new ArgumentException("need at least 2 points"); + _contents.AppendLine($"{points[0].X:F4} {_height - points[0].Y:F4} m"); + for (int i = 1; i < points.Length; i++) + _contents.AppendLine($"{points[i].X:F4} {_height - points[i].Y:F4} l"); + foreach (var p in points) UpdateRect(p); + _lastPoint = points[points.Length - 1]; + if (_firstPoint == null) _firstPoint = points[0]; + _drawCount++; + return _lastPoint; } /// - /// Draw a circle given its center and radius. + /// Draw a quadrilateral. /// - /// the center of the circle - /// the radius of the circle. Must be positive - /// - /// - public Point DrawCircle(Point center, float radius) + public Point DrawQuad(Quad quad) { - if (!(radius > Utils.FLT_EPSILON)) - throw new Exception("radius must be postive"); - Point p1 = center - new Point(radius, 0); - return DrawSector(center, p1, 360, fullSector: false); + return DrawPolyline(new[] { quad.UL, quad.LL, quad.LR, quad.UR, quad.UL }); } /// - /// Draw a circle sector. + /// Draw a sector. /// - /// the center of the circle - /// one of the two end points of the pie’s arc segment. The other one is calculated from the angle - /// the angle of the sector in degrees - /// whether to draw connecting lines from the ends of the arc to the circle center - /// the other end point of the arc - /// - public Point DrawSector(Point center, Point point, float beta, bool fullSector = true) + public Point DrawSector(Point center, Point point, float angle, bool fullSector = true) { - string l3 = "{0} {1} m\n"; - string l4 = "{0} {1} {2} {3} {4} {5} c\n"; - string l5 = "{0} {1} l\n"; - double betar = ((-beta) * Math.PI / 180); - double w360 = (Math.Sign(betar) * 360.0f * (Math.PI / 180) * -1); - double w90 = (Math.Sign(betar) * 90.0f * (Math.PI / 180)); - double w45 = w90 / 2; - while (Math.Abs(betar) > 2 * Math.PI) - betar += w360; - - if (LastPoint == null || !LastPoint.EqualTo(point)) - { - Point t = point * IPctm; - DrawCont += string.Format(System.Globalization.CultureInfo.InvariantCulture, l3, Utils.FloatToString(t.X), Utils.FloatToString(t.Y)); - LastPoint = point; - } - - Point q = new Point(0, 0); - Point c = center; - Point p = point; - Point s = p - c; - float rad = s.Abs(); - - if (!(rad > Utils.FLT_EPSILON)) - throw new Exception("radius must be positive"); - - double alfa = HorizontalAngle(center, point); - while (Math.Abs(betar) > Math.Abs(w90)) - { - float q1 = c.X + (float)(Math.Cos(alfa + w90) * rad); - float q2 = c.Y + (float)(Math.Sin(alfa + w90) * rad); - q = new Point(q1, q2); - float r1 = c.X + (float)(Math.Cos(alfa + w45) * rad / Math.Cos(w45)); - float r2 = c.Y + (float)(Math.Sin(alfa + w45) * rad / Math.Cos(w45)); - Point r = new Point(r1, r2); + double rad = angle * Math.PI / 180.0; + double dx = point.X - center.X, dy = point.Y - center.Y; + double cos = Math.Cos(rad), sin = Math.Sin(rad); + float ex = (float)(center.X + dx * cos - dy * sin); + float ey = (float)(center.Y + dx * sin + dy * cos); - float kappah = (float)(((double)1 - Math.Cos(w45)) * 4 / 3 / (r - q).Abs()); - float kappa = kappah * (p - q).Abs(); - Point cp1 = p + (r - p) * kappa; - Point cp2 = q + (r - q) * kappa; - - Point t1 = cp1 * IPctm; - Point t2 = cp2 * IPctm; - Point t3 = q * IPctm; - DrawCont += string.Format(System.Globalization.CultureInfo.InvariantCulture, l4, Utils.FloatToString(t1.X), Utils.FloatToString(t1.Y), Utils.FloatToString(t2.X), Utils.FloatToString(t2.Y), Utils.FloatToString(t3.X), Utils.FloatToString(t3.Y)); - betar -= w90; - alfa += w90; - p = q; - } - - if (Math.Abs(betar) > 1e-3) - { - double beta2 = betar / 2.0f; - double q1 = c.X + (Math.Cos(alfa + betar) * rad); - double q2 = c.Y + (Math.Sin(alfa + betar) * rad); - q = new Point((float)q1, (float)q2); - double r1 = c.X + Math.Cos(alfa + beta2) * rad / Math.Cos(beta2); - double r2 = c.Y + Math.Sin(alfa + beta2) * rad / Math.Cos(beta2); - Point r = new Point((float)r1, (float)r2); - - double kappah = (1 - Math.Cos(beta2)) * 4 / 3 / (r - q).Abs(); - double kappa = kappah * (p - q).Abs() / (1 - Math.Cos(betar)); - Point cp1 = p + (r - p) * kappa; - Point cp2 = q + (r - q) * kappa; - - Point t1 = cp1 * IPctm; - Point t2 = cp2 * IPctm; - Point t3 = q * IPctm; - DrawCont += string.Format(System.Globalization.CultureInfo.InvariantCulture, l4, Utils.FloatToString(t1.X), Utils.FloatToString(t1.Y), Utils.FloatToString(t2.X), Utils.FloatToString(t2.Y), Utils.FloatToString(t3.X), Utils.FloatToString(t3.Y)); - } - if (fullSector) { - Point t = point * IPctm; - DrawCont += string.Format(System.Globalization.CultureInfo.InvariantCulture, l3, Utils.FloatToString(t.X), Utils.FloatToString(t.Y)); - t = center * IPctm; - DrawCont += string.Format(System.Globalization.CultureInfo.InvariantCulture, l5, Utils.FloatToString(t.X), Utils.FloatToString(t.Y)); - t = q * IPctm; - DrawCont += string.Format(System.Globalization.CultureInfo.InvariantCulture, l5, Utils.FloatToString(t.X), Utils.FloatToString(t.Y)); + _contents.AppendLine($"{center.X:F4} {_height - center.Y:F4} m"); + _contents.AppendLine($"{point.X:F4} {_height - point.Y:F4} l"); } - - LastPoint = q; - return LastPoint; - } - - public void UpdateRect(Point x) - { - if (Rect == null) - Rect = new Rect(x, x); else { - Rect.X0 = Math.Min(Rect.X0, x.X); - Rect.Y0 = Math.Min(Rect.Y0, x.Y); - Rect.X1 = Math.Max(Rect.X1, x.X); - Rect.Y1 = Math.Max(Rect.Y1, x.Y); + _contents.AppendLine($"{point.X:F4} {_height - point.Y:F4} m"); } - } - public void UpdateRect(Rect x) - { - if (Rect == null) - Rect = x; - else - { - Rect.X0 = Math.Min(Rect.X0, x.X0); - Rect.Y0 = Math.Min(Rect.Y0, x.Y0); - Rect.X1 = Math.Max(Rect.X1, x.X1); - Rect.Y1 = Math.Max(Rect.Y1, x.Y1); - } - } + DrawArc(center, point, new Point(ex, ey), angle); - public static double HorizontalAngle(Point c, Point p) - { - Point s = (p - c).Unit; - double alfa = Math.Asin(Math.Abs(s.Y)); - if (s.X < 0) - { - if (s.Y <= 0) - alfa = -((float)Math.PI - alfa); - else - alfa = (float)Math.PI - alfa; - } - else - { - if (s.Y >= 0) { } - else alfa = -alfa; - } - return alfa; - } + if (fullSector) + _contents.AppendLine($"{center.X:F4} {_height - center.Y:F4} l"); - public Point DrawCurve(Point p1, Point p2, Point p3) - { - float kappa = 0.55228474983f; - Point k1 = p1 + (p2 - p1) * kappa; - Point k2 = p3 + (p2 - p3) * kappa; - return DrawBezier(p1, k1, k2, p3); + UpdateRect(center); UpdateRect(point); UpdateRect(new Point(ex, ey)); + _lastPoint = new Point(ex, ey); + if (_firstPoint == null) _firstPoint = center; + _drawCount++; + return new Point(ex, ey); } /// - /// Draw a line between two points. + /// Draw a squiggly (wavy) line from p1 to p2. /// - /// starting point - /// end point - /// - public Point DrawLine(Point p1, Point p2) + public Point DrawSquiggle(Point p1, Point p2, float breadth = 2) { - Point t; - if (LastPoint == null || !LastPoint.EqualTo(p1)) - { - t = p1 * IPctm; - DrawCont += $"{Utils.FloatToString(t.X)} {Utils.FloatToString(t.Y)} m\n"; - LastPoint = p1; - UpdateRect(p1); - } - - t = p2 * IPctm; - DrawCont += $"{Utils.FloatToString(t.X)} {Utils.FloatToString(t.Y)} l\n"; - UpdateRect(p2); - LastPoint = p2; - return LastPoint; + float dx = (float)(p2.X - p1.X), dy = (float)(p2.Y - p1.Y); + float length = (float)Math.Sqrt(dx * dx + dy * dy); + if (length < 4 * breadth) { DrawLine(p1, p2); return p2; } + + int nsegs = (int)(length / (4 * breadth)); + float segLen = length / nsegs; + float nx = -dy / length * breadth, ny = dx / length * breadth; + float sx = dx / length * segLen, sy = dy / length * segLen; + + _contents.AppendLine($"{p1.X:F4} {_height - p1.Y:F4} m"); + float cx = (float)p1.X, cy = (float)p1.Y; + for (int i = 0; i < nsegs; i++) + { + float m1x = cx + sx * 0.25f + nx; + float m1y = cy + sy * 0.25f + ny; + float m2x = cx + sx * 0.75f - nx; + float m2y = cy + sy * 0.75f - ny; + float ex2 = cx + sx; + float ey2 = cy + sy; + _contents.AppendLine($"{m1x:F4} {_height - m1y:F4} {m2x:F4} {_height - m2y:F4} {ex2:F4} {_height - ey2:F4} c"); + cx = ex2; cy = ey2; + } + UpdateRect(p1); UpdateRect(p2); + _lastPoint = p2; + if (_firstPoint == null) _firstPoint = p1; + _drawCount++; + return p2; } /// - /// Draw an ellipse inside a tetrapod. + /// Draw a zigzag line from p1 to p2. /// - /// - /// - public Point DrawOval(Rect tetra) + public Point DrawZigzag(Point p1, Point p2, float breadth = 2) { - _DrawOval(tetra.Quad); - return LastPoint; + float dx = (float)(p2.X - p1.X), dy = (float)(p2.Y - p1.Y); + float length = (float)Math.Sqrt(dx * dx + dy * dy); + if (length < 4 * breadth) { DrawLine(p1, p2); return p2; } + + int nsegs = (int)(length / (4 * breadth)); + float segLen = length / nsegs; + float nx = -dy / length * breadth, ny = dx / length * breadth; + float sx = dx / length * segLen, sy = dy / length * segLen; + + var points = new List { p1 }; + float cx = (float)p1.X, cy = (float)p1.Y; + for (int i = 0; i < nsegs; i++) + { + points.Add(new Point(cx + sx * 0.25f + nx, cy + sy * 0.25f + ny)); + points.Add(new Point(cx + sx * 0.5f, cy + sy * 0.5f)); + points.Add(new Point(cx + sx * 0.75f - nx, cy + sy * 0.75f - ny)); + cx += sx; cy += sy; + points.Add(new Point(cx, cy)); + } + return DrawPolyline(points.ToArray()); } - public Point DrawOval(Quad tetra) - { - _DrawOval(tetra); - return LastPoint; - } + // ─── Finish / Commit ──────────────────────────────────────────── - private void _DrawOval(Quad tetra) + /// + /// Finish the current set of drawings with stroke/fill/opacity properties. + /// + public void Finish(float[] color = null, float[] fill = null, float width = 1, + string lineCap = null, string lineJoin = null, float[] dashes = null, + bool closePath = true, bool even_odd = false, float opacity = 1, + string blendMode = null, int oc = 0, string morph = null) { - Point mt = tetra.UpperLeft + (tetra.UpperRight - tetra.UpperLeft) * 0.5f; - Point mr = tetra.UpperRight + (tetra.LowerRight - tetra.UpperRight) * 0.5f; - Point mb = tetra.LowerLeft + (tetra.LowerRight - tetra.LowerLeft) * 0.5f; - Point ml = tetra.UpperLeft + (tetra.LowerLeft - tetra.UpperLeft) * 0.5f; + if (_drawCount == 0) return; + var sb = new StringBuilder(); + sb.AppendLine("q"); - if (LastPoint == null || !LastPoint.EqualTo(ml)) + if (opacity < 1) { - Point t = ml * IPctm; - DrawCont += $"{Utils.FloatToString(t.X)} {Utils.FloatToString(t.Y)} m\n"; - LastPoint = ml; + sb.AppendLine($"/GS0 gs"); } - DrawCurve(ml, tetra.LowerLeft, mb); - DrawCurve(mb, tetra.LowerRight, mr); - DrawCurve(mr, tetra.UpperRight, mt); - DrawCurve(mt, tetra.UpperLeft, ml); - UpdateRect(tetra.Rect); - LastPoint = ml; - } + sb.AppendLine($"{width:F4} w"); - /// - /// Draw several connected lines between points contained in the sequence points - /// - /// a sequence of Point objects - /// - public Point DrawPolyline(Point[] points) - { - for (int i = 0; i < points.Length; i++) + if (color != null && color.Length > 0) { - if (i == 0) - { - if (LastPoint == null || !LastPoint.EqualTo(points[i])) - { - Point t = points[i] * IPctm; - DrawCont += $"{Utils.FloatToString(t.X)} {Utils.FloatToString(t.Y)} m\n"; - LastPoint = points[i]; - } - } - else - { - Point t = points[i] * IPctm; - DrawCont += $"{Utils.FloatToString(t.X)} {Utils.FloatToString(t.Y)} l\n"; - } - UpdateRect(points[i]); + if (color.Length == 1) sb.AppendLine($"{color[0]:F4} G"); + else if (color.Length == 3) sb.AppendLine($"{color[0]:F4} {color[1]:F4} {color[2]:F4} RG"); + else if (color.Length == 4) sb.AppendLine($"{color[0]:F4} {color[1]:F4} {color[2]:F4} {color[3]:F4} K"); } - LastPoint = points[points.Length - 1]; - return LastPoint; - } - /// - /// Draw a Quad. - /// - /// - /// - public Point DrawQuad(Quad quad) - { - Point[] points = new Point[5] { quad.UpperLeft, quad.LowerLeft, quad.LowerRight, quad.UpperRight, quad.UpperLeft }; - return DrawPolyline(points); - } + if (fill != null && fill.Length > 0) + { + if (fill.Length == 1) sb.AppendLine($"{fill[0]:F4} g"); + else if (fill.Length == 3) sb.AppendLine($"{fill[0]:F4} {fill[1]:F4} {fill[2]:F4} rg"); + else if (fill.Length == 4) sb.AppendLine($"{fill[0]:F4} {fill[1]:F4} {fill[2]:F4} {fill[3]:F4} k"); + } - /// - /// Draw a rectangle. Matches MuPdf Shape.draw_rect. - /// - /// Rectangle in page coordinates. - /// - /// If null, zero, or negative: draws a plain PDF rectangle (re), same as MuPdf radius=None. - /// If positive: corner radius as a fraction of the shorter side; must satisfy 0 < radius ≤ 0.5. - /// Use for separate horizontal/vertical fractions (MuPdf 2-tuple form). - /// - /// Last point (top-left for plain rectangle; end of path for rounded). - public Point DrawRect(Rect rect, float? radius = null) - { - if (!radius.HasValue || radius.Value <= 0f) - return DrawRectPlain(rect); + sb.Append(_contents); + if (closePath) sb.AppendLine("h"); - float rv = radius.Value; - if (rv > 0.5f) - throw new ArgumentOutOfRangeException(nameof(radius), radius, "Radius must be greater than 0 and at most 0.5 (MuPdf draw_rect)."); + if (fill != null && color != null) sb.AppendLine(even_odd ? "B*" : "B"); + else if (fill != null) sb.AppendLine(even_odd ? "f*" : "f"); + else sb.AppendLine("S"); - float d = Math.Min(rect.Width, rect.Height) * rv; - Point px = new Point(d, 0); - Point py = new Point(0, d); - return DrawRectRoundedCorners(rect, px, py); + sb.AppendLine("Q"); + _totalContents.Append(sb); + _contents.Clear(); + _drawCount = 0; + _lastPoint = null; + _firstPoint = null; + _pathCount++; } /// - /// Draw a rectangle with rounded corners: horizontal radius × width and vertical × height (MuPdf radius=(rx, ry)). - /// Each fraction must satisfy 0 < value ≤ 0.5. A pair (0.5, 0.5) yields an ellipse-like shape. + /// Write the accumulated content to the page. /// - public Point DrawRect(Rect rect, float radiusX, float radiusY) - { - if (radiusX <= 0f || radiusY <= 0f || radiusX > 0.5f || radiusY > 0.5f) - throw new ArgumentOutOfRangeException( - $"{nameof(radiusX)}/{nameof(radiusY)}", - "Each radius fraction must be greater than 0 and at most 0.5 (MuPdf draw_rect)."); - - Point px = new Point(radiusX * rect.Width, 0); - Point py = new Point(0, radiusY * rect.Height); - return DrawRectRoundedCorners(rect, px, py); - } - - private Point DrawRectPlain(Rect rect) + public void Commit(bool overlay = true) { - Point t = rect.BottomLeft * IPctm; - DrawCont += $"{Utils.FloatToString(t.X)} {Utils.FloatToString(t.Y)} {Utils.FloatToString(rect.Width)} {Utils.FloatToString(rect.Height)} re\n"; - UpdateRect(rect); - LastPoint = rect.TopLeft; - return LastPoint; + if (_totalContents.Length == 0) return; + var pdf = _doc.NativePdfDocument; + var pdfPage = _page.NativePdfPage; + var content = Encoding.UTF8.GetBytes(_totalContents.ToString()); + var buf = Helpers.BufferFromBytes(content); + var stream = mupdf.mupdf.pdf_add_stream(pdf, buf, new mupdf.PdfObj(), 0); + + var contents = mupdf.mupdf.pdf_dict_get(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Contents")); + if (contents.m_internal == null || mupdf.mupdf.pdf_is_array(contents) == 0) + { + var arr = mupdf.mupdf.pdf_new_array(pdf, 2); + if (contents.m_internal != null) mupdf.mupdf.pdf_array_push(arr, contents); + mupdf.mupdf.pdf_dict_put(pdfPage.obj(), mupdf.mupdf.pdf_new_name("Contents"), arr); + contents = arr; + } + if (overlay) mupdf.mupdf.pdf_array_push(contents, stream); + else mupdf.mupdf.pdf_array_insert(contents, stream, 0); + + _totalContents.Clear(); + _pathCount = 0; } - private Point DrawRectRoundedCorners(Rect r, Point px, Point py) - { - Point lp; - - lp = DrawLine(r.TopLeft + py, r.BottomLeft - py); - lp = DrawCurve(lp, r.BottomLeft, r.BottomLeft + px); - - lp = DrawLine(lp, r.BottomRight - px); - lp = DrawCurve(lp, r.BottomRight, r.BottomRight - py); - - lp = DrawLine(lp, r.TopRight + py); - lp = DrawCurve(lp, r.TopRight, r.TopRight - px); - - lp = DrawLine(lp, r.TopLeft + px); - LastPoint = DrawCurve(lp, r.TopLeft, r.TopLeft + py); - - UpdateRect(r); - return LastPoint; - } + // ─── Text methods ─────────────────────────────────────────────── /// - /// Draw a squiggly line from p1 to p2. + /// Insert text starting at a given point. /// - /// - /// - /// - /// - public Point DrawSquiggle(Point p1, Point p2, float breadth = 2) + public int InsertText(Point point, string text, float fontsize = 11, string fontname = "helv", + float[] color = null, int renderMode = 0, float borderWidth = 0.05f, int rotate = 0) { - Point s = p2 - p1; - float rad = s.Abs(); - int cnt = 4 * Convert.ToInt32(Math.Round(rad / (4 * breadth), 0)); - if (cnt < 4) - throw new Exception("points too close"); - float mb = rad / cnt; - - Matrix matrix = Utils.HorMatrix(p1, p2); - Matrix iMat = ~matrix; - float k = 2.4142135623765633f; - - List points = new List(); - - int i; - for (i = 1; i < cnt; i++) - { - Point p; - if (i % 4 == 1) - p = (new Point(i, -k)) * mb; - else if (i % 4 == 3) - p = (new Point(i, k)) * mb; - else - p = (new Point(i, 0)) * mb; - points.Add(p * iMat); - } - points = (new List() { p1}).Concat(points).Concat(new List() { p2 }).ToList(); - cnt = points.Count; - i = 0; - - while ((i + 2) < cnt) - { - DrawCurve(points[i], points[i + 1], points[i + 2]); - i += 2; - } - - return p2; + var tw = new TextWriter(_page.Rect, color: color); + tw.Append(point, text, fontsize: fontsize, fontname: fontname); + tw.WriteText(_page); + return text.Split('\n').Length; } /// - /// Draw a zigzag line from Point objects p1 to p2 + /// Insert text into a rectangle. /// - /// starting point - /// end point - /// the amplitude of the movement - /// - /// - public Point DrawZigzag(Point p1, Point p2, float breadth = 2.0f) + public (int rc, List rest) InsertTextbox(Rect rect, string text, float fontsize = 11, + string fontname = "helv", float[] color = null, int align = 0, int renderMode = 0, + float borderWidth = 0.05f, int rotate = 0, float expandTabs = 1) { - Point s = p2 - p1; - float rad = s.Abs(); - int cnt = 4 * (int)(Math.Round(rad / (4 * breadth))); - if (cnt < 4) - throw new Exception("points too close"); - float mb = rad / cnt; - Matrix matrix = Utils.HorMatrix(p1, p2); - Matrix iMat = ~matrix; - - List points = new List(); - for (int i = 1; i < cnt; i++) - { - Point p; - if (i % 4 == 1) - p = (new Point(i, -1) * mb); - else if (i % 4 == 3) - p = (new Point(i, 1) * mb); - else continue; - points.Add(p * iMat); - } - points.Insert(0, p1); - points.Add(p2); - DrawPolyline(points.ToArray()); - - return p2; + var tw = new TextWriter(_page.Rect, color: color); + var rest = tw.FillTextbox(rect, text, fontsize: fontsize, fontname: fontname, align: align); + return (rest.Count == 0 ? 0 : -1, rest); } - /// - /// Finish the current drawing segment. - /// Apply colors, opacity, dashes, line style and width, or morphing.Also whether to close the path by connecting last to first point. - /// - /// - /// - /// - /// - /// - /// - /// request the “even-odd rule” for filling operations. Default is False, so that the “nonzero winding number rule” is used. - /// morph the text or the compound drawing around some arbitrary Point fixpoint by applying Matrix matrix to it. This implies that fixpoint is a fixed point of this operation: it will not change its position. Default is no morphing (None). The matrix can contain any values in its first 4 components, matrix.e == matrix.f == 0 must be true, however. This means that any combination of scaling, shearing, rotating, flipping, etc. is possible, but translations are not. - /// - /// (new in v1.18.1) set transparency for fill colors. Default is 1 (intransparent). - /// (new in v1.18.1) set transparency for stroke colors. Value < 0 or > 1 will be ignored. Default is 1 (intransparent). - /// (new in v1.18.4) the xref number of an OCG or OCMD to make this drawing conditionally displayable. - public void Finish( - float width = 1.0f, - float[] color = null, - float[] fill = null, - int lineCap = 0, - int lineJoin = 0, - string dashes = null, - bool evenOdd = false, - Morph morph = null, - bool closePath = true, - float fillOpacity = 1.0f, - float strokeOpacity = 1.0f, - int oc = 0 - ) - { - if (string.IsNullOrEmpty(DrawCont)) - return; - if (color == null) - color = new float[]{ 0f, }; - if (width == 0) - color = null; - else if (color == null) - width = 0; - - string colorStr = Utils.GetColorCode(color, "c"); - string fillStr = Utils.GetColorCode(fill, "f"); - - string optCont = Page.GetOptionalContent(oc); - string emc = ""; - if (!string.IsNullOrEmpty(optCont)) - { - DrawCont = $"/OC /{optCont} BDC\n" + DrawCont; - emc = "EMC\n"; - } - - if (width != 1 && width != 0) - DrawCont += $"{Utils.FloatToString(width)} w\n"; - - if (lineCap != 0) - DrawCont = $"{lineCap} J\n" + DrawCont; - if (lineJoin != 0) - DrawCont = $"{lineJoin} j\n" + DrawCont; - - if ((string.IsNullOrEmpty(dashes) == false) && (dashes != "[] 0")) - DrawCont = $"{dashes} d\n" + DrawCont; + // ─── Internal helpers ─────────────────────────────────────────── - string alpha = Page.SetOpacity(CA: strokeOpacity, ca: fillOpacity); - if (!string.IsNullOrEmpty(alpha)) - DrawCont = $"/{alpha} gs\n" + DrawCont; - - if (closePath) - { - DrawCont += "h\n"; - LastPoint = null; - } - - if (color != null) - DrawCont += colorStr; - - if (fill != null) - { - DrawCont += fillStr; - if (color != null) - { - if (!evenOdd) - { - DrawCont += "B\n"; - } - else - { - DrawCont += "B*\n"; - } - } - else - { - if (!evenOdd) - DrawCont += "f\n"; - else - DrawCont += "f*\n"; - } - } - else - { - DrawCont += "S\n"; + private void DrawArc(Point center, Point from, Point to, float angle) + { + // Approximate arc with bezier segments + int nSegs = (int)Math.Ceiling(Math.Abs(angle) / 90.0); + if (nSegs == 0) nSegs = 1; + float segAngle = angle / nSegs; + float rad = (float)(segAngle * Math.PI / 360.0); // half angle + float kappa = (float)(4.0 / 3.0 * Math.Tan(rad)); + + double dx = from.X - center.X, dy = from.Y - center.Y; + double radius = Math.Sqrt(dx * dx + dy * dy); + double currentAngle = Math.Atan2(dy, dx); + + for (int i = 0; i < nSegs; i++) + { + double a1 = currentAngle + i * segAngle * Math.PI / 180.0; + double a2 = a1 + segAngle * Math.PI / 180.0; + float px1 = (float)(center.X + radius * Math.Cos(a1)); + float py1 = (float)(center.Y + radius * Math.Sin(a1)); + float px2 = (float)(center.X + radius * Math.Cos(a2)); + float py2 = (float)(center.Y + radius * Math.Sin(a2)); + float cpx1 = (float)(px1 - kappa * radius * Math.Sin(a1)); + float cpy1 = (float)(py1 + kappa * radius * Math.Cos(a1)); + float cpx2 = (float)(px2 + kappa * radius * Math.Sin(a2)); + float cpy2 = (float)(py2 - kappa * radius * Math.Cos(a2)); + _contents.AppendLine($"{cpx1:F4} {_height - cpy1:F4} {cpx2:F4} {_height - cpy2:F4} {px2:F4} {_height - py2:F4} c"); } + } - DrawCont += emc; - if (morph != null) - { - Matrix m1 = new Matrix(1, 0, 0, 1, morph.P.X + X, Height - morph.P.Y - Y); - Matrix mat = ~m1 * morph.M * m1; - DrawCont = $"{Utils.FloatToString(mat.A)} {Utils.FloatToString(mat.B)} {Utils.FloatToString(mat.C)} {Utils.FloatToString(mat.D)} {Utils.FloatToString(mat.E)} {Utils.FloatToString(mat.F)} cm\n" + DrawCont; - } + private void UpdateRect(Point p) => _rect.IncludePoint(p); - TotalCont += "\nq\n" + DrawCont + "Q\n"; - DrawCont = ""; - LastPoint = null; - return; - } + // ─── IDisposable ──────────────────────────────────────────────── /// - /// Insert text into a given rectangle. + /// Releases all resources used by the . /// - /// the textbox to fill - /// text to be inserted - /// font size - /// overwrite the font property - /// a Base-14 font, font name or '/name' - /// name of a font file - /// - /// - /// RGB stroke color triple - /// RGB fill color triple - /// handles tabulators with string function - /// left, center, right, justified - /// text rendering control - /// thickness of glyph borders - /// 0, 90, 180, or 270 degrees - /// morph box with a matrix and a fixpoint - /// - /// - /// - /// unused or deficit rectangle area (float) - public float InsertTextbox( - Rect rect, - List buffer, - string fontName = "helv", - string fontFile = null, - float fontSize = 11, - float lineHeight = 0, - bool setSimple = false, - int encoding = 0, - float[] color = null, - float[] fill = null, - int expandTabs = 1, - int align = 1, - int renderMode = 0, - float borderWidth = 1.0f, - int rotate = 0, - Morph morph = null, - float strokeOpacity = 1, - float fillOpacity = 1, - int oc = 0 - ) + public void Dispose() { - if (string.IsNullOrEmpty(fontName) && string.IsNullOrEmpty(fontFile)) - throw new Exception("should include fontName and fontFile."); - return _InsertTextbox(rect, buffer, fontName, fontFile, fontSize, lineHeight, setSimple, encoding, - color, fill, expandTabs, align, renderMode, borderWidth, rotate, morph, strokeOpacity, fillOpacity, oc); + if (!_disposed) { _disposed = true; } + GC.SuppressFinalize(this); } + ~Shape() { Dispose(); } + + // Python/legacy compatibility aliases (mirrors _alias(Shape, ...)). + public Point draw_bezier(Point p1, Point p2, Point p3, Point p4) => DrawBezier(p1, p2, p3, p4); + public Point draw_circle(Point center, float radius) => DrawCircle(center, radius); + public Point draw_curve(Point p1, Point p2, Point p3) => DrawCurve(p1, p2, p3); + public Point draw_line(Point p1, Point p2) => DrawLine(p1, p2); + public Point draw_oval(Rect rect) => DrawOval(rect); + public Point draw_polyline(Point[] points) => DrawPolyline(points); + public Point draw_quad(Quad quad) => DrawQuad(quad); + public Point draw_rect(Rect rect) => DrawRect(rect); + public Point draw_sector(Point center, Point point, float angle, bool fullSector = true) => DrawSector(center, point, angle, fullSector); + public Point draw_squiggle(Point p1, Point p2, float breadth = 2) => DrawSquiggle(p1, p2, breadth); + public Point draw_zigzag(Point p1, Point p2, float breadth = 2) => DrawZigzag(p1, p2, breadth); + public int insert_text(Point point, string text, float fontsize = 11, string fontname = "helv", + float[] color = null, int renderMode = 0, float borderWidth = 0.05f, int rotate = 0) + => InsertText(point, text, fontsize, fontname, color, renderMode, borderWidth, rotate); + public (int rc, List rest) insert_textbox(Rect rect, string text, float fontsize = 11, + string fontname = "helv", float[] color = null, int align = 0, int renderMode = 0, + float borderWidth = 0.05f, int rotate = 0, float expandTabs = 1) + => InsertTextbox(rect, text, fontsize, fontname, color, align, renderMode, borderWidth, rotate, expandTabs); + /// - /// Insert text into a given rectangle. + /// Returns a string that represents the current shape. /// - /// the textbox to fill - /// text to be inserted - /// font size - /// overwrite the font property - /// a Base-14 font, font name or '/name' - /// name of a font file - /// - /// - /// RGB stroke color triple - /// RGB fill color triple - /// handles tabulators with string function - /// left, center, right, justified - /// text rendering control - /// thickness of glyph borders - /// 0, 90, 180, or 270 degrees - /// morph box with a matrix and a fixpoint - /// - /// - /// - /// unused or deficit rectangle area (float) - public float InsertTextbox( - Rect rect, - string buffer, - string fontFile, - string fontName, - float fontSize = 11, - float lineHeight = 0, - bool setSimple = false, - int encoding = 0, - float[] color = null, - float[] fill = null, - int expandTabs = 1, - int align = 1, - int renderMode = 0, - float borderWidth = 0.05f, - int rotate = 0, - Morph morph = null, - float strokeOpacity = 1, - float fillOpacity = 1, - int oc = 0 - ) - { - if (string.IsNullOrEmpty(fontName) && string.IsNullOrEmpty(fontFile)) - throw new Exception("should include fontName and fontFile."); - string[] list = buffer.Split('\n'); - return _InsertTextbox(rect, new List(list), fontName, fontFile, fontSize, lineHeight, setSimple, encoding, - color, fill, expandTabs, align, renderMode, borderWidth, rotate, morph, strokeOpacity, fillOpacity, oc); - } - - internal float _InsertTextbox( - Rect rect, - List buffer, - string fontName = "helv", - string fontFile = null, - float fontSize = 11, - float lineHeight = 0, - bool setSimple = true, - int encoding = 0, - float[] color = null, - float[] fill = null, - int expandTabs = 1, - int align = 1, - int renderMode = 0, - float borderWidth = 0.05f, - int rotate = 0, - Morph morph = null, - float strokeOpacity = 1, - float fillOpacity = 1, - int oc = 0 - ) - { - if (rect.IsEmpty || rect.IsInfinite) - throw new Exception("text box must be finite and not empty"); - - string colorStr = Utils.GetColorCode(color, "c"); - string fillStr = Utils.GetColorCode(fill, "f"); - - - if (fill == null && renderMode == 0) - { - fill = color; - fillStr = Utils.GetColorCode(color, "f"); - } - - string optCont = Page.GetOptionalContent(oc); - string bdc = "", emc = ""; - if (!string.IsNullOrEmpty(optCont)) - { - bdc = $"/OC /{optCont} BDC\n"; - emc = "EMC\n"; - } - - string alpha = Page.SetOpacity(CA: strokeOpacity, ca: fillOpacity); - if (string.IsNullOrEmpty(alpha)) - alpha = ""; - else - alpha = $"/{alpha} gs\n"; - - if (rotate % 90 != 0) - throw new Exception("rotate must be multiple of 90"); - - int rot = rotate; - while (rot < 0) - rot += 360; - rot = rot % 360; - - if (buffer.Count == 0 ) - return (rot == 0 || rot == 180) ? rect.Height : rect.Width; - - string cmp90 = "0 1 -1 0 0 0 cm\n"; - string cmm90 = "0 -1 1 0 0 0 cm\n"; - string cm180 = "-1 0 0 -1 0 0 cm\n"; - float height = this.Height; - - string fname = fontName; - if (fname.StartsWith("/")) - fname = fname.Substring(1); - - int xref = Page.InsertFont(fontName: fname, fontFile: fontFile, encoding: encoding, setSimple: setSimple); - FontInfo fontInfo = Utils.CheckFontInfo(Doc, xref); - - if (fontInfo == null) - throw new Exception("not found font info"); - - int ordering = fontInfo.Ordering; - bool simple = fontInfo.Simple; - List<(int, double)> glyphs = fontInfo.Glyphs; - string bfName = fontInfo.Name; - float asc = fontInfo.Ascender; - float des = fontInfo.Descender; - float lheightFactor; - if (lineHeight != 0) - lheightFactor = lineHeight; - else if (asc - des <= 1) - lheightFactor = 1.2f; - else - lheightFactor = asc - des; - float lheight = fontSize * lheightFactor; - string t0 = string.Join("\n", buffer); - int maxCode = 0; - foreach (char c in t0) - maxCode = maxCode < Convert.ToInt32(c) ? Convert.ToInt32(c) : maxCode; - - string t1 = ""; - if (simple && maxCode > 255) - foreach (char c in t0) - t1 += Convert.ToInt32(c) < 256 ? c : '?'; - - string[] t2 = string.IsNullOrEmpty(t1) ? t0.Split('\n') : t1.Split('\n'); - glyphs = Doc.GetCharWidths(xref, maxCode + 1); - - List<(int, double)> tj_glyphs; - if (simple && !(bfName == "Symbol" || bfName == "ZapfDingbats")) - tj_glyphs = null; - else - tj_glyphs = glyphs; - - float PixLen(string x) - { - if (ordering < 0) - { - double sum = 0; - foreach (char c in x) - sum += glyphs[Convert.ToInt32(c)].Item2; - return (float)(sum * fontSize); - } - else - { - return x.Length * fontSize; - } - } - - float blen; - if (ordering < 0) - blen = (float)(glyphs[32].Item2 * fontSize); - else - blen = fontSize; - - string text = ""; - string cm = ""; - if (morph != null) - { - Matrix m1 = new Matrix(1, 0, 0, 1, morph.P.X + X, Height - morph.P.Y - Y); - Matrix mat = ~m1 * morph.M * m1; - cm = $"{Utils.FloatToString(mat.A)} {Utils.FloatToString(mat.B)} {Utils.FloatToString(mat.C)} {Utils.FloatToString(mat.D)} {Utils.FloatToString(mat.E)} {Utils.FloatToString(mat.F)} cm\n"; - } - - int progr = 1; - Point cPnt = new Point(0, fontSize * asc); - Point point = new Point(); - float pos = 0, maxWidth = 0, maxHeight = 0; - if (rot == 0) - { - point = rect.TopLeft + cPnt; - maxWidth = rect.Width; - maxHeight = rect.Height; - } - else if (rot == 90) - { - cPnt = new Point(fontSize * asc, 0); - point = rect.BottomLeft + cPnt; - maxWidth = rect.Height; - maxHeight = rect.Width; - cm += cmp90; - } - else if (rot == 180) - { - cPnt = -(new Point(0, fontSize * asc)); - point = rect.BottomRight + cPnt; - maxWidth = rect.Width; - maxHeight = rect.Height; - cm += cm180; - } - else - { - cPnt = -(new Point(fontSize * asc, 0)); - point = rect.TopRight + cPnt; - pos = point.X + X; - maxWidth = rect.Height; - progr = -1; - maxHeight = rect.Width; - cm += cmm90; - } - - List justTab = new List(); - for (int i = 0; i < t2.Length; i++) - { - string[] line_t = t2[i].Replace("\t", new string(' ', expandTabs)).Split(' '); - string lbuff = ""; - float rest = maxWidth; - - foreach (string word in line_t) - { - float pl_w = PixLen(word); - if (rest >= pl_w) - { - lbuff += word + " "; - rest -= (pl_w + blen); - continue; - } - if (!string.IsNullOrEmpty(lbuff)) - { - lbuff = lbuff.TrimEnd() + "\n"; - text += lbuff; - justTab.Add(true); - } - lbuff = ""; - rest = maxWidth; - if (pl_w <= maxWidth) - { - lbuff = word + " "; - rest = maxWidth - pl_w - blen; - continue; - } - - if (justTab.Count > 0) - justTab[justTab.Count - 1] = false; - foreach (char c in word) - { - if (PixLen(lbuff) <= (maxWidth - PixLen(Convert.ToString(c)))) - lbuff += c; - else - { - lbuff += "\n"; - text += lbuff; - justTab.Add(false); - lbuff = Convert.ToString(c); - } - } - - lbuff += " "; - rest = maxWidth - PixLen(lbuff); - } - if (!string.IsNullOrEmpty(lbuff)) - { - text += lbuff.TrimEnd(); - justTab.Add(false); - } - if (i < t2.Count() - 1) - { - text += "\n"; - } - } - if (text.EndsWith("\n")) - text = text.Substring(0, text.Length - 1); - - int lbCount = text.Split('\n').Length; - - float more = (lheight * lbCount) - des * fontSize - maxHeight; - if (more > Utils.FLT_EPSILON) - return (-1) * more; - - more = Math.Abs(more); - if (more < Utils.FLT_EPSILON) - more = 0; - - string nres = $"\nq\n{bdc}{alpha}BT\n" + cm; - string template = "1 0 0 1 {0} {1} Tm /{2} {3} Tf "; - string[] text_t = text.Split('\n'); - - justTab[justTab.Count - 1] = false; - - for (int i = 0; i < text_t.Length; i++) - { - float pl = maxWidth - PixLen(text_t[i]); - Point pnt = point + cPnt * (i * lheightFactor); - float spacing = 0; - if (align == 1) - { - if (rot == 0 && rot == 180) - pnt = pnt + new Point(pl / 2, 0) * progr; - else - pnt = pnt - new Point(0, pl / 2) * progr; - } - else if (align == 2) - { - if (rot == 0 || rot == 180) - pnt = pnt + new Point(pl, 0) * progr; - else - pnt = pnt - new Point(0, pl) * progr; - } - else if (align == 3) - { - int spaces = text_t[i].Count(c => c == ' '); - if (spaces > 0 && justTab[i]) - spacing = pl / spaces; - } - float top = height - pnt.Y - Y; - float left = pnt.X + X; - if (rot == 90) - { - left = height - pnt.Y - Y; - top = -pnt.X - X; - } - else if (rot == 270) - { - left = -height + pnt.Y + Y; - top = pnt.X + X; - } - else if (rot == 180) - { - left = -pnt.X - X; - top = -height + pnt.Y + Y; - } - - nres += string.Format(System.Globalization.CultureInfo.InvariantCulture, template, Utils.FloatToString(left), Utils.FloatToString(top), fname, Utils.FloatToString(fontSize)); - if (renderMode > 0) - nres += $"{renderMode} Tr "; - if (align == 3) - nres += $"{Utils.FloatToString(spacing)} Tw "; - - if (color != null) - nres += colorStr; - if (fill != null) - nres += fillStr; - if (borderWidth != 1) - nres += $"{Utils.FloatToString(borderWidth)} w "; - nres += $"{Utils.GetTJstr(text_t[i], tj_glyphs, simple, ordering)}TJ\n"; - } - nres += $"ET\n{emc}Q\n"; - - TextCont += nres; - UpdateRect(rect); - return more; - } + public override string ToString() => $"Shape(paths={_pathCount}, draws={_drawCount})"; } } diff --git a/MuPDF.NET/Story.cs b/MuPDF.NET/Story.cs index a27476e7..ac89ef1b 100644 --- a/MuPDF.NET/Story.cs +++ b/MuPDF.NET/Story.cs @@ -1,707 +1,731 @@ -using mupdf; using System; using System.Collections.Generic; -using System.IO; using System.Text; namespace MuPDF.NET { + public class StoryElementPositionInfo + { + public int depth { get; set; } + public int heading { get; set; } + public string id { get; set; } + public Rect rect { get; set; } + public string text { get; set; } + public int open_close { get; set; } + public int rect_num { get; set; } + public string href { get; set; } + public int page_num { get; set; } + } + + /// + /// Represents a Story for rich-content layout driven by HTML and CSS. + /// public class Story : IDisposable { - static Story() + private mupdf.FzStory _nativeStory; + private bool _disposed; + + internal mupdf.FzStory NativeStory + { + get + { + if (_disposed) throw new ObjectDisposedException(nameof(Story)); + return _nativeStory; + } + } + + /// + /// Initializes a new instance of the class. + /// + public Story(string html = "", string userCss = "", float em = 12, Archive archive = null) + { + var buf = Helpers.BufferFromBytes(Encoding.UTF8.GetBytes(html ?? "")); + _nativeStory = new mupdf.FzStory(buf, userCss ?? "", em, + archive?.NativeArchive ?? new mupdf.FzArchive()); + } + + internal Story(mupdf.FzStory story) + { + _nativeStory = story; + } + + public delegate (Rect mediabox, Rect rect, Matrix ctm) StoryRectFn(int rectNum, Rect filled); + + /// + /// Compute layout into a given rectangle. + /// + /// Returns (more, filledRect) where more indicates unplaced content remains. + /// + public (bool more, Rect filledRect) Place(Rect where) + { + var fzRect = where.ToFzRect(); + var filled = new mupdf.FzRect(); + int more = NativeStory.fz_place_story(fzRect, filled); + return (more != 0, new Rect(filled.x0, filled.y0, filled.x1, filled.y1)); + } + + /// + /// Wrapper for fz_place_story_flags(). + /// + public (bool more, Rect filledRect) Place(Rect where, int flags) { - Utils.InitApp(); + var fzRect = where.ToFzRect(); + var filled = new mupdf.FzRect(); + int more = NativeStory.fz_place_story_flags(fzRect, filled, flags); + return (more != 0, new Rect(filled.x0, filled.y0, filled.x1, filled.y1)); } - private FzStory _nativeStory; + /// + /// Draw the placed content to a device. + /// + public void Draw(mupdf.FzDevice dev, Matrix ctm = null) + { + NativeStory.fz_draw_story(dev, (ctm ?? Matrix.Identity).ToFzMatrix()); + } - public delegate string ContentFunction(List positions); - public delegate (Rect, Rect, Matrix) RectFunction(int rectN, Rect filled); // Define the delegate signature according to actual use + /// + /// Reset the story to allow re-placement. + /// + public void Reset() + { + NativeStory.fz_reset_story(); + } /// - /// the story's underlying Body + /// Get the story's DOM body text. /// - public Xml Body + public string Body { get { - Xml dom = GetDocument(); - return dom.GetBodyTag(); + try + { + var dom = NativeStory.fz_story_document(); + if (dom.m_internal == null) return ""; + var body = dom.fz_dom_body(); + if (body.m_internal == null) return ""; + return body.fz_xml_text() ?? ""; + } + catch + { + return ""; + } } } - public Story(string html = "", string userCss = null, float em = 12, Archive archive = null) + /// + /// Set an attribute on an element found by id. + /// + public void ElementSetAttribute(string id, string att, string value) { - byte[] bytes = Encoding.UTF8.GetBytes(html); - FzBuffer buf = Utils.fz_new_buffer_from_data(bytes); - - FzArchive arch = archive != null ? archive.ToFzArchive() : new FzArchive(); - _nativeStory = new FzStory(buf, userCss, em, arch); + var dom = NativeStory.fz_story_document(); + var body = dom.fz_dom_body(); + var el = body.fz_dom_find(null, "id", id); + if (el.m_internal != null) + el.fz_dom_add_attribute(att, value); } /// - /// Look for `` items in `self` and adds unique `id` attributes if not already present. + /// Look for h1..h6 items and add unique id attributes if absent. /// public void AddHeaderIds() { - Xml dom = Body; + var dom = NativeStory.fz_story_document(); + var body = dom.fz_dom_body(); int i = 0; - Xml x = dom.Find(null, null, null); - while (x != null) + var x = body.fz_dom_find(null, null, null); + while (x != null && x.m_internal != null) { - string name = x.TagName; - if (name.Length == 2 && name[0] == 'h' && "123456".Contains(name[1].ToString())) + var name = x.fz_xml_tag(); + if (!string.IsNullOrEmpty(name) && name.Length == 2 && name[0] == 'h' && "123456".IndexOf(name[1]) >= 0) { - string attr = x.GetAttributeValue("id"); - if (attr == null) + var attr = x.fz_dom_attribute("id"); + if (string.IsNullOrEmpty(attr)) { - string id_ = $"h_id_{i}"; - x.SetAttribute("id", id_); - i += 1; + string id = $"h_id_{i}"; + x.fz_dom_add_attribute("id", id); + i++; } } - x = x.FindNext(null, null, null); + x = x.fz_dom_find_next(null, null, null); } } - public void Dispose() + /// + /// Trigger a callback function to record where items have been placed. + /// + public void ElementPositions(Action function, Dictionary args = null) + { + if (function == null) + throw new ArgumentException("callback 'function' must be a callable with exactly one argument"); + + var cb = new StoryPositionCollector(function, args); + mupdf.mupdf.ll_fz_story_positions_director(NativeStory.m_internal, cb); + } + + /// + /// Python-compatible story writing loop. + /// + public void Write(DocumentWriter writer, StoryRectFn rectfn, Action positionfn = null, Action pagefn = null) { - if (_nativeStory != null) + mupdf.FzDevice dev = null; + int page_num = 0; + int rect_num = 0; + var filled = new Rect(0, 0, 0, 0); + + while (true) { - _nativeStory.Dispose(); - _nativeStory = null; + var (mediabox, rect, ctm) = rectfn(rect_num, filled); + rect_num += 1; + bool newPage = mediabox != null && !mediabox.IsEmpty; + if (newPage) + page_num += 1; + + var placed = Place(rect); + bool more = placed.more; + filled = placed.filledRect; + + if (positionfn != null) + { + ElementPositions(position => + { + position.page_num = page_num; + positionfn(position); + }); + } + + if (writer != null) + { + if (newPage) + { + if (dev != null) + { + pagefn?.Invoke(page_num, mediabox, dev, 1); + writer.EndPage(); + } + dev = writer.BeginPage(mediabox); + pagefn?.Invoke(page_num, mediabox, dev, 0); + } + if (dev != null) + Draw(dev, ctm); + if (!more) + { + pagefn?.Invoke(page_num, mediabox, dev, 1); + writer.EndPage(); + } + } + else + { + if (dev != null) + Draw(dev, ctm); + } + if (!more) + break; } } /// - /// Get Document object + /// Write stabilized multi-page output. Iterates until the content function + /// returns the same HTML, then produces the final document. /// - /// - public Xml GetDocument() + public static byte[] WriteStabilized(Func contentfn, string userCss = "", float em = 12, + string mediabox = "letter", Archive archive = null, Action addHeaderCb = null, + Action addFooterCb = null) { - FzXml dom = _nativeStory.fz_story_document(); - return new Xml(dom); + var paperRect = Helpers.PaperRect(mediabox); + byte[] result = null; + string previousHtml = null; + + for (int iteration = 0; ; iteration++) + { + string html = contentfn(iteration); + bool stable = (html == previousHtml); + previousHtml = html; + + var story = new Story(html, userCss, em, archive); + var writer = stable ? new DocumentWriter(paperRect) : null; + + bool more = true; + int pageNum = 0; + while (more) + { + var contentRect = new Rect(paperRect.X0 + 36, paperRect.Y0 + 36, + paperRect.X1 - 36, paperRect.Y1 - 36); + (more, _) = story.Place(contentRect); + + if (writer != null) + { + var dev = writer.BeginPage(paperRect); + story.Draw(dev); + addHeaderCb?.Invoke(story); + addFooterCb?.Invoke(story); + writer.EndPage(); + } + pageNum++; + } + + if (writer != null) + result = writer.Close(); + + story.Dispose(); + + if (stable) + break; + } + + return result ?? Array.Empty(); + } + + /// + /// Write multi-page output with links preserved. + /// + public static byte[] WriteWithLinks(Func contentfn, string userCss = "", float em = 12, + string mediabox = "letter") + { + return WriteStabilized(contentfn, userCss, em, mediabox); } /// - /// Write the content part prepared by Story.place() to the page. + /// Adds links to a PDF document from collected story element positions. /// - /// the Device created by dev = writer.begin_page(mediabox). The device knows how to call all MuPDF functions needed to write the content. - /// a matrix for transforming content when writing to the page. An example may be writing rotated text. The default means no transformation (i.e. the Identity matrix). - public void Draw(DeviceWrapper device, Matrix matrix = null) + public static Document AddPdfLinks(Document document, List positions) { - FzMatrix ctm2 = (matrix != null) ? matrix.ToFzMatrix() : new FzMatrix(); - if (ctm2 == null) - ctm2 = new FzMatrix(); - FzDevice dev = device == null ? new FzDevice() : device.ToFzDevice(); - _nativeStory.fz_draw_story(dev, ctm2); - if (device == null) + if (document == null) throw new ArgumentNullException(nameof(document)); + if (positions == null) return document; + + var id_to_position = new Dictionary(); + foreach (var position in positions) + { + if ((position.open_close & 1) != 0 && !string.IsNullOrEmpty(position.id) && !id_to_position.ContainsKey(position.id)) + id_to_position[position.id] = position; + } + + foreach (var position_from in positions) { - dev.fz_close_device(); - dev.Dispose(); + if ((position_from.open_close & 1) == 0 || string.IsNullOrEmpty(position_from.href)) + continue; + + var link = new Dictionary + { + ["from"] = new Rect(position_from.rect), + }; + + if (position_from.href.StartsWith("#", StringComparison.Ordinal)) + { + string target_id = position_from.href.Substring(1); + if (!id_to_position.TryGetValue(target_id, out var position_to)) + throw new InvalidOperationException($"No destination with id={target_id}, required by position_from"); + link["kind"] = Constants.LINK_GOTO; + link["to"] = new Point(position_to.rect.X0, position_to.rect.Y0); + link["page"] = position_to.page_num - 1; + } + else + { + if (position_from.href.StartsWith("name:", StringComparison.Ordinal)) + { + link["kind"] = Constants.LINK_NAMED; + link["name"] = position_from.href.Substring(5); + } + else + { + link["kind"] = Constants.LINK_URI; + link["uri"] = position_from.href; + } + } + + document[position_from.page_num - 1].InsertLinkVoid(link); } + + return document; } /// - /// Rewind the story’s document to the beginning for starting over its output. + /// Static stabilized writer loop matching Python's write_stabilized. /// - public void Reset() + public static void WriteStabilized( + DocumentWriter writer, + Func, string> contentfn, + StoryRectFn rectfn, + string user_css = null, + float em = 12, + Action positionfn = null, + Action pagefn = null, + Archive archive = null, + bool add_header_ids = true) { - _nativeStory.fz_reset_story(); + var positions = new List(); + string content = null; + while (true) + { + string content_prev = content; + content = contentfn(positions); + bool stable = content == content_prev; + + var story = new Story(content, user_css, em, archive); + if (add_header_ids) + story.AddHeaderIds(); + + positions = new List(); + story.Write( + stable ? writer : null, + rectfn, + position => + { + positions.Add(position); + if (stable && positionfn != null) + positionfn(position); + }, + pagefn + ); + story.Dispose(); + + if (stable) + break; + } } - public Rect ScaleFn(Rect rect, float scale) + /// + /// Static stabilized writer + link insertion matching Python write_stabilized_with_links. + /// + public static Document WriteStabilizedWithLinks( + Func, string> contentfn, + StoryRectFn rectfn, + string user_css = null, + float em = 12, + Action positionfn = null, + Action pagefn = null, + Archive archive = null, + bool add_header_ids = true) { - return new Rect(rect.X0, rect.Y0, rect.X0 + scale * rect.Width, scale * rect.Height); + var writer = new DocumentWriter(new Rect(0, 0, 595, 842)); + var positions = new List(); + WriteStabilized( + writer, + contentfn, + rectfn, + user_css, + em, + position => + { + positions.Add(position); + positionfn?.Invoke(position); + }, + pagefn, + archive, + add_header_ids + ); + byte[] pdf = writer.Close(); + writer.Dispose(); + + var document = new Document(pdf, "pdf"); + return AddPdfLinks(document, positions); } /// - /// Finds smallest value scale from scale_min to scale_max where scale * rect is large enough to contain the story + /// Instance writer + link insertion matching Python write_with_links. /// - /// - /// Minimum scale to consider; must be >= 0 - /// Maximum scale to consider, must be >= scale_min or None for infinite - /// Maximum error in returned scale - /// If true we output diagnostics - /// - public FitResult FitScale(Rect rect, float scaleMin = 0, float scaleMax = 0, float delta = 0.001f, bool verbose = false) + public Document WriteWithLinks(StoryRectFn rectfn, Action positionfn = null, Action pagefn = null) { - return Fit(ScaleFn, rect, scaleMin, scaleMax, delta, verbose); + var writer = new DocumentWriter(new Rect(0, 0, 595, 842)); + var positions = new List(); + Write( + writer, + rectfn, + position => + { + positions.Add(position); + positionfn?.Invoke(position); + }, + pagefn + ); + byte[] pdf = writer.Close(); + writer.Dispose(); + + var document = new Document(pdf, "pdf"); + return AddPdfLinks(document, positions); } /// - /// Finds optimal rect that contains the story + /// The result from a Story fit* method. /// - /// A callable taking a floating point `parameter` and returning a `fitz.Rect()`. - ///
If the rect is empty, we assume the story will not fit and do not call `self.place()`. - /// - /// - /// Minimum parameter to consider - /// Maximum parameter to consider - /// Maximum error in returned parameter. - /// If true we output diagnostics. - /// Returns a `Story.FitResult` instance. - public FitResult Fit(Func fn, Rect rect, float pmin, float pmax, float delta = 0.001f, bool verbose = false) + public class FitResult { - void Log(string text) + public bool? big_enough { get; set; } + public Rect filled { get; set; } + public bool? more { get; set; } + public int? numcalls { get; set; } + public double? parameter { get; set; } + public Rect rect { get; set; } + + public override string ToString() { - Console.WriteLine($"Fit(): {text}"); + return $" big_enough={big_enough} filled={filled} more={more} numcalls={numcalls} parameter={parameter} rect={rect}"; } + } + + /// + /// Finds optimal rect that contains this story. + /// + public FitResult Fit(Func fn, double? pmin = null, double? pmax = null, double delta = 0.001, bool verbose = false, int flags = 0) + { + if (fn == null) throw new ArgumentNullException(nameof(fn)); - State state = new State(pmin, pmax, verbose); + double? statePmin = pmin; + double? statePmax = pmax; + FitResult statePminResult = null; + FitResult statePmaxResult = null; + int numcalls = 0; + double? lastParameter = null; - if (verbose) - Log($"starting. {state.Pmin} {state.Pmax}."); + void Log(string text) + { + if (verbose) System.Diagnostics.Debug.WriteLine($"fit(): {text}"); + } Reset(); - FitResult Ret(Rect _rect, State _state) + FitResult Ret() { - bool bigEnough = false; - FitResult result = null; - if (_state.Pmax != 0) + if (statePmax.HasValue) { - if (_state.LastP != _state.Pmax) + if (!lastParameter.HasValue || lastParameter.Value != statePmax.Value) { - if (verbose) - Log($"Calling update() with pmax, because was overwritten by later calls."); - bigEnough = Update(_rect, _state.Pmax); + Log("Calling update() with pmax, because was overwritten by later calls."); + bool bigEnough = Update(statePmax.Value); + if (!bigEnough) + throw new InvalidOperationException("fit(): internal state error: expected pmax to be big enough."); } - result = _state.PmaxResult; - } - else - { - result = _state.PminResult != null ? _state.PminResult : new FitResult(numcalls: _state.Numcalls); + return statePmaxResult; } - - if (verbose) - Log($"finished. {_state.Pmin0} {_state.Pmax0} {_state.Pmax}: returning {result}"); - return result; + return statePminResult ?? new FitResult { numcalls = numcalls }; } - bool Update(Rect _rect, float parameter) + bool Update(double parameter) { - Rect r = fn(_rect, parameter); + var rect = fn(parameter); + if (rect == null) throw new InvalidOperationException("fit(): fn(parameter) returned null."); + bool bigEnough; FitResult result; - if (r.IsEmpty) + + if (rect.IsEmpty) { bigEnough = false; - result = new FitResult(parameter: parameter, numcalls: state.Numcalls); - if (verbose) - Log("update(): not calling self.place() because _rect is empty."); + result = new FitResult + { + parameter = parameter, + numcalls = numcalls + }; + Log("update(): not calling self.place() because rect is empty."); } else { - (bool more, Rect filled) = Place(r); - state.Numcalls += 1; - bigEnough = !more; - - result = new FitResult( - filled: filled, - more: more, - numcalls: state.Numcalls, - parameter: parameter, - rect: r, - bigEnough: bigEnough - ); - if (verbose) - Log($"Update(): called self.place(): {state.Numcalls}: {more} {parameter} {r}."); + var placed = Place(rect, flags); + numcalls += 1; + bigEnough = !placed.more; + result = new FitResult + { + filled = placed.filledRect, + more = placed.more, + numcalls = numcalls, + parameter = parameter, + rect = rect, + big_enough = bigEnough + }; + Log($"update(): called self.place(): {numcalls}: more={placed.more} parameter={parameter} rect={rect}."); } if (bigEnough) { - state.Pmax = parameter; - state.PmaxResult = result; + statePmax = parameter; + statePmaxResult = result; } else { - state.Pmin = parameter; - state.PminResult = result; + statePmin = parameter; + statePminResult = result; } - state.LastP = parameter; + + lastParameter = parameter; return bigEnough; } - float Opposite(float p, int direction) + double Opposite(double? p, int direction) { - if (p == 0) + if (!p.HasValue || p.Value == 0) return direction; - if (direction * p > 0) - return 2 * p; - return -p; + if (direction * p.Value > 0) + return 2 * p.Value; + return -p.Value; } - if (state.Pmin == 0) + if (!statePmin.HasValue) { - if (verbose) Log("finding Pmin."); - float parameter = Opposite(state.Pmax, -1); + Log("finding pmin."); + double parameter = Opposite(statePmax, -1); while (true) { - if (!Update(rect, parameter)) + if (!Update(parameter)) break; parameter *= 2; } } else { - if (Update(rect, state.Pmin)) + if (Update(statePmin.Value)) { - if (verbose) Log($"{state.Pmin} is big enough."); - FitResult ret = Ret(rect, state); - return ret; + Log($"statePmin={statePmin} is big enough."); + return Ret(); } } - if (state.Pmax == 0) + if (!statePmax.HasValue) { - if (verbose) Log("Finding Pmax"); - float parameter = Opposite(state.Pmin, 1); + Log("finding pmax."); + double parameter = Opposite(statePmin, +1); while (true) { - if (Update(rect, parameter)) + if (Update(parameter)) break; parameter *= 2; } } else { - if (!Update(rect, state.Pmax)) + if (!Update(statePmax.Value)) { - state.Pmax = 0; - if (verbose) Log($"No solution possible {state.Pmax}."); - FitResult ret = Ret(rect, state); - return ret; + statePmax = null; + Log("No solution possible statePmax=null."); + return Ret(); } } - if (verbose) - Log($"doing binary search with {state.Pmin} {state.Pmax}."); + Log($"doing binary search with statePmin={statePmin} statePmax={statePmax}."); while (true) { - if (state.Pmax - state.Pmin < delta) - return Ret(rect, state); - float parameter = (state.Pmin + state.Pmax) / 2; - Update(rect, parameter); + if (!statePmax.HasValue || !statePmin.HasValue || statePmax.Value - statePmin.Value < delta) + return Ret(); + double parameter = (statePmin.Value + statePmax.Value) / 2; + Update(parameter); } } /// - /// Adds links to PDF document + /// Finds smallest scale where scale*rect contains this story. /// - /// A PDF Document or raw PDF content, for example an io.BytesIO instance - /// List of Position's for stream - /// Document if a `Document` instance, otherwise a new `Document` instance - /// - public static Document AddPdfLinks(MemoryStream stream, List positions) + public FitResult FitScale(Rect rect, double scale_min = 0, double? scale_max = null, double delta = 0.001, bool verbose = false, int flags = 0) { - Document document = new Document("pdf", stream.ToArray()); - Dictionary id2Position = new Dictionary(); - foreach (Position position in positions) - { - if ((position.OpenClose & true) && position.Id != null) - { - if (id2Position.ContainsKey(position.Id)) - { - // pass - } - else - id2Position.Add(position.Id, position); - } - } - - foreach (Position positionFrom in positions) - { - if ((positionFrom.OpenClose & true) && positionFrom.Href != null) - { - LinkInfo link = new LinkInfo(); - link.From = new Rect(positionFrom.Rect); - Position positionTo; - if (positionFrom.Href.StartsWith("#")) - { - string targetId = positionFrom.Href.Substring(1); - if (!id2Position.TryGetValue(targetId, out positionTo)) - { - throw new Exception($"No destination with id={targetId}, required by position_from: {positionFrom}"); - } - - link.Kind = LinkType.LINK_GOTO; - link.To = new Point(positionTo.Rect.X0, positionTo.Rect.Y0); - link.Page = positionTo.PageNum - 1; - } - else - { - if (positionFrom.Href.StartsWith("name:")) - { - link.Kind = LinkType.LINK_NAMED; - link.Name = positionFrom.Href.Substring(5); - } - else - { - link.Kind = LinkType.LINK_URI; - link.Uri = positionFrom.Href; - } - } - document[positionFrom.PageNum - 1].InsertLink(link); - } - } - - return document; + double x0 = rect.X0; + double y0 = rect.Y0; + double width = rect.Width; + double height = rect.Height; + Rect Fn(double scale) => new Rect(x0, y0, x0 + scale * width, y0 + scale * height); + return Fit(Fn, scale_min, scale_max, delta, verbose, flags); } /// - /// Calculate that part of the story’s content, that will fit in the provided rectangle. The method maintains a pointer which part of the story’s content has already been written and upon the next invocation resumes from that pointer’s position. + /// Finds smallest height where width x height contains this story. /// - /// layout the current part of the content to fit into this rectangle. This must be a sub-rectangle of the page’s MediaBox. - /// a bool (int) more and a rectangle filled. If more == 0, all content of the story has been written, otherwise more is waiting to be written to subsequent rectangles / pages. Rectangle filled is the part of where that has actually been filled. - public (bool, Rect) Place(Rect where) + public FitResult FitHeight(double width, double height_min = 0, double? height_max = null, Point origin = null, double delta = 0.001, bool verbose = false) { - FzRect filled = new FzRect(); - bool more = _nativeStory.fz_place_story(where.ToFzRect(), filled) != 0; - return (more, new Rect(filled)); - } - - public Rect HeightFn(Rect rect, float height) - { - return new Rect(rect.X0, rect.Y0, rect.X1, rect.Y0 + height); + origin ??= new Point(0, 0); + double x0 = origin.X; + double y0 = origin.Y; + double x1 = x0 + width; + Rect Fn(double height) => new Rect(x0, y0, x1, y0 + height); + return Fit(Fn, height_min, height_max, delta, verbose, 0); } /// - /// Finds smallest height in range `height_min..height_max` where a rect with size `(width, height)` is large enough to contain the story + /// Finds smallest width where width x height contains this story. /// - /// width of rect - /// Minimum height to consider; must be >= 0. - /// Maximum height to consider, must be >= height_min or `None` for infinite. - /// point of rect. - /// Maximum error in returned height. - /// If true we output diagnostics. - /// Returns a `Story.FitResult` instance. - public FitResult FitHeight(float width, float heightMin = 0, float heightMax = 0, Point origin = null, float delta = 0.001f, bool verbose = false) - { - if (origin != null) - origin = new Point(0, 0); - Rect rect = new Rect(origin.X, origin.Y, origin.X + width, 0); - return Fit(HeightFn, rect, heightMin, heightMax, delta, verbose); - } - - public Rect WidthFn(Rect rect, float width) - { - return new Rect(rect.X0, rect.Y0, rect.X0 + width, rect.Y1); - } - - public FitResult FitWidth(float height, float widthMin = 0, float widthMax = 0, Point origin = null, float delta = 0.001f, bool verbose = false) + public FitResult FitWidth(double height, double width_min = 0, double? width_max = null, Point origin = null, double delta = 0.001, bool verbose = false) { - Rect rect = new Rect(origin.X, origin.Y, 0, origin.Y + height); - return Fit(WidthFn, rect, widthMin, widthMax, delta, verbose); + origin ??= new Point(0, 0); + double x0 = origin.X; + double y0 = origin.Y; + double y1 = y0 + height; + Rect Fn(double width) => new Rect(x0, y0, x0 + width, y1); + return Fit(Fn, width_min, width_max, delta, verbose, 0); } - /// - /// Similar to Write() except that we don’t have a writer arg and we return a PDF Document in which links have been created for each internal html link - /// - /// - /// - /// - /// a Document - public Document WriteWithLinks(RectFunction rectFn = null, Action positionFn = null, Action pageFn = null) + // Python naming wrappers + public void add_header_ids() => AddHeaderIds(); + public (bool more, Rect filledRect) place(Rect where, int flags = 0) => Place(where, flags); + public void element_positions(Action function, Dictionary args = null) => ElementPositions(function, args); + public void write(DocumentWriter writer, StoryRectFn rectfn, Action positionfn = null, Action pagefn = null) + => Write(writer, rectfn, positionfn, pagefn); + public static Document add_pdf_links(Document document, List positions) => AddPdfLinks(document, positions); + public static void write_stabilized(DocumentWriter writer, Func, string> contentfn, StoryRectFn rectfn, string user_css = null, float em = 12, Action positionfn = null, Action pagefn = null, Archive archive = null, bool add_header_ids = true) + => WriteStabilized(writer, contentfn, rectfn, user_css, em, positionfn, pagefn, archive, add_header_ids); + public static Document write_stabilized_with_links(Func, string> contentfn, StoryRectFn rectfn, string user_css = null, float em = 12, Action positionfn = null, Action pagefn = null, Archive archive = null, bool add_header_ids = true) + => WriteStabilizedWithLinks(contentfn, rectfn, user_css, em, positionfn, pagefn, archive, add_header_ids); + public Document write_with_links(StoryRectFn rectfn, Action positionfn = null, Action pagefn = null) + => WriteWithLinks(rectfn, positionfn, pagefn); + public FitResult fit(Func fn, double? pmin = null, double? pmax = null, double delta = 0.001, bool verbose = false, int flags = 0) + => Fit(fn, pmin, pmax, delta, verbose, flags); + public FitResult fit_scale(Rect rect, double scale_min = 0, double? scale_max = null, double delta = 0.001, bool verbose = false, int flags = 0) + => FitScale(rect, scale_min, scale_max, delta, verbose, flags); + public FitResult fit_height(double width, double height_min = 0, double? height_max = null, Point origin = null, double delta = 0.001, bool verbose = false) + => FitHeight(width, height_min, height_max, origin, delta, verbose); + public FitResult fit_width(double height, double width_min = 0, double? width_max = null, Point origin = null, double delta = 0.001, bool verbose = false) + => FitWidth(height, width_min, width_max, origin, delta, verbose); + + private sealed class StoryPositionCollector : mupdf.StoryPositionsCallback { - MemoryStream stream = new MemoryStream(100); - DocumentWriter writer = new DocumentWriter(stream); - List positions = new List(); - - Action positionFn2 = position => - { - positions.Add(position); - positionFn(position); - }; - - Write(writer, rectFn, positionFn: positionFn2, pageFn); - writer.Close(); - stream.Seek(0, (SeekOrigin)1); - return Story.AddPdfLinks(stream, positions); - } + private readonly Action _callback; + private readonly Dictionary _args; - /// - /// Places and draws Story to a DocumentWriter. Avoids the need for calling code to implement a loop that calls Story.place() and Story.draw() etc, - /// - /// - /// - /// - /// - public void Write(DocumentWriter writer, RectFunction rectFn, Action positionFn, Action pageFn) - { - DeviceWrapper dev = null; - int pageNum = 0; - int rectNum = 0; - Rect filled = new Rect(0, 0, 0, 0); - while (true) + internal StoryPositionCollector(Action callback, Dictionary args) { - (Rect mediabox, Rect rect, Matrix ctm) = rectFn(rectNum, filled); - rectNum += 1; - if (mediabox != null) - pageNum += 1; - (bool more, Rect _filled) = Place(rect); - filled = _filled; - if (positionFn != null) // if (positionFn) - { - Action positionFn2 = position => - { - position.PageNum = pageNum; - positionFn(position); - }; - - ElementPositions(positionFn); - } - - if (writer != null) - { - if (mediabox != null) - { - if (dev != null) - { - if (pageFn != null) - { - pageFn(pageNum, mediabox, dev, true); - } - writer.EndPage(); - } - dev = writer.BeginPage(mediabox); - if (pageFn != null) - { - pageFn(pageNum, mediabox, dev, false); - } - } - Draw(dev, ctm); - if (!more) - { - if (pageFn != null) - { - pageFn(pageNum, mediabox, dev, true); - } - writer.EndPage(); - } - dev.Dispose(); - } - else - Draw(null, ctm); - - if (!more) - break; + _callback = callback; + _args = args; } - } - - /// - /// Similar to WriteStabilized() except that we don’t have a writer arg and instead return a PDF Document in which links have been created for each internal html link - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static Document WriteStabilizedWithLinks( - ContentFunction contentfn, - RectFunction rectfn, - string userCss = null, - int em = 12, - Action positionFn = null, - Action pagefn = null, - Archive archive = null, - bool addHeaderIds = true - ) - { - MemoryStream stream = new MemoryStream(); - DocumentWriter writer = new DocumentWriter(stream); - List positions = new List(); - - Action positionFn2 = position => - { - positions.Add(position); - positionFn(position); - }; - - Story.WriteStabilized(writer, contentfn, rectfn, userCss, em, positionFn2, pagefn, archive, addHeaderIds); - writer.Close(); - stream.Seek(0, (SeekOrigin)1); - return Story.AddPdfLinks(stream, positions); - } - /// - /// Trigger a callback function to record where items have been placed. - /// - /// - /// - public void ElementPositions(Action function, Position arg = null) - { - Action function2 = position => + public override void call(mupdf.fz_story_element_position position) { - Position position2 = new Position + var p = new StoryElementPositionInfo { - Depth = position.Depth, - Heading = position.Heading, - Id = position.Id, - Rect = position.Rect, - Text = position.Text, - OpenClose = position.OpenClose, - RectNum = position.RectNum, - Href = position.Href + depth = position.depth, + heading = position.heading, + id = position.id, + rect = new Rect(position.rect.x0, position.rect.y0, position.rect.x1, position.rect.y1), + text = position.text, + open_close = position.open_close, + rect_num = position.rectangle_num, + href = position.href, }; - if (arg != null) - { - position2 = new Position(arg); // copy position - } - function(position2); - }; - - } - - /// - /// Static method that does iterative layout of html content to a DocumentWriter - /// - /// A DocumentWriter - /// A function taking a list of ElementPositions and returning a string containing html - /// A callable taking (RectNum: int, Filled: Rect) and returning (Mediabox, Rect, Ctm) - /// - /// - /// - /// None, or a callable taking (page_num, medibox, dev, after) - /// - /// If true, we add unique ids to all header tags that don't already have an id - public static void WriteStabilized( - DocumentWriter writer, // Assuming Writer is a defined class - ContentFunction contentfn, - RectFunction rectfn, - string userCss = null, - int em = 12, - Action positionFn = null, - Action pageFn = null, - Archive archive = null, // Assuming Archive is a defined class - bool addHeaderIds = true - ) - { - List positions = new List(); - string content = null; - - while (true) - { - string contentPrev = content; - content = contentfn(positions); - bool stable = false; - if (content == contentPrev) - { - stable = true; - } - string content2 = content; - Story story = new Story(content2, userCss, em, archive); // Assuming Story is a defined class - if (addHeaderIds) - { - story.AddHeaderIds(); // Assuming AddHeaderIds is a method of Story - } - positions.Clear(); - void PositionFn2(Position position) - { - positions.Add(position); - if (stable && positionFn != null) - { - positionFn(position); - } - } - story.Write( - stable ? writer : null, - rectfn, - PositionFn2, - pageFn - ); - if (stable) + if (_args != null) { - break; + // Keep Python behavior "args injection" best-effort by copying known keys. + if (_args.TryGetValue("page_num", out var pageNumObj) && pageNumObj is int pg) + p.page_num = pg; } + _callback(p); } } - } - - internal class State - { - public float Pmin { get; set; } - - public float Pmax { get; set; } - - public FitResult PminResult { get; set; } - public FitResult PmaxResult { get; set; } + // ─── IDisposable ──────────────────────────────────────────────── - public int Result { get; set; } - - public int Numcalls { get; set; } - - public float Pmin0 { get; set; } - - public float Pmax0 { get; set; } - - public float LastP { get; set; } - public State(float pmin, float pmax, bool verbose) + /// + /// Releases all resources used by the . + /// + public void Dispose() { - Pmin = pmin; - Pmax = pmax; - PminResult = null; - PmaxResult = null; - Result = 0; - Numcalls = 0; - if (verbose) + if (!_disposed) { - Pmin0 = pmin; - Pmax0 = pmax; + _nativeStory?.Dispose(); + _nativeStory = null; + _disposed = true; } + GC.SuppressFinalize(this); } - } - - public class FitResult - { - /// - /// True if the fit succeeded - /// - public bool BigEnough { get; set; } = false; - - /// - /// From the last call to Story.place() - /// - public dynamic Filled { get; set; } - - /// - /// False if the fit succeeded - /// - public bool More { get; set; } - - /// - /// Number of calls made to self.place() - /// - public int NumCalls { get; set; } - /// - /// The successful parameter value, or the largest failing value - /// - public float Parameter { get; set; } + ~Story() { Dispose(); } /// - /// The rect created from parameter + /// Returns a string that represents the current story. /// - public Rect Rect { get; set; } - - public FitResult(bool bigEnough = false, dynamic filled = null, bool more = false, int numcalls = 0, float parameter = 0, Rect rect = null) - { - BigEnough = bigEnough; - Filled = filled; - More = more; - NumCalls = numcalls; - Parameter = parameter; - Rect = rect; - } - - public override string ToString() - { - return $"BigEnough={BigEnough}, Filled={Filled}, More={More}, NumCalls={NumCalls}, Parameter={Parameter}, Rect={Rect}"; - } + public override string ToString() => "Story()"; } } diff --git a/MuPDF.NET/Table.cs b/MuPDF.NET/Table.cs index 7d79e7fc..e85f3895 100644 --- a/MuPDF.NET/Table.cs +++ b/MuPDF.NET/Table.cs @@ -1,3680 +1,1731 @@ -/* -Copyright (C) 2023 Artifex Software, Inc. - -This file is part of MuPDF.NET. - -MuPDF.NET is free software: you can redistribute it and/or modify it under the -terms of the GNU Affero General Public License as published by the Free -Software Foundation, either version 3 of the License, or (at your option) -any later version. - -MuPDF.NET is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -details. - -You should have received a copy of the GNU Affero General Public License -along with MuPDF. If not, see - -Alternative licensing terms are available from the licensor. -For commercial licensing, see or contact -Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, -CA 94129, USA, for further information. - ---------------------------------------------------------------------- -Portions of this code have been ported from pdfplumber, see -https://pypi.org/project/pdfplumber/. - -The ported code is under the following MIT license: - ---------------------------------------------------------------------- -The MIT License (MIT) - -Copyright (c) 2015, Jeremy Singer-Vine - -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. ---------------------------------------------------------------------- -Also see here: https://github.com/jsvine/pdfplumber/blob/stable/LICENSE.txt ---------------------------------------------------------------------- - -The porting mainly pertains to the upstream table module and relevant parts of -the text utilities within pdfplumber's repository on GitHub. -With respect to those text utilities, we have removed functions or features that are not -used by table processing. Examples are: - -* the text search function -* simple text extraction -* text extraction by lines - -Original pdfplumber code does neither detect, nor identify table headers. -This MuPDF.NET port adds respective code to the 'Table' class as method '_get_header'. -This is implemented as new class TableHeader with the properties: -* bbox: A tuple for the header's bbox -* cells: A tuple for each bbox of a column header -* names: A list of strings with column header text -* external: A bool indicating whether the header is outside the table cells. - -*/ - -using mupdf; +// Copyright (C) 2023 Artifex Software, Inc. +// +// Portions of this code have been ported from pdfplumber / PyMuPDF table.py. +// pdfplumber is under the MIT License, Copyright (c) 2015 Jeremy Singer-Vine. + using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Text; -using System.Text.RegularExpressions; namespace MuPDF.NET { - // Global state for table processing - internal static class TableGlobals - { - internal static List EDGES = new List(); // vector graphics from MuPDF - internal static List CHARS = new List(); // text characters from MuPDF - internal static TextPage TEXTPAGE = null; // textpage for cell text extraction - - // Constants - internal static readonly HashSet WHITE_SPACES = new HashSet { ' ', '\t', '\n', '\r', '\f', '\v' }; - // TEXT_FONT_BOLD = 16, but for char flags use FZ_STEXT_BOLD - internal static readonly int TEXT_BOLD = (int)mupdf.mupdf.FZ_STEXT_BOLD; - // TEXT_STRIKEOUT = mupdf.FZ_STEXT_STRIKEOUT - internal static readonly int TEXT_STRIKEOUT = (int)mupdf.mupdf.FZ_STEXT_STRIKEOUT; - // TEXT_COLLECT_STYLES = mupdf.FZ_STEXT_COLLECT_STYLES - internal static readonly int TEXT_COLLECT_STYLES = (int)mupdf.mupdf.FZ_STEXT_COLLECT_STYLES; - // TEXT_COLLECT_VECTORS = mupdf.FZ_STEXT_COLLECT_VECTORS - internal static readonly int TEXT_COLLECT_VECTORS = (int)mupdf.mupdf.FZ_STEXT_COLLECT_VECTORS; - // TEXT_SEGMENT = mupdf.FZ_STEXT_SEGMENT - internal static readonly int TEXT_SEGMENT = (int)mupdf.mupdf.FZ_STEXT_SEGMENT; - // Upstream table FLAGS: TEXTFLAGS_TEXT | TEXT_COLLECT_STYLES | TEXT_ACCURATE_BBOXES | TEXT_MEDIABOX_CLIP - internal static readonly int FLAGS = - (int)TextFlagsExtension.TEXTFLAGS_TEXT | - TEXT_COLLECT_STYLES | - (int)TextFlags.TEXT_ACCURATE_BBOXES | - (int)TextFlags.TEXT_MEDIABOX_CLIP; - // Upstream TABLE_DETECTOR_FLAGS: TEXT_ACCURATE_BBOXES | TEXT_SEGMENT | TEXT_COLLECT_VECTORS | TEXT_MEDIABOX_CLIP - internal static readonly int TABLE_DETECTOR_FLAGS = - (int)TextFlags.TEXT_ACCURATE_BBOXES | - TEXT_SEGMENT | - TEXT_COLLECT_VECTORS | - (int)TextFlags.TEXT_MEDIABOX_CLIP; - } - + // ──────────────────────────────────────────────────────────────────── // Constants + // ──────────────────────────────────────────────────────────────────── + + /// + /// Default constants used by the table-detection algorithms. + /// internal static class TableConstants { - internal static readonly string[] NON_NEGATIVE_SETTINGS = { + public const float DefaultSnapTolerance = 3; + public const float DefaultJoinTolerance = 3; + public const float DefaultMinWordsVertical = 3; + public const float DefaultMinWordsHorizontal = 1; + public const float DefaultXTolerance = 3; + public const float DefaultYTolerance = 3; + public const float DefaultXDensity = 7.25f; + public const float DefaultYDensity = 13; + + public static readonly string[] TableStrategies = + { "lines", "lines_strict", "text", "explicit" }; + + public static readonly Dictionary Ligatures = new Dictionary + { + { "\uFB00", "ff" }, + { "\uFB03", "ffi" }, + { "\uFB04", "ffl" }, + { "\uFB01", "fi" }, + { "\uFB02", "fl" }, + { "\uFB06", "st" }, + { "\uFB05", "st" }, + }; + + public static readonly string[] NonNegativeSettings = + { "snap_tolerance", "snap_x_tolerance", "snap_y_tolerance", "join_tolerance", "join_x_tolerance", "join_y_tolerance", "edge_min_length", "min_words_vertical", "min_words_horizontal", - "intersection_tolerance", "intersection_x_tolerance", "intersection_y_tolerance" + "intersection_tolerance", "intersection_x_tolerance", "intersection_y_tolerance", }; - internal static readonly Dictionary LIGATURES = new Dictionary + public static readonly HashSet WhiteSpaces = new HashSet { - { "ff", "ff" }, - { "ffi", "ffi" }, - { "ffl", "ffl" }, - { "fi", "fi" }, - { "fl", "fl" }, - { "st", "st" }, - { "ſt", "st" } + ' ', '\t', '\n', '\r', '\f', '\v' }; } - // Character dictionary structure - internal class CharDict + // ──────────────────────────────────────────────────────────────────── + // CellGroup / TableRow + // ──────────────────────────────────────────────────────────────────── + + /// + /// A group of cells forming a contiguous region with a bounding box. + /// + public class CellGroup { - public float adv { get; set; } - public float bottom { get; set; } - public float doctop { get; set; } - public string fontname { get; set; } - public float height { get; set; } - public Tuple matrix { get; set; } - public string ncs { get; set; } - public Tuple non_stroking_color { get; set; } - public object non_stroking_pattern { get; set; } - public string object_type { get; set; } - public int page_number { get; set; } - public float size { get; set; } - public Tuple stroking_color { get; set; } - public object stroking_pattern { get; set; } - public bool bold { get; set; } - public string text { get; set; } - public float top { get; set; } - public bool upright { get; set; } - public float width { get; set; } - public float x0 { get; set; } - public float x1 { get; set; } - public float y0 { get; set; } - public float y1 { get; set; } + /// + /// Individual cell bounding boxes. A null entry means the cell is + /// spanned by a neighbour (row-/column-span). + /// + public List<(float x0, float y0, float x1, float y1)?> Cells { get; set; } + + /// + /// Bounding box enclosing all non-null cells. + /// + public (float x0, float y0, float x1, float y1) Bbox { get; set; } + + public CellGroup(List<(float x0, float y0, float x1, float y1)?> cells) + { + Cells = cells; + var valid = cells.Where(c => c.HasValue).Select(c => c!.Value).ToList(); + if (valid.Count == 0) + { + Bbox = (0, 0, 0, 0); + } + else + { + Bbox = ( + valid.Min(c => c.x0), + valid.Min(c => c.y0), + valid.Max(c => c.x1), + valid.Max(c => c.y1) + ); + } + } } - // Edge structure for table detection - public class Edge + /// + /// One row of a . + /// + public class TableRow : CellGroup { - public float x0 { get; set; } - public float x1 { get; set; } - public float top { get; set; } - public float bottom { get; set; } - public float width { get; set; } - public float height { get; set; } - public string orientation { get; set; } // "h" or "v" - public string object_type { get; set; } - public float doctop { get; set; } - public int page_number { get; set; } - public float y0 { get; set; } - public float y1 { get; set; } + public TableRow(List<(float x0, float y0, float x1, float y1)?> cells) + : base(cells) { } } - // Helper functions - internal static class TableHelpers + // ──────────────────────────────────────────────────────────────────── + // TableHeader + // ──────────────────────────────────────────────────────────────────── + + /// + /// Describes the detected header of a . + /// + public class TableHeader { - // rect_in_rect - Check whether rectangle 'inner' is fully inside rectangle 'outer' - internal static bool RectInRect(Rect inner, Rect outer) - { - return inner.X0 >= outer.X0 && inner.Y0 >= outer.Y0 && - inner.X1 <= outer.X1 && inner.Y1 <= outer.Y1; - } + /// Bounding box of the header region. + public (float x0, float y0, float x1, float y1) Bbox { get; } - // chars_in_rect - Check whether any of the chars are inside rectangle - internal static bool CharsInRect(List chars, Rect rect) - { - return chars.Any(c => - rect.X0 <= c.x0 && c.x1 <= rect.X1 && - rect.Y0 <= c.y0 && c.y1 <= rect.Y1); - } + /// Per-column header cell bounding boxes (null = spanned). + public List<(float x0, float y0, float x1, float y1)?> Cells { get; } - // _iou - Compute intersection over union of two rectangles - internal static float Iou(Rect r1, Rect r2) - { - float ix = Math.Max(0, Math.Min(r1.X1, r2.X1) - Math.Max(r1.X0, r2.X0)); - float iy = Math.Max(0, Math.Min(r1.Y1, r2.Y1) - Math.Max(r1.Y0, r2.Y0)); - float intersection = ix * iy; - if (intersection == 0) - return 0; - float area1 = (r1.X1 - r1.X0) * (r1.Y1 - r1.Y0); - float area2 = (r2.X1 - r2.X0) * (r2.Y1 - r2.Y0); - return intersection / (area1 + area2 - intersection); - } + /// Column names extracted from the header cells (null = spanned cell). + public List Names { get; } - // intersects_words_h - Check whether any words are cut through by horizontal line y - internal static bool IntersectsWordsH(Rect bbox, float y, List wordRects) + /// + /// True when the header sits above the first data row (external), + /// false when the first data row is the header. + /// + public bool External { get; } + + public TableHeader( + (float x0, float y0, float x1, float y1) bbox, + List<(float x0, float y0, float x1, float y1)?> cells, + List names, + bool external) { - return wordRects.Any(r => RectInRect(r, bbox) && r.Y0 < y && y < r.Y1); + Bbox = bbox; + Cells = cells; + Names = names; + External = external; } + } - // get_table_dict_from_rect - Extract MuPDF table structure information - // Note: This requires native MuPDF interop to call fz_find_table_within_bounds - // This would need to be implemented via P/Invoke or native wrapper - internal static Dictionary GetTableDictFromRect(TextPage textpage, Rect rect) + // ──────────────────────────────────────────────────────────────────── + // Table + // ──────────────────────────────────────────────────────────────────── + + /// + /// Represents a single table detected on a PDF page, with methods to + /// extract its content as text or Markdown. + /// + public class Table + { + internal Page Page; + internal TextPage TextPage; + + /// All cell bounding boxes that compose this table. + public List<(float x0, float y0, float x1, float y1)> Cells { get; set; } + + /// Detected header for this table. + public TableHeader Header { get; internal set; } + + /// Bounding box of the entire table. + public (float x0, float y0, float x1, float y1) Bbox { - var tableDict = new Dictionary(); - // TODO: Implement native interop call to MuPDF's table detection function - // This is used by make_table_from_bbox which is called when layout_information finds tables - return tableDict; + get + { + return ( + Cells.Min(c => c.x0), + Cells.Min(c => c.y0), + Cells.Max(c => c.x1), + Cells.Max(c => c.y1) + ); + } } - // make_table_from_bbox - Detect table structure within a given rectangle - internal static List MakeTableFromBbox(TextPage textpage, List wordRects, Rect rect) + /// + /// Table rows, each containing a list of cell bounding boxes + /// aligned to the column grid. + /// + public List Rows { - var cells = new List(); - var block = GetTableDictFromRect(textpage, rect); - - if (!block.ContainsKey("type") || Convert.ToInt32(block["type"]) != mupdf.mupdf.FZ_STEXT_BLOCK_GRID) - return cells; - - var bboxList = block["bbox"] as List; - if (bboxList == null || bboxList.Count < 4) - return cells; - - var bbox = new Rect( - Convert.ToSingle(bboxList[0]), - Convert.ToSingle(bboxList[1]), - Convert.ToSingle(bboxList[2]), - Convert.ToSingle(bboxList[3]) - ); - - var xpos = (block["xpos"] as List)?.Cast>() - .Select(x => Tuple.Create(Convert.ToSingle(x[0]), Convert.ToSingle(x[1]))) - .OrderBy(x => x.Item1).ToList() ?? new List>(); - - var ypos = (block["ypos"] as List)?.Cast>() - .Select(y => Tuple.Create(Convert.ToSingle(y[0]), Convert.ToSingle(y[1]))) - .OrderBy(y => y.Item1).ToList() ?? new List>(); - - var maxUncertain = block["max_uncertain"] as List; - float xmaxu = maxUncertain != null && maxUncertain.Count > 0 ? Convert.ToSingle(maxUncertain[0]) : 0; - float ymaxu = maxUncertain != null && maxUncertain.Count > 1 ? Convert.ToSingle(maxUncertain[1]) : 0; - - // Modify ypos to remove uncertain positions - var nypos = new List(); - foreach (var (y, yunc) in ypos) + get { - if (yunc > 0) continue; - if (IntersectsWordsH(bbox, y, wordRects)) continue; - if (nypos.Count > 0 && (y - nypos[nypos.Count - 1] < 3)) - nypos[nypos.Count - 1] = y; - else - nypos.Add(y); + var sorted = Cells.OrderBy(c => c.y0).ThenBy(c => c.x0).ToList(); + var xs = Cells.Select(c => c.x0).Distinct().OrderBy(x => x).ToList(); + var rows = new List(); + foreach (var grp in sorted.GroupBy(c => c.y0)) + { + var xdict = new Dictionary(); + foreach (var cell in grp) + xdict[cell.x0] = cell; + var rowCells = xs.Select(x => + xdict.ContainsKey(x) + ? ((float x0, float y0, float x1, float y1)?)xdict[x] + : null + ).ToList(); + rows.Add(new TableRow(rowCells)); + } + return rows; } + } - ymaxu = Math.Max(0, (float)Math.Round((nypos.Count - 2) * 0.35)); + /// Number of rows. + public int RowCount => Rows.Count; - var nxpos = xpos.Where(x => x.Item2 <= ymaxu).Select(x => x.Item1).ToList(); - if (bbox.X1 > nxpos[nxpos.Count - 1] + 3) - nxpos.Add(bbox.X1); + /// Number of columns (widest row). + public int ColCount => Rows.Max(r => r.Cells.Count); - // Compose cells from remaining x and y positions - for (int i = 0; i < nypos.Count - 1; i++) - { - var rowBox = new Rect(bbox.X0, nypos[i], bbox.X1, nypos[i + 1]); - var rowWords = wordRects.Where(r => RectInRect(r, rowBox)) - .OrderBy(r => r.X0).ToList(); - - var thisXpos = nxpos.Where(x => !rowWords.Any(r => r.X0 < x && x < r.X1)).ToList(); - - for (int j = 0; j < thisXpos.Count - 1; j++) + internal Table(Page page, List<(float x0, float y0, float x1, float y1)> cells) + { + Page = page; + TextPage = null; + Cells = cells; + Header = GetHeader(); + } + + /// + /// Extract text from every cell using the pdfplumber-style + /// character-clustering algorithm. + /// + /// + /// When true, cell text is extracted with Markdown styling + /// (bold, italic, strikeout, monospace). + /// + /// + /// A list of rows, each row being a list of cell-text strings. + /// A null entry means the cell was spanned (no bbox). + /// + public List> Extract(bool markdown = false) + { + var tableArr = new List>(); + foreach (var row in Rows) + { + var arr = new List(); + foreach (var cell in row.Cells) { - var cell = new Rect(thisXpos[j], nypos[i], thisXpos[j + 1], nypos[i + 1]); - if (!cell.IsEmpty) - cells.Add(cell); + if (cell == null) + { + arr.Add(null); + } + else + { + arr.Add(TextPage != null + ? TableHelpers.ExtractCells(TextPage, cell.Value, markdown) + : ""); + } } + tableArr.Add(arr); } - - return cells; + return tableArr; } - // extract_cells - Extract text from a cell as plain or MD styled text - internal static string ExtractCells(TextPage textpage, Rect cell, bool markdown = false) + /// + /// Render the table as a GitHub-flavoured Markdown string. + /// + /// + /// When true, HTML-escape characters that would interfere with Markdown. + /// + /// + /// When true, null cells are filled from neighbours to approximate spans. + /// + public string ToMarkdown(bool clean = false, bool fillEmpty = true) { - if (textpage == null) - return ""; + int rows = RowCount; + int cols = ColCount; + + var cellBoxes = Rows.Select(r => r.Cells.ToList()).ToList(); - var text = new StringBuilder(); - var pageInfo = textpage.ExtractRAWDict(cropbox: null, sort: false); + var cells = new string[rows][]; + for (int i = 0; i < rows; i++) + cells[i] = new string[cols]; - if (pageInfo?.Blocks == null) - return ""; + for (int i = 0; i < rows; i++) + for (int j = 0; j < cellBoxes[i].Count; j++) + if (cellBoxes[i][j] != null) + cells[i][j] = TableHelpers.ExtractCells( + TextPage, cellBoxes[i][j].Value, markdown: true); - foreach (var block in pageInfo.Blocks) + if (fillEmpty) { - if (block.Type != 0) continue; + for (int j = 0; j < rows; j++) + for (int i = 0; i < cols - 1; i++) + if (cells[j][i + 1] == null) + cells[j][i + 1] = cells[j][i]; - var blockBbox = block.Bbox; - if (blockBbox == null) continue; + for (int i = 0; i < cols; i++) + for (int j = 0; j < rows - 1; j++) + if (cells[j + 1][i] == null) + cells[j + 1][i] = cells[j][i]; + } - if (blockBbox.X0 > cell.X1 || blockBbox.X1 < cell.X0 || - blockBbox.Y0 > cell.Y1 || blockBbox.Y1 < cell.Y0) - continue; + var sb = new StringBuilder(); + sb.Append('|'); + for (int i = 0; i < Header.Names.Count; i++) + { + string? name = Header.Names[i]; + if (string.IsNullOrEmpty(name)) + name = $"Col{i + 1}"; + name = name.Replace("\n", "
"); + if (clean) + name = WebUtility.HtmlEncode(name.Replace("-", "-")); + sb.Append(name); + sb.Append('|'); + } + sb.AppendLine(); - if (block.Lines == null) continue; + sb.Append('|'); + for (int i = 0; i < cols; i++) + sb.Append("---|"); + sb.AppendLine(); - foreach (var line in block.Lines) + int startRow = Header.External ? 0 : 1; + for (int r = startRow; r < rows; r++) + { + sb.Append('|'); + for (int c = 0; c < cols; c++) { - if (line.Bbox == null) continue; + string cell = cells[r][c] ?? ""; + if (clean) + cell = WebUtility.HtmlEncode(cell.Replace("-", "-")); + sb.Append(cell); + sb.Append('|'); + } + sb.AppendLine(); + } + sb.AppendLine(); + return sb.ToString(); + } - var lbbox = line.Bbox; - if (lbbox.X0 > cell.X1 || lbbox.X1 < cell.X0 || - lbbox.Y0 > cell.Y1 || lbbox.Y1 < cell.Y0) - continue; + // ── Header detection (PyMuPDF extension) ──────────────────────── - if (text.Length > 0) - text.Append(markdown ? "
" : "\n"); + /// + /// Identify the table header (PyMuPDF extension). + /// Starting from the first row, determine whether it qualifies as the + /// header. A one-line table or single-column table always uses the first + /// row. Returns null if the table has no rows. + /// + private TableHeader GetHeader(float yTolerance = 3) + { + TableRow row; + try + { + row = Rows[0]; + } + catch + { + return null; + } - var lineDir = line.Dir; - bool horizontal = lineDir != null && - (lineDir.X == 0 && lineDir.Y == 1 || lineDir.X == 1 && lineDir.Y == 0); + var rowCells = row.Cells; + var bbox = row.Bbox; - if (line.Spans == null) continue; + var headerTopRow = new TableHeader( + bbox, rowCells, Extract()[0], false); - foreach (var span in line.Spans) - { - if (span.Bbox == null) continue; + if (Rows.Count < 2) return headerTopRow; + if (rowCells.Count < 2) return headerTopRow; - var sbbox = span.Bbox; - if (sbbox.X0 > cell.X1 || sbbox.X1 < cell.X0 || - sbbox.Y0 > cell.Y1 || sbbox.Y1 < cell.Y0) - continue; + var row2 = Rows[1]; + if (row2.Cells.All(c => c == null)) + return headerTopRow; - var spanText = new StringBuilder(); - if (span.Chars != null) - { - foreach (var char_ in span.Chars) - { - if (char_.Bbox == null) continue; - - var charRect = new Rect(char_.Bbox); - var cellRect = new Rect(cell.X0, cell.Y0, cell.X1, cell.Y1); - var intersection = charRect & cellRect; - - if (intersection != null && !intersection.IsEmpty && - (intersection.Width * intersection.Height) > 0.5 * (charRect.Width * charRect.Height)) - { - spanText.Append(char_.C); - } - else if (TableGlobals.WHITE_SPACES.Contains(char_.C)) - { - spanText.Append(" "); - } - } - } - else if (!string.IsNullOrEmpty(span.Text)) - { - spanText.Append(span.Text); - } + return headerTopRow; + } + } - if (spanText.Length == 0) continue; + // ──────────────────────────────────────────────────────────────────── + // TableSettings + // ──────────────────────────────────────────────────────────────────── - if (!markdown) - { - text.Append(spanText); - continue; - } + /// + /// Configuration for the table-detection algorithm. + /// Mirrors the Python TableSettings dataclass. + /// + public class TableSettings + { + /// Strategy for detecting vertical lines: lines, lines_strict, text, explicit. + public string VerticalStrategy { get; set; } = "lines"; + + /// Strategy for detecting horizontal lines. + public string HorizontalStrategy { get; set; } = "lines"; + + /// Explicit vertical lines when VerticalStrategy == "explicit". + public List ExplicitVerticalLines { get; set; } + + /// Explicit horizontal lines when HorizontalStrategy == "explicit". + public List ExplicitHorizontalLines { get; set; } + + public float SnapTolerance { get; set; } = TableConstants.DefaultSnapTolerance; + public float SnapXTolerance { get; set; } = float.NaN; + public float SnapYTolerance { get; set; } = float.NaN; + public float JoinTolerance { get; set; } = TableConstants.DefaultJoinTolerance; + public float JoinXTolerance { get; set; } = float.NaN; + public float JoinYTolerance { get; set; } = float.NaN; + public float EdgeMinLength { get; set; } = 3; + public float MinWordsVertical { get; set; } = TableConstants.DefaultMinWordsVertical; + public float MinWordsHorizontal { get; set; } = TableConstants.DefaultMinWordsHorizontal; + public float IntersectionTolerance { get; set; } = 3; + public float IntersectionXTolerance { get; set; } = float.NaN; + public float IntersectionYTolerance { get; set; } = float.NaN; + public Dictionary TextSettings { get; set; } - string prefix = ""; - string suffix = ""; - float flags = span.Flags; + /// + /// Validate and fill in defaults for tolerance values. + /// + public void Validate() + { + if (SnapTolerance < 0 || JoinTolerance < 0 || EdgeMinLength < 0 || + MinWordsVertical < 0 || MinWordsHorizontal < 0 || IntersectionTolerance < 0) + throw new ArgumentException("Table tolerance settings cannot be negative."); - if (horizontal && ((int)flags & TableGlobals.TEXT_STRIKEOUT) != 0) - { - prefix += "~~"; - suffix = "~~" + suffix; - } - if (((int)flags & TableGlobals.TEXT_BOLD) != 0) - { - prefix += "**"; - suffix = "**" + suffix; - } - if (((int)flags & (int)FontStyle.TEXT_FONT_ITALIC) != 0) - { - prefix += "_"; - suffix = "_" + suffix; - } - if (((int)flags & (int)FontStyle.TEXT_FONT_MONOSPACED) != 0) - { - prefix += "`"; - suffix = "`" + suffix; - } + if (!TableConstants.TableStrategies.Contains(VerticalStrategy)) + throw new ArgumentException( + $"vertical_strategy must be one of {string.Join(",", TableConstants.TableStrategies)}"); + if (!TableConstants.TableStrategies.Contains(HorizontalStrategy)) + throw new ArgumentException( + $"horizontal_strategy must be one of {string.Join(",", TableConstants.TableStrategies)}"); - string spanTextStr = spanText.ToString(); - if (span.Chars != null && span.Chars.Count > 2) - spanTextStr = spanTextStr.TrimEnd(); + TextSettings ??= new Dictionary(); - if (suffix.Length > 0 && text.ToString().EndsWith(suffix)) - { - text.Remove(text.Length - suffix.Length, suffix.Length); - text.Append(spanTextStr + suffix); - } - else - { - if (string.IsNullOrWhiteSpace(spanTextStr)) - text.Append(" "); - else - text.Append(prefix + spanTextStr + suffix); - } - } - } - } + if (!TextSettings.ContainsKey("x_tolerance")) + TextSettings["x_tolerance"] = TextSettings.ContainsKey("tolerance") + ? TextSettings["tolerance"] : 3f; + if (!TextSettings.ContainsKey("y_tolerance")) + TextSettings["y_tolerance"] = TextSettings.ContainsKey("tolerance") + ? TextSettings["tolerance"] : 3f; + TextSettings.Remove("tolerance"); - return text.ToString().Trim(); + if (float.IsNaN(SnapXTolerance)) SnapXTolerance = SnapTolerance; + if (float.IsNaN(SnapYTolerance)) SnapYTolerance = SnapTolerance; + if (float.IsNaN(JoinXTolerance)) JoinXTolerance = JoinTolerance; + if (float.IsNaN(JoinYTolerance)) JoinYTolerance = JoinTolerance; + if (float.IsNaN(IntersectionXTolerance)) IntersectionXTolerance = IntersectionTolerance; + if (float.IsNaN(IntersectionYTolerance)) IntersectionYTolerance = IntersectionTolerance; } - // to_list - Convert collection to list - internal static List ToList(object collection) + /// + /// Create a validated from an existing + /// instance, a dictionary, or null (defaults). + /// + public static TableSettings Resolve(TableSettings? settings = null) { - if (collection is List list) - return list; - if (collection is IEnumerable enumerable) - return enumerable.ToList(); - return new List { (T)collection }; + var ts = settings ?? new TableSettings(); + ts.Validate(); + return ts; } - // Helper function for clustering objects - internal static List> ClusterObjects(IEnumerable xs, Func keyFn, float tolerance) + /// + /// Resolve settings from a key/value dictionary. Keys prefixed with + /// text_ are placed into . + /// + public static TableSettings Resolve(Dictionary dict) { - if (tolerance == 0) - return xs.OrderBy(keyFn).Select(x => new List { x }).ToList(); - - var xsList = xs.ToList(); - if (xsList.Count < 2) - return xsList.Select(x => new List { x }).ToList(); - - var values = xsList.Select(keyFn).Distinct().OrderBy(v => v).ToList(); - var clusters = ClusterList(values, tolerance); - - var clusterDict = new Dictionary(); - for (int i = 0; i < clusters.Count; i++) + if (dict == null) { - foreach (var val in clusters[i]) - clusterDict[val] = i; + var ts0 = new TableSettings(); + ts0.Validate(); + return ts0; } - var grouped = xsList.GroupBy(x => clusterDict[keyFn(x)]).OrderBy(g => g.Key); - return grouped.Select(g => g.ToList()).ToList(); - } - - internal static List> ClusterList(List xs, float tolerance = 0) - { - if (tolerance == 0) - return xs.OrderBy(x => x).Select(x => new List { x }).ToList(); - - if (xs.Count < 2) - return xs.Select(x => new List { x }).ToList(); - - var groups = new List>(); - var sortedXs = xs.OrderBy(x => x).ToList(); - var currentGroup = new List { sortedXs[0] }; - float last = sortedXs[0]; - - for (int i = 1; i < sortedXs.Count; i++) + var ts = new TableSettings(); + var textSettings = new Dictionary(); + foreach (var kv in dict) { - float x = sortedXs[i]; - if (x <= (last + tolerance)) + if (kv.Key.StartsWith("text_")) { - currentGroup.Add(x); - } - else - { - groups.Add(currentGroup); - currentGroup = new List { x }; + textSettings[kv.Key.Substring(5)] = kv.Value; + continue; } - last = x; - } - groups.Add(currentGroup); - return groups; + switch (kv.Key) + { + case "vertical_strategy": ts.VerticalStrategy = (string)kv.Value; break; + case "horizontal_strategy": ts.HorizontalStrategy = (string)kv.Value; break; + case "snap_tolerance": ts.SnapTolerance = Convert.ToSingle(kv.Value); break; + case "snap_x_tolerance": ts.SnapXTolerance = Convert.ToSingle(kv.Value); break; + case "snap_y_tolerance": ts.SnapYTolerance = Convert.ToSingle(kv.Value); break; + case "join_tolerance": ts.JoinTolerance = Convert.ToSingle(kv.Value); break; + case "join_x_tolerance": ts.JoinXTolerance = Convert.ToSingle(kv.Value); break; + case "join_y_tolerance": ts.JoinYTolerance = Convert.ToSingle(kv.Value); break; + case "edge_min_length": ts.EdgeMinLength = Convert.ToSingle(kv.Value); break; + case "min_words_vertical": ts.MinWordsVertical = Convert.ToSingle(kv.Value); break; + case "min_words_horizontal": ts.MinWordsHorizontal = Convert.ToSingle(kv.Value); break; + case "intersection_tolerance": ts.IntersectionTolerance = Convert.ToSingle(kv.Value); break; + case "intersection_x_tolerance": ts.IntersectionXTolerance = Convert.ToSingle(kv.Value); break; + case "intersection_y_tolerance": ts.IntersectionYTolerance = Convert.ToSingle(kv.Value); break; + case "explicit_vertical_lines": + ts.ExplicitVerticalLines = ((IEnumerable)kv.Value) + .Select(v => Convert.ToSingle(v)).ToList(); + break; + case "explicit_horizontal_lines": + ts.ExplicitHorizontalLines = ((IEnumerable)kv.Value) + .Select(v => Convert.ToSingle(v)).ToList(); + break; + } + } + if (textSettings.Count > 0) ts.TextSettings = textSettings; + ts.Validate(); + return ts; } + } - internal static Rect ObjectsToBbox(IEnumerable objects) - { - var rects = new List(); - foreach (var obj in objects) - { - if (obj is CharDict charDict) - { - rects.Add(new Rect(charDict.x0, charDict.top, charDict.x1, charDict.bottom)); - } - else if (obj is Dictionary dict) - { - if (dict.ContainsKey("x0") && dict.ContainsKey("top") && dict.ContainsKey("x1") && dict.ContainsKey("bottom")) - { - rects.Add(new Rect( - Convert.ToSingle(dict["x0"]), - Convert.ToSingle(dict["top"]), - Convert.ToSingle(dict["x1"]), - Convert.ToSingle(dict["bottom"]) - )); - } - } - } + // ──────────────────────────────────────────────────────────────────── + // TableFinder + // ──────────────────────────────────────────────────────────────────── - if (rects.Count == 0) - return new Rect(0, 0, 0, 0); + /// + /// Finds all tables on a PDF page using pdfplumber-style algorithms. + /// + public class TableFinder + { + /// The page being analysed. + public Page Page { get; } - return new Rect( - rects.Min(r => r.X0), - rects.Min(r => r.Y0), - rects.Max(r => r.X1), - rects.Max(r => r.Y1) - ); - } - } + /// Detected tables, sorted top-to-bottom then left-to-right. + public List
Tables { get; internal set; } - // TextMap class - maps each unicode character to a char object - internal class TextMap - { - public List> tuples { get; set; } - public string as_string { get; set; } + /// Active settings used during detection. + public TableSettings Settings { get; } - public TextMap(List> tuples = null) - { - this.tuples = tuples ?? new List>(); - this.as_string = string.Join("", this.tuples.Select(t => t.Item1)); - } + internal List> Edges; + internal List> Chars; - public Dictionary MatchToDict( - Match m, - int mainGroup = 0, - bool returnGroups = true, - bool returnChars = true) + /// + /// Create a and immediately detect all tables + /// on the given page. + /// + public TableFinder(Page page, TableSettings settings = null) { - var subset = tuples.Skip(m.Groups[mainGroup].Index).Take(m.Groups[mainGroup].Length).ToList(); - var chars = subset.Where(t => t.Item2 != null).Select(t => t.Item2).ToList(); - var bbox = TableHelpers.ObjectsToBbox(chars); + Page = page; + Settings = TableSettings.Resolve(settings); + Chars = new List>(); + Edges = new List>(); - var result = new Dictionary - { - { "text", m.Groups[mainGroup].Value }, - { "x0", bbox.X0 }, - { "top", bbox.Y0 }, - { "x1", bbox.X1 }, - { "bottom", bbox.Y1 } - }; + TableHelpers.MakeChars(page, Chars); + TableHelpers.MakeEdges(page, Edges, Chars, Settings); - if (returnGroups) - { - var groups = new List(); - for (int i = 1; i < m.Groups.Count; i++) - groups.Add(m.Groups[i].Value); - result["groups"] = groups; - } + var resolvedEdges = GetEdges(); - if (returnChars) - result["chars"] = chars; + var intersections = TableHelpers.EdgesToIntersections( + resolvedEdges, + Settings.IntersectionXTolerance, + Settings.IntersectionYTolerance); - return result; - } - } + var cells = TableHelpers.IntersectionsToCells(intersections); - // WordMap class - maps words to chars - internal class WordMap - { - public List, List>> tuples { get; set; } + var cellGroups = TableHelpers.CellsToTables(page, cells, Chars); - public WordMap(List, List>> tuples) - { - this.tuples = tuples; + Tables = cellGroups.Select(g => new Table(page, g)).ToList(); } - public TextMap ToTextmap( - bool layout = false, - float layoutWidth = 0, - float layoutHeight = 0, - int layoutWidthChars = 0, - int layoutHeightChars = 0, - float xDensity = TableFlags.TABLE_DEFAULT_X_DENSITY, - float yDensity = TableFlags.TABLE_DEFAULT_Y_DENSITY, - float xShift = 0, - float yShift = 0, - float yTolerance = TableFlags.TABLE_DEFAULT_Y_TOLERANCE, - bool useTextFlow = false, - bool presorted = false, - bool expandLigatures = true) + /// + /// Index into the tables list. + /// + public Table this[int i] { - var textmap = new List>(); - - if (tuples.Count == 0) - return new TextMap(textmap); - - var expansions = expandLigatures ? TableConstants.LIGATURES : new Dictionary(); + get + { + int count = Tables.Count; + if (i >= count) + throw new IndexOutOfRangeException("table not on page"); + while (i < 0) + i += count; + return Tables[i]; + } + } - int layoutWidthCharsFinal = layoutWidthChars; - int layoutHeightCharsFinal = layoutHeightChars; + /// + /// Resolve and merge all edges for the current page according to the + /// active strategy settings (lines, lines_strict, text, or explicit). + /// + private List> GetEdges() + { + var settings = Settings; - if (layout) + foreach (var orientation in new[] { "vertical", "horizontal" }) { - if (layoutWidthChars > 0) - { - if (layoutWidth > 0) - throw new ArgumentException("`layout_width` and `layout_width_chars` cannot both be set."); - } - else - { - layoutWidthCharsFinal = (int)Math.Round(layoutWidth / xDensity); - } - - if (layoutHeightChars > 0) - { - if (layoutHeight > 0) - throw new ArgumentException("`layout_height` and `layout_height_chars` cannot both be set."); - } - else + string strategy = orientation == "vertical" + ? settings.VerticalStrategy : settings.HorizontalStrategy; + if (strategy == "explicit") { - layoutHeightCharsFinal = (int)Math.Round(layoutHeight / yDensity); + var lines = orientation == "vertical" + ? settings.ExplicitVerticalLines + : settings.ExplicitHorizontalLines; + if (lines == null || lines.Count < 2) + throw new ArgumentException( + $"If {orientation}_strategy == 'explicit', " + + $"explicit_{orientation}_lines must have at least 2 entries."); } } - var blankLine = layout ? Enumerable.Range(0, layoutWidthCharsFinal) - .Select(_ => Tuple.Create(" ", (CharDict)null)).ToList() : new List>(); - - int numNewlines = 0; + string vStrat = settings.VerticalStrategy; + string hStrat = settings.HorizontalStrategy; - var wordsSortedDoctop = presorted || useTextFlow - ? tuples - : tuples.OrderBy(t => Convert.ToSingle(t.Item1["doctop"])).ToList(); - - if (wordsSortedDoctop.Count == 0) - return new TextMap(textmap); - - var firstWord = wordsSortedDoctop[0].Item1; - float doctopStart = Convert.ToSingle(firstWord["doctop"]) - Convert.ToSingle(firstWord["top"]); + List> words; + if (vStrat == "text" || hStrat == "text") + words = TableHelpers.ExtractWords(Chars, settings.TextSettings); + else + words = new List>(); - var clusters = TableHelpers.ClusterObjects(wordsSortedDoctop, t => Convert.ToSingle(t.Item1["doctop"]), yTolerance); + var v = new List>(); - for (int i = 0; i < clusters.Count; i++) + if (settings.ExplicitVerticalLines != null) { - var ws = clusters[i]; - float yDist = layout - ? (Convert.ToSingle(ws[0].Item1["doctop"]) - (doctopStart + yShift)) / yDensity - : 0; - - int numNewlinesPrepend = Math.Max( - i > 0 ? 1 : 0, - (int)Math.Round(yDist) - numNewlines - ); - - for (int j = 0; j < numNewlinesPrepend; j++) + var pageRect = Page.Rect; + foreach (float desc in settings.ExplicitVerticalLines) { - if (textmap.Count == 0 || textmap[textmap.Count - 1].Item1 == "\n") - textmap.AddRange(blankLine); - textmap.Add(Tuple.Create("\n", (CharDict)null)); + v.Add(new Dictionary + { + ["x0"] = desc, + ["x1"] = desc, + ["top"] = (float)pageRect.Y0, + ["bottom"] = (float)pageRect.Y1, + ["height"] = (float)(pageRect.Y1 - pageRect.Y0), + ["orientation"] = "v", + }); } + } - numNewlines += numNewlinesPrepend; + if (vStrat == "lines") + v.AddRange(TableHelpers.FilterEdges(Edges, "v")); + else if (vStrat == "lines_strict") + v.AddRange(TableHelpers.FilterEdges(Edges, "v", edgeType: "line")); + else if (vStrat == "text") + v.AddRange(TableHelpers.WordsToEdgesV(words, (int)settings.MinWordsVertical)); - int lineLen = 0; - var lineWordsSortedX0 = presorted || useTextFlow - ? ws - : ws.OrderBy(t => Convert.ToSingle(t.Item1["x0"])).ToList(); + var h = new List>(); - foreach (Tuple, List> tuple in lineWordsSortedX0) + if (settings.ExplicitHorizontalLines != null) + { + var pageRect = Page.Rect; + foreach (float desc in settings.ExplicitHorizontalLines) { - var word = tuple.Item1; - var chars = tuple.Item2; - float xDist = layout ? (Convert.ToSingle(word["x0"]) - xShift) / xDensity : 0; - int numSpacesPrepend = Math.Max(Math.Min(1, lineLen), (int)Math.Round(xDist) - lineLen); - - for (int k = 0; k < numSpacesPrepend; k++) - textmap.Add(Tuple.Create(" ", (CharDict)null)); - lineLen += numSpacesPrepend; - - foreach (var c in chars) + h.Add(new Dictionary { - string letters = expansions.ContainsKey(c.text) ? expansions[c.text] : c.text; - foreach (char letter in letters) - { - textmap.Add(Tuple.Create(letter.ToString(), c)); - lineLen++; - } - } - } - - if (layout) - { - for (int k = 0; k < layoutWidthCharsFinal - lineLen; k++) - textmap.Add(Tuple.Create(" ", (CharDict)null)); + ["x0"] = (float)pageRect.X0, + ["x1"] = (float)pageRect.X1, + ["width"] = (float)(pageRect.X1 - pageRect.X0), + ["top"] = desc, + ["bottom"] = desc, + ["orientation"] = "h", + }); } } - if (layout) - { - int numNewlinesAppend = layoutHeightCharsFinal - (numNewlines + 1); - for (int i = 0; i < numNewlinesAppend; i++) - { - if (i > 0) - textmap.AddRange(blankLine); - textmap.Add(Tuple.Create("\n", (CharDict)null)); - } + if (hStrat == "lines") + h.AddRange(TableHelpers.FilterEdges(Edges, "h")); + else if (hStrat == "lines_strict") + h.AddRange(TableHelpers.FilterEdges(Edges, "h", edgeType: "line")); + else if (hStrat == "text") + h.AddRange(TableHelpers.WordsToEdgesH(words, (int)settings.MinWordsHorizontal)); - if (textmap.Count > 0 && textmap[textmap.Count - 1].Item1 == "\n") - textmap.RemoveAt(textmap.Count - 1); - } + var edges = new List>(); + edges.AddRange(v); + edges.AddRange(h); + + edges = TableHelpers.MergeEdges( + edges, + settings.SnapXTolerance, + settings.SnapYTolerance, + settings.JoinXTolerance, + settings.JoinYTolerance); - return new TextMap(textmap); + return TableHelpers.FilterEdges(edges, minLength: settings.EdgeMinLength); } } - // WordExtractor class - internal class WordExtractor + // ──────────────────────────────────────────────────────────────────── + // TableHelpers – static helper functions + // ──────────────────────────────────────────────────────────────────── + + /// + /// Static helper methods implementing the pdfplumber-style table-detection + /// algorithms: edge snapping, intersection detection, cell enumeration, + /// text extraction, etc. + /// + public static class TableHelpers { - public float x_tolerance { get; set; } - public float y_tolerance { get; set; } - public bool keep_blank_chars { get; set; } - public bool use_text_flow { get; set; } - public bool horizontal_ltr { get; set; } - public bool vertical_ttb { get; set; } - public List extra_attrs { get; set; } - public string split_at_punctuation { get; set; } - public Dictionary expansions { get; set; } - - public WordExtractor( - float x_tolerance = TableFlags.TABLE_DEFAULT_X_TOLERANCE, - float y_tolerance = TableFlags.TABLE_DEFAULT_Y_TOLERANCE, - bool keep_blank_chars = false, - bool use_text_flow = false, - bool horizontal_ltr = true, - bool vertical_ttb = false, - List extra_attrs = null, - bool split_at_punctuation = false, - bool expand_ligatures = true) + // ── Geometry primitives ───────────────────────────────────────── + + /// + /// Check whether rectangle is fully inside + /// rectangle . + /// + public static bool RectInRect( + (float x0, float y0, float x1, float y1) inner, + (float x0, float y0, float x1, float y1) outer) { - this.x_tolerance = x_tolerance; - this.y_tolerance = y_tolerance; - this.keep_blank_chars = keep_blank_chars; - this.use_text_flow = use_text_flow; - this.horizontal_ltr = horizontal_ltr; - this.vertical_ttb = vertical_ttb; - this.extra_attrs = extra_attrs ?? new List(); - this.split_at_punctuation = split_at_punctuation - ? "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" - : ""; - this.expansions = expand_ligatures ? TableConstants.LIGATURES : new Dictionary(); + return inner.x0 >= outer.x0 && inner.y0 >= outer.y0 + && inner.x1 <= outer.x1 && inner.y1 <= outer.y1; } - public Dictionary MergeChars(List orderedChars) + /// + /// Check whether any character in lies inside + /// rectangle . + /// + public static bool CharsInRect( + List> chars, + (float x0, float y0, float x1, float y1) rect) + { + return chars.Any(c => + rect.x0 <= F(c, "x0") && F(c, "x1") <= rect.x1 && + rect.y0 <= F(c, "y0") && rect.y1 >= F(c, "y1")); + } + + /// + /// Compute Intersection over Union (IoU) of two rectangles. + /// + public static float Iou( + (float x0, float y0, float x1, float y1) r1, + (float x0, float y0, float x1, float y1) r2) { - var bbox = TableHelpers.ObjectsToBbox(orderedChars); - float doctopAdj = orderedChars[0].doctop - orderedChars[0].top; - bool upright = orderedChars[0].upright; - int direction = (upright ? horizontal_ltr : vertical_ttb) ? 1 : -1; + float ix = Math.Max(0, Math.Min(r1.x1, r2.x1) - Math.Max(r1.x0, r2.x0)); + float iy = Math.Max(0, Math.Min(r1.y1, r2.y1) - Math.Max(r1.y0, r2.y0)); + float intersection = ix * iy; + if (intersection == 0) return 0; + float area1 = (r1.x1 - r1.x0) * (r1.y1 - r1.y0); + float area2 = (r2.x1 - r2.x0) * (r2.y1 - r2.y0); + return intersection / (area1 + area2 - intersection); + } - var matrix = orderedChars[0].matrix; - int rotation = 0; + // ── Edge conversion helpers ───────────────────────────────────── - if (!upright && matrix.Item2 < 0) - { - orderedChars = orderedChars.AsEnumerable().Reverse().ToList(); - rotation = 270; - } + /// + /// Convert a line dictionary to an edge dictionary by adding an + /// orientation key. + /// + public static Dictionary LineToEdge(Dictionary line) + { + var edge = new Dictionary(line); + edge["orientation"] = F(line, "top") == F(line, "bottom") ? "h" : "v"; + return edge; + } + + /// + /// Decompose a rectangle dictionary into its four constituent edges. + /// + public static List> RectToEdges(Dictionary rect) + { + var result = new List>(); + + var top = new Dictionary(rect); + top["object_type"] = "rect_edge"; + top["height"] = 0f; + top["y0"] = F(rect, "y1"); + top["bottom"] = F(rect, "top"); + top["orientation"] = "h"; + + var bottom = new Dictionary(rect); + bottom["object_type"] = "rect_edge"; + bottom["height"] = 0f; + bottom["y1"] = F(rect, "y0"); + bottom["top"] = F(rect, "top") + F(rect, "height"); + bottom["doctop"] = F(rect, "doctop") + F(rect, "height"); + bottom["orientation"] = "h"; + + var left = new Dictionary(rect); + left["object_type"] = "rect_edge"; + left["width"] = 0f; + left["x1"] = F(rect, "x0"); + left["orientation"] = "v"; + + var right = new Dictionary(rect); + right["object_type"] = "rect_edge"; + right["width"] = 0f; + right["x0"] = F(rect, "x1"); + right["orientation"] = "v"; + + result.Add(top); + result.Add(bottom); + result.Add(left); + result.Add(right); + return result; + } + + /// + /// Convert any object (line, rect, curve_edge) to a list of edges. + /// + public static List> ObjToEdges(Dictionary obj) + { + string t = (string)obj["object_type"]; + if (t.Contains("_edge")) + return new List> { obj }; + if (t == "line") + return new List> { LineToEdge(obj) }; + if (t == "rect") + return RectToEdges(obj); + return new List>(); + } - if (matrix.Item1 < 0 && matrix.Item4 < 0) - rotation = 180; - else if (matrix.Item2 > 0) - rotation = 90; + // ── Filtering ─────────────────────────────────────────────────── + + /// + /// Filter edges by orientation, type, and minimum length. + /// + public static List> FilterEdges( + List> edges, + string orientation = null, + string edgeType = null, + float minLength = 1) + { + if (orientation != null && orientation != "v" && orientation != "h") + throw new ArgumentException("Orientation must be 'v' or 'h'"); - var word = new Dictionary + return edges.Where(e => { - { "text", string.Join("", orderedChars.Select(c => expansions.ContainsKey(c.text) ? expansions[c.text] : c.text)) }, - { "x0", bbox.X0 }, - { "x1", bbox.X1 }, - { "top", bbox.Y0 }, - { "doctop", bbox.Y0 + doctopAdj }, - { "bottom", bbox.Y1 }, - { "upright", upright }, - { "direction", direction }, - { "rotation", rotation } - }; + string dim = (string)e["orientation"] == "v" ? "height" : "width"; + bool etOk = edgeType == null || (string)e["object_type"] == edgeType; + bool orientOk = orientation == null || (string)e["orientation"] == orientation; + return etOk && orientOk && F(e, dim) >= minLength; + }).ToList(); + } + + // ── Clustering ────────────────────────────────────────────────── + + /// + /// Cluster a list of float values so that consecutive values within + /// of each other are in the same cluster. + /// + public static List> ClusterList(List xs, float tolerance = 0) + { + if (xs.Count < 2) + return xs.Select(x => new List { x }).ToList(); - foreach (var key in extra_attrs) + var sorted = xs.OrderBy(x => x).ToList(); + var groups = new List>(); + var current = new List { sorted[0] }; + float last = sorted[0]; + + for (int i = 1; i < sorted.Count; i++) { - if (orderedChars.Count > 0) + if (sorted[i] <= last + tolerance) + current.Add(sorted[i]); + else { - var prop = typeof(CharDict).GetProperty(key); - if (prop != null) - word[key] = prop.GetValue(orderedChars[0]); + groups.Add(current); + current = new List { sorted[i] }; } + last = sorted[i]; } - - return word; + groups.Add(current); + return groups; } - public bool CharBeginsNewWord(CharDict prevChar, CharDict currChar) + /// + /// Build a mapping from value → cluster index. + /// + internal static Dictionary MakeClusterDict( + IEnumerable values, float tolerance) { - if (currChar.upright) - { - float x = x_tolerance; - float y = y_tolerance; - float ay = prevChar.top; - float cy = currChar.top; - float ax, bx, cx; + var clusters = ClusterList( + new List(new HashSet(values)), tolerance); + var dict = new Dictionary(); + for (int i = 0; i < clusters.Count; i++) + foreach (float v in clusters[i]) + dict[v] = i; + return dict; + } - if (horizontal_ltr) - { - ax = prevChar.x0; - bx = prevChar.x1; - cx = currChar.x0; - } - else - { - ax = -prevChar.x1; - bx = -prevChar.x0; - cx = -currChar.x1; - } + /// + /// Group objects by a float key, clustering keys within + /// . + /// + internal static List> ClusterObjects( + List xs, Func keyFn, float tolerance) + { + var values = xs.Select(keyFn); + var clusterDict = MakeClusterDict(values, tolerance); + var tuples = xs.Select(x => (obj: x, cluster: clusterDict[keyFn(x)])) + .OrderBy(t => t.cluster) + .ToList(); + var result = new List>(); + foreach (var grp in tuples.GroupBy(t => t.cluster)) + result.Add(grp.Select(t => t.obj).ToList()); + return result; + } + + // ── Edge snapping / merging ───────────────────────────────────── - return (cx < ax) || (cx > bx + x) || (cy > ay + y); + private static Dictionary MoveObject( + Dictionary obj, string axis, float value) + { + var n = new Dictionary(obj); + if (axis == "h") + { + n["x0"] = F(obj, "x0") + value; + n["x1"] = F(obj, "x1") + value; } else { - float x = y_tolerance; - float y = x_tolerance; - float ay = prevChar.x0; - float cy = currChar.x0; - float ax, bx, cx; - - if (vertical_ttb) - { - ax = prevChar.top; - bx = prevChar.bottom; - cx = currChar.top; - } - else + n["top"] = F(obj, "top") + value; + n["bottom"] = F(obj, "bottom") + value; + if (obj.ContainsKey("doctop")) + n["doctop"] = F(obj, "doctop") + value; + if (obj.ContainsKey("y0")) { - ax = -prevChar.bottom; - bx = -prevChar.top; - cx = -currChar.bottom; + n["y0"] = F(obj, "y0") - value; + n["y1"] = F(obj, "y1") - value; } - - return (cx < ax) || (cx > bx + x) || (cy > ay + y); } + return n; } - public IEnumerable> IterCharsToWords(IEnumerable orderedChars) + private static List> SnapObjects( + List> objs, string attr, float tolerance) { - var currentWord = new List(); - - foreach (var char_ in orderedChars) + string axis = (attr == "x0" || attr == "x1") ? "h" : "v"; + var clusters = ClusterObjects(objs, o => F(o, attr), tolerance); + var snapped = new List>(); + foreach (var cluster in clusters) { - string text = char_.text; - - if (!keep_blank_chars && string.IsNullOrWhiteSpace(text)) - { - if (currentWord.Count > 0) - { - yield return currentWord; - currentWord = new List(); - } - } - else if (split_at_punctuation.Contains(text)) - { - currentWord.Add(char_); - yield return currentWord; - currentWord = new List(); - } - else if (currentWord.Count > 0 && CharBeginsNewWord(currentWord[currentWord.Count - 1], char_)) - { - yield return currentWord; - currentWord = new List { char_ }; - } - else - { - currentWord.Add(char_); - } + float avg = cluster.Sum(o => F(o, attr)) / cluster.Count; + foreach (var obj in cluster) + snapped.Add(MoveObject(obj, axis, avg - F(obj, attr))); } - - if (currentWord.Count > 0) - yield return currentWord; + return snapped; } - public IEnumerable IterSortChars(IEnumerable chars) + /// + /// Snap edges within tolerance of each other to their positional average. + /// + public static List> SnapEdges( + List> edges, + float xTolerance = TableConstants.DefaultSnapTolerance, + float yTolerance = TableConstants.DefaultSnapTolerance) { - var charsList = chars.ToList(); - var uprightClusters = TableHelpers.ClusterObjects(charsList, c => c.upright ? -1 : 0, 0); - - foreach (var uprightCluster in uprightClusters) + var byOrientation = new Dictionary>> { - bool upright = uprightCluster[0].upright; - string clusterKey = upright ? "doctop" : "x0"; - - var subclusters = TableHelpers.ClusterObjects(uprightCluster, c => GetCharValue(c, clusterKey), y_tolerance); - - foreach (var sc in subclusters) - { - string sortKey = upright ? "x0" : "doctop"; - var toYield = sc.OrderBy(c => GetCharValue(c, sortKey)).ToList(); - - if (!(upright ? horizontal_ltr : vertical_ttb)) - toYield.Reverse(); + ["v"] = new List>(), + ["h"] = new List>(), + }; + foreach (var e in edges) + byOrientation[(string)e["orientation"]].Add(e); - foreach (var c in toYield) - yield return c; - } - } + var snappedV = SnapObjects(byOrientation["v"], "x0", xTolerance); + var snappedH = SnapObjects(byOrientation["h"], "top", yTolerance); + var result = new List>(snappedV); + result.AddRange(snappedH); + return result; } - private float GetCharValue(CharDict c, string key) + private static Dictionary ResizeObject( + Dictionary obj, string key, float value) { + var n = new Dictionary(obj); + float old = F(obj, key); + float diff = value - old; + n[key] = value; switch (key) { case "x0": - return c.x0; - case "doctop": - return c.doctop; + n["width"] = F(obj, "x1") - value; + break; + case "x1": + n["width"] = value - F(obj, "x0"); + break; case "top": - return c.top; - default: - return 0; + n["doctop"] = F(obj, "doctop") + diff; + n["height"] = F(obj, "height") - diff; + if (obj.ContainsKey("y1")) + n["y1"] = F(obj, "y1") - diff; + break; + case "bottom": + n["height"] = F(obj, "height") + diff; + if (obj.ContainsKey("y0")) + n["y0"] = F(obj, "y0") - diff; + break; } + return n; } - public IEnumerable, List>> IterExtractTuples(IEnumerable chars) + private static List> JoinEdgeGroup( + List> edges, string orientation, + float tolerance = TableConstants.DefaultJoinTolerance) { - var orderedChars = use_text_flow ? chars : IterSortChars(chars); - var groupedChars = orderedChars.GroupBy(c => new { c.upright }); + string minProp, maxProp; + if (orientation == "h") + { minProp = "x0"; maxProp = "x1"; } + else + { minProp = "top"; maxProp = "bottom"; } - foreach (var group in groupedChars) + var sorted = edges.OrderBy(e => F(e, minProp)).ToList(); + var joined = new List> { sorted[0] }; + for (int i = 1; i < sorted.Count; i++) { - foreach (var wordChars in IterCharsToWords(group)) + var e = sorted[i]; + var last = joined[joined.Count - 1]; + if (F(e, minProp) <= F(last, maxProp) + tolerance) + { + if (F(e, maxProp) > F(last, maxProp)) + joined[joined.Count - 1] = ResizeObject(last, maxProp, F(e, maxProp)); + } + else { - yield return Tuple.Create(MergeChars(wordChars), wordChars); + joined.Add(e); } } + return joined; } - public WordMap ExtractWordmap(IEnumerable chars) - { - return new WordMap(IterExtractTuples(chars).ToList()); - } - - public List> ExtractWords(IEnumerable chars) - { - return IterExtractTuples(chars).Select(t => t.Item1).ToList(); - } - } - - // Helper functions for text extraction - internal static class TextExtractionHelpers - { - internal static List> ExtractWords(List chars, Dictionary kwargs = null) - { - if (kwargs == null) kwargs = new Dictionary(); - var extractor = new WordExtractor( - x_tolerance: kwargs.ContainsKey("x_tolerance") ? Convert.ToSingle(kwargs["x_tolerance"]) : TableFlags.TABLE_DEFAULT_X_TOLERANCE, - y_tolerance: kwargs.ContainsKey("y_tolerance") ? Convert.ToSingle(kwargs["y_tolerance"]) : TableFlags.TABLE_DEFAULT_Y_TOLERANCE, - keep_blank_chars: kwargs.ContainsKey("keep_blank_chars") && (bool)kwargs["keep_blank_chars"], - use_text_flow: kwargs.ContainsKey("use_text_flow") && (bool)kwargs["use_text_flow"], - horizontal_ltr: !kwargs.ContainsKey("horizontal_ltr") || (bool)kwargs["horizontal_ltr"], - vertical_ttb: kwargs.ContainsKey("vertical_ttb") && (bool)kwargs["vertical_ttb"], - split_at_punctuation: kwargs.ContainsKey("split_at_punctuation") && (bool)kwargs["split_at_punctuation"], - expand_ligatures: !kwargs.ContainsKey("expand_ligatures") || (bool)kwargs["expand_ligatures"] - ); - return extractor.ExtractWords(chars); - } - - internal static TextMap CharsToTextmap(List chars, Dictionary kwargs = null) - { - if (kwargs == null) kwargs = new Dictionary(); - kwargs["presorted"] = true; - - var extractor = new WordExtractor( - x_tolerance: kwargs.ContainsKey("x_tolerance") ? Convert.ToSingle(kwargs["x_tolerance"]) : TableFlags.TABLE_DEFAULT_X_TOLERANCE, - y_tolerance: kwargs.ContainsKey("y_tolerance") ? Convert.ToSingle(kwargs["y_tolerance"]) : TableFlags.TABLE_DEFAULT_Y_TOLERANCE, - keep_blank_chars: kwargs.ContainsKey("keep_blank_chars") && (bool)kwargs["keep_blank_chars"], - use_text_flow: kwargs.ContainsKey("use_text_flow") && (bool)kwargs["use_text_flow"], - expand_ligatures: !kwargs.ContainsKey("expand_ligatures") || (bool)kwargs["expand_ligatures"] - ); - - var wordmap = extractor.ExtractWordmap(chars); - return wordmap.ToTextmap( - layout: kwargs.ContainsKey("layout") && (bool)kwargs["layout"], - layoutWidth: kwargs.ContainsKey("layout_width") ? Convert.ToSingle(kwargs["layout_width"]) : 0, - layoutHeight: kwargs.ContainsKey("layout_height") ? Convert.ToSingle(kwargs["layout_height"]) : 0, - layoutWidthChars: kwargs.ContainsKey("layout_width_chars") ? Convert.ToInt32(kwargs["layout_width_chars"]) : 0, - layoutHeightChars: kwargs.ContainsKey("layout_height_chars") ? Convert.ToInt32(kwargs["layout_height_chars"]) : 0, - xDensity: kwargs.ContainsKey("x_density") ? Convert.ToSingle(kwargs["x_density"]) : TableFlags.TABLE_DEFAULT_X_DENSITY, - yDensity: kwargs.ContainsKey("y_density") ? Convert.ToSingle(kwargs["y_density"]) : TableFlags.TABLE_DEFAULT_Y_DENSITY, - xShift: kwargs.ContainsKey("x_shift") ? Convert.ToSingle(kwargs["x_shift"]) : 0, - yShift: kwargs.ContainsKey("y_shift") ? Convert.ToSingle(kwargs["y_shift"]) : 0, - yTolerance: kwargs.ContainsKey("y_tolerance") ? Convert.ToSingle(kwargs["y_tolerance"]) : TableFlags.TABLE_DEFAULT_Y_TOLERANCE, - useTextFlow: kwargs.ContainsKey("use_text_flow") && (bool)kwargs["use_text_flow"], - presorted: kwargs.ContainsKey("presorted") && (bool)kwargs["presorted"], - expandLigatures: !kwargs.ContainsKey("expand_ligatures") || (bool)kwargs["expand_ligatures"] - ); - } - - internal static string ExtractText(List chars, Dictionary kwargs = null) + /// + /// Snap then join a list of edges into a more seamless set. + /// + public static List> MergeEdges( + List> edges, + float snapXTolerance, + float snapYTolerance, + float joinXTolerance, + float joinYTolerance) { - if (kwargs == null) kwargs = new Dictionary(); - var charsList = TableHelpers.ToList(chars); - if (charsList.Count == 0) - return ""; - - if (kwargs.ContainsKey("layout") && (bool)kwargs["layout"]) - return CharsToTextmap(charsList, kwargs).as_string; - - float yTolerance = kwargs.ContainsKey("y_tolerance") ? Convert.ToSingle(kwargs["y_tolerance"]) : TableFlags.TABLE_DEFAULT_Y_TOLERANCE; - var extractor = new WordExtractor( - x_tolerance: kwargs.ContainsKey("x_tolerance") ? Convert.ToSingle(kwargs["x_tolerance"]) : TableFlags.TABLE_DEFAULT_X_TOLERANCE, - y_tolerance: yTolerance, - keep_blank_chars: kwargs.ContainsKey("keep_blank_chars") && (bool)kwargs["keep_blank_chars"], - use_text_flow: kwargs.ContainsKey("use_text_flow") && (bool)kwargs["use_text_flow"], - expand_ligatures: !kwargs.ContainsKey("expand_ligatures") || (bool)kwargs["expand_ligatures"] - ); - - var words = extractor.ExtractWords(charsList); - if (words.Count == 0) - return ""; + if (edges.Count == 0) return edges; - int rotation = words[0].ContainsKey("rotation") ? Convert.ToInt32(words[0]["rotation"]) : 0; + if (snapXTolerance > 0 || snapYTolerance > 0) + edges = SnapEdges(edges, snapXTolerance, snapYTolerance); - if (rotation == 90) + Func, (string, float)> getGroup = edge => { - words = words.OrderBy(w => Convert.ToSingle(w["x1"])).ThenByDescending(w => Convert.ToSingle(w["top"])).ToList(); - return string.Join(" ", words.Select(w => w["text"].ToString())); - } - else if (rotation == 270) - { - words = words.OrderByDescending(w => Convert.ToSingle(w["x1"])).ThenBy(w => Convert.ToSingle(w["top"])).ToList(); - return string.Join(" ", words.Select(w => w["text"].ToString())); - } - else - { - var lines = TableHelpers.ClusterObjects(words, w => Convert.ToSingle(w["doctop"]), yTolerance); - var result = string.Join("\n", lines.Select(line => string.Join(" ", line.Select(w => w["text"].ToString())))); - if (rotation == 180) - { - var charArray = result.ToCharArray(); - Array.Reverse(charArray); - return new string(charArray.Select(c => c == '\n' ? ' ' : c).ToArray()); - } - return result; - } - } + string o = (string)edge["orientation"]; + return o == "h" ? ("h", F(edge, "top")) : ("v", F(edge, "x0")); + }; - internal static string CollateLine(List lineChars, float tolerance = TableFlags.TABLE_DEFAULT_X_TOLERANCE) - { - var coll = new StringBuilder(); - float? lastX1 = null; - foreach (var char_ in lineChars.OrderBy(c => c.x0)) + var sorted = edges.OrderBy(e => getGroup(e)).ToList(); + var result = new List>(); + foreach (var grp in sorted.GroupBy(e => getGroup(e))) { - if (lastX1.HasValue && char_.x0 > (lastX1.Value + tolerance)) - coll.Append(" "); - lastX1 = char_.x1; - coll.Append(char_.text); + float tol = grp.Key.Item1 == "h" ? joinXTolerance : joinYTolerance; + result.AddRange(JoinEdgeGroup(grp.ToList(), grp.Key.Item1, tol)); } - return coll.ToString(); + return result; } - internal static List DedupeChars(List chars, float tolerance = 1) + // ── Intersection detection ────────────────────────────────────── + + /// + /// Given a list of edges, find all intersection points (within tolerance). + /// Returns a dictionary keyed by (x, y) vertex with lists of + /// vertical and horizontal edges that meet there. + /// + public static Dictionary<(float x, float y), Dictionary>>> + EdgesToIntersections( + List> edges, + float xTolerance = 1, + float yTolerance = 1) { - var key = new Func(c => new { c.fontname, c.size, c.upright, c.text }); - var posKey = new Func(c => new { c.doctop, c.x0 }); + var intersections = new Dictionary<(float, float), + Dictionary>>>(); - var sortedChars = chars.OrderBy(key).ToList(); - var uniqueChars = new List(); + var vEdges = edges.Where(e => (string)e["orientation"] == "v") + .OrderBy(e => F(e, "x0")).ThenBy(e => F(e, "top")).ToList(); + var hEdges = edges.Where(e => (string)e["orientation"] == "h") + .OrderBy(e => F(e, "top")).ThenBy(e => F(e, "x0")).ToList(); - foreach (var group in sortedChars.GroupBy(key)) + foreach (var v in vEdges) { - var yClusters = TableHelpers.ClusterObjects(group.ToList(), c => c.doctop, tolerance); - foreach (var yCluster in yClusters) + foreach (var h in hEdges) { - var xClusters = TableHelpers.ClusterObjects(yCluster, c => c.x0, tolerance); - foreach (var xCluster in xClusters) + if (F(v, "top") <= F(h, "top") + yTolerance + && F(v, "bottom") >= F(h, "top") - yTolerance + && F(v, "x0") >= F(h, "x0") - xTolerance + && F(v, "x0") <= F(h, "x1") + xTolerance) { - uniqueChars.Add(xCluster.OrderBy(c => posKey(c)).First()); + var vertex = (F(v, "x0"), F(h, "top")); + if (!intersections.ContainsKey(vertex)) + intersections[vertex] = new Dictionary>> + { + ["v"] = new List>(), + ["h"] = new List>(), + }; + intersections[vertex]["v"].Add(v); + intersections[vertex]["h"].Add(h); } } } - - return uniqueChars.OrderBy(c => chars.IndexOf(c)).ToList(); + return intersections; } - } - - // Edge processing functions - internal static class EdgeProcessing - { - // line_to_edge - Convert line to edge - internal static Edge LineToEdge(Dictionary line) - { - var edge = new Edge - { - x0 = Convert.ToSingle(line["x0"]), - x1 = Convert.ToSingle(line["x1"]), - top = Convert.ToSingle(line["top"]), - bottom = Convert.ToSingle(line["bottom"]), - width = line.ContainsKey("width") ? Convert.ToSingle(line["width"]) : 0, - height = line.ContainsKey("height") ? Convert.ToSingle(line["height"]) : 0, - orientation = Convert.ToSingle(line["top"]) == Convert.ToSingle(line["bottom"]) ? "h" : "v", - object_type = line.ContainsKey("object_type") ? line["object_type"].ToString() : "line", - doctop = line.ContainsKey("doctop") ? Convert.ToSingle(line["doctop"]) : 0, - page_number = line.ContainsKey("page_number") ? Convert.ToInt32(line["page_number"]) : 0, - y0 = line.ContainsKey("y0") ? Convert.ToSingle(line["y0"]) : 0, - y1 = line.ContainsKey("y1") ? Convert.ToSingle(line["y1"]) : 0 - }; - return edge; - } + // ── Cell enumeration ──────────────────────────────────────────── - // rect_to_edges - Convert rectangle to 4 edges - internal static List RectToEdges(Dictionary rect) + private static (float, float, float, float) ObjToBbox(Dictionary obj) { - var edges = new List(); - float x0 = Convert.ToSingle(rect["x0"]); - float top = Convert.ToSingle(rect["top"]); - float x1 = Convert.ToSingle(rect["x1"]); - float bottom = Convert.ToSingle(rect["bottom"]); - float width = x1 - x0; - float height = bottom - top; - float doctop = rect.ContainsKey("doctop") ? Convert.ToSingle(rect["doctop"]) : top; - - // Top edge - edges.Add(new Edge - { - x0 = x0, - x1 = x1, - top = bottom, - bottom = top, - width = width, - height = 0, - orientation = "h", - object_type = "rect_edge", - doctop = doctop, - y0 = bottom, - y1 = top - }); - - // Bottom edge - edges.Add(new Edge - { - x0 = x0, - x1 = x1, - top = top + height, - bottom = top + height, - width = width, - height = 0, - orientation = "h", - object_type = "rect_edge", - doctop = doctop + height, - y0 = top + height, - y1 = top + height - }); - - // Left edge - edges.Add(new Edge - { - x0 = x0, - x1 = x0, - top = top, - bottom = bottom, - width = 0, - height = height, - orientation = "v", - object_type = "rect_edge", - doctop = doctop, - y0 = bottom, - y1 = top - }); - - // Right edge - edges.Add(new Edge - { - x0 = x1, - x1 = x1, - top = top, - bottom = bottom, - width = 0, - height = height, - orientation = "v", - object_type = "rect_edge", - doctop = doctop, - y0 = bottom, - y1 = top - }); - - return edges; + return (F(obj, "x0"), F(obj, "top"), F(obj, "x1"), F(obj, "bottom")); } - // curve_to_edges - Convert curve to edges - internal static List CurveToEdges(Dictionary curve) + /// + /// Given intersection points, enumerate all rectangular cells formed + /// by connected edges. + /// + public static List<(float x0, float y0, float x1, float y1)> IntersectionsToCells( + Dictionary<(float x, float y), Dictionary>>> intersections) { - var edges = new List(); - var pts = curve["pts"] as List; - if (pts == null) return edges; - - float doctop = curve.ContainsKey("doctop") ? Convert.ToSingle(curve["doctop"]) : 0; - float top = curve.ContainsKey("top") ? Convert.ToSingle(curve["top"]) : 0; - - for (int i = 0; i < pts.Count - 1; i++) + bool EdgeConnects((float x, float y) p1, (float x, float y) p2) { - var p0Obj = pts[i] as List; - var p1Obj = pts[i + 1] as List; - if (p0Obj == null || p1Obj == null || p0Obj.Count < 2 || p1Obj.Count < 2) - continue; - - float p0x = Convert.ToSingle(p0Obj[0]); - float p0y = Convert.ToSingle(p0Obj[1]); - float p1x = Convert.ToSingle(p1Obj[0]); - float p1y = Convert.ToSingle(p1Obj[1]); - - string orientation = null; - if (p0x == p1x) - orientation = "v"; - else if (p0y == p1y) - orientation = "h"; + HashSet<(float, float, float, float)> EdgesToSet(List> el) + => new HashSet<(float, float, float, float)>(el.Select(ObjToBbox)); - if (orientation == null) continue; - - edges.Add(new Edge + if (p1.x == p2.x) { - x0 = Math.Min(p0x, p1x), - x1 = Math.Max(p0x, p1x), - top = Math.Min(p0y, p1y), - bottom = Math.Max(p0y, p1y), - width = Math.Abs(p0x - p1x), - height = Math.Abs(p0y - p1y), - orientation = orientation, - object_type = "curve_edge", - doctop = Math.Min(p0y, p1y) + (doctop - top), - y0 = Math.Max(p0y, p1y), - y1 = Math.Min(p0y, p1y) - }); + var common = EdgesToSet(intersections[p1]["v"]) + .Intersect(EdgesToSet(intersections[p2]["v"])); + if (common.Any()) return true; + } + if (p1.y == p2.y) + { + var common = EdgesToSet(intersections[p1]["h"]) + .Intersect(EdgesToSet(intersections[p2]["h"])); + if (common.Any()) return true; + } + return false; } - return edges; - } - - // obj_to_edges - Convert object to edges - internal static List ObjToEdges(Dictionary obj) - { - string objType = obj.ContainsKey("object_type") ? obj["object_type"].ToString() : ""; - - if (objType.Contains("_edge")) - return new List { LineToEdge(obj) }; - else if (objType == "line") - return new List { LineToEdge(obj) }; - else if (objType == "rect") - return RectToEdges(obj); - else if (objType == "curve") - return CurveToEdges(obj); - - return new List(); - } - - // filter_edges - Filter edges by orientation, type, and min length - internal static List FilterEdges( - List edges, - string orientation = null, - string edgeType = null, - float minLength = 1) - { - if (orientation != null && orientation != "v" && orientation != "h") - throw new ArgumentException("Orientation must be 'v' or 'h'"); + var points = intersections.Keys.OrderBy(p => p.x).ThenBy(p => p.y).ToList(); + int n = points.Count; + var cells = new List<(float x0, float y0, float x1, float y1)>(); - return edges.Where(e => + for (int i = 0; i < n; i++) { - string dim = e.orientation == "v" ? "height" : "width"; - float dimValue = e.orientation == "v" ? e.height : e.width; - bool etCorrect = edgeType == null || e.object_type == edgeType; - bool orientCorrect = orientation == null || e.orientation == orientation; - return etCorrect && orientCorrect && dimValue >= minLength; - }).ToList(); - } + var pt = points[i]; + var rest = points.Skip(i + 1).ToList(); + var below = rest.Where(p => p.x == pt.x && p.y > pt.y) + .OrderBy(p => p.y).ToList(); + var right = rest.Where(p => p.y == pt.y && p.x > pt.x) + .OrderBy(p => p.x).ToList(); - // snap_objects - Snap objects to their average position - internal static List> SnapObjects( - IEnumerable> objs, - string attr, - float tolerance) - { - string axis = attr == "x0" || attr == "x1" ? "h" : "v"; - var objsList = objs.ToList(); - var clusters = TableHelpers.ClusterObjects(objsList, obj => Convert.ToSingle(obj[attr]), tolerance); - var avgs = clusters.Select(cluster => cluster.Average(obj => Convert.ToSingle(obj[attr]))).ToList(); - - var snappedClusters = new List>>(); - for (int i = 0; i < clusters.Count; i++) - { - float avg = avgs[i]; - var snapped = clusters[i].Select(obj => + foreach (var belowPt in below) { - var newObj = new Dictionary(obj); - float oldValue = Convert.ToSingle(obj[attr]); - float diff = avg - oldValue; - - if (axis == "h") - { - newObj["x0"] = Convert.ToSingle(obj["x0"]) + diff; - newObj["x1"] = Convert.ToSingle(obj["x1"]) + diff; - } - else + if (!EdgeConnects(pt, belowPt)) continue; + foreach (var rightPt in right) { - newObj["top"] = Convert.ToSingle(obj["top"]) + diff; - newObj["bottom"] = Convert.ToSingle(obj["bottom"]) + diff; - if (obj.ContainsKey("doctop")) - newObj["doctop"] = Convert.ToSingle(obj["doctop"]) + diff; - if (obj.ContainsKey("y0")) - newObj["y0"] = Convert.ToSingle(obj["y0"]) - diff; - if (obj.ContainsKey("y1")) - newObj["y1"] = Convert.ToSingle(obj["y1"]) - diff; + if (!EdgeConnects(pt, rightPt)) continue; + var bottomRight = (rightPt.x, belowPt.y); + if (intersections.ContainsKey(bottomRight) + && EdgeConnects(bottomRight, rightPt) + && EdgeConnects(bottomRight, belowPt)) + { + cells.Add((pt.x, pt.y, bottomRight.x, bottomRight.y)); + goto nextBelow; + } } - return newObj; - }).ToList(); - snappedClusters.Add(snapped); + nextBelow:; + } } - - return snappedClusters.SelectMany(x => x).ToList(); + return cells; } - // snap_edges - Snap edges within tolerance - internal static List SnapEdges( - List edges, - float xTolerance = TableFlags.TABLE_DEFAULT_SNAP_TOLERANCE, - float yTolerance = TableFlags.TABLE_DEFAULT_SNAP_TOLERANCE) - { - var byOrientation = new Dictionary> - { - { "v", new List() }, - { "h", new List() } - }; - - foreach (var e in edges) - byOrientation[e.orientation].Add(e); - - var snappedV = SnapEdgesByOrientation(byOrientation["v"], "x0", xTolerance); - var snappedH = SnapEdgesByOrientation(byOrientation["h"], "top", yTolerance); - - return snappedV.Concat(snappedH).ToList(); - } + // ── Cells → Tables ────────────────────────────────────────────── - private static List SnapEdgesByOrientation(List edges, string attr, float tolerance) + /// + /// Group cells into contiguous tables based on shared corners. + /// + public static List> CellsToTables( + Page page, + List<(float x0, float y0, float x1, float y1)> cells, + List> chars) { - if (edges.Count == 0) return edges; + (float, float)[] BboxToCorners((float x0, float y0, float x1, float y1) bbox) + => new[] + { + (bbox.x0, bbox.y0), (bbox.x0, bbox.y1), + (bbox.x1, bbox.y0), (bbox.x1, bbox.y1) + }; - var clusters = TableHelpers.ClusterObjects(edges, e => GetEdgeValue(e, attr), tolerance); - var avgs = clusters.Select(cluster => cluster.Average(e => GetEdgeValue(e, attr))).ToList(); + var remaining = new List<(float x0, float y0, float x1, float y1)>(cells); + var currentCorners = new HashSet<(float, float)>(); + var currentCells = new List<(float x0, float y0, float x1, float y1)>(); + var tables = new List>(); - var result = new List(); - for (int i = 0; i < clusters.Count; i++) + while (remaining.Count > 0) { - float avg = avgs[i]; - foreach (var e in clusters[i]) + int initialCount = currentCells.Count; + for (int i = remaining.Count - 1; i >= 0; i--) { - var snapped = new Edge - { - x0 = e.x0, - x1 = e.x1, - top = e.top, - bottom = e.bottom, - width = e.width, - height = e.height, - orientation = e.orientation, - object_type = e.object_type, - doctop = e.doctop, - page_number = e.page_number, - y0 = e.y0, - y1 = e.y1 - }; - - float diff = avg - GetEdgeValue(e, attr); - if (attr == "x0") - { - snapped.x0 = avg; - snapped.x1 = e.x1 + diff; - snapped.width = snapped.x1 - snapped.x0; - } - else if (attr == "top") + var cell = remaining[i]; + var corners = BboxToCorners(cell); + if (currentCells.Count == 0) { - snapped.top = avg; - snapped.bottom = e.bottom + diff; - snapped.height = snapped.bottom - snapped.top; - snapped.doctop = e.doctop + diff; + foreach (var c in corners) currentCorners.Add(c); + currentCells.Add(cell); + remaining.RemoveAt(i); } - - result.Add(snapped); - } - } - - return result; - } - - private static float GetEdgeValue(Edge e, string attr) - { - switch (attr) - { - case "x0": - return e.x0; - case "top": - return e.top; - default: - return 0; - } - } - - // resize_object - Resize an object by changing a key value - internal static Dictionary ResizeObject(Dictionary obj, string key, float value) - { - if (key != "x0" && key != "x1" && key != "top" && key != "bottom") - throw new ArgumentException("Key must be 'x0', 'x1', 'top', or 'bottom'"); - - var newObj = new Dictionary(obj); - float oldValue = Convert.ToSingle(obj[key]); - float diff = value - oldValue; - - newObj[key] = value; - - if (key == "x0") - { - if (value > Convert.ToSingle(obj["x1"])) - throw new ArgumentException("x0 must be <= x1"); - newObj["width"] = Convert.ToSingle(obj["x1"]) - value; - } - else if (key == "x1") - { - if (value < Convert.ToSingle(obj["x0"])) - throw new ArgumentException("x1 must be >= x0"); - newObj["width"] = value - Convert.ToSingle(obj["x0"]); - } - else if (key == "top") - { - if (value > Convert.ToSingle(obj["bottom"])) - throw new ArgumentException("top must be <= bottom"); - newObj["doctop"] = Convert.ToSingle(obj["doctop"]) + diff; - newObj["height"] = Convert.ToSingle(obj["height"]) - diff; - if (obj.ContainsKey("y1")) - newObj["y1"] = Convert.ToSingle(obj["y1"]) - diff; - } - else if (key == "bottom") - { - if (value < Convert.ToSingle(obj["top"])) - throw new ArgumentException("bottom must be >= top"); - newObj["height"] = Convert.ToSingle(obj["height"]) + diff; - if (obj.ContainsKey("y0")) - newObj["y0"] = Convert.ToSingle(obj["y0"]) - diff; - } - - return newObj; - } - - // join_edge_group - Join edges along the same line - internal static List JoinEdgeGroup_( - List edges, - string orientation, - float tolerance = TableFlags.TABLE_DEFAULT_JOIN_TOLERANCE) - { - string minProp, maxProp; - if (orientation == "h") - { - minProp = "x0"; - maxProp = "x1"; - } - else if (orientation == "v") - { - minProp = "top"; - maxProp = "bottom"; - } - else - { - throw new ArgumentException("Orientation must be 'v' or 'h'"); - } - - var sortedEdges = edges.OrderBy(e => GetEdgeValue(e, minProp)).ToList(); - if (sortedEdges.Count == 0) return new List(); - - var joined = new List { sortedEdges[0] }; - - for (int i = 1; i < sortedEdges.Count; i++) - { - var e = sortedEdges[i]; - var last = joined[joined.Count - 1]; - - float eMin = GetEdgeValue(e, minProp); - float lastMax = GetEdgeValue(last, maxProp); - - if (eMin <= (lastMax + tolerance)) - { - float eMax = GetEdgeValue(e, maxProp); - if (eMax > lastMax) - { - // Extend current edge - var extended = new Edge - { - x0 = last.x0, - x1 = last.x1, - top = last.top, - bottom = last.bottom, - width = last.width, - height = last.height, - orientation = last.orientation, - object_type = last.object_type, - doctop = last.doctop, - page_number = last.page_number, - y0 = last.y0, - y1 = last.y1 - }; - - if (orientation == "h") - { - extended.x1 = e.x1; - extended.width = extended.x1 - extended.x0; - } - else - { - extended.bottom = e.bottom; - extended.height = extended.bottom - extended.top; - } - - joined[joined.Count - 1] = extended; - } - } - else - { - joined.Add(e); - } - } - - return joined; - } - - internal static List JoinEdgeGroup( - List edges, - string orientation, - float tolerance) - { - Func minProp; - Func maxProp; - Action setMaxProp; - - // Select properties based on orientation - if (orientation == "h") - { - minProp = e => e.x0; - maxProp = e => e.x1; - setMaxProp = (e, v) => e.x1 = v; - } - else if (orientation == "v") - { - minProp = e => e.top; - maxProp = e => e.bottom; - setMaxProp = (e, v) => e.bottom = v; - } - else - { - throw new ArgumentException("Orientation must be 'h' or 'v'"); - } - - if (edges == null || edges.Count == 0) - return new List(); - - // Sort edges by their minimum extent - var sortedEdges = edges - .OrderBy(minProp) - .ToList(); - - var joined = new List { sortedEdges[0] }; - - // Merge overlapping / nearby edges - for (int i = 1; i < sortedEdges.Count; i++) - { - var current = sortedEdges[i]; - var last = joined[joined.Count - 1]; - - if (minProp(current) <= maxProp(last) + tolerance) - { - // Extend the last edge if needed - if (maxProp(current) > maxProp(last)) - { - setMaxProp(last, maxProp(current)); - } - } - else - { - // Separate edge → start a new segment - joined.Add(current); - } - } - - return joined; - } - - // merge_edges - Merge edges using snap and join - internal static List MergeEdges_( - List edges, - float snapXTolerance, - float snapYTolerance, - float joinXTolerance, - float joinYTolerance) - { - if (snapXTolerance > 0 || snapYTolerance > 0) - edges = SnapEdges(edges, snapXTolerance, snapYTolerance); - - // Use Tuple for grouping key - var sorted = edges.OrderBy(e => Tuple.Create(e.orientation, e.orientation == "h" ? e.top : e.x0)).ToList(); - var edgeGroups = sorted.GroupBy(e => Tuple.Create(e.orientation, e.orientation == "h" ? e.top : e.x0)); - - var merged = new List(); - foreach (var group in edgeGroups) - { - string orientation = group.Key.Item1; // First element of tuple is orientation - float tolerance = orientation == "h" ? joinXTolerance : joinYTolerance; - merged.AddRange(JoinEdgeGroup(group.ToList(), orientation, tolerance)); - } - - return merged; - } - - public static List MergeEdges( - List edges, - float snapXTolerance, - float snapYTolerance, - float joinXTolerance, - float joinYTolerance) - { - // Local grouping key - (string, float) GetGroupKey(Edge edge) - { - return edge.orientation == "h" - ? ("h", edge.top) - : ("v", edge.x0); - } - - // Optional snapping - if (snapXTolerance > 0 || snapYTolerance > 0) - { - edges = SnapEdges(edges, snapXTolerance, snapYTolerance); - } - - // Sort by group key - var sortedEdges = edges - .OrderBy(e => GetGroupKey(e).Item1) - .ThenBy(e => GetGroupKey(e).Item2) - .ToList(); - - // Group edges - var groupedEdges = sortedEdges - .GroupBy(GetGroupKey); - - // Join edge groups - var mergedEdges = new List(); - - foreach (var group in groupedEdges) - { - string orientation = group.Key.Item1; - float joinTolerance = - orientation == "h" ? joinXTolerance : joinYTolerance; - - var joined = JoinEdgeGroup( - group.ToList(), - orientation, - joinTolerance - ); - - mergedEdges.AddRange(joined); - } - - return mergedEdges; - } - - // bbox_to_rect - Convert bbox tuple to rect dict - internal static Dictionary BboxToRect(Tuple bbox) - { - return new Dictionary - { - { "x0", bbox.Item1 }, - { "top", bbox.Item2 }, - { "x1", bbox.Item3 }, - { "bottom", bbox.Item4 } - }; - } - - // objects_to_rect - Get smallest rect containing objects - internal static Dictionary ObjectsToRect(IEnumerable objects) - { - var bbox = TableHelpers.ObjectsToBbox(objects); - return BboxToRect(Tuple.Create(bbox.X0, bbox.Y0, bbox.X1, bbox.Y1)); - } - - // merge_bboxes - Merge multiple bboxes - internal static Tuple MergeBboxes(IEnumerable> bboxes) - { - var bboxList = bboxes.ToList(); - if (bboxList.Count == 0) - return Tuple.Create(0f, 0f, 0f, 0f); - - return Tuple.Create( - bboxList.Min(b => b.Item1), - bboxList.Min(b => b.Item2), - bboxList.Max(b => b.Item3), - bboxList.Max(b => b.Item4) - ); - } - - // words_to_edges_h - Find horizontal edges from words - internal static List WordsToEdgesH( - List> words, - int wordThreshold = (int)TableFlags.TABLE_DEFAULT_MIN_WORDS_HORIZONTAL) - { - var byTop = TableHelpers.ClusterObjects(words, w => Convert.ToSingle(w["top"]), 1); - var largeClusters = byTop.Where(x => x.Count >= wordThreshold).ToList(); - - if (largeClusters.Count == 0) - return new List(); - - var rects = largeClusters.Select(cluster => ObjectsToRect(cluster.Cast())).ToList(); - float minX0 = rects.Min(r => Convert.ToSingle(r["x0"])); - float maxX1 = rects.Max(r => Convert.ToSingle(r["x1"])); - - var edges = new List(); - foreach (var r in rects) - { - float top = Convert.ToSingle(r["top"]); - float bottom = Convert.ToSingle(r["bottom"]); - - // Top edge - edges.Add(new Edge - { - x0 = minX0, - x1 = maxX1, - top = top, - bottom = top, - width = maxX1 - minX0, - height = 0, - orientation = "h", - object_type = "text_edge" - }); - - // Bottom edge - edges.Add(new Edge - { - x0 = minX0, - x1 = maxX1, - top = bottom, - bottom = bottom, - width = maxX1 - minX0, - height = 0, - orientation = "h", - object_type = "text_edge" - }); - } - - return edges; - } - - // get_bbox_overlap - Get overlap between two bboxes - internal static Tuple GetBboxOverlap( - Tuple a, - Tuple b) - { - float oLeft = Math.Max(a.Item1, b.Item1); - float oRight = Math.Min(a.Item3, b.Item3); - float oBottom = Math.Min(a.Item4, b.Item4); - float oTop = Math.Max(a.Item2, b.Item2); - float oWidth = oRight - oLeft; - float oHeight = oBottom - oTop; - - if (oHeight >= 0 && oWidth >= 0 && oHeight + oWidth > 0) - return Tuple.Create(oLeft, oTop, oRight, oBottom); - - return null; - } - - // words_to_edges_v - Find vertical edges from words - internal static List WordsToEdgesV( - List> words, - int wordThreshold = (int)TableFlags.TABLE_DEFAULT_MIN_WORDS_VERTICAL) - { - var byX0 = TableHelpers.ClusterObjects(words, w => Convert.ToSingle(w["x0"]), 1); - var byX1 = TableHelpers.ClusterObjects(words, w => Convert.ToSingle(w["x1"]), 1); - - Func, float> getCenter = w => - (Convert.ToSingle(w["x0"]) + Convert.ToSingle(w["x1"])) / 2; - var byCenter = TableHelpers.ClusterObjects(words, getCenter, 1); - - var clusters = byX0.Concat(byX1).Concat(byCenter).ToList(); - var sortedClusters = clusters.OrderByDescending(x => x.Count).ToList(); - var largeClusters = sortedClusters.Where(x => x.Count >= wordThreshold).ToList(); - - if (largeClusters.Count == 0) - return new List(); - - var bboxes = largeClusters.Select(cluster => - { - var rect = ObjectsToRect(cluster.Cast()); - return Tuple.Create( - Convert.ToSingle(rect["x0"]), - Convert.ToSingle(rect["top"]), - Convert.ToSingle(rect["x1"]), - Convert.ToSingle(rect["bottom"]) - ); - }).ToList(); - - var condensedBboxes = new List>(); - foreach (var bbox in bboxes) - { - bool hasOverlap = condensedBboxes.Any(c => GetBboxOverlap(bbox, c) != null); - if (!hasOverlap) - condensedBboxes.Add(bbox); - } - - if (condensedBboxes.Count == 0) - return new List(); - - var condensedRects = condensedBboxes.Select(bbox => BboxToRect(bbox)) - .OrderBy(r => Convert.ToSingle(r["x0"])).ToList(); - - float maxX1 = condensedRects.Max(r => Convert.ToSingle(r["x1"])); - float minTop = condensedRects.Min(r => Convert.ToSingle(r["top"])); - float maxBottom = condensedRects.Max(r => Convert.ToSingle(r["bottom"])); - - var edges = new List(); - foreach (var r in condensedRects) - { - edges.Add(new Edge - { - x0 = Convert.ToSingle(r["x0"]), - x1 = Convert.ToSingle(r["x0"]), - top = minTop, - bottom = maxBottom, - width = 0, - height = maxBottom - minTop, - orientation = "v", - object_type = "text_edge" - }); - } - - // Add rightmost edge - edges.Add(new Edge - { - x0 = maxX1, - x1 = maxX1, - top = minTop, - bottom = maxBottom, - width = 0, - height = maxBottom - minTop, - orientation = "v", - object_type = "text_edge" - }); - - return edges; - } - - // edges_to_intersections - Find intersection points of edges - internal static Dictionary, Dictionary>> EdgesToIntersections( - List edges, - float xTolerance = 1, - float yTolerance = 1) - { - var intersections = new Dictionary, Dictionary>>(); - var vEdges = edges.Where(e => e.orientation == "v") - .OrderBy(e => e.x0).ThenBy(e => e.top).ToList(); - var hEdges = edges.Where(e => e.orientation == "h") - .OrderBy(e => e.top).ThenBy(e => e.x0).ToList(); - - foreach (var v in vEdges) - { - foreach (var h in hEdges) - { - if ((v.top <= (h.top + yTolerance)) && - (v.bottom >= (h.top - yTolerance)) && - (v.x0 >= (h.x0 - xTolerance)) && - (v.x0 <= (h.x1 + xTolerance))) - { - var vertex = Tuple.Create(v.x0, h.top); - if (!intersections.ContainsKey(vertex)) - { - intersections[vertex] = new Dictionary> - { - { "v", new List() }, - { "h", new List() } - }; - } - intersections[vertex]["v"].Add(v); - intersections[vertex]["h"].Add(h); - } - } - } - - return intersections; - } - - // intersections_to_cells - Convert intersections to cells - internal static List IntersectionsToCells_( - Dictionary, Dictionary>> intersections) - { - var cells = new List(); - var points = intersections.Keys.OrderBy(p => p.Item2).ThenBy(p => p.Item1).ToList(); - int nPoints = points.Count; - - Func, Tuple, bool> edgeConnects = (p1, p2) => - { - Func, HashSet>> edgesToSet = edges => - { - return new HashSet>(edges.Select(e => - Tuple.Create(e.x0, e.top, e.x1, e.bottom))); - }; - - if (p1.Item1 == p2.Item1) // Same x - { - var common = new HashSet>(edgesToSet(intersections[p1]["v"])); - common.IntersectWith(edgesToSet(intersections[p2]["v"])); - if (common.Count > 0) - return true; - } - - if (p1.Item2 == p2.Item2) // Same y - { - var common = new HashSet>(edgesToSet(intersections[p1]["h"])); - common.IntersectWith(edgesToSet(intersections[p2]["h"])); - if (common.Count > 0) - return true; - } - - return false; - }; - - for (int i = 0; i < nPoints - 1; i++) - { - var pt = points[i]; - var rest = points.Skip(i + 1).ToList(); - - var below = rest.Where(x => x.Item1 == pt.Item1).ToList(); - var right = rest.Where(x => x.Item2 == pt.Item2).ToList(); - - foreach (var belowPt in below) - { - if (!edgeConnects(pt, belowPt)) - continue; - - foreach (var rightPt in right) - { - if (!edgeConnects(pt, rightPt)) - continue; - - var bottomRight = Tuple.Create(rightPt.Item1, belowPt.Item2); - - if (intersections.ContainsKey(bottomRight) && - edgeConnects(bottomRight, rightPt) && - edgeConnects(bottomRight, belowPt)) - { - cells.Add(new Rect(pt.Item1, pt.Item2, bottomRight.Item1, bottomRight.Item2)); - } - } - } - } - - return cells; - } - - internal static List IntersectionsToCells( - Dictionary, Dictionary>> intersections) - { - // ---------- edge_connects ---------- - bool EdgeConnects( - Tuple p1, - Tuple p2) - { - HashSet<(float, float, float, float)> EdgesToSet(List edges) - { - var set = new HashSet<(float, float, float, float)>(); - foreach (var e in edges) - set.Add(ObjToBBox(e)); - return set; - } - - // Same X → vertical edges - if (p1.Item1 == p2.Item1) - { - var common = EdgesToSet(intersections[p1]["v"]) - .Intersect(EdgesToSet(intersections[p2]["v"])); - - if (common.Any()) - return true; - } - - // Same Y → horizontal edges - if (p1.Item2 == p2.Item2) - { - var common = EdgesToSet(intersections[p1]["h"]) - .Intersect(EdgesToSet(intersections[p2]["h"])); - - if (common.Any()) - return true; - } - - return false; - } - - var points = intersections.Keys - .OrderBy(p => p.Item1) - .ThenBy(p => p.Item2) - .ToList(); - - int nPoints = points.Count; - - // ---------- find_smallest_cell ---------- - Rect FindSmallestCell(int i) - { - if (i == nPoints - 1) - return null; - - var pt = points[i]; - var rest = points.Skip(i + 1); - - var below = rest.Where(p => p.Item1 == pt.Item1).ToList(); - var right = rest.Where(p => p.Item2 == pt.Item2).ToList(); - - foreach (var belowPt in below) - { - if (!EdgeConnects(pt, belowPt)) - continue; - - foreach (var rightPt in right) - { - if (!EdgeConnects(pt, rightPt)) - continue; - - var bottomRight = Tuple.Create(rightPt.Item1, belowPt.Item2); - - if (intersections.ContainsKey(bottomRight) && - EdgeConnects(bottomRight, rightPt) && - EdgeConnects(bottomRight, belowPt)) - { - float x0 = pt.Item1; - float y0 = pt.Item2; - float x1 = bottomRight.Item1; - float y1 = bottomRight.Item2; - - return new Rect( - x0, - y0, - x1, - y1 - ); - } - } - } - - return null; - } - - // ---------- generate cells ---------- - var cells = new List(); - - for (int i = 0; i < points.Count; i++) - { - var cell = FindSmallestCell(i); - if (cell != null) - cells.Add(cell); - } - - return cells; - } - - // ---------- obj_to_bbox ---------- - private static (float, float, float, float) ObjToBBox(Edge e) - { - return (e.x0, e.top, e.x1, e.bottom); - } - - // cells_to_tables - Group cells into tables - internal static List> CellsToTables(Page page, List cells) - { - Func>> bboxToCorners = bbox => - { - return new List> - { - Tuple.Create(bbox.X0, bbox.Y0), - Tuple.Create(bbox.X0, bbox.Y1), - Tuple.Create(bbox.X1, bbox.Y0), - Tuple.Create(bbox.X1, bbox.Y1) - }; - }; - - var remainingCells = new List(cells); - var currentCorners = new HashSet>(); - var currentCells = new List(); - var tables = new List>(); - - while (remainingCells.Count > 0) - { - int initialCellCount = currentCells.Count; - var cellsToRemove = new List(); - - foreach (var cell in remainingCells) - { - var cellCorners = bboxToCorners(cell); - - if (currentCells.Count == 0) - { - foreach (var corner in cellCorners) - currentCorners.Add(corner); - currentCells.Add(cell); - cellsToRemove.Add(cell); - } - else - { - int cornerCount = cellCorners.Count(c => currentCorners.Contains(c)); - if (cornerCount > 0) - { - foreach (var corner in cellCorners) - currentCorners.Add(corner); - currentCells.Add(cell); - cellsToRemove.Add(cell); - } - } - } - - foreach (var cell in cellsToRemove) - remainingCells.Remove(cell); - - if (currentCells.Count == initialCellCount) - { - tables.Add(new List(currentCells)); - currentCorners.Clear(); - currentCells.Clear(); - } - } - - if (currentCells.Count > 0) - tables.Add(currentCells); - - // MuPDF modification: Remove tables without text or having only 1 column - for (int i = tables.Count - 1; i >= 0; i--) - { - var table = tables[i]; - var r = new Rect(0, 0, 0, 0); - var x1Vals = new HashSet(); - var x0Vals = new HashSet(); - - foreach (var c in table) - { - r = r | c; - x1Vals.Add(c.X1); - x0Vals.Add(c.X0); - } - - if (x1Vals.Count < 2 || x0Vals.Count < 2) - { - tables.RemoveAt(i); - continue; - } - - // Check if table has only whitespace - try - { - var textpage = TableGlobals.TEXTPAGE ?? page.GetTextPage(); - string text = textpage.ExtractTextBox(r.ToFzRect()); - if (string.IsNullOrWhiteSpace(text)) - { - tables.RemoveAt(i); - continue; - } - } - catch - { - // If text extraction fails, keep the table - } - } - - // Sort tables top-to-bottom-left-to-right - tables = tables.OrderBy(t => t.Min(c => Tuple.Create(c.Y0, c.X0))).ToList(); - - return tables; - } - } - - // CellGroup base class - public class CellGroup - { - public List cells { get; set; } - public Rect bbox { get; set; } - - public CellGroup(List cells) - { - this.cells = cells; - if (cells != null && cells.Count > 0) - { - var validCells = cells.Where(c => c != null).ToList(); - if (validCells.Count > 0) - { - bbox = new Rect( - validCells.Min(c => c.X0), - validCells.Min(c => c.Y0), - validCells.Max(c => c.X1), - validCells.Max(c => c.Y1) - ); - } - } - } - } - - // TableRow class - public class TableRow : CellGroup - { - public TableRow(List cells) : base(cells) - { - } - } - - // TableHeader class - public class TableHeader - { - public Rect bbox { get; set; } - public List cells { get; set; } - public List names { get; set; } - public bool external { get; set; } - - public TableHeader(Rect bbox, List cells, List names, bool external) - { - this.bbox = bbox; - this.cells = cells; - this.names = names; - this.external = external; - } - } - - // Table class - public class Table - { - public Page page { get; set; } - public TextPage textpage { get; set; } - public List cells { get; set; } - public TableHeader header { get; set; } - - public Table(Page page, List cells) - { - this.page = page; - this.cells = cells; - this.textpage = null; - this.header = GetHeader(); - } - - public Rect bbox - { - get - { - if (cells == null || cells.Count == 0) - return null; - return new Rect( - cells.Min(c => c.X0), - cells.Min(c => c.Y0), - cells.Max(c => c.X1), - cells.Max(c => c.Y1) - ); - } - } - - public List rows - { - get - { - var sorted = cells.OrderBy(c => c.Y0).ThenBy(c => c.X0).ToList(); - var xs = cells.Select(c => c.X0).Distinct().OrderBy(x => x).ToList(); - var rows = new List(); - - foreach (var group in sorted.GroupBy(c => c.Y0)) - { - var rowCells = group.OrderBy(c => c.X0).ToList(); - var xdict = rowCells.ToDictionary(c => c.X0, c => c); - var row = new TableRow(xs.Select(x => xdict.ContainsKey(x) ? xdict[x] : null).ToList()); - rows.Add(row); - } - - return rows; - } - } - - public int row_count - { - get { return rows.Count; } - } - - public int col_count - { - get { return rows.Count > 0 ? rows.Max(r => r.cells.Count) : 0; } - } - - public List> Extract(Dictionary kwargs = null) - { - if (kwargs == null) - kwargs = new Dictionary(); - - var chars = TableGlobals.CHARS; - var tableArr = new List>(); - - bool CharInBbox(CharDict char_, Rect bbox) - { - float v_mid = (char_.top + char_.bottom) / 2; - float h_mid = (char_.x0 + char_.x1) / 2; - return h_mid >= bbox.X0 && h_mid < bbox.X1 && v_mid >= bbox.Y0 && v_mid < bbox.Y1; - } - - foreach (var row in rows) - { - var arr = new List(); - var rowChars = chars.Where(c => CharInBbox(c, row.bbox)).ToList(); - - foreach (var cell in row.cells) - { - if (cell == null) - { - arr.Add(null); - } - else - { - var cellChars = rowChars.Where(c => CharInBbox(c, cell)).ToList(); - if (cellChars.Count > 0) - { - var cellKwargs = new Dictionary(kwargs); - cellKwargs["x_shift"] = cell.X0; - cellKwargs["y_shift"] = cell.Y0; - if (cellKwargs.ContainsKey("layout")) - { - cellKwargs["layout_width"] = cell.X1 - cell.X0; - cellKwargs["layout_height"] = cell.Y1 - cell.Y0; - } - var cellText = ExtractText(cellChars, cellKwargs); - arr.Add(cellText); - } - else - { - arr.Add(""); - } - } - } - tableArr.Add(arr); - } - - return tableArr; - } - - private string ExtractText(List chars, Dictionary kwargs) - { - return TextExtractionHelpers.ExtractText(chars, kwargs); - } - - public string ToMarkdown(bool clean = false, bool fillEmpty = true) - { - var output = new StringBuilder(); - output.Append("|"); - int rows = row_count; - int cols = col_count; - - // cell coordinates - var cellBoxes = this.rows.Select(r => r.cells.ToList()).ToList(); - - // cell text strings - var cells = new List>(); - for (int i = 0; i < rows; i++) - { - cells.Add(new List()); - for (int colIdx = 0; colIdx < cols; colIdx++) - { - cells[i].Add(null); - } - } - - for (int i = 0; i < cellBoxes.Count; i++) - { - for (int colIdx = 0; colIdx < cellBoxes[i].Count && colIdx < cols; colIdx++) - { - if (cellBoxes[i][colIdx] != null) - { - cells[i][colIdx] = TableHelpers.ExtractCells(textpage, cellBoxes[i][colIdx], markdown: true); - } - } - } - - if (fillEmpty) - { - // for rows, copy content from left to right - for (int rowIdx = 0; rowIdx < rows; rowIdx++) - { - for (int i = 0; i < cols - 1; i++) - { - if (cells[rowIdx][i + 1] == null) - { - cells[rowIdx][i + 1] = cells[rowIdx][i]; - } - } - } - - // for columns, copy top to bottom - for (int i = 0; i < cols; i++) - { - for (int rowIdx = 0; rowIdx < rows - 1; rowIdx++) - { - if (cells[rowIdx + 1][i] == null) - { - cells[rowIdx + 1][i] = cells[rowIdx][i]; - } - } - } - } - - // generate header string and MD separator - for (int i = 0; i < header.names.Count; i++) - { - string name = header.names[i]; - if (string.IsNullOrEmpty(name)) - { - name = $"Col{i + 1}"; - } - name = name.Replace("\n", "
"); - if (clean) - { - name = System.Security.SecurityElement.Escape(name.Replace("-", "-")); - } - output.Append(name + "|"); - } - output.Append("\n"); - // insert GitHub header line separator - output.Append("|" + string.Join("|", Enumerable.Range(0, col_count).Select(_ => "---")) + "|\n"); - - // skip first row in details if header is part of the table - int startRow = header.external ? 0 : 1; - - // iterate over detail rows - for (int i = startRow; i < rows; i++) - { - output.Append("|"); - for (int k = 0; k < cols; k++) - { - string cell = cells[i][k]; - if (cell == null) - cell = ""; - if (clean) - { - cell = System.Security.SecurityElement.Escape(cell.Replace("-", "-")); - } - output.Append(cell + "|"); - } - output.Append("\n"); - } - return output.ToString() + "\n"; - } - - // to_pandas - Return a tabular dataframe-style view of the table - // Note: This would require a suitable dataframe binding or similar - // For C#, users can convert the Extract() result to their preferred data structure - public object ToPandas(Dictionary kwargs = null) - { - // In C#: Could return DataTable, or users can use Extract() and convert manually - throw new NotImplementedException("ToPandas is not implemented in C#. Use Extract() and convert to your preferred data structure (e.g., DataTable)."); - } - - private string ExtractCells(TextPage textpage, Rect cell, bool markdown = false) - { - return TableHelpers.ExtractCells(textpage, cell, markdown); - } - - private TableHeader GetHeader(float yTolerance = 3.0f) - { - float yDelta = yTolerance; - - // Helper function: Check if top row has different background color - bool TopRowBgColor() - { - try - { - var bbox0 = rows[0].bbox; - var bboxt = new Rect(bbox0.X0, bbox0.Y0 - bbox0.Height, bbox0.X1, bbox0.Y0); - var (_, topColor0) = page.GetPixmap(clip: bbox0).ColorTopUsage(); - var (_, topColort) = page.GetPixmap(clip: bboxt).ColorTopUsage(); - return !topColor0.SequenceEqual(topColort); - } - catch - { - return false; - } - } - - // Helper function: Check if row contains bold text - bool RowHasBold(Rect rowBbox) - { - return TableGlobals.CHARS.Any(c => - TableHelpers.RectInRect(new Rect(c.x0, c.y0, c.x1, c.y1), rowBbox) && c.bold); - } - - if (rows == null || rows.Count == 0) - return null; - - var row = rows[0]; - var cells = row.cells; - var bbox = row.bbox; - - // Return this if we determine that the top row is the header - var extractResult = Extract(); - var headerTopRow = new TableHeader( - bbox, - cells, - extractResult.Count > 0 ? extractResult[0] : new List(), - false - ); - - // 1-line tables have no extra header - if (rows.Count < 2) - return headerTopRow; - - // 1-column tables have no extra header - if (cells.Count < 2) - return headerTopRow; - - // Assume top row is the header if second row is empty - var row2 = rows[1]; - if (row2.cells.All(c => c == null)) - return headerTopRow; - - // Special check: is top row bold? - bool topRowBold = RowHasBold(bbox); - - // Assume top row is header if it is bold and any cell of 2nd row is non-bold - if (topRowBold && !RowHasBold(row2.bbox)) - return headerTopRow; - - if (TopRowBgColor()) - return headerTopRow; - - // Column coordinates (x1 values) in top row - var colX = cells.Take(cells.Count - 1).Select(c => c != null ? c.X1 : (float?)null).ToList(); - - // Clip = page area above the table - var clip = new Rect(bbox.X0, 0, bbox.X1, bbox.Y0); - - // Get text blocks above table - dynamic pageInfo = page.GetText("dict", clip: clip, flags: (int)TextFlagsExtension.TEXTFLAGS_TEXT); - List blocks = pageInfo?.Blocks ?? new List(); - - // Non-empty, non-superscript spans above table, sorted descending by y1 - var spans = new List>(); - foreach (var block in blocks) - { - if (block.Lines == null) continue; - foreach (var line in block.Lines) - { - if (line.Spans == null) continue; - foreach (var span in line.Spans) - { - if (span.Bbox == null) continue; - string text = span.Text ?? ""; - bool isWhitespace = text.All(c => TableGlobals.WHITE_SPACES.Contains(c)); - bool isSuperscript = ((int)span.Flags & (int)FontStyle.TEXT_FONT_SUPERSCRIPT) != 0; - - if (!isWhitespace && !isSuperscript) - { - spans.Add(new Dictionary - { - { "text", text }, - { "bbox", new List { span.Bbox.X0, span.Bbox.Y0, span.Bbox.X1, span.Bbox.Y1 } }, - { "flags", span.Flags } - }); - } - } - } - } - - spans = spans.OrderByDescending(s => ((List)s["bbox"])[3]).ToList(); - - var select = new List(); - var lineHeights = new List(); - var lineBolds = new List(); - - // Walk through spans and fill the 3 lists - for (int i = 0; i < spans.Count; i++) - { - var s = spans[i]; - var sbbox = s["bbox"] as List; - if (sbbox == null || sbbox.Count < 4) continue; - - float y1 = Convert.ToSingle(sbbox[3]); - float h = y1 - Convert.ToSingle(sbbox[1]); - bool bold = (Convert.ToInt32(s["flags"]) & (int)FontStyle.TEXT_FONT_BOLD) != 0; - - if (i == 0) - { - select.Add(y1); - lineHeights.Add(h); - lineBolds.Add(bold); - continue; - } - - float y0 = select[select.Count - 1]; - float h0 = lineHeights[lineHeights.Count - 1]; - bool bold0 = lineBolds[lineBolds.Count - 1]; - - if (bold0 && !bold) - break; - - if (y0 - y1 <= yDelta || Math.Abs((y0 - h0) - Convert.ToSingle(sbbox[1])) <= yDelta) - { - sbbox[1] = y0 - h0; - sbbox[3] = y0; - s["bbox"] = sbbox; - spans[i] = s; - if (bold) - lineBolds[lineBolds.Count - 1] = bold; - continue; - } - else if (y0 - y1 > 1.5 * h0) - { - break; - } - - select.Add(y1); - lineHeights.Add(h); - lineBolds.Add(bold); - } - - if (select.Count == 0) - return headerTopRow; - - select = select.Take(5).ToList(); - - // Assume top row as header if text above is too far away - if (bbox.Y0 - select[0] >= lineHeights[0]) - return headerTopRow; - - // Accept top row as header if bold, but line above is not - if (topRowBold && !lineBolds[0]) - return headerTopRow; - - if (spans.Count == 0) - return headerTopRow; - - // Re-compute clip above table - var nclip = new Rect(0, 0, 0, 0); - foreach (var s in spans.Where(s => Convert.ToSingle(((List)s["bbox"])[3]) >= select[select.Count - 1])) - { - var sbbox = s["bbox"] as List; - if (sbbox != null && sbbox.Count >= 4) - { - var srect = new Rect( - Convert.ToSingle(sbbox[0]), - Convert.ToSingle(sbbox[1]), - Convert.ToSingle(sbbox[2]), - Convert.ToSingle(sbbox[3]) - ); - nclip = nclip | srect; - } - } - - if (!nclip.IsEmpty) - clip = nclip; - - clip.Y1 = bbox.Y0; - - // Confirm that no word in clip is intersecting a column separator - // Get words from textpage or page - var textpageForWords = page.GetTextPage(clip: clip); - var words = textpageForWords.ExtractWords(); - var wordRects = words.Select(w => new Rect(w.X0, w.Y0, w.X1, w.Y1)).ToList(); - var wordTops = wordRects.Select(r => r.Y0).Distinct().OrderByDescending(y => y).ToList(); - - select.Clear(); - - // Exclude lines with words that intersect a column border - foreach (var top in wordTops) - { - bool hasIntersecting = colX.Any(x => - x.HasValue && wordRects.Any(r => r.Y0 == top && r.X0 < x.Value && r.X1 > x.Value)); - - if (!hasIntersecting) - { - select.Add(top); - } - else - { - break; - } - } - - if (select.Count == 0) - return headerTopRow; - - var hdrBbox = new Rect(clip.X0, select[select.Count - 1], clip.X1, clip.Y1); - hdrBbox.X0 = this.bbox.X0; - hdrBbox.X1 = this.bbox.X1; - - var hdrCells = cells.Select(c => - c != null ? new Rect(c.X0, hdrBbox.Y0, c.X1, hdrBbox.Y1) : (Rect)null - ).ToList(); - - // Column names: no line breaks, no excess spaces - var hdrNames = hdrCells.Select(c => - { - if (c == null) return ""; - try - { - return page.GetTextbox(c).Replace("\n", " ").Replace(" ", " ").Trim(); - } - catch - { - return ""; - } - }).ToList(); - - return new TableHeader(hdrBbox, hdrCells, hdrNames, true); - } - } - - // TableSettings class - public class TableSettings - { - public string vertical_strategy { get; set; } = "lines"; - public string horizontal_strategy { get; set; } = "lines"; - public List explicit_vertical_lines { get; set; } = null; - public List explicit_horizontal_lines { get; set; } = null; - public float snap_tolerance { get; set; } = TableFlags.TABLE_DEFAULT_SNAP_TOLERANCE; - public float snap_x_tolerance { get; set; } = TableFlags.TABLE_UNSET; - public float snap_y_tolerance { get; set; } = TableFlags.TABLE_UNSET; - public float join_tolerance { get; set; } = TableFlags.TABLE_DEFAULT_JOIN_TOLERANCE; - public float join_x_tolerance { get; set; } = TableFlags.TABLE_UNSET; - public float join_y_tolerance { get; set; } = TableFlags.TABLE_UNSET; - public float edge_min_length { get; set; } = 3.0f; - public float min_words_vertical { get; set; } = TableFlags.TABLE_DEFAULT_MIN_WORDS_VERTICAL; - public float min_words_horizontal { get; set; } = TableFlags.TABLE_DEFAULT_MIN_WORDS_HORIZONTAL; - public float intersection_tolerance { get; set; } = 3.0f; - public float intersection_x_tolerance { get; set; } = TableFlags.TABLE_UNSET; - public float intersection_y_tolerance { get; set; } = TableFlags.TABLE_UNSET; - public Dictionary text_settings { get; set; } = null; - - public TableSettings PostInit() - { - // Validate non-negative settings - var nonNegativeSettings = new[] - { - "snap_tolerance", "snap_x_tolerance", "snap_y_tolerance", - "join_tolerance", "join_x_tolerance", "join_y_tolerance", - "edge_min_length", "min_words_vertical", "min_words_horizontal", - "intersection_tolerance", "intersection_x_tolerance", "intersection_y_tolerance" - }; - - foreach (var setting in nonNegativeSettings) - { - var value = (float)GetType().GetProperty(setting).GetValue(this); - if (value < 0) - { - throw new ArgumentException($"Table setting '{setting}' cannot be negative"); - } - } - - // Validate strategies - if (!TableFlags.TABLE_STRATEGIES.Contains(vertical_strategy)) - { - throw new ArgumentException($"vertical_strategy must be one of {{{string.Join(",", TableFlags.TABLE_STRATEGIES)}}}"); - } - - if (!TableFlags.TABLE_STRATEGIES.Contains(horizontal_strategy)) - { - throw new ArgumentException($"horizontal_strategy must be one of {{{string.Join(",", TableFlags.TABLE_STRATEGIES)}}}"); - } - - if (text_settings == null) - { - text_settings = new Dictionary(); - } - - // Set defaults for unset tolerances - if (snap_x_tolerance == TableFlags.TABLE_UNSET) - snap_x_tolerance = snap_tolerance; - if (snap_y_tolerance == TableFlags.TABLE_UNSET) - snap_y_tolerance = snap_tolerance; - if (join_x_tolerance == TableFlags.TABLE_UNSET) - join_x_tolerance = join_tolerance; - if (join_y_tolerance == TableFlags.TABLE_UNSET) - join_y_tolerance = join_tolerance; - if (intersection_x_tolerance == TableFlags.TABLE_UNSET) - intersection_x_tolerance = intersection_tolerance; - if (intersection_y_tolerance == TableFlags.TABLE_UNSET) - intersection_y_tolerance = intersection_tolerance; - - return this; - } - - public static TableSettings Resolve(object settings = null) - { - if (settings == null) - { - return new TableSettings().PostInit(); - } - - if (settings is TableSettings ts) - { - return ts.PostInit(); - } - - if (settings is Dictionary dict) - { - var coreSettings = new Dictionary(); - var textSettings = new Dictionary(); - - foreach (var kvp in dict) - { - if (kvp.Key.StartsWith("text_")) - { - textSettings[kvp.Key.Substring(5)] = kvp.Value; - } - else - { - coreSettings[kvp.Key] = kvp.Value; - } - } - - coreSettings["text_settings"] = textSettings; - - var tableSettings = new TableSettings(); - foreach (var kvp in coreSettings) - { - var prop = typeof(TableSettings).GetProperty(kvp.Key); - if (prop != null && prop.CanWrite) - { - prop.SetValue(tableSettings, kvp.Value); - } - } - - return tableSettings.PostInit(); - } - - throw new ArgumentException($"Cannot resolve settings: {settings}"); - } - } - - // FindTables function - C# port of upstream find_tables - public static class TableFinderHelper - { - /// - /// Find tables on a page and return a TableFinder object. - /// This is the C# port of the upstream find_tables function. - /// - public static TableFinder FindTables( - Page page, - Rect clip = null, - string vertical_strategy = "lines", - string horizontal_strategy = "lines", - List vertical_lines = null, - List horizontal_lines = null, - float snap_tolerance = TableFlags.TABLE_DEFAULT_SNAP_TOLERANCE, - float? snap_x_tolerance = null, - float? snap_y_tolerance = null, - float join_tolerance = TableFlags.TABLE_DEFAULT_JOIN_TOLERANCE, - float? join_x_tolerance = null, - float? join_y_tolerance = null, - float edge_min_length = 3.0f, - float min_words_vertical = TableFlags.TABLE_DEFAULT_MIN_WORDS_VERTICAL, - float min_words_horizontal = TableFlags.TABLE_DEFAULT_MIN_WORDS_HORIZONTAL, - float intersection_tolerance = 3.0f, - float? intersection_x_tolerance = null, - float? intersection_y_tolerance = null, - float text_tolerance = 3.0f, - float text_x_tolerance = 3.0f, - float text_y_tolerance = 3.0f, - string strategy = null, - List> add_lines = null, - List add_boxes = null, - List paths = null - ) - { - // Clear global state - TableGlobals.CHARS.Clear(); - TableGlobals.EDGES.Clear(); - TableGlobals.TEXTPAGE = null; - - // Handle page rotation - int oldRotation = page.Rotation; - bool needsRotationReset = oldRotation != 0; - Rect oldMediabox = null; - - if (needsRotationReset) - { - oldMediabox = page.MediaBox; - page.SetRotation(0); - // For now, we'll just reset rotation - full implementation may require more complex handling - } - - // Handle UNSET values (use TABLE_UNSET) - float snapX = snap_x_tolerance ?? TableFlags.TABLE_UNSET; - float snapY = snap_y_tolerance ?? TableFlags.TABLE_UNSET; - float joinX = join_x_tolerance ?? TableFlags.TABLE_UNSET; - float joinY = join_y_tolerance ?? TableFlags.TABLE_UNSET; - float interX = intersection_x_tolerance ?? TableFlags.TABLE_UNSET; - float interY = intersection_y_tolerance ?? TableFlags.TABLE_UNSET; - - if (strategy != null) - { - vertical_strategy = strategy; - horizontal_strategy = strategy; - } - - Dictionary settings = new Dictionary - { - { "vertical_strategy", vertical_strategy }, - { "horizontal_strategy", horizontal_strategy }, - { "explicit_vertical_lines", vertical_lines }, - { "explicit_horizontal_lines", horizontal_lines }, - { "snap_tolerance", snap_tolerance }, - { "snap_x_tolerance", snapX }, - { "snap_y_tolerance", snapY }, - { "join_tolerance", join_tolerance }, - { "join_x_tolerance", joinX }, - { "join_y_tolerance", joinY }, - { "edge_min_length", edge_min_length }, - { "min_words_vertical", min_words_vertical }, - { "min_words_horizontal", min_words_horizontal }, - { "intersection_tolerance", intersection_tolerance }, - { "intersection_x_tolerance", interX }, - { "intersection_y_tolerance", interY }, - { "text_tolerance", text_tolerance }, - { "text_x_tolerance", text_x_tolerance }, - { "text_y_tolerance", text_y_tolerance } - }; - - TableFinder tbf = null; - try - { - // Get layout information if available - List layoutBoxes = new List(); - try - { - // Try to get layout information - this may not be available in all MuPDF.NET versions - // page.get_layout() and page.layout_information - // For now, we'll skip this and proceed with table detection - } - catch - { - // Layout information not available, continue without it - } - - // Resolve settings - TableSettings tset = TableSettings.Resolve(settings); - - // Create character list - TextPage textpage = TablePageProcessing.MakeChars(page, clip: clip); - TableGlobals.TEXTPAGE = textpage; - - // Create edges - TablePageProcessing.MakeEdges( - page, - clip: clip, - tset: tset, - paths: paths, - addLines: add_lines, - addBoxes: add_boxes - ); - - // Create TableFinder - tbf = new TableFinder(page, tset); - tbf.textpage = textpage; - - // Filter tables based on layout boxes if available - if (layoutBoxes.Count > 0) - { - tbf.tables = tbf.tables.Where(tab => - layoutBoxes.Any(box => IoU(tab.bbox, box) >= 0.6f) - ).ToList(); - - // Find layout boxes that don't match any found table - List unmatchedBoxes = layoutBoxes.Where(box => - tbf.tables.All(tab => IoU(box, tab.bbox) < 0.6f) - ).ToList(); - - // Create tables from unmatched layout boxes - if (unmatchedBoxes.Count > 0) + else { - // Extract words for make_table_from_bbox - var words = textpage.ExtractWords(); - List wordRects = words.Select(w => new Rect(w.X0, w.Y0, w.X1, w.Y1)).ToList(); - - // Create a textpage with TABLE_DETECTOR_FLAGS for make_table_from_bbox - TextPage tp2 = page.GetTextPage(flags: TableGlobals.TABLE_DETECTOR_FLAGS); - - foreach (Rect rect in unmatchedBoxes) + int cornerCount = corners.Count(c => currentCorners.Contains(c)); + if (cornerCount > 0) { - List cells = TableHelpers.MakeTableFromBbox(tp2, wordRects, rect); - if (cells.Count > 0) - { - tbf.tables.Add(new Table(page, cells)); - } + foreach (var c in corners) currentCorners.Add(c); + currentCells.Add(cell); + remaining.RemoveAt(i); } } } - - // Set textpage for all tables - foreach (var table in tbf.tables) + if (currentCells.Count == initialCount) { - table.textpage = textpage; + tables.Add(new List<(float, float, float, float)>(currentCells)); + currentCorners.Clear(); + currentCells.Clear(); } } - catch (Exception ex) - { - // Log exception - System.Diagnostics.Debug.WriteLine($"find_tables: exception occurred: {ex.Message}"); - return null; - } - finally + if (currentCells.Count > 0) + tables.Add(new List<(float, float, float, float)>(currentCells)); + + for (int i = tables.Count - 1; i >= 0; i--) { - if (needsRotationReset && oldRotation != 0) + var x1Vals = new HashSet(tables[i].Select(c => c.x1)); + var x0Vals = new HashSet(tables[i].Select(c => c.x0)); + if (x1Vals.Count < 2 || x0Vals.Count < 2) { - page.SetRotation(oldRotation); - // Note: Full page_rotation_reset would also restore mediabox and xref + tables.RemoveAt(i); + continue; } + float rx0 = tables[i].Min(c => c.x0); + float ry0 = tables[i].Min(c => c.y0); + float rx1 = tables[i].Max(c => c.x1); + float ry1 = tables[i].Max(c => c.y1); + if (!CharsInRect(chars, (rx0, ry0, rx1, ry1))) + tables.RemoveAt(i); } - return tbf; - } - - /// - /// Compute intersection over union (IoU) of two rectangles. - /// - private static float IoU(Rect r1, Rect r2) - { - float ix = Math.Max(0, Math.Min(r1.X1, r2.X1) - Math.Max(r1.X0, r2.X0)); - float iy = Math.Max(0, Math.Min(r1.Y1, r2.Y1) - Math.Max(r1.Y0, r2.Y0)); - float intersection = ix * iy; - - if (intersection == 0) - return 0; + tables.Sort((a, b) => + { + float ay = a.Min(c => c.y0); + float ax = a.Min(c => c.x0); + float by = b.Min(c => c.y0); + float bx = b.Min(c => c.x0); + int cmp = ay.CompareTo(by); + return cmp != 0 ? cmp : ax.CompareTo(bx); + }); - float area1 = (r1.X1 - r1.X0) * (r1.Y1 - r1.Y0); - float area2 = (r2.X1 - r2.X0) * (r2.Y1 - r2.Y0); - return intersection / (area1 + area2 - intersection); + return tables; } - } - // TableFinder class - public class TableFinder - { - public Page page { get; set; } - public TextPage textpage { get; set; } - public TableSettings settings { get; set; } - public List edges { get; set; } - public Dictionary, Dictionary>> intersections { get; set; } - public List cells { get; set; } - public List
tables { get; set; } + // ── Word-based edge generation ────────────────────────────────── - public TableFinder(Page page, TableSettings settings = null) + private static (float x0, float top, float x1, float bottom) ObjectsToBbox( + List> objects) { - this.page = page; - this.settings = settings ?? TableSettings.Resolve(); - this.edges = GetEdges(); - this.intersections = EdgeProcessing.EdgesToIntersections( - this.edges, - this.settings.intersection_x_tolerance, - this.settings.intersection_y_tolerance + return ( + objects.Min(o => F(o, "x0")), + objects.Min(o => F(o, "top")), + objects.Max(o => F(o, "x1")), + objects.Max(o => F(o, "bottom")) ); - this.cells = EdgeProcessing.IntersectionsToCells(this.intersections); - var cellGroups = EdgeProcessing.CellsToTables(this.page, this.cells); - this.tables = cellGroups.Select(cg => new Table(this.page, cg)).ToList(); } - private List GetEdges() + private static Dictionary ObjectsToRect( + List> objects) { - var settings = this.settings; - var edges = new List(); - - // Validate explicit strategies - foreach (string orientation in new[] { "vertical", "horizontal" }) + var bb = ObjectsToBbox(objects); + return new Dictionary { - string strategy = orientation == "vertical" ? settings.vertical_strategy : settings.horizontal_strategy; - if (strategy == "explicit") - { - var lines = orientation == "vertical" ? settings.explicit_vertical_lines : settings.explicit_horizontal_lines; - if (lines == null || lines.Count < 2) - { - throw new ArgumentException( - $"If {orientation}_strategy == 'explicit', " + - $"explicit_{orientation}_lines must be specified as a list of two or more edges."); - } - } - } + ["x0"] = bb.x0, ["top"] = bb.top, + ["x1"] = bb.x1, ["bottom"] = bb.bottom, + }; + } - string vStrat = settings.vertical_strategy; - string hStrat = settings.horizontal_strategy; + /// + /// Find imaginary horizontal edges connecting tops of word clusters. + /// + public static List> WordsToEdgesH( + List> words, int wordThreshold = 1) + { + var byTop = ClusterObjects(words, w => F(w, "top"), 1); + var largeClusters = byTop.Where(c => c.Count >= wordThreshold).ToList(); + var rects = largeClusters.Select(ObjectsToRect).ToList(); + if (rects.Count == 0) return new List>(); - List> words = new List>(); - if (vStrat == "text" || hStrat == "text") - { - words = TextExtractionHelpers.ExtractWords(TableGlobals.CHARS, settings.text_settings ?? new Dictionary()); - } + float minX0 = rects.Min(r => F(r, "x0")); + float maxX1 = rects.Max(r => F(r, "x1")); + var edges = new List>(); - // Vertical edges - var vExplicit = new List(); - if (settings.explicit_vertical_lines != null) + foreach (var r in rects) { - foreach (var desc in settings.explicit_vertical_lines) + edges.Add(new Dictionary { - if (desc is float x) - { - vExplicit.Add(new Edge - { - x0 = x, - x1 = x, - top = page.Rect.Y0, - bottom = page.Rect.Y1, - height = page.Rect.Height, - orientation = "v" - }); - } - else if (desc is Dictionary dict) - { - // Convert dictionary to Edge - var convertedEdges = EdgeProcessing.ObjToEdges(dict); - foreach (var e in convertedEdges) - { - if (e.orientation == "v") - vExplicit.Add(e); - } - } - else if (desc is Edge edge) - { - if (edge.orientation == "v") - vExplicit.Add(edge); - } - } + ["x0"] = minX0, ["x1"] = maxX1, + ["top"] = F(r, "top"), ["bottom"] = F(r, "top"), + ["width"] = maxX1 - minX0, + ["orientation"] = "h", + }); + edges.Add(new Dictionary + { + ["x0"] = minX0, ["x1"] = maxX1, + ["top"] = F(r, "bottom"), ["bottom"] = F(r, "bottom"), + ["width"] = maxX1 - minX0, + ["orientation"] = "h", + }); } + return edges; + } - List vBase; - if (vStrat == "lines") - { - vBase = EdgeProcessing.FilterEdges(TableGlobals.EDGES, "v"); - } - else if (vStrat == "lines_strict") + /// + /// Find imaginary vertical edges connecting left/right/centre of word clusters. + /// + public static List> WordsToEdgesV( + List> words, int wordThreshold = 3) + { + var byX0 = ClusterObjects(words, w => F(w, "x0"), 1); + var byX1 = ClusterObjects(words, w => F(w, "x1"), 1); + var byCenter = ClusterObjects(words, w => (F(w, "x0") + F(w, "x1")) / 2f, 1); + + var clusters = new List>>(); + clusters.AddRange(byX0); + clusters.AddRange(byX1); + clusters.AddRange(byCenter); + + var sorted = clusters.OrderByDescending(c => c.Count).ToList(); + var large = sorted.Where(c => c.Count >= wordThreshold).ToList(); + var bboxes = large.Select(c => ObjectsToBbox(c)).ToList(); + + var condensed = new List<(float x0, float top, float x1, float bottom)>(); + foreach (var bb in bboxes) { - vBase = EdgeProcessing.FilterEdges(TableGlobals.EDGES, "v", "line"); + bool overlap = condensed.Any(c => GetBboxOverlap( + (bb.x0, bb.top, bb.x1, bb.bottom), + (c.x0, c.top, c.x1, c.bottom)) != null); + if (!overlap) condensed.Add(bb); } - else if (vStrat == "text") + + if (condensed.Count == 0) return new List>(); + + float maxX1 = condensed.Max(c => c.x1); + float minTop = condensed.Min(c => c.top); + float maxBottom = condensed.Max(c => c.bottom); + + var edges = condensed.Select(b => new Dictionary { - vBase = EdgeProcessing.WordsToEdgesV(words, (int)settings.min_words_vertical); - } - else if (vStrat == "explicit") + ["x0"] = b.x0, ["x1"] = b.x0, + ["top"] = minTop, ["bottom"] = maxBottom, + ["height"] = maxBottom - minTop, + ["orientation"] = "v", + }).ToList(); + + edges.Add(new Dictionary { - vBase = new List(); - } - else + ["x0"] = maxX1, ["x1"] = maxX1, + ["top"] = minTop, ["bottom"] = maxBottom, + ["height"] = maxBottom - minTop, + ["orientation"] = "v", + }); + return edges; + } + + /// + /// Get the overlapping bbox of two bounding boxes, or null if none. + /// + public static (float x0, float y0, float x1, float y1)? GetBboxOverlap( + (float x0, float y0, float x1, float y1) a, + (float x0, float y0, float x1, float y1) b) + { + float oLeft = Math.Max(a.x0, b.x0); + float oRight = Math.Min(a.x1, b.x1); + float oBottom = Math.Min(a.y1, b.y1); + float oTop = Math.Max(a.y0, b.y0); + float oWidth = oRight - oLeft; + float oHeight = oBottom - oTop; + if (oHeight >= 0 && oWidth >= 0 && oHeight + oWidth > 0) + return (oLeft, oTop, oRight, oBottom); + return null; + } + + // ── Text extraction (word-level) ──────────────────────────────── + + /// + /// Extract words from character dictionaries (simplified version + /// of pdfplumber's WordExtractor). + /// + public static List> ExtractWords( + List> chars, + Dictionary settings = null) + { + float xTol = TableConstants.DefaultXTolerance; + float yTol = TableConstants.DefaultYTolerance; + if (settings != null) { - vBase = new List(); + if (settings.ContainsKey("x_tolerance")) + xTol = Convert.ToSingle(settings["x_tolerance"]); + if (settings.ContainsKey("y_tolerance")) + yTol = Convert.ToSingle(settings["y_tolerance"]); } - var v = vBase.Concat(vExplicit).ToList(); + if (chars.Count == 0) + return new List>(); + + var uprightChars = chars.Where(c => c.ContainsKey("upright") && (bool)c["upright"]).ToList(); + var lines = ClusterObjects(uprightChars, c => F(c, "doctop"), yTol); - // Horizontal edges - var hExplicit = new List(); - if (settings.explicit_horizontal_lines != null) + var words = new List>(); + foreach (var lineChars in lines) { - foreach (var desc in settings.explicit_horizontal_lines) + var sorted = lineChars.OrderBy(c => F(c, "x0")).ToList(); + var current = new List>(); + foreach (var ch in sorted) { - if (desc is float y) + string text = (string)ch["text"]; + if (string.IsNullOrWhiteSpace(text)) { - hExplicit.Add(new Edge + if (current.Count > 0) { - x0 = page.Rect.X0, - x1 = page.Rect.X1, - top = y, - bottom = y, - width = page.Rect.Width, - orientation = "h" - }); + words.Add(MergeWord(current)); + current.Clear(); + } + continue; } - else if (desc is Dictionary dict) + if (current.Count > 0) { - // Convert dictionary to Edge - var convertedEdges = EdgeProcessing.ObjToEdges(dict); - foreach (var e in convertedEdges) + float prevX1 = F(current[current.Count - 1], "x1"); + if (F(ch, "x0") > prevX1 + xTol) { - if (e.orientation == "h") - hExplicit.Add(e); + words.Add(MergeWord(current)); + current.Clear(); } } - else if (desc is Edge edge) - { - if (edge.orientation == "h") - hExplicit.Add(edge); - } + current.Add(ch); } + if (current.Count > 0) + words.Add(MergeWord(current)); } - - List hBase; - if (hStrat == "lines") - { - hBase = EdgeProcessing.FilterEdges(TableGlobals.EDGES, "h"); - } - else if (hStrat == "lines_strict") - { - hBase = EdgeProcessing.FilterEdges(TableGlobals.EDGES, "h", "line"); - } - else if (hStrat == "text") - { - hBase = EdgeProcessing.WordsToEdgesH(words, (int)settings.min_words_horizontal); - } - else if (hStrat == "explicit") - { - hBase = new List(); - } - else - { - hBase = new List(); - } - - var h = hBase.Concat(hExplicit).ToList(); - - edges = v.Concat(h).ToList(); - edges = EdgeProcessing.MergeEdges( - edges, - settings.snap_x_tolerance, - settings.snap_y_tolerance, - settings.join_x_tolerance, - settings.join_y_tolerance - ); - - return EdgeProcessing.FilterEdges(edges, minLength: settings.edge_min_length); - } - - public static List
FindTables(Page page, Rect clip, TableSettings settings) - { - var finder = new TableFinder(page, settings); - return finder.tables; + return words; } - public Table this[int i] + private static Dictionary MergeWord( + List> chars) { - get + var sb = new StringBuilder(); + foreach (var c in chars) { - int tcount = tables.Count; - if (i >= tcount) - throw new IndexOutOfRangeException("table not on page"); - while (i < 0) - i += tcount; - return tables[i]; + string t = (string)c["text"]; + if (TableConstants.Ligatures.ContainsKey(t)) + sb.Append(TableConstants.Ligatures[t]); + else + sb.Append(t); } + float x0 = chars.Min(c => F(c, "x0")); + float x1 = chars.Max(c => F(c, "x1")); + float top = chars.Min(c => F(c, "top")); + float bottom = chars.Max(c => F(c, "bottom")); + float doctop = top + (F(chars[0], "doctop") - F(chars[0], "top")); + return new Dictionary + { + ["text"] = sb.ToString(), + ["x0"] = x0, ["x1"] = x1, + ["top"] = top, ["bottom"] = bottom, + ["doctop"] = doctop, + ["upright"] = true, + ["direction"] = 1, + ["rotation"] = 0, + }; } - } - // Functions for making chars and edges from page - internal static class TablePageProcessing - { - // make_chars - Extract text as "rawdict" to fill CHARS - internal static TextPage MakeChars(Page page, Rect clip = null) - { - int pageNumber = page.Number + 1; - float pageHeight = page.Rect.Height; - var ctm = page.TransformationMatrix; + // ── Cell text extraction ──────────────────────────────────────── - var flags = TableGlobals.FLAGS; - var textpage = page.GetTextPage(clip: clip, flags: flags); - TableGlobals.TEXTPAGE = textpage; + /// + /// Extract text from a single cell bbox using the TextPage, + /// optionally with Markdown styling. + /// + public static string ExtractCells( + TextPage textpage, + (float x0, float y0, float x1, float y1) cell, + bool markdown = false) + { + if (textpage == null) return ""; - var pageInfo = textpage.ExtractRAWDict(cropbox: clip, sort: false); - float doctopBase = pageHeight * page.Number; + var sb = new StringBuilder(); + var stp = textpage.NativeStextPage.m_internal; + var block = stp.first_block; - foreach (var block in pageInfo.Blocks) + while (block != null) { - if (block.Lines == null) continue; + if (block.type != 0) { block = block.next; continue; } + float bx0 = block.bbox.x0, by0 = block.bbox.y0; + float bx1 = block.bbox.x1, by1 = block.bbox.y1; + if (bx0 > cell.x1 || bx1 < cell.x0 || by0 > cell.y1 || by1 < cell.y0) + { block = block.next; continue; } - foreach (var line in block.Lines) { - var ldir = line.Dir; - var ldirRounded = Tuple.Create((float)Math.Round(ldir.X, 4), (float)Math.Round(ldir.Y, 4)); - var matrix = new Matrix(ldirRounded.Item1, -ldirRounded.Item2, ldirRounded.Item2, ldirRounded.Item1, 0, 0); - bool upright = ldirRounded.Item2 == 0; + var line = new mupdf.FzStextBlock(block).begin().m_internal; + while (line != null) + { + float lx0 = line.bbox.x0, ly0 = line.bbox.y0; + float lx1 = line.bbox.x1, ly1 = line.bbox.y1; + if (lx0 > cell.x1 || lx1 < cell.x0 || ly0 > cell.y1 || ly1 < cell.y0) + { line = line.next; continue; } - if (line.Spans == null) continue; - var sortedSpans = line.Spans.OrderBy(s => s.Bbox.X0).ToList(); + if (sb.Length > 0) + sb.Append(markdown ? "
" : "\n"); - foreach (var span in sortedSpans) - { - string fontname = span.Font; - float fontsize = span.Size; - bool spanBold = ((int)span.Flags & (int)FontStyle.TEXT_FONT_BOLD) != 0; - var colorInt = span.Color; - - // Extract RGB from int color (ARGB format: AARRGGBB) - // Normalize to 0-1 range for PDF color space - float r = ((colorInt >> 16) & 0xFF) / 255.0f; - float g = ((colorInt >> 8) & 0xFF) / 255.0f; - float b = (colorInt & 0xFF) / 255.0f; - - if (span.Chars == null) continue; - var sortedChars = span.Chars.OrderBy(c => c.Bbox.x0).ToList(); - - foreach (var char_ in sortedChars) + var ch = line.first_char; + while (ch != null) { - var charBbox = char_.Bbox; - var bboxCtm = new Rect(charBbox) * ctm; - var origin = new Point(char_.Origin) * ctm; - matrix.E = origin.X; - matrix.F = origin.Y; - string text = char_.C.ToString(); - - var charDict = new CharDict - { - adv = upright ? (charBbox.x1 - charBbox.x0) : (charBbox.y1 - charBbox.y0), - bottom = charBbox.y1, - doctop = charBbox.y0 + doctopBase, - fontname = fontname, - height = charBbox.y1 - charBbox.y0, - matrix = Tuple.Create(matrix.A, matrix.B, matrix.C, matrix.D, matrix.E, matrix.F), - ncs = "DeviceRGB", - non_stroking_color = Tuple.Create(r, g, b), - non_stroking_pattern = null, - object_type = "char", - page_number = pageNumber, - size = upright ? fontsize : (charBbox.y1 - charBbox.y0), - stroking_color = Tuple.Create(r, g, b), - stroking_pattern = null, - bold = spanBold, - text = text, - top = charBbox.y0, - upright = upright, - width = charBbox.x1 - charBbox.x0, - x0 = charBbox.x0, - x1 = charBbox.x1, - y0 = bboxCtm.Y0, - y1 = bboxCtm.Y1 - }; - - TableGlobals.CHARS.Add(charDict); + float cx0 = ch.quad.ul.x, cy0 = ch.quad.ul.y; + float cx1 = ch.quad.lr.x, cy1 = ch.quad.lr.y; + + float overlapX0 = Math.Max(cx0, cell.x0); + float overlapY0 = Math.Max(cy0, cell.y0); + float overlapX1 = Math.Min(cx1, cell.x1); + float overlapY1 = Math.Min(cy1, cell.y1); + float overlapArea = Math.Max(0, overlapX1 - overlapX0) * + Math.Max(0, overlapY1 - overlapY0); + float charArea = Math.Max(0.0001f, (cx1 - cx0) * (cy1 - cy0)); + + if (overlapArea > 0.5f * charArea) + sb.Append((char)ch.c); + else if (ch.c == 32 || ch.c == 9) + sb.Append(' '); + + ch = ch.next; } + line = line.next; } } + block = block.next; } - - return textpage; + return sb.ToString().Trim(); } - // make_edges - Extract all page vector graphics to fill the EDGES list - internal static void MakeEdges( + // ── Page-level character / edge extraction ────────────────────── + + /// + /// Fill the list with per-character + /// dictionaries extracted from the page's text. + /// + internal static void MakeChars( Page page, - Rect clip = null, - TableSettings tset = null, - List paths = null, - List> addLines = null, - List addBoxes = null) + List> chars, + Rect clip = null) { - if (tset == null) - tset = TableSettings.Resolve(); - - float snapX = tset.snap_x_tolerance; - float snapY = tset.snap_y_tolerance; - float minLength = tset.edge_min_length; - bool linesStrict = tset.vertical_strategy == "lines_strict" || tset.horizontal_strategy == "lines_strict"; - - float pageHeight = page.Rect.Height; - float doctopBasis = page.Number * pageHeight; int pageNumber = page.Number + 1; - var prect = page.Rect; - - if (page.Rotation == 90 || page.Rotation == 270) - { - float w = prect.Width; - float h = prect.Height; - prect = new Rect(0, 0, h, w); - } - - if (clip == null) - clip = prect; - else - clip = new Rect(clip.X0, clip.Y0, clip.X1, clip.Y1); - - // Helper: Check if two rects are neighbors - bool AreNeighbors(Rect r1, Rect r2) - { - if ((r2.X0 - snapX <= r1.X0 && r1.X0 <= r2.X1 + snapX || - r2.X0 - snapX <= r1.X1 && r1.X1 <= r2.X1 + snapX) && - (r2.Y0 - snapY <= r1.Y0 && r1.Y0 <= r2.Y1 + snapY || - r2.Y0 - snapY <= r1.Y1 && r1.Y1 <= r2.Y1 + snapY)) - return true; - - if ((r1.X0 - snapX <= r2.X0 && r2.X0 <= r1.X1 + snapX || - r1.X0 - snapX <= r2.X1 && r2.X1 <= r1.X1 + snapX) && - (r1.Y0 - snapY <= r2.Y0 && r2.Y0 <= r1.Y1 + snapY || - r1.Y0 - snapY <= r2.Y1 && r2.Y1 <= r1.Y1 + snapY)) - return true; - - return false; - } + float pageHeight = page.Height; + float doctopBase = pageHeight * page.Number; + var stp = CreateTextPage(page, clip); + var block = stp.NativeStextPage.m_internal.first_block; - // Helper: Clean graphics - detect and join rectangles - Tuple, List> CleanGraphics(List npaths = null) + while (block != null) { - List allpaths = npaths ?? page.GetDrawings(); - var pathsList = new List(); - - foreach (var p in allpaths) + if (block.type != 0) { block = block.next; continue; } { - if (linesStrict && p.Type == "f" && p.Rect.Width > snapX && p.Rect.Height > snapY) - continue; - pathsList.Add(p); - } - - var prects = pathsList.Select(p => p.Rect).Distinct() - .OrderBy(r => r.Y1).ThenBy(r => r.X0).ToList(); - var newRects = new List(); - - while (prects.Count > 0) - { - var prect0 = prects[0]; - bool repeat = true; - - while (repeat) + var line = new mupdf.FzStextBlock(block).begin().m_internal; + while (line != null) { - repeat = false; - for (int i = prects.Count - 1; i > 0; i--) + bool upright = Math.Abs(line.dir.x - 1.0f) < 0.01f + || Math.Abs(line.dir.y - 1.0f) < 0.01f; + float dirX = (float)Math.Round(line.dir.x, 4); + float dirY = (float)Math.Round(line.dir.y, 4); + + var ch = line.first_char; + while (ch != null) { - if (AreNeighbors(prect0, prects[i])) + float x0 = ch.quad.ul.x, y0q = ch.quad.ul.y; + float x1 = ch.quad.lr.x, y1q = ch.quad.lr.y; + float bTop = Math.Min(y0q, y1q); + float bBottom = Math.Max(y0q, y1q); + float width = x1 - x0; + float height = bBottom - bTop; + float adv = upright ? width : height; + + var charDict = new Dictionary { - prect0 = prect0 | prects[i]; - prects.RemoveAt(i); - repeat = true; - } + ["adv"] = adv, + ["bottom"] = bBottom, + ["doctop"] = bTop + doctopBase, + ["fontname"] = "", + ["height"] = height, + ["matrix"] = new float[] { dirX, -dirY, dirY, dirX, x0, y0q }, + ["object_type"] = "char", + ["page_number"] = pageNumber, + ["size"] = upright ? ch.size : height, + ["bold"] = false, + ["text"] = ((char)ch.c).ToString(), + ["top"] = bTop, + ["upright"] = upright, + ["width"] = width, + ["x0"] = x0, + ["x1"] = x1, + ["y0"] = pageHeight - bBottom, + ["y1"] = pageHeight - bTop, + }; + chars.Add(charDict); + ch = ch.next; } + line = line.next; } - - if (TableHelpers.CharsInRect(TableGlobals.CHARS, prect0)) - newRects.Add(prect0); - - prects.RemoveAt(0); } - - return Tuple.Create(newRects, pathsList); + block = block.next; } + } + + /// + /// Create a TextPage for the given page, optionally clipped to a rectangle. + /// + internal static TextPage CreateTextPage(Page page, Rect clip = null) + { + return page.GetTextPage(0, clip?.IRect); + } - var (bboxes, cleanedPaths) = CleanGraphics(paths); + /// + /// Fill the list with vector-graphic line + /// dictionaries extracted from the page's drawings. + /// Page.GetDrawings() returns paths as List<Dictionary<string,object>>. + /// Each path has "items" (list of drawing commands), "width", "rect", etc. + /// Each item is a tuple-like list: ["l", p1, p2] or ["re", rect]. + /// + internal static void MakeEdges( + Page page, + List> edges, + List> chars, + TableSettings tset, + Rect clip = null) + { + float snapX = tset.SnapXTolerance; + float snapY = tset.SnapYTolerance; + float minLength = tset.EdgeMinLength; + float pageHeight = page.Height; + float doctopBasis = page.Number * pageHeight; + int pageNumber = page.Number + 1; + var prect = page.Rect; + var clipRect = clip ?? prect; - // Helper: Check if line is roughly axis-parallel - bool IsParallel(Point p1, Point p2) - { - return Math.Abs(p1.X - p2.X) <= snapX || Math.Abs(p1.Y - p2.Y) <= snapY; - } + bool IsParallel(float x1, float y1, float x2, float y2) + => Math.Abs(x1 - x2) <= snapX || Math.Abs(y1 - y2) <= snapY; - // Helper: Make line dictionary - Dictionary MakeLine(PathInfo p, Point p1, Point p2, Rect clipRect) + Dictionary MakeLineDict( + float lw, float p1x, float p1y, float p2x, float p2y) { - if (!IsParallel(p1, p2)) - return null; + if (!IsParallel(p1x, p1y, p2x, p2y)) return null; + float x0 = Math.Min(p1x, p2x); + float x1m = Math.Max(p1x, p2x); + float y0 = Math.Min(p1y, p2y); + float y1m = Math.Max(p1y, p2y); - float x0 = Math.Min(p1.X, p2.X); - float x1 = Math.Max(p1.X, p2.X); - float y0 = Math.Min(p1.Y, p2.Y); - float y1 = Math.Max(p1.Y, p2.Y); - - if (x0 > clipRect.X1 || x1 < clipRect.X0 || y0 > clipRect.Y1 || y1 < clipRect.Y0) + if (x0 > clipRect.X1 || x1m < clipRect.X0 || + y0 > clipRect.Y1 || y1m < clipRect.Y0) return null; - if (x0 < clipRect.X0) x0 = clipRect.X0; - if (x1 > clipRect.X1) x1 = clipRect.X1; - if (y0 < clipRect.Y0) y0 = clipRect.Y0; - if (y1 > clipRect.Y1) y1 = clipRect.Y1; + x0 = Math.Max(x0, (float)clipRect.X0); + x1m = Math.Min(x1m, (float)clipRect.X1); + y0 = Math.Max(y0, (float)clipRect.Y0); + y1m = Math.Min(y1m, (float)clipRect.Y1); - float width = x1 - x0; - float height = y1 - y0; - if (width == 0 && height == 0) - return null; + float w = x1m - x0; + float h = y1m - y0; + if (w == 0 && h == 0) return null; return new Dictionary { - { "x0", x0 }, - { "y0", pageHeight - y0 }, - { "x1", x1 }, - { "y1", pageHeight - y1 }, - { "width", width }, - { "height", height }, - { "pts", new List { new List { x0, y0 }, new List { x1, y1 } } }, - { "linewidth", p.Width }, - { "stroke", true }, - { "fill", false }, - { "evenodd", false }, - { "stroking_color", p.Color ?? p.Fill }, - { "non_stroking_color", null }, - { "object_type", "line" }, - { "page_number", pageNumber }, - { "stroking_pattern", null }, - { "non_stroking_pattern", null }, - { "top", y0 }, - { "bottom", y1 }, - { "doctop", y0 + doctopBasis } + ["x0"] = x0, + ["y0"] = pageHeight - y0, + ["x1"] = x1m, + ["y1"] = pageHeight - y1m, + ["width"] = w, + ["height"] = h, + ["linewidth"] = lw, + ["object_type"] = "line", + ["page_number"] = pageNumber, + ["top"] = y0, + ["bottom"] = y1m, + ["doctop"] = y0 + doctopBasis, + ["orientation"] = (h == 0) ? "h" : "v", }; } - // Process paths - foreach (var p in cleanedPaths) - { - if (p.Items == null) continue; + var paths = page.GetDrawings(); + if (paths == null || paths.Count == 0) return; - var items = new List(p.Items); + foreach (var pathDict in paths) + { + float lw = pathDict.ContainsKey("width") ? Convert.ToSingle(pathDict["width"]) : 1f; + if (!pathDict.ContainsKey("items")) continue; + var items = pathDict["items"] as IEnumerable; + if (items == null) continue; - // If closePath, add line from last to first point - if (p.ClosePath && items.Count > 0 && items[0].Type == "l" && items[items.Count - 1].Type == "l") - { - var lastItem = items[items.Count - 1]; - var firstItem = items[0]; - if (lastItem.P2 != null && firstItem.P1 != null) - { - items.Add(new Item - { - Type = "l", - P1 = lastItem.P2, - P2 = firstItem.P1 - }); - } - } + var pathRect = pathDict.ContainsKey("rect") ? pathDict["rect"] : null; + bool closePath = pathDict.ContainsKey("closePath") && Convert.ToBoolean(pathDict["closePath"]); - foreach (var item in items) + foreach (var rawItem in items) { - if (item.Type == "l") // Line + if (rawItem is Dictionary itemDict) { - if (item.P1 != null && item.LastPoint != null) + string itemType = itemDict.ContainsKey("type") ? (string)itemDict["type"] : ""; + if (itemType == "l" && itemDict.ContainsKey("p1") && itemDict.ContainsKey("p2")) { - var lineDict = MakeLine(p, item.P1, item.LastPoint, clip); - if (lineDict != null) - { - var edge = EdgeProcessing.LineToEdge(lineDict); - TableGlobals.EDGES.Add(edge); - } + var p1 = (float[])itemDict["p1"]; + var p2 = (float[])itemDict["p2"]; + var ld = MakeLineDict(lw, p1[0], p1[1], p2[0], p2[1]); + if (ld != null) edges.Add(LineToEdge(ld)); } - } - else if (item.Type == "re" && item.Rect != null) // Rectangle - { - var rect = item.Rect; - rect.Normalize(); - - // Check if simulates a vertical line - if (rect.Width <= minLength && rect.Width < rect.Height) + else if (itemType == "re" && itemDict.ContainsKey("rect")) { - float x = Math.Abs(rect.X1 + rect.X0) / 2; - var p1 = new Point(x, rect.Y0); - var p2 = new Point(x, rect.Y1); - var lineDict = MakeLine(p, p1, p2, clip); - if (lineDict != null) + var r = (float[])itemDict["rect"]; + float rX0 = r[0], rY0 = r[1], rX1 = r[2], rY1 = r[3]; + float rW = Math.Abs(rX1 - rX0), rH = Math.Abs(rY1 - rY0); + if (rW <= minLength && rW < rH) { - var edge = EdgeProcessing.LineToEdge(lineDict); - TableGlobals.EDGES.Add(edge); + float mx = (rX0 + rX1) / 2f; + var ld = MakeLineDict(lw, mx, rY0, mx, rY1); + if (ld != null) edges.Add(LineToEdge(ld)); + continue; } - continue; - } - - // Check if simulates a horizontal line - if (rect.Height <= minLength && rect.Height < rect.Width) - { - float y = Math.Abs(rect.Y1 + rect.Y0) / 2; - var p1 = new Point(rect.X0, y); - var p2 = new Point(rect.X1, y); - var lineDict = MakeLine(p, p1, p2, clip); - if (lineDict != null) + if (rH <= minLength && rH < rW) { - var edge = EdgeProcessing.LineToEdge(lineDict); - TableGlobals.EDGES.Add(edge); + float my = (rY0 + rY1) / 2f; + var ld = MakeLineDict(lw, rX0, my, rX1, my); + if (ld != null) edges.Add(LineToEdge(ld)); + continue; } - continue; + var l1 = MakeLineDict(lw, rX0, rY0, rX0, rY1); + if (l1 != null) edges.Add(LineToEdge(l1)); + var l2 = MakeLineDict(lw, rX0, rY1, rX1, rY1); + if (l2 != null) edges.Add(LineToEdge(l2)); + var l3 = MakeLineDict(lw, rX1, rY1, rX1, rY0); + if (l3 != null) edges.Add(LineToEdge(l3)); + var l4 = MakeLineDict(lw, rX1, rY0, rX0, rY0); + if (l4 != null) edges.Add(LineToEdge(l4)); } - - // Decompose rectangle into 4 lines - var tl = new Point(rect.X0, rect.Y0); - var tr = new Point(rect.X1, rect.Y0); - var bl = new Point(rect.X0, rect.Y1); - var br = new Point(rect.X1, rect.Y1); - - var lineDict1 = MakeLine(p, tl, bl, clip); - if (lineDict1 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict1)); - - var lineDict2 = MakeLine(p, bl, br, clip); - if (lineDict2 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict2)); - - var lineDict3 = MakeLine(p, br, tr, clip); - if (lineDict3 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict3)); - - var lineDict4 = MakeLine(p, tr, tl, clip); - if (lineDict4 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict4)); - } - else if (item.Type == "qu" && item.Quad != null) // Quad - { - var quad = item.Quad; - var ul = quad.UpperLeft; - var ur = quad.UpperRight; - var ll = quad.LowerLeft; - var lr = quad.LowerRight; - - var lineDict1 = MakeLine(p, ul, ll, clip); - if (lineDict1 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict1)); - - var lineDict2 = MakeLine(p, ll, lr, clip); - if (lineDict2 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict2)); - - var lineDict3 = MakeLine(p, lr, ur, clip); - if (lineDict3 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict3)); - - var lineDict4 = MakeLine(p, ur, ul, clip); - if (lineDict4 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict4)); } } } + } - // Add border lines for all enveloping bboxes - var defaultPath = new PathInfo { Color = new float[] { 0, 0, 0 }, Fill = null, Width = 1 }; - foreach (var bbox in bboxes) - { - var tl = new Point(bbox.X0, bbox.Y0); - var tr = new Point(bbox.X1, bbox.Y0); - var bl = new Point(bbox.X0, bbox.Y1); - var br = new Point(bbox.X1, bbox.Y1); - - var lineDict1 = MakeLine(defaultPath, tl, tr, clip); - if (lineDict1 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict1)); - - var lineDict2 = MakeLine(defaultPath, bl, br, clip); - if (lineDict2 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict2)); - - var lineDict3 = MakeLine(defaultPath, tl, bl, clip); - if (lineDict3 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict3)); - - var lineDict4 = MakeLine(defaultPath, tr, br, clip); - if (lineDict4 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict4)); - } - - // Add user-specified lines - if (addLines != null) - { - foreach (var (p1, p2) in addLines) - { - var lineDict = MakeLine(defaultPath, p1, p2, clip); - if (lineDict != null) - TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict)); - } - } - - // Add user-specified boxes - if (addBoxes != null) - { - foreach (var box in addBoxes) - { - var tl = new Point(box.X0, box.Y0); - var tr = new Point(box.X1, box.Y0); - var bl = new Point(box.X0, box.Y1); - var br = new Point(box.X1, box.Y1); - - var lineDict1 = MakeLine(defaultPath, tl, bl, clip); - if (lineDict1 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict1)); - - var lineDict2 = MakeLine(defaultPath, bl, br, clip); - if (lineDict2 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict2)); - - var lineDict3 = MakeLine(defaultPath, br, tr, clip); - if (lineDict3 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict3)); + // ── Convenience accessor ──────────────────────────────────────── - var lineDict4 = MakeLine(defaultPath, tr, tl, clip); - if (lineDict4 != null) TableGlobals.EDGES.Add(EdgeProcessing.LineToEdge(lineDict4)); - } - } + /// + /// Safely read a float value from a string-keyed dictionary. + /// + internal static float F(Dictionary d, string key) + { + return Convert.ToSingle(d[key]); } } + } diff --git a/MuPDF.NET/TextPage.PythonCompat.cs b/MuPDF.NET/TextPage.PythonCompat.cs new file mode 100644 index 00000000..fd2069a3 --- /dev/null +++ b/MuPDF.NET/TextPage.PythonCompat.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace MuPDF.NET +{ + /// + /// Python-name compatibility for (src/__init__.py TextPage). + /// + public partial class TextPage + { + /// Python TextPage.search(needle, hit_max=0, quads=1) with quads=True (quad hits). + public List search(string needle, int hit_max = 16) + => Search(needle, hit_max); + + /// Python TextPage.search(..., quads=False) (merged axis-aligned rectangles). + public List search_rects(string needle, int hit_max = 16) + => SearchRects(needle, hit_max); + } +} diff --git a/MuPDF.NET/TextPage.cs b/MuPDF.NET/TextPage.cs index 567609dc..5afcb6a9 100644 --- a/MuPDF.NET/TextPage.cs +++ b/MuPDF.NET/TextPage.cs @@ -1,1610 +1,871 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -using mupdf; -using Newtonsoft.Json; +using System.Text; +using System.Text.Json; namespace MuPDF.NET { - public class TextPage : IDisposable + /// + /// Represents the text content of a page, created by Page.GetTextPage(). + /// Port of the Python TextPage class from __init__.py. + /// + public partial class TextPage : IDisposable { - static TextPage() - { - Utils.InitApp(); - } - - internal FzStextPage _nativeTextPage; - - public bool ThisOwn { get; set; } + private mupdf.FzStextPage _nativeStp; + private bool _disposed; + internal Page Parent { get; set; } - public Page Parent = null; - - /// - /// Rect of Stext Page - /// - private FzRect _mediaBox = null; - private FzRect MediaBox - { - get { - if (_mediaBox == null) - { - _mediaBox = new FzRect(_nativeTextPage.m_internal.mediabox); - } - return _mediaBox; - } - } - - /// - /// Block List of Text - /// - private List _blocks = null; - public List Blocks + internal mupdf.FzStextPage NativeStextPage { get { - if (_blocks == null) - { - List blocks = new List(); - for ( - fz_stext_block block = _nativeTextPage.m_internal.first_block; - block != null; - block = block.next - ) - { - blocks.Add(new FzStextBlock(block)); - } - _blocks = blocks; - } - return _blocks; + if (_disposed) throw new ObjectDisposedException(nameof(TextPage)); + return _nativeStp; } } - /// - /// MuPDFStextPage Constructor - /// - /// Page Rectangle Size - public TextPage(FzRect rect) - { - _nativeTextPage = new FzStextPage(rect); - Parent = null; - } - - public TextPage(FzStextPage stPage) + internal TextPage(mupdf.FzStextPage stp) { - _nativeTextPage = stPage; - Parent = null; + _nativeStp = stp; } /// - /// MuPDFStextPage Contructor + /// Page rectangle of the text page. + /// Corresponds to Python TextPage.rect property. /// - /// MuPDFStextPage object - public TextPage(TextPage stPage) + public Rect Rect { - _nativeTextPage = stPage._nativeTextPage; - Parent = stPage.Parent; - } - - public TextPage(FzPage page) - { - _nativeTextPage = new FzStextPage(page, new FzStextOptions()); - } - - public void Dispose() - { - if (_nativeTextPage != null) + get { - _nativeTextPage.Dispose(); - _nativeTextPage = null; + var r = _nativeStp.m_internal.mediabox; + return new Rect(r.x0, r.y0, r.x1, r.y1); } } + // ─── Text Extraction ──────────────────────────────────────────── + /// - /// Extract Stext Page + /// Extract plain text from the page. + /// Corresponds to Python TextPage._extractText(format_=0) / extractText(). /// - /// format of return value - /// string of Text, HTML, XHTML, .. according to format - public string ExtractText(ExtractFormat format) + public string ExtractText(bool sort = false) { - FzStextPage stPage = _nativeTextPage; - FzBuffer buffer = new FzBuffer(1024); - FzOutput output = new mupdf.FzOutput(buffer); - - if (format == ExtractFormat.HTML) - { - output.fz_print_stext_page_as_html(stPage, 0); - } - else if (format == ExtractFormat.XML) + if (!sort) { - output.fz_print_stext_page_as_xml(stPage, 0); + var buf = mupdf.mupdf.fz_new_buffer_from_stext_page(NativeStextPage); + return Encoding.UTF8.GetString(buf.fz_buffer_extract()); } - else if (format == ExtractFormat.XHTML) + var blocks = ExtractBlocks(); + blocks.Sort((a, b) => { - output.fz_print_stext_page_as_xhtml(stPage, 0); - } - else - { - output.fz_print_stext_page_as_text(stPage); - } - - string ret = buffer.fz_string_from_buffer(); - - output.fz_close_output(); - output.Dispose(); - - return ret; + int cmp = a.y1.CompareTo(b.y1); + return cmp != 0 ? cmp : a.x0.CompareTo(b.x0); + }); + var sb = new StringBuilder(); + foreach (var bl in blocks) sb.Append(bl.text); + return sb.ToString(); } /// - /// Extract Blocks in StextPage + /// Extract text blocks as list of (x0, y0, x1, y1, text, blockNo, blockType). + /// Corresponds to Python TextPage.extractBLOCKS(). + /// Faithfully ported from extra.i extractBLOCKS. /// - /// Return List of TextBlock - public List ExtractBlocks() + public List<(float x0, float y0, float x1, float y1, string text, int blockNo, int blockType)> ExtractBlocks() { - int blockNum = -1; - FzRect stPageRect = new FzRect(_nativeTextPage.m_internal.mediabox); - FzBuffer res = new FzBuffer(1024); - List lines = new List(); + var lines = new List<(float, float, float, float, string, int, int)>(); + int block_n = -1; + var tp_rect = new mupdf.FzRect(NativeStextPage.m_internal.mediabox); - foreach (FzStextBlock block in Blocks) + for (var block_iter = NativeStextPage.begin(); + block_iter.m_internal != NativeStextPage.end().m_internal; + block_iter = block_iter.__increment__()) { - blockNum += 1; - FzRect blockRect = new FzRect(FzRect.Fixed.Fixed_EMPTY); - string text = ""; - if (block.m_internal.type == (int)STextBlockType.FZ_STEXT_BLOCK_TEXT) + var block = block_iter.__ref__(); + block_n++; + var blockrect = new mupdf.FzRect(mupdf.FzRect.Fixed.Fixed_EMPTY); + + if (block.m_internal.type == mupdf.mupdf.FZ_STEXT_BLOCK_TEXT) { - res.fz_clear_buffer(); - int lineNum = -1; - int lastChar = 0; - for ( - fz_stext_line line = block.begin().__ref__().m_internal; - line != null; - line = line.next - ) + var res = mupdf.mupdf.fz_new_buffer(1024); + int last_char = 0; + + for (var line_iter = block.begin(); + line_iter.m_internal != block.end().m_internal; + line_iter = line_iter.__increment__()) { - lineNum += 1; - FzRect lineRect = new FzRect(FzRect.Fixed.Fixed_EMPTY); - for (fz_stext_char ch = line.first_char; ch != null; ch = ch.next) + var line = line_iter.__ref__(); + var linerect = new mupdf.FzRect(mupdf.FzRect.Fixed.Fixed_EMPTY); + + for (var ch_iter = line.begin(); + ch_iter.m_internal != line.end().m_internal; + ch_iter = ch_iter.__increment__()) { - FzRect cbbox = GetCharBbox(new FzStextLine(line), new FzStextChar(ch)); - if ( - !IsRectsOverlap(stPageRect, cbbox) - && stPageRect.fz_is_infinite_rect() == 0 - ) + var ch = ch_iter.__ref__(); + var cbbox = Helpers.JM_char_bbox(line.m_internal, ch.m_internal); + if (!Helpers.JM_rects_overlap(tp_rect, cbbox) + && mupdf.mupdf.fz_is_infinite_rect(tp_rect) == 0) + { continue; - - res.fz_append_rune(ch.c); - lastChar = ch.c; - lineRect = FzRect.fz_union_rect(lineRect, cbbox); + } + mupdf.mupdf.fz_append_rune(res, ch.m_internal.c); + last_char = ch.m_internal.c; + linerect = mupdf.mupdf.fz_union_rect(linerect, cbbox); } + if (last_char != 10 && mupdf.mupdf.fz_is_empty_rect(linerect) == 0) + { + mupdf.mupdf.fz_append_byte(res, 10); + } + blockrect = mupdf.mupdf.fz_union_rect(blockrect, linerect); + } - if (lastChar != 10 && lineRect.fz_is_empty_rect() != 0) - res.fz_append_rune(10); - - blockRect = FzRect.fz_union_rect(blockRect, lineRect); + if (mupdf.mupdf.fz_is_empty_rect(blockrect) == 0) + { + string text = Encoding.UTF8.GetString(res.fz_buffer_extract()); + lines.Add((blockrect.x0, blockrect.y0, blockrect.x1, blockrect.y1, + text, block_n, block.m_internal.type)); } - text = Utils.EscapeStrFromBuffer(res); } - else if ( - IsRectsOverlap(stPageRect, new FzRect(block.m_internal.bbox)) - || stPageRect.fz_is_infinite_rect() != 0 - ) - { - FzImage img = block.i_image(); - FzColorspace cs = img.colorspace(); - text = string.Format( - "", - cs.fz_colorspace_name(), - img.w(), - img.h(), - img.bpc() - ); - blockRect = FzRect.fz_union_rect(blockRect, new FzRect(block.m_internal.bbox)); - } - if (blockRect.fz_is_empty_rect() == 0) + else { - TextBlock line = new TextBlock(); - line.X0 = blockRect.x0; - line.Y0 = blockRect.y0; - line.X1 = blockRect.x1; - line.Y1 = blockRect.y1; - line.BlockNum = blockNum; - line.Text = text; - line.Type = block.m_internal.type; - - lines.Add(line); + if (Helpers.JM_rects_overlap(tp_rect, new mupdf.FzRect(block.m_internal.bbox)) + || mupdf.mupdf.fz_is_infinite_rect(tp_rect) != 0) + { + var img = block.i_image(); + var cs = img.colorspace(); + string text = $""; + blockrect = mupdf.mupdf.fz_union_rect(blockrect, new mupdf.FzRect(block.m_internal.bbox)); + if (mupdf.mupdf.fz_is_empty_rect(blockrect) == 0) + { + lines.Add((blockrect.x0, blockrect.y0, blockrect.x1, blockrect.y1, + text, block_n, block.m_internal.type)); + } + } } } return lines; } /// - /// Extract Stext Page + /// Extract text words as list of (x0, y0, x1, y1, word, blockNo, lineNo, wordNo). + /// Corresponds to Python TextPage.extractWORDS(). + /// Faithfully ported from extra.i extractWORDS. /// - /// Rectangle to extract in StextPage - /// - /// Page information of CropBox - public PageInfo ExtractDict(Rect cropbox, bool sort = false) - { - PageInfo pageDict = TextPage2Dict(false); - if (cropbox != null) - { - pageDict.Width = cropbox.Width; - pageDict.Height = cropbox.Height; - } - if (sort is true) - { - List blocks = pageDict.Blocks; - blocks.Sort( - (b1, b2) => + public List<(float x0, float y0, float x1, float y1, string word, int blockNo, int lineNo, int wordNo)> ExtractWords(string delimiters = null) + { + var lines = new List<(float, float, float, float, string, int, int, int)>(); + int block_n = -1; + var tp_rect = new mupdf.FzRect(NativeStextPage.m_internal.mediabox); + var buff = mupdf.mupdf.fz_new_buffer(64); + var wbbox = new mupdf.FzRect(mupdf.FzRect.Fixed.Fixed_EMPTY); + int buflen = 0; + int last_char_rtl = 0; + + for (var block_iter = NativeStextPage.begin(); + block_iter.m_internal != NativeStextPage.end().m_internal; + block_iter = block_iter.__increment__()) + { + var block = block_iter.__ref__(); + block_n++; + if (block.m_internal.type != mupdf.mupdf.FZ_STEXT_BLOCK_TEXT) + continue; + + int line_n = -1; + for (var line_iter = block.begin(); + line_iter.m_internal != block.end().m_internal; + line_iter = line_iter.__increment__()) + { + var line = line_iter.__ref__(); + line_n++; + int word_n = 0; + mupdf.mupdf.fz_clear_buffer(buff); + buflen = 0; + + for (var ch_iter = line.begin(); + ch_iter.m_internal != line.end().m_internal; + ch_iter = ch_iter.__increment__()) { - if (b1.Bbox.Y1 == b2.Bbox.Y1) + var ch = ch_iter.__ref__(); + var cbbox = Helpers.JM_char_bbox(line.m_internal, ch.m_internal); + if (!Helpers.JM_rects_overlap(tp_rect, cbbox) + && mupdf.mupdf.fz_is_infinite_rect(tp_rect) == 0) { - return b1.Bbox.X0.CompareTo(b2.Bbox.X0); + continue; } - else + if (buflen == 0 && ch.m_internal.c == 0x200d) + continue; + + bool word_delimiter = JM_is_word_delimiter(ch.m_internal.c, delimiters); + int this_char_rtl = JM_is_rtl_char(ch.m_internal.c) ? 1 : 0; + + if (word_delimiter || this_char_rtl != last_char_rtl) { - return b1.Bbox.Y1.CompareTo(b2.Bbox.Y1); + if (buflen == 0 && word_delimiter) + continue; + if (mupdf.mupdf.fz_is_empty_rect(wbbox) == 0) + { + string w = Encoding.UTF8.GetString(buff.fz_buffer_extract()); + lines.Add((wbbox.x0, wbbox.y0, wbbox.x1, wbbox.y1, w, block_n, line_n, word_n)); + word_n++; + wbbox = new mupdf.FzRect(mupdf.FzRect.Fixed.Fixed_EMPTY); + } + mupdf.mupdf.fz_clear_buffer(buff); + buflen = 0; + if (word_delimiter) + continue; } + mupdf.mupdf.fz_append_rune(buff, ch.m_internal.c); + last_char_rtl = this_char_rtl; + buflen++; + wbbox = mupdf.mupdf.fz_union_rect(wbbox, cbbox); } - ); - - pageDict.Blocks = blocks; + if (buflen > 0 && mupdf.mupdf.fz_is_empty_rect(wbbox) == 0) + { + string w = Encoding.UTF8.GetString(buff.fz_buffer_extract()); + lines.Add((wbbox.x0, wbbox.y0, wbbox.x1, wbbox.y1, w, block_n, line_n, word_n)); + word_n++; + wbbox = new mupdf.FzRect(mupdf.FzRect.Fixed.Fixed_EMPTY); + } + buflen = 0; + } } - return pageDict; + return lines; } /// - /// Extract Stext Page in HTML + /// Extract text in HTML format. + /// Corresponds to Python TextPage.extractHTML(). /// - /// string of HTML public string ExtractHtml() { - return ExtractText(ExtractFormat.HTML); + var res = mupdf.mupdf.fz_new_buffer(1024); + var output = new mupdf.FzOutput(res); + mupdf.mupdf.fz_print_stext_page_as_html(output, NativeStextPage, 0); + output.fz_close_output(); + return Encoding.UTF8.GetString(res.fz_buffer_extract()); } /// - /// Extract Stext Page with Image Information + /// Extract text in XHTML format. + /// Corresponds to Python TextPage.extractXHTML(). /// - /// Encode image with given hash value - public List ExtractImageInfo(int hashes = 0) + public string ExtractXhtml() { - int blockNum = -1; - List rc = new List(); - foreach (FzStextBlock block in Blocks) - { - blockNum += 1; - - if (block.m_internal.type == (int)STextBlockType.FZ_STEXT_BLOCK_TEXT) - continue; - FzImage img = block.i_image(); - vectoruc digest = new vectoruc(); - if (hashes != 0) - { - FzIrect r = new FzIrect( - Utils.FZ_MIN_INF_RECT, - Utils.FZ_MIN_INF_RECT, - Utils.FZ_MAX_INF_RECT, - Utils.FZ_MAX_INF_RECT - ); - - Debug.Assert(r.fz_is_infinite_irect() != 0, "Rect is infinite"); - - FzMatrix m = new FzMatrix(img.w(), 0, 0, img.h(), 0, 0); - - SWIGTYPE_p_int swigW = new SWIGTYPE_p_int(new IntPtr(), false); - SWIGTYPE_p_int swigH = new SWIGTYPE_p_int(new IntPtr(), false); - FzPixmap pixmap = img.fz_get_pixmap_from_image(r, m, swigW, swigH); - digest = pixmap.fz_md5_pixmap2(); - } - FzColorspace cs = new FzColorspace( - mupdf.mupdf.ll_fz_keep_colorspace(img.m_internal.colorspace) - ); - Block blockDict = new Block(); - blockDict.Number = blockNum; - blockDict.Bbox = new Rect(new FzRect(block.m_internal.bbox)); - blockDict.Transform = new Matrix(block.i_transform()); - blockDict.Width = img.w(); - blockDict.Height = img.h(); - blockDict.ColorSpace = cs.fz_colorspace_n(); - blockDict.CsName = cs.fz_colorspace_name(); - blockDict.Xres = img.xres(); - blockDict.Yres = img.yres(); - blockDict.Bpc = img.bpc(); - blockDict.Size = img.fz_image_size(); - if (hashes != 0) - { - blockDict.Digest = digest; - } - rc.Add(blockDict); - } - return rc; + var res = mupdf.mupdf.fz_new_buffer(1024); + var output = new mupdf.FzOutput(res); + mupdf.mupdf.fz_print_stext_page_as_xhtml(output, NativeStextPage, 0); + output.fz_close_output(); + return Encoding.UTF8.GetString(res.fz_buffer_extract()); } /// - /// Extract StextPage in JSON format + /// Extract text in XML format. + /// Corresponds to Python TextPage.extractXML(). /// - /// Rectangle area to extract - /// - public string ExtractJSON(Rect cropbox = null, bool sort = false) + public string ExtractXml() { - PageInfo pageDict = TextPage2Dict(false); - if (cropbox != null) - { - pageDict.Width = cropbox.Width; - pageDict.Height = cropbox.Height; - } - - if (sort) - { - List blocks = pageDict.Blocks; - blocks.Sort( - (b1, b2) => - { - if (b1.Bbox.Y1 == b2.Bbox.Y1) - { - return b1.Bbox.X0.CompareTo(b2.Bbox.X0); - } - else - { - return b1.Bbox.Y1.CompareTo(b2.Bbox.Y1); - } - } - ); - - pageDict.Blocks = blocks; - } - string ret = JsonConvert.SerializeObject(pageDict, Formatting.Indented); - return ret; + var res = mupdf.mupdf.fz_new_buffer(1024); + var output = new mupdf.FzOutput(res); + mupdf.mupdf.fz_print_stext_page_as_xml(output, NativeStextPage, 0); + output.fz_close_output(); + return Encoding.UTF8.GetString(res.fz_buffer_extract()); } - public string ExtractXML() + private void _getNewBlockList(Dictionary page_dict, bool raw) { - return ExtractText(ExtractFormat.XML); + JM_make_textpage_dict(NativeStextPage, page_dict, raw); } - /// - /// Extract Stext Page in format of PageStruct - /// - /// Rectangle Area to Extract - /// - /// - public PageInfo ExtractRAWDict(Rect cropbox, bool sort = false) + private Dictionary _textpage_dict(bool raw = false) { - PageInfo pageDict = TextPage2Dict(true); - if (cropbox != null) - { - pageDict.Width = cropbox.Width; - pageDict.Height = cropbox.Height; - } - if (sort is true) + var page_dict = new Dictionary { - List blocks = pageDict.Blocks; - blocks.Sort( - (b1, b2) => - { - if (b1.Bbox.Y1 == b2.Bbox.Y1) - { - return b1.Bbox.X0.CompareTo(b2.Bbox.X0); - } - else - { - return b1.Bbox.Y1.CompareTo(b2.Bbox.Y1); - } - } - ); - - pageDict.Blocks = blocks; - } - return pageDict; + ["width"] = Rect.Width, + ["height"] = Rect.Height, + }; + _getNewBlockList(page_dict, raw); + return page_dict; } /// - /// Extract selection in format of string + /// Extract text as a dictionary (page level) with full block/line/span detail. + /// Corresponds to Python TextPage.extractDICT(raw=False). + /// Faithfully ported from extra.i _as_dict / JM_make_text_block / JM_make_spanlist. /// - /// begin point of selection - /// end point of selection - /// returns text in format of string - public string ExtractSelection(Point a, Point b) + public Dictionary ExtractDict(bool sort = false) { - return mupdf.mupdf.fz_copy_selection(_nativeTextPage, a.ToFzPoint(), b.ToFzPoint(), 0); - } - - /// - /// Extract Stext Page in format of Text - /// - /// - /// Return String - public string ExtractText(bool sort = false) - { - if (sort is false) + var page_dict = _textpage_dict(raw: false); + if (sort) { - return ExtractText(ExtractFormat.TEXT); - } - - List blocks = ExtractBlocks(); - blocks.Sort( - (b1, b2) => + var block_list = (List>)page_dict["blocks"]; + block_list.Sort((a, b) => { - if (b1.Y1 == b2.Y1) - { - return b1.X0.CompareTo(b2.X0); - } - else - { - return b1.Y1.CompareTo(b2.Y1); - } - } - ); - - string ret = ""; - foreach (TextBlock b in blocks) - { - ret += b.Text; + var ba = (float[])a["bbox"]; + var bb = (float[])b["bbox"]; + int cmp = ba[3].CompareTo(bb[3]); + return cmp != 0 ? cmp : ba[0].CompareTo(bb[0]); + }); } - - return ret; + return page_dict; } /// - /// Extract TextBoxes + /// Extract text as a raw dictionary with character-level detail (no span text merging). + /// Corresponds to Python TextPage.extractRAWDICT(raw=True). /// - /// Rectangle Area to extract - /// Returns string in area - public string ExtractTextBox(FzRect area) + public Dictionary ExtractRawDict(bool sort = false) { - bool isNeedNewLine = false; - string ret = ""; - - foreach (FzStextBlock block in Blocks) + var page_dict = _textpage_dict(raw: true); + if (sort) { - if (block.m_internal.type != (int)STextBlockType.FZ_STEXT_BLOCK_TEXT) - continue; - - for ( - fz_stext_line line = block.begin().__ref__().m_internal; - line != null; - line = line.next - ) + var block_list = (List>)page_dict["blocks"]; + block_list.Sort((a, b) => { - bool isLineHadText = false; - for (fz_stext_char ch = line.first_char; ch != null; ch = ch.next) - { - FzRect r = GetCharBbox(new FzStextLine(line), new FzStextChar(ch)); - if (IsRectsOverlap(area, r)) - { - isLineHadText = true; - if (isNeedNewLine) - { - ret += "\n"; - isNeedNewLine = false; - } - - ret += MakeEscape(ch.c); - } - } - if (isLineHadText) - isNeedNewLine = true; - } + var ba = (float[])a["bbox"]; + var bb = (float[])b["bbox"]; + int cmp = ba[3].CompareTo(bb[3]); + return cmp != 0 ? cmp : ba[0].CompareTo(bb[0]); + }); } - - return Utils.DecodeRawUnicodeEscape(ret); + return page_dict; } /// - /// Extract Words + /// Extract text in JSON format. + /// Corresponds to Python TextPage.extractJSON(). /// - /// - /// - public List ExtractWords(char[] delimiters = null) + public string ExtractJson(bool sort = false) { - int bufferLen; - int blockNum = -1; - FzRect wordBox = new FzRect(FzRect.Fixed.Fixed_EMPTY); - FzRect stPageRect = MediaBox; - - List lines = new List(); - FzBuffer buf = new FzBuffer(64); - foreach (FzStextBlock block in Blocks) - { - blockNum += 1; - if (block.m_internal.type != (int)STextBlockType.FZ_STEXT_BLOCK_TEXT) - continue; - - int lineNum = -1; - for ( - fz_stext_line line = block.begin().__ref__().m_internal; - line != null; - line = line.next - ) - { - lineNum += 1; - int wordNum = 0; - buf.fz_clear_buffer(); - bufferLen = 0; - for (fz_stext_char ch = line.first_char; ch != null; ch = ch.next) - { - FzRect cbBox = GetCharBbox(new FzStextLine(line), new FzStextChar(ch)); - if ( - !IsRectsOverlap(stPageRect, cbBox) - && stPageRect.fz_is_infinite_rect() == 0 - ) - continue; - - bool isWordDelimiter = IsWordDelimiter(ch.c, delimiters); - if (isWordDelimiter) - { - if (bufferLen == 0) - continue; - if (wordBox.fz_is_empty_rect() == 0) - { - wordNum = AppendWord( - lines, - buf, - wordBox, - blockNum, - lineNum, - wordNum - ); - wordBox = new FzRect(FzRect.Fixed.Fixed_EMPTY); - } - buf.fz_clear_buffer(); - bufferLen = 0; - continue; - } - buf.fz_append_rune(ch.c); - bufferLen += 1; - wordBox = FzRect.fz_union_rect( - wordBox, - GetCharBbox(new FzStextLine(line), new FzStextChar(ch)) - ); - } - if (bufferLen != 0 && wordBox.fz_is_empty_rect() == 0) - { - wordNum = AppendWord(lines, buf, wordBox, blockNum, lineNum, wordNum); - wordBox = new FzRect(FzRect.Fixed.Fixed_EMPTY); - } - bufferLen = 0; - } - } - return lines; + var dict = ExtractDict(sort); + return JsonSerializer.Serialize(dict, new JsonSerializerOptions { WriteIndented = true }); } /// - /// Extract Stext Page in format of XHtml + /// Extract raw dictionary in JSON format. + /// Corresponds to Python TextPage.extractRAWJSON(). /// - /// Returns string in format of XHtml - public string ExtractXHtml() - { - return ExtractText(ExtractFormat.XHTML); - } - - public uint PoolSize() + public string ExtractRawJson(bool sort = false) { - FzPool pool = new FzPool(_nativeTextPage.m_internal.pool); - uint size = pool.fz_pool_size(); - pool.m_internal = null; - return size; + var dict = ExtractRawDict(sort); + return JsonSerializer.Serialize(dict, new JsonSerializerOptions { WriteIndented = true }); } /// - /// Extract Hits of Matching in format of FzQuad List + /// Return a list with image meta information. + /// Corresponds to Python TextPage.extractIMGINFO(). /// - /// Stext Page - /// Text to search - /// - public static List Search( - TextPage stPage, - string needle, - int hitMax = 10000, - bool quad = true - ) + public List> ExtractImgInfo(bool hashes = false) { - FzRect rect = stPage.MediaBox; - List quads = new List(); - if (string.IsNullOrEmpty(needle)) - return quads; - - /*Hits hits = new Hits(); - - hits.Len = 0; - hits.Quads = quads; - hits.HFuzz = 0.2f; - hits.VFuzz = 0.1f; - - FzBuffer buffer = stPage.GetBufferFromStextPage(); - string hayStackString = buffer.fz_string_from_buffer(); - - int hayStack = 0; - int begin = 0; - int end = 0;*/ - - vectorq ret = stPage._nativeTextPage.search_stext_page(needle, null, hitMax); // use MuPDF api for search function - - foreach (FzQuad q in ret) - quads.Add(new Quad(q)); - - return quads; - - /* int inside = 0; - - foreach (FzStextBlock block in stPage.Blocks) + int block_n = -1; + var rc = new List>(); + for (var block_iter = NativeStextPage.begin(); + block_iter.m_internal != NativeStextPage.end().m_internal; + block_iter = block_iter.__increment__()) { - if (block.m_internal.type != (int)STextBlockType.FZ_STEXT_BLOCK_TEXT) + var block = block_iter.__ref__(); + block_n++; + if (block.m_internal.type == mupdf.mupdf.FZ_STEXT_BLOCK_TEXT) continue; - for ( - fz_stext_line line = block.begin().__ref__().m_internal; - line != null; - line = line.next - ) - { - for (fz_stext_char ch = line.first_char; ch != null; ch = ch.next) - { - if (rect.fz_is_infinite_rect() == 0) - { - FzRect r = stPage.GetCharBbox( - new FzStextLine(line), - new FzStextChar(ch) - ); - if (!stPage.IsRectsOverlap(rect, r)) - continue; - } - while (true) - { - if (inside == 0) - { - if (hayStack >= begin) - { - inside = 1; - } - } - if (inside != 0) - { - if (hayStack < end) - { - stPage.OnHighlightChar( - hits, - new FzStextLine(line), - new FzStextChar(ch) - ); - break; - } - else - { - inside = 0; - (begin, end) = stPage.FindString( - hayStackString.Substring(hayStack), - needle - ); - - if (begin == -1) - { - goto no_more_matches; - } - else - { - begin += hayStack; - end += hayStack; - continue; - } - } - } - break; - } - Tuple res = TextPage.Char2Canon( - hayStackString.Substring(hayStack) - ); - hayStack += res.Item1; - } - hayStack += 1; - } - hayStack += 1; - } - no_more_matches: - ; - buffer.fz_clear_buffer(); - int items = quads.Count; - if (items == 0) - return quads; - int i = 0; - List ret = new List(); - for (i = 0; i < items; i++) - { - if (!quad) - ret.Add(quads[i].Rect); - } + var wrappedBlock = block; + var img = wrappedBlock.i_image(); + int img_size = 0; + var mask = img.mask(); + bool has_mask = mask.m_internal != null; - if (quad) - return quads; + var compr_buff = mupdf.mupdf.fz_compressed_image_buffer(img); + if (compr_buff.m_internal != null) + img_size = (int)compr_buff.fz_compressed_buffer_size(); - i = 0; - while (i < items - 1) - { - Quad v1 = quads[i]; - Quad v2 = quads[i + 1]; - if ((v1.Rect.Y1 != v2.Rect.Y1) || (v1.Rect & v2.Rect).IsEmpty) + byte[] digest = null; + if (hashes) { - i += 1; - continue; + var r = new mupdf.FzIrect(mupdf.mupdf.fz_infinite_irect); + var m = new mupdf.FzMatrix(img.w(), 0, 0, img.h(), 0, 0); + var pix = img.fz_get_pixmap_from_image(r, m, null, null); + var md5 = pix.fz_md5_pixmap2(); + digest = new byte[md5.Count]; + for (int i = 0; i < md5.Count; i++) + digest[i] = md5[i]; + if (img_size == 0) + img_size = img.w() * img.h() * img.n(); } - quads[i] = (v1.Rect | v2.Rect).Quad; - quads.RemoveAt(i + 1); - items -= 1; - } - - return quads; - */ - } - - internal void OnHighlightChar(Hits hits, FzStextLine line, FzStextChar ch) - { - float vFuzz = ch.m_internal.size * hits.VFuzz; - float hFuzz = ch.m_internal.size * hits.HFuzz; - FzQuad chQuad = GetCharQuad(line, ch); - if (hits.Len > 0) - { - FzQuad end = hits.Quads[hits.Len - 1].ToFzQuad(); - if ( - true - && HDist( - new FzPoint(line.m_internal.dir), - new FzPoint(end.lr), - new FzPoint(chQuad.ll) - ) < hFuzz - && VDist( - new FzPoint(line.m_internal.dir), - new FzPoint(end.lr), - new FzPoint(chQuad.ll) - ) < vFuzz - && HDist( - new FzPoint(line.m_internal.dir), - new FzPoint(end.ur), - new FzPoint(chQuad.ul) - ) < hFuzz - && VDist( - new FzPoint(line.m_internal.dir), - new FzPoint(end.ur), - new FzPoint(chQuad.ll) - ) < vFuzz - ) + var cs = img.colorspace(); + var bbox = block.m_internal.bbox; + var matrix = wrappedBlock.i_transform(); + var block_dict = new Dictionary { - end.ur = chQuad.ur; - end.lr = chQuad.lr; - Debug.Assert(hits.Quads[-1].ToFzQuad() == end); - return; - } + ["number"] = block_n, + ["bbox"] = new float[] { bbox.x0, bbox.y0, bbox.x1, bbox.y1 }, + ["transform"] = new float[] { matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f }, + ["width"] = img.w(), + ["height"] = img.h(), + ["colorspace"] = mupdf.mupdf.fz_colorspace_n(cs), + ["cs-name"] = mupdf.mupdf.fz_colorspace_name(cs), + ["xres"] = img.xres(), + ["yres"] = img.yres(), + ["bpc"] = img.bpc(), + ["size"] = img_size, + ["has-mask"] = has_mask + }; + if (hashes) + block_dict["digest"] = digest; + rc.Add(block_dict); } - - hits.Quads.Add(new Quad(chQuad)); - hits.Len += 1; - } - - internal float HDist(FzPoint dir, FzPoint a, FzPoint b) - { - float dx = b.x - a.x; - float dy = b.y - a.y; - return Math.Abs(dx * dir.x + dy * dir.y); + return rc; } - internal float VDist(FzPoint dir, FzPoint a, FzPoint b) - { - float dx = b.x - a.x; - float dy = b.y - a.y; - return Math.Abs(dx * dir.y + dy * dir.x); - } + // ─── Search ───────────────────────────────────────────────────── /// - /// Get Buffer from Stext Page + /// Search for a string. Returns list of Quad hit rectangles. + /// Corresponds to Python TextPage.search(..., quads=True). /// - /// Buffer - internal FzBuffer GetBufferFromStextPage() + public List Search(string needle, int maxHits = 16) { - FzRect r = MediaBox; - FzBuffer buf = new FzBuffer(256); - foreach (FzStextBlock block in Blocks) + var result = new List(); + if (string.IsNullOrEmpty(needle)) return result; + var quads = NativeStextPage.search_stext_page(needle, null, maxHits); + foreach (var q in quads) { - if (block.m_internal.type == (int)STextBlockType.FZ_STEXT_BLOCK_TEXT) - { - for ( - fz_stext_line line = block.begin().__ref__().m_internal; - line != null; - line = line.next - ) - { - for (fz_stext_char ch = line.first_char; ch != null; ch = ch.next) - { - if ( - !IsRectsOverlap( - r, - GetCharBbox(new FzStextLine(line), new FzStextChar(ch)) - ) - && r.fz_is_infinite_rect() == 0 - ) - { - continue; - } - buf.fz_append_rune(ch.c); - } - buf.fz_append_byte(Convert.ToInt32('\n')); - } - buf.fz_append_byte(Convert.ToInt32('\n')); - } + result.Add(new Quad( + new Point(q.ul.x, q.ul.y), + new Point(q.ur.x, q.ur.y), + new Point(q.ll.x, q.ll.y), + new Point(q.lr.x, q.lr.y))); + if (result.Count >= maxHits) break; } - return buf; + return result; } - public (int, int) FindString(string s, string needle) - { - for (int index = 0; index < s.Length - needle.Length; index++) - { - int end = MatchString(s.Substring(index), needle); - - if (end != -1) - { - end += index; - return (index, end); - } - } - - return (-1, -1); - } - - public int MatchString(string h0, string n0) - { - int h = 0; - int n = 0; - int e = h; - Tuple hc = Char2Canon(h0.Substring(h)); - h += hc.Item1; - Tuple nc = Char2Canon(n0.Substring(n)); - n += nc.Item1; - while (hc.Item2 == nc.Item2) + /// + /// Search for a string; returns axis-aligned rectangles with the same merge pass as PyMuPDF + /// TextPage.search(..., quads=False) (join overlapping hits on the same baseline). + /// + public List SearchRects(string needle, int maxHits = 16) + { + var quads = Search(needle, maxHits); + if (quads.Count == 0) + return new List(); + var val = new List(quads.Count); + foreach (var q in quads) + val.Add(q.Rect); + int items = val.Count; + int i = 0; + while (i < items - 1) { - e = h; - if (hc.Item2 == Convert.ToInt32(' ')) - { - while (true) - { - hc = Char2Canon(h0.Substring(h)); - h += hc.Item1; - if (hc.Item2 != Convert.ToInt32(' ')) - { - break; - } - } - } - else - { - hc = Char2Canon(h0.Substring(h)); - h += hc.Item1; - } - if (nc.Item2 == Convert.ToInt32(' ')) - { - while (true) - { - nc = Char2Canon(n0.Substring(n)); - n += nc.Item1; - if (nc.Item2 != Convert.ToInt32(' ')) - { - break; - } - } - } - else + var v1 = val[i]; + var v2 = val[i + 1]; + if (Math.Abs(v1.Y1 - v2.Y1) > Constants.Epsilon || (v1 & v2).IsEmpty) { - nc = Char2Canon(n0.Substring(n)); - n += nc.Item1; + i++; + continue; } + val[i] = v1 | v2; + val.RemoveAt(i + 1); + items--; } - - return nc.Item2 != 0 ? -1 : e; + return val; } - public static int Canon(int c) + /// + /// Extract text inside a rectangle. + /// Corresponds to Python TextPage.extractTextbox(). + /// + public string ExtractTextbox(Rect rect) { - if (c == 0xA0 || c == 0x2028 || c == 0x2029) - { - return Convert.ToInt32(' '); - } - if ( - c == Convert.ToInt32('\r') - || c == Convert.ToInt32('\n') - || c == Convert.ToInt32('\t') - ) - { - return Convert.ToInt32(' '); - } - - int A = Convert.ToInt32('A'); - if (c >= A && c <= Convert.ToInt32('Z')) - { - return c - A + Convert.ToInt32('a'); - } - - return c; + var area = new mupdf.FzRect((float)rect.X0, (float)rect.Y0, (float)rect.X1, (float)rect.Y1); + return NativeStextPage.fz_copy_rectangle(area, 0); } - public static Tuple Char2Canon(string s) + /// + /// Extract text between two selection points. + /// Corresponds to Python TextPage.extractSelection(). + /// + public string ExtractSelection(Point pointA, Point pointB) { - ll_fz_chartorune_outparams outparams = new ll_fz_chartorune_outparams(); - - int n = mupdf.mupdf.ll_fz_chartorune_outparams_fn(s, outparams); - int c = Canon(outparams.rune); - /*if (s.Length == 0) - return new Tuple(1, -1);*/ - return new Tuple(n, c); + var a = new mupdf.FzPoint((float)pointA.X, (float)pointA.Y); + var b = new mupdf.FzPoint((float)pointB.X, (float)pointB.Y); + return NativeStextPage.fz_copy_selection(a, b, 0); } - internal int AppendWord( - List lines, - FzBuffer buf, - FzRect wordBox, - int blockNum, - int lineNum, - int wordNum - ) - { - string s = Utils.EscapeStrFromBuffer(buf); - WordBlock item = new WordBlock() - { - X0 = wordBox.x0, - Y0 = wordBox.y0, - X1 = wordBox.x1, - Y1 = wordBox.y1, - Text = s, - BlockNum = blockNum, - LineNum = lineNum, - WordNum = wordNum - }; + // ─── Internal: Faithful port of extra.i _as_dict ──────────────── - lines.Add(item); - return wordNum + 1; - } - - internal bool IsWordDelimiter(int ch, char[] delimiters) + private void JM_make_textpage_dict( + mupdf.FzStextPage tp, + Dictionary page_dict, + bool raw) { - if (ch <= 32 || ch == 160) - return true; - if (delimiters == null) - return false; - - char _ch = (char)ch; - foreach (char _c in delimiters) - { - if (_c == _ch) - return true; - } - - return false; + var text_buffer = mupdf.mupdf.fz_new_buffer(128); + var block_list = new List>(); + var tp_rect = new mupdf.FzRect(tp.m_internal.mediabox); + var block = tp.m_internal.first_block; + int block_n = -1; + JM_as_dict(block_list, block, text_buffer, raw, tp_rect, block_n); + page_dict["blocks"] = block_list; } /// - /// Utf8 to Unicode + /// Recursive function for output by blocks as identified by MuPDF SEGMENT logic. + /// Port of extra.i _as_dict(). /// - /// - /// - internal string MakeEscape(int ch) + private int JM_as_dict( + List> block_list, + mupdf.fz_stext_block block, + mupdf.FzBuffer text_buffer, + bool raw, + mupdf.FzRect tp_rect, + int block_n) { - if (ch == 92) - { - return "\\u005c"; - } - else if (32 <= ch && ch <= 127 || ch == 10) - { - return ((char)ch).ToString(); - } - else if (0xd800 <= ch && ch <= 0xdfff) - { - return "\\ufffd"; - } - else if (ch <= 0xffff) - { - return "\\u" + ch.ToString("x4"); - } - else + while (block != null) { - return "\\U" + ch.ToString("x8"); + if (block.type == mupdf.mupdf.FZ_STEXT_BLOCK_TEXT) + { + if (Helpers.JM_rects_overlap(tp_rect, new mupdf.FzRect(block.bbox)) + || mupdf.mupdf.fz_is_infinite_rect(tp_rect) != 0) + { + var block_dict = new Dictionary(); + block_n++; + block_dict["type"] = block.type; + block_dict["number"] = block_n; + JM_make_text_block(block, block_dict, raw, text_buffer, tp_rect); + block_list.Add(block_dict); + } + } + else if (block.type == mupdf.mupdf.FZ_STEXT_BLOCK_IMAGE) + { + if (mupdf.mupdf.fz_contains_rect(tp_rect, new mupdf.FzRect(block.bbox)) != 0 + || mupdf.mupdf.fz_is_infinite_rect(tp_rect) != 0) + { + var block_dict = new Dictionary(); + block_n++; + block_dict["type"] = block.type; + block_dict["number"] = block_n; + block_dict["bbox"] = new float[] { block.bbox.x0, block.bbox.y0, block.bbox.x1, block.bbox.y1 }; + JM_make_image_block(block, block_dict); + block_list.Add(block_dict); + } + } + block = block.next; } + return block_n; } /// - /// Make Block List of Stext Page + /// Port of extra.i JM_make_text_block(). /// - /// PageStruct including BlockList (width/height set by caller; blocks are filled here). - /// - internal void GetNewBlockList(PageInfo pageDict, bool raw) - { - MakeTextPage2Dict(pageDict, raw); - } - - internal PageInfo TextPage2Dict(bool raw = false) - { - PageInfo pageDict = new PageInfo - { - Width = MediaBox.x1 - MediaBox.x0, - Height = MediaBox.y1 - MediaBox.y0, - Blocks = new List() - }; + private void JM_make_text_block( + mupdf.fz_stext_block block, + Dictionary block_dict, + bool raw, + mupdf.FzBuffer buff, + mupdf.FzRect tp_rect) + { + var line_list = new List>(); + var block_rect = new mupdf.FzRect(mupdf.FzRect.Fixed.Fixed_EMPTY); + + var wrappedBlock = new mupdf.FzStextBlock(block); + for (var line_iter = wrappedBlock.begin(); + line_iter.m_internal != wrappedBlock.end().m_internal; + line_iter = line_iter.__increment__()) + { + var line = line_iter.__ref__(); + var fzLine = line.m_internal; + + var intersect = mupdf.mupdf.fz_intersect_rect(tp_rect, new mupdf.FzRect(fzLine.bbox)); + if (mupdf.mupdf.fz_is_empty_rect(intersect) != 0 + && mupdf.mupdf.fz_is_infinite_rect(tp_rect) == 0) + { + continue; + } - GetNewBlockList(pageDict, raw); - return pageDict; - } + var line_dict = new Dictionary(); + var line_rect = JM_make_spanlist(line_dict, line, raw, buff, tp_rect); - public string ExtractRawJSON(Rect cropbox = null, bool sort = false) - { - PageInfo val = TextPage2Dict(true); - if (cropbox != null) - { - val.Width = cropbox.Width; - val.Height = cropbox.Height; - } - if (sort == true) - { - List blocks = val.Blocks; - blocks.Sort( - (b1, b2) => - { - if (b1.Bbox.Y1 == b2.Bbox.Y1) - { - return b1.Bbox.X0.CompareTo(b2.Bbox.X0); - } - else - { - return b1.Bbox.Y1.CompareTo(b2.Bbox.Y1); - } - } - ); - val.Blocks = blocks; + block_rect = mupdf.mupdf.fz_union_rect(block_rect, line_rect); + line_dict["wmode"] = (int)fzLine.wmode; + line_dict["dir"] = new float[] { fzLine.dir.x, fzLine.dir.y }; + line_dict["bbox"] = new float[] { line_rect.x0, line_rect.y0, line_rect.x1, line_rect.y1 }; + line_list.Add(line_dict); } - string ret = JsonConvert.SerializeObject(val, Formatting.Indented); - return ret; + block_dict["bbox"] = new float[] { block_rect.x0, block_rect.y0, block_rect.x1, block_rect.y1 }; + block_dict["lines"] = line_list; } - internal void MakeTextPage2Dict(PageInfo pageDict, bool raw) - { - FzBuffer textBuffer = new FzBuffer(128); - FzRect stPageRect = MediaBox; - int blockNum = -1; - foreach (FzStextBlock block in Blocks) - { - blockNum += 1; - - int btype = block.m_internal.type; - FzRect blockBboxNative = new FzRect(block.m_internal.bbox); - bool pageFinite = stPageRect.fz_is_infinite_rect() == 0; - - if (btype == mupdf.mupdf.FZ_STEXT_BLOCK_STRUCT) + /// + /// Port of extra.i JM_make_spanlist(). + /// Builds span list for a single line, splitting spans by style changes. + /// + private mupdf.FzRect JM_make_spanlist( + Dictionary line_dict, + mupdf.FzStextLine line, + bool raw, + mupdf.FzBuffer buff, + mupdf.FzRect tp_rect) + { + var span_list = new List>(); + mupdf.mupdf.fz_clear_buffer(buff); + var span_rect = new mupdf.FzRect(mupdf.FzRect.Fixed.Fixed_EMPTY); + var line_rect = new mupdf.FzRect(mupdf.FzRect.Fixed.Fixed_EMPTY); + float span_origin_x = 0, span_origin_y = 0; + + float old_size = -1; + uint old_flags = 0; + uint old_char_flags = 0; + string old_font = ""; + uint old_argb = 0; + ushort old_bidi = 0; + + Dictionary span = null; + List> char_list = null; + + for (var ch_iter = line.begin(); + ch_iter.m_internal != line.end().m_internal; + ch_iter = ch_iter.__increment__()) + { + var ch = ch_iter.__ref__(); + var fzCh = ch.m_internal; + var fzLine = line.m_internal; + + var r = Helpers.JM_char_bbox(fzLine, fzCh); + if (!Helpers.JM_rects_overlap(tp_rect, r) + && mupdf.mupdf.fz_is_infinite_rect(tp_rect) == 0) { - /* MuPdf recurses via block->u.s.down->first_block; fz_stext_block has no union accessors in these bindings. */ continue; } - if (pageFinite) - { - if (btype == mupdf.mupdf.FZ_STEXT_BLOCK_IMAGE) - { - if (stPageRect.fz_contains_rect(blockBboxNative) == 0) - continue; - } - else if ( - btype == mupdf.mupdf.FZ_STEXT_BLOCK_TEXT - || btype == mupdf.mupdf.FZ_STEXT_BLOCK_VECTOR - || btype == mupdf.mupdf.FZ_STEXT_BLOCK_GRID - ) - { - if (!IsRectsOverlap(stPageRect, blockBboxNative)) - continue; - } - } - - Block blockDict = new Block(); - - blockDict.Number = blockNum; - blockDict.Type = btype; - if (btype == mupdf.mupdf.FZ_STEXT_BLOCK_IMAGE) + int flags = Helpers.JM_char_font_flags(fzCh.font, fzLine, fzCh); + float size = fzCh.size; + uint char_flags = (uint)(fzCh.flags & ~mupdf.mupdf.FZ_STEXT_SYNTHETIC); + string fontName = Helpers.JM_font_name(fzCh.font); + uint argb = fzCh.argb; + float asc = Helpers.JM_font_ascender(fzCh.font); + float desc = Helpers.JM_font_descender(fzCh.font); + ushort bidi = fzCh.bidi; + + bool style_changed = + size != old_size + || (uint)flags != old_flags + || char_flags != old_char_flags + || argb != old_argb + || fontName != old_font + || bidi != old_bidi; + + if (style_changed) { - blockDict.Bbox = new Rect(blockBboxNative); - FzImage image = block.i_image(); - int n = image.colorspace().fz_colorspace_n(); - int w = image.w(); - int h = image.h(); - int type = (int)ImageType.FZ_IMAGE_UNKNOWN; - - FzCompressedBuffer compressedBuffer = image.fz_compressed_image_buffer(); - if (compressedBuffer != null && compressedBuffer.m_internal != null) - type = compressedBuffer.m_internal.params_.type; - - if (type < (int)ImageType.FZ_IMAGE_BMP || type == (int)ImageType.FZ_IMAGE_JBIG2) - type = (int)ImageType.FZ_IMAGE_UNKNOWN; - - FzBuffer buf = null; - FzBuffer ownedBuf = null; - FzBuffer maskOwned = null; - string ext; - try + if (old_size >= 0) { - if (compressedBuffer == null || type == (int)ImageType.FZ_IMAGE_UNKNOWN) - { - ownedBuf = mupdf.mupdf.fz_new_buffer_from_image_as_png( - image, - new FzColorParams() - ); - buf = ownedBuf; - ext = "png"; - } - else if (n == 4 && Utils.GetImageExtension(type) == "jpeg") + if (raw) { - ownedBuf = image.fz_new_buffer_from_image_as_jpeg( - new FzColorParams(), - 95, - 1 - ); - buf = ownedBuf; - ext = "jpeg"; + span["chars"] = char_list; + char_list = null; } else { - buf = new FzBuffer( - mupdf.mupdf.ll_fz_keep_buffer(compressedBuffer.m_internal.buffer) - ); - ext = Utils.GetImageExtension(type); - } - - blockDict.Width = w; - blockDict.Height = h; - blockDict.Ext = ext; - blockDict.ColorSpace = n; - blockDict.Xres = image.xres(); - blockDict.Yres = image.yres(); - blockDict.Bpc = image.bpc(); - blockDict.Transform = new Matrix(block.i_transform()); - blockDict.Image = Utils.BinFromBuffer(buf); - blockDict.Size = (uint)blockDict.Image.Length; - /* - FzImage maskIm = image.mask(); - if (maskIm != null) - { - maskOwned = mupdf.mupdf.fz_new_buffer_from_image_as_png( - maskIm, - new FzColorParams() - ); - blockDict.Mask = Utils.BinFromBuffer(maskOwned); + span["text"] = Encoding.UTF8.GetString( + buff.fz_buffer_extract()); + mupdf.mupdf.fz_clear_buffer(buff); } - */ + span["origin"] = new float[] { span_origin_x, span_origin_y }; + span["bbox"] = new float[] { span_rect.x0, span_rect.y0, span_rect.x1, span_rect.y1 }; + line_rect = mupdf.mupdf.fz_union_rect(line_rect, span_rect); + span_list.Add(span); + span = null; } - catch (Exception) - { - blockDict.Image = Array.Empty(); - blockDict.Mask = null; - blockDict.Size = 0; - blockDict.Ext = "png"; - } - finally + + span = new Dictionary(); + float spanAsc = asc, spanDesc = desc; + if (asc < 1e-3f) { - ownedBuf?.Dispose(); - maskOwned?.Dispose(); + spanAsc = 0.9f; + spanDesc = -0.1f; } + span["size"] = size; + span["flags"] = (uint)flags; + span["bidi"] = (uint)bidi; + span["char_flags"] = char_flags; + span["font"] = fontName; + span["color"] = argb & 0xffffffu; + span["alpha"] = argb >> 24; + span["ascender"] = spanAsc; + span["descender"] = spanDesc; + + old_size = size; + old_flags = (uint)flags; + old_char_flags = char_flags; + old_font = fontName; + old_argb = argb; + old_bidi = bidi; + span_rect = r; + span_origin_x = fzCh.origin.x; + span_origin_y = fzCh.origin.y; } - else if (btype == mupdf.mupdf.FZ_STEXT_BLOCK_TEXT) - { - List lineList = new List(); - FzRect blockRect = new FzRect(FzRect.Fixed.Fixed_EMPTY); - ushort syntheticMask = (ushort)mupdf.mupdf.FZ_STEXT_SYNTHETIC; + span_rect = mupdf.mupdf.fz_union_rect(span_rect, r); - for ( - fz_stext_line line = block.begin().__ref__().m_internal; - line != null; - line = line.next - ) - { - { - FzRect lineBbox = new FzRect(line.bbox); - float ix0 = Math.Max(stPageRect.x0, lineBbox.x0); - float iy0 = Math.Max(stPageRect.y0, lineBbox.y0); - float ix1 = Math.Min(stPageRect.x1, lineBbox.x1); - float iy1 = Math.Min(stPageRect.y1, lineBbox.y1); - bool intersectionEmpty = ix0 >= ix1 || iy0 >= iy1; - if (intersectionEmpty && pageFinite) - continue; - } - - Line lineDict = new Line(); - - List charList = new List(); - List spanList = new List(); - mupdf.mupdf.fz_clear_buffer(textBuffer); - FzRect spanRect = new FzRect(FzRect.Fixed.Fixed_EMPTY); - FzRect lineRect = new FzRect(FzRect.Fixed.Fixed_EMPTY); - - MuPDFCharStyle style = new MuPDFCharStyle(); - MuPDFCharStyle oldStyle = new MuPDFCharStyle(); - - Span span = new Span(); - FzPoint spanOrigin = new FzPoint(); - - for (fz_stext_char ch = line.first_char; ch != null; ch = ch.next) - { - try - { - FzRect r = GetCharBbox(new FzStextLine(line), new FzStextChar(ch)); - if (!IsRectsOverlap(stPageRect, r) && pageFinite) - continue; - float flags = CharFontFlags( - new FzFont(mupdf.mupdf.ll_fz_keep_font(ch.font)), - new FzStextLine(line), - new FzStextChar(ch) - ); - FzPoint origin = new FzPoint(ch.origin); - style.Size = ch.size; - style.Flags = flags; - style.Font = GetFontName( - new FzFont(mupdf.mupdf.ll_fz_keep_font(ch.font)) - ); - style.Argb = ch.argb; - style.CharFlags = (uint)(ch.flags & ~syntheticMask); - style.Bidi = ch.bidi; - style.Asc = ( - new FzFont(mupdf.mupdf.ll_fz_keep_font(ch.font)) - ).fz_font_ascender(); - style.Desc = ( - new FzFont(mupdf.mupdf.ll_fz_keep_font(ch.font)) - ).fz_font_descender(); - if ( - style.Size != oldStyle.Size - || style.Flags != oldStyle.Flags - || style.CharFlags != oldStyle.CharFlags - || style.Argb != oldStyle.Argb - || style.Font != oldStyle.Font - || style.Bidi != oldStyle.Bidi - ) - { - if (oldStyle.Size >= 0) - { - if (raw) - { - span.Chars = charList; - charList = new List(); - } - else - { - span.Text = Utils.EscapeStrFromBuffer(textBuffer); - mupdf.mupdf.fz_clear_buffer(textBuffer); - } - span.Origin = new Point(spanOrigin); - span.Bbox = new Rect(spanRect); - lineRect = FzRect.fz_union_rect(lineRect, spanRect); - spanList.Add(span); - } - span = new Span(); - float asc = style.Asc; - float desc = style.Desc; - if (style.Asc < 1e-3) - { - asc = 0.9f; - desc = -0.1f; - } - - span.Size = style.Size; - span.Flags = style.Flags; - span.CharFlags = style.CharFlags; - span.Bidi = style.Bidi; - span.Font = style.Font; - span.Color = (int)(style.Argb & 0xffffff); - span.Alpha = (int)(style.Argb >> 24); - span.Asc = asc; - span.Desc = desc; - - oldStyle = new MuPDFCharStyle(style); - spanRect = r; - spanOrigin = origin; - } - spanRect = FzRect.fz_union_rect(spanRect, r); - - if (raw) - { - Char charDict = new Char(); - charDict.Origin = new FzPoint(ch.origin); - charDict.Bbox = r; - charDict.C = (char)ch.c; - charDict.Synthetic = (ch.flags & syntheticMask) != 0; - charList.Add(charDict); - } - else - { - textBuffer.fz_append_rune(ch.c); - } - } - catch (Exception) - { - continue; - } - } - - if (span != null) - { - if (raw) - { - span.Chars = charList; - } - else - { - span.Text = Utils.EscapeStrFromBuffer(textBuffer); - textBuffer.fz_clear_buffer(); - } - span.Origin = new Point(spanOrigin); - span.Bbox = new Rect(spanRect); - - if (spanRect.fz_is_empty_rect() == 0) - { - spanList.Add(span); - lineRect = FzRect.fz_union_rect(lineRect, spanRect); - } - span = null; - } - - lineDict.Spans = new List(spanList); + if (raw) + { + var char_dict = new Dictionary(); + char_dict["origin"] = new float[] { fzCh.origin.x, fzCh.origin.y }; + char_dict["bbox"] = new float[] { r.x0, r.y0, r.x1, r.y1 }; + char_dict["c"] = char.ConvertFromUtf32(fzCh.c); + char_dict["synthetic"] = (fzCh.flags & mupdf.mupdf.FZ_STEXT_SYNTHETIC) != 0; + if (char_list == null) + char_list = new List>(); + char_list.Add(char_dict); + } + else + { + mupdf.mupdf.fz_append_rune(buff, fzCh.c); + } + } - blockRect = FzRect.fz_union_rect(blockRect, lineRect); - lineDict.WMode = line.wmode; - lineDict.Dir = new Point(new FzPoint(line.dir)); - lineDict.Bbox = new Rect(lineRect); - lineList.Add(lineDict); - } - blockDict.Bbox = new Rect(blockRect); - blockDict.Lines = lineList; + if (span != null) + { + if (raw) + { + span["chars"] = char_list; + char_list = null; } - else if ( - btype == mupdf.mupdf.FZ_STEXT_BLOCK_VECTOR - || btype == mupdf.mupdf.FZ_STEXT_BLOCK_GRID - ) + else { - blockDict.Bbox = new Rect(blockBboxNative); + span["text"] = Encoding.UTF8.GetString( + buff.fz_buffer_extract()); + mupdf.mupdf.fz_clear_buffer(buff); } + span["origin"] = new float[] { span_origin_x, span_origin_y }; + span["bbox"] = new float[] { span_rect.x0, span_rect.y0, span_rect.x1, span_rect.y1 }; - pageDict.Blocks.Add(blockDict); + if (mupdf.mupdf.fz_is_empty_rect(span_rect) == 0) + { + span_list.Add(span); + line_rect = mupdf.mupdf.fz_union_rect(line_rect, span_rect); + } + span = null; } - } - /// - /// Make flags according to font style or type - /// - /// source font - /// target line - /// target char - /// - internal float CharFontFlags(FzFont font, FzStextLine line, FzStextChar ch) - { - float flags = DetectSuperScript(line, ch); // detect super string - flags += font.fz_font_is_italic() * (int)FontStyle.TEXT_FONT_ITALIC; - flags += font.fz_font_is_serif() * (int)FontStyle.TEXT_FONT_SERIFED; - flags += font.fz_font_is_monospaced() * (int)FontStyle.TEXT_FONT_MONOSPACED; - flags += font.fz_font_is_bold() * (int)FontStyle.TEXT_FONT_BOLD; - return flags; - } - - /// - /// Detect super string - /// - /// target line - /// target char - /// - internal float DetectSuperScript(FzStextLine line, FzStextChar ch) - { - if ( - line.m_internal.wmode == 0 - && line.m_internal.dir.x == 1 - && line.m_internal.dir.y == 0 - ) - return ( - ch.m_internal.origin.y - < (line.m_internal.first_char.origin.y - ch.m_internal.size * 0.1) - ) - ? 1.0f - : 0.0f; - return 0.0f; + line_dict["spans"] = span_list; + return line_rect; } /// - /// Get the font name from FzFont object + /// Port of extra.i JM_make_image_block(). /// - /// - /// - internal string GetFontName(FzFont font) + private void JM_make_image_block( + mupdf.fz_stext_block block, + Dictionary block_dict) { - string name = font.fz_font_name(); - int s = name.IndexOf("+"); - - return name.Substring(s + 1); - } - - public FzQuad GetCharQuad(FzStextLine line, FzStextChar ch) - { - if (line.m_internal.wmode != 0) - return new FzQuad(ch.m_internal.quad); - - FzFont font = new FzFont(mupdf.mupdf.ll_fz_keep_font(ch.m_internal.font)); - float asc = font.fz_font_ascender(); - float desc = font.fz_font_descender(); - float fontSize = ch.m_internal.size; - float asc_desc = asc - desc + (float)Utils.FLT_EPSILON; + var wrappedBlock = new mupdf.FzStextBlock(block); + var image = wrappedBlock.i_image(); + var transform = wrappedBlock.i_transform(); - if (asc_desc >= 1) - { - return new FzQuad(ch.m_internal.quad); - } - - FzRect bbox = mupdf.mupdf.fz_font_bbox(font); - float fontWidth = bbox.y1 - bbox.y0; - - if (asc < 1e-3) - { - desc = -0.1f; - asc = 0.9f; - asc_desc = 1.0f; - } + int w = image.w(); + int h = image.h(); + var cs = image.colorspace(); + int n = mupdf.mupdf.fz_colorspace_n(cs); - asc_desc = asc - desc; - asc = asc * fontSize / asc_desc; - desc = desc * fontSize / asc_desc; - - float c = line.m_internal.dir.x; - float s = line.m_internal.dir.y; - FzMatrix trm1 = new FzMatrix(c, -s, s, c, 0, 0); - FzMatrix trm2 = new FzMatrix(c, s, -s, c, 0, 0); - if (c == -1) - { - trm1.d = 1; - trm2.d = 1; - } - FzMatrix xlate1 = new FzMatrix( - 1, - 0, - 0, - 1, - -ch.m_internal.origin.x, - -ch.m_internal.origin.y - ); - FzMatrix xlate2 = new FzMatrix( - 1, - 0, - 0, - 1, - ch.m_internal.origin.x, - ch.m_internal.origin.y - ); - - FzQuad quad = mupdf.mupdf.fz_transform_quad(new FzQuad(ch.m_internal.quad), xlate1); - quad = mupdf.mupdf.fz_transform_quad(quad, trm1); - - if (c == 1 && quad.ul.y > 0) - { - quad.ul.y = asc; - quad.ur.y = asc; - quad.ll.y = desc; - quad.lr.y = desc; - } - else - { - quad.ul.y = -asc; - quad.ur.y = -asc; - quad.ll.y = -desc; - quad.lr.y = -desc; - } - - if (quad.ll.x < 0) - { - quad.ll.x = 0; - quad.ul.x = 0; - } - - float cwidth = quad.lr.x - quad.ll.x; - if (cwidth < Utils.FLT_EPSILON) + byte[] imageBytes; + string ext; + try { - int glyph = mupdf.mupdf.fz_encode_character(font, ch.m_internal.c); - if (glyph != 0) + var compBuf = mupdf.mupdf.fz_compressed_image_buffer(image); + if (compBuf.m_internal != null) + { + int imgType = compBuf.m_internal.params_.type; + ext = JM_image_extension(imgType); + if (imgType < mupdf.mupdf.FZ_IMAGE_BMP || imgType == mupdf.mupdf.FZ_IMAGE_JBIG2) + { + var pngBuf = mupdf.mupdf.fz_new_buffer_from_image_as_png(image, new mupdf.FzColorParams(mupdf.mupdf.fz_default_color_params)); + imageBytes = pngBuf.fz_buffer_extract(); + ext = "png"; + } + else if (n == 4 && ext == "jpeg") + { + var jpgBuf = mupdf.mupdf.fz_new_buffer_from_image_as_jpeg(image, new mupdf.FzColorParams(mupdf.mupdf.fz_default_color_params), 95, 1); + imageBytes = jpgBuf.fz_buffer_extract(); + } + else + { + var rawBuf = new mupdf.FzBuffer(compBuf.m_internal.buffer); + imageBytes = rawBuf.fz_buffer_extract(); + } + } + else { - fontWidth = mupdf.mupdf.fz_advance_glyph(font, glyph, line.m_internal.wmode); - quad.lr.x = quad.ll.x + fontWidth * fontSize; - quad.ur.x = quad.lr.x; + var pngBuf = mupdf.mupdf.fz_new_buffer_from_image_as_png(image, new mupdf.FzColorParams(mupdf.mupdf.fz_default_color_params)); + imageBytes = pngBuf.fz_buffer_extract(); + ext = "png"; } } - quad = mupdf.mupdf.fz_transform_quad(quad, trm2); - quad = mupdf.mupdf.fz_transform_quad(quad, xlate2); + catch + { + imageBytes = Array.Empty(); + ext = "png"; + } - return quad; + block_dict["width"] = w; + block_dict["height"] = h; + block_dict["ext"] = ext; + block_dict["colorspace"] = n; + block_dict["xres"] = image.xres(); + block_dict["yres"] = image.yres(); + block_dict["bpc"] = image.bpc(); + block_dict["transform"] = new float[] { transform.a, transform.b, transform.c, transform.d, transform.e, transform.f }; + block_dict["size"] = imageBytes.Length; + block_dict["image"] = imageBytes; } - public FzRect GetCharBbox(FzStextLine line, FzStextChar ch) + // ─── Private helpers ──────────────────────────────────────────── + + private static bool JM_is_word_delimiter(int c, string delimiters) { - try - { - FzQuad q = GetCharQuad(line, ch); - FzRect r = q.fz_rect_from_quad(); - if (line.m_internal.wmode != 0) - return r; - if (r.y1 < r.y0 + ch.m_internal.size) - r.y0 = r.y1 - ch.m_internal.size; - return r; - } - catch (Exception) + if (c <= 32 || c == 160 || (0x202a <= c && c <= 0x202e)) + return true; + if (string.IsNullOrEmpty(delimiters)) + return false; + foreach (char d in delimiters) { - // Fallback when font/char access fails (e.g. null or invalid font pointer) - FzQuad q = new FzQuad(ch.m_internal.quad); - return q.fz_rect_from_quad(); + if (d == c) return true; } + return false; } - public bool IsRectsOverlap(FzRect a, FzRect b) + private static bool JM_is_rtl_char(int c) { - if (false || a.x0 >= b.x1 || a.y0 >= b.y1 || a.x1 <= b.x0 || a.y1 <= b.y0) - return false; - return true; + return c >= 0x590 && c <= 0x900; } - } - - public class MuPDFCharStyle - { - public float Size { get; set; } - - public float Flags { get; set; } - - public string Font { get; set; } - public uint Argb { get; set; } - - public uint CharFlags { get; set; } - - public ushort Bidi { get; set; } - - public float Asc { get; set; } - - public float Desc { get; set; } - - public MuPDFCharStyle(Dictionary rhs) + private static string JM_image_extension(int type) { - Size = rhs["Size"]; - Flags = rhs["Flags"]; - Font = rhs["Font"]; - Asc = rhs["Asc"]; - Desc = rhs["Desc"]; - Argb = rhs.ContainsKey("Argb") - ? Convert.ToUInt32(rhs["Argb"]) - : (rhs.ContainsKey("Color") ? unchecked((uint)(int)rhs["Color"]) : 0u); - CharFlags = rhs.ContainsKey("CharFlags") ? Convert.ToUInt32(rhs["CharFlags"]) : 0; - Bidi = rhs.ContainsKey("Bidi") ? Convert.ToUInt16(rhs["Bidi"]) : (ushort)0; + switch (type) + { + case 1: return "png"; + case 2: return "jpeg"; + case 3: return "jxr"; + case 4: return "jpx"; + case 5: return "bmp"; + case 6: return "gif"; + case 7: return "tiff"; + case 8: return "pnm"; + default: return "unknown"; + } } - public MuPDFCharStyle(MuPDFCharStyle rhs) - { - Size = rhs.Size; - Flags = rhs.Flags; - Font = rhs.Font; - Argb = rhs.Argb; - CharFlags = rhs.CharFlags; - Bidi = rhs.Bidi; - Asc = rhs.Asc; - Desc = rhs.Desc; - } + // ─── IDisposable ──────────────────────────────────────────────── - public MuPDFCharStyle() + public void Dispose() { - Size = -1; - Flags = -1; - Font = ""; - Argb = 0; - CharFlags = 0; - Bidi = 0; - Asc = 0; - Desc = 0; + if (!_disposed) + { + _nativeStp?.Dispose(); + _nativeStp = null; + _disposed = true; + } + GC.SuppressFinalize(this); } - public override string ToString() - { - return $"{Size} {Flags} {Font} {Argb:x8} {CharFlags} {Bidi} {Asc} {Desc}"; - } + ~TextPage() { Dispose(); } } } diff --git a/MuPDF.NET/TextWriter.cs b/MuPDF.NET/TextWriter.cs index 0d405cbd..1912b233 100644 --- a/MuPDF.NET/TextWriter.cs +++ b/MuPDF.NET/TextWriter.cs @@ -1,618 +1,336 @@ -using mupdf; using System; using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; namespace MuPDF.NET { - public class TextWriter + /// + /// Stores text spans for later output on compatible PDF pages. + /// + public class TextWriter : IDisposable { - static TextWriter() - { - Utils.InitApp(); - } - - private FzText _nativeText; + private bool _disposed; + private Rect _rect; + private float _opacity; + private float[] _color; + private List<(Point pos, string text, Font font, float fontsize)> _textSpans; /// - /// Text opacity + /// Rectangle of the TextWriter. /// - public float Opacity { get; set; } - + public Rect Rect => _rect; /// - /// Page rectangle used by this TextWriter + /// Text opacity. /// - public Rect Rect { get; set; } - - public Matrix Ctm { get; set; } - - public Matrix ICtm { get; set; } - + public float Opacity { get => _opacity; set => _opacity = value; } /// - /// Last written to a PDF page + /// Text color. /// - public Point LastPoint { get; set; } + public float[] Color { get => _color; set => _color = value; } /// - /// Area occupied so far + /// Rectangle of the written text. /// - public Rect TextRect { get; set; } - - public List UsedFonts { get; set; } - - public bool ThisOwn { get; set; } + public Rect TextRect { get; private set; } + /// + /// Last written point. + /// + public Point LastPoint { get; private set; } = new Point(0, 0); /// - /// text color + /// Number of text spans. /// - public float[] Color { get; set; } = { 0, 0, 0 }; + public int SpanCount => _textSpans.Count; + /// + /// Create a TextWriter for a given page rectangle. + /// public TextWriter(Rect pageRect, float opacity = 1, float[] color = null) { - _nativeText = mupdf.mupdf.fz_new_text(); - - Opacity = opacity; - Rect = pageRect; - Ctm = new Matrix(1, 0, 0, -1, 0, Rect.Height); - ICtm = ~Ctm; - LastPoint = new Point(); + _rect = pageRect; + _opacity = opacity; + _color = color ?? new float[] { 0 }; + _textSpans = new List<(Point, string, Font, float)>(); TextRect = new Rect(); - UsedFonts = new List(); - if (color != null) - Color = color; - ThisOwn = false; - } - - public Rect Bbox - { - get - { - return new Rect(mupdf.mupdf.fz_bound_text(_nativeText, new FzStrokeState(0), new FzMatrix())) + new Rect(10, 10, -10, -10); - } } /// - /// Add some new text in horizontal writing. + /// Add text at a given point. + /// Returns the pixel width of the text. /// - /// start position of the text, the bottom left point of the first character. - /// a string of arbitrary length. It will be written starting at position “pos”. - /// a Font. If omitted, fitz.Font("helv") will be used. - /// the fontsize, a positive number, default 11. - /// the language to use, e.g. “en” for English. Meaningful values should be compliant with the ISO 639 standards 1, 2, 3 or 5. Reserved for future use: currently has no effect as far as we know. - /// whether the text should be written from right to left. Applicable for languages like Arabian or Hebrew. Default is False. If True, any Latin parts within the text will automatically converted. - /// - /// - /// - public (Rect, Point) Append(Point pos, string text, Font font, float fontSize = 11.0f, string language = null, int right2left = 0, int smallCaps = 0) + public float Append(Point pos, string text, Font font = null, float fontsize = 11, + string language = null, int rightToLeft = 0, int smallCaps = 0, + string fontname = null, string fontfile = null) { - pos = pos * ICtm; - int markupDir = 0; - int wmode = 0; - - bool newFontFlag = false; + if (string.IsNullOrEmpty(text)) return 0; - if (font == null || font.IsNull) - { - font = new Font("helv"); - newFontFlag = true; - } - - if (!font.IsWriteable) - { - throw new Exception($"Unsupported font {font.Name}"); - } + var f = font ?? new Font(fontname ?? "helv"); + _textSpans.Add((pos, text, f, fontsize)); - if (right2left != 0) - { - text = CleanRtl(text); - text = String.Join("", text.Reverse().ToArray()); - right2left = 0; - } + float advance = f.TextLength(text, fontsize); + LastPoint = new Point(pos.X + advance, pos.Y); - fz_text_language lang = mupdf.mupdf.fz_text_language_from_string(language); - FzMatrix trm = mupdf.mupdf.fz_make_matrix(fontSize, 0, 0, fontSize, pos.X, pos.Y); - - if (smallCaps == 0) - trm = _nativeText.fz_show_string(font.ToFzFont(), trm, text, wmode, right2left, (fz_bidi_direction)markupDir, lang); + var spanRect = new Rect(pos.X, pos.Y - fontsize, pos.X + advance, pos.Y + fontsize * 0.3); + if (TextRect.IsEmpty) + TextRect = spanRect; else - trm = Utils.ShowStringCS(_nativeText, font, trm, text, wmode, right2left, (fz_bidi_direction)markupDir, lang); - - LastPoint = new Point(trm.e, trm.f) * Ctm; - TextRect = Bbox * Ctm; - (Rect, Point) ret = (TextRect, LastPoint); - if (font.Flags["mono"] == 1) - UsedFonts.Add(font); + TextRect = TextRect.IncludeRect(spanRect); - if (newFontFlag) - { - font.Dispose(); - } - - return ret; + return advance; } /// - /// Revert the sequence of Latin text parts + /// Add text and advance to the next line. /// - /// target string to be reverted - /// reverted string - public string CleanRtl(string text) + public float AppendLine(Point pos, string text, Font font = null, float fontsize = 11, + string fontname = null) { - List idx = new List(); - List idx2 = new List(); - - if (string.IsNullOrEmpty(text)) - return null; - - string[] words = text.Split(' '); - for (int i = 0; i < words.Length; i++) - { - string w = words[i]; - - if (!(w.Length < 2 || w.ToCharArray().Any(c => (int)c > 255))) - { - words[i] = new string(w.ToCharArray().Reverse().ToArray()); - idx.Add(i); - } - } - - - for (int j = 0; j < idx.Count ; j ++) - { - if (idx2.Count == 0) - idx2.Add(idx[j]); - else if (idx[j] > idx2[idx2.Count - 1] + 1) - { - if (idx2.Count > 1) - { - Array.Reverse(words, idx2[0], idx2[idx2.Count - 1] - idx2[0] + 1); - } - idx2 = new List() { idx[j] }; - } - else if (idx[j] == idx2[idx2.Count - 1] + 1) - idx2.Add(idx[j]); - } - - text = string.Join(" ", words); - return text; + float advance = Append(pos, text, font, fontsize, fontname: fontname); + LastPoint = new Point(pos.X, pos.Y + fontsize * 1.2); + return advance; } /// - /// Add some new text in vertical, top-to-bottom writing. + /// Fill a rectangle with text, wrapping words that exceed the width. + /// Returns unwritten lines that did not fit in the rectangle. /// - /// start position of the text, the bottom left point of the first character. - /// a string. It will be written starting at position “pos”. - /// a Font. If omitted, fitz.Font("helv") will be used. - /// the fontsize, a positive float, default 11. - /// the language to use - /// - /// - public (Rect, Point) Appendv(Point pos, string text, Font font = null, float fontSiz = 11.0f, - string language = null, bool smallCaps = false) + public List FillTextbox(Rect rect, string text, Point pos = null, + Font font = null, float fontsize = 11, int align = 0, bool rightToLeft = false, + bool warn = false, float[] color = null, string fontname = null, + float? lineheight = null) { - float lheight = fontSiz * 1.2f; - foreach (char c in text) - { - Append(pos, c.ToString(), font: font, fontSize: fontSiz, language: language, smallCaps: smallCaps ? 1 : 0); - pos.Y += lheight; - } + if (string.IsNullOrEmpty(text)) return new List(); - return (TextRect, LastPoint); - } - - /// - /// Write the TextWriter text to a page, which is the only mandatory parameter. The other parameters can be used to temporarily override the values used when the TextWriter was created. - /// - /// write to this Page. - /// override the value of the TextWriter for this output. - /// override the value of the TextWriter for this output. - /// put in foreground (default) or background. - /// modify the text appearance by applying a matrix to it. - /// - /// The PDF Tr operator value. Values: 0 (default), 1, 2, 3 (invisible). - /// the xref of an OCG or OCMD. - /// - public void WriteText(Page page, float[] color = null, float opacity = -1, int overlay = 1, Morph morph = null, - Matrix matrix = null, int renderMode = 0, int oc = 0) - { - if ((Rect - page.Rect).Abs() > 1e-3) - throw new Exception("incompatible page rect"); - if (matrix != null && morph != null) - throw new Exception("only one of matrix, m"); - if (opacity == -1) - opacity = Opacity; - if (color == null) - color = Color; - - PdfPage pdfPage = page.GetPdfPage(); - float alpha = 1; - FzColorspace colorSpace; - - if (opacity >= 0 && opacity < 1) - alpha = opacity; - int nCol = 1; - float[] devColor = { 0, 0 , 0, 0 }; - if (color != null) - { - devColor = Annot.ColorFromSequence(color); + var f = font ?? new Font(fontname ?? "helv"); - if (devColor == null) - nCol = -1; - else - nCol = devColor.Length; - } + float tolerance = fontsize * 0.2f; + float spaceLen = f.TextLength(" ", fontsize); + float stdWidth = (float)rect.Width - tolerance; + float stdStart = (float)rect.X0 + tolerance; - if (nCol == 3) - colorSpace = mupdf.mupdf.fz_device_rgb(); - else if (nCol == 4) - colorSpace = mupdf.mupdf.fz_device_cmyk(); + float asc = f.Ascender; + float dsc = f.Descender; + float lheight; + if (lineheight == null) + lheight = (asc - dsc <= 1) ? 1.2f : asc - dsc; else - colorSpace = mupdf.mupdf.fz_device_gray(); - - PdfDocument pdfDoc = pdfPage.doc(); - PdfObj resources = pdfDoc.pdf_new_dict(5); - FzBuffer contents = mupdf.mupdf.fz_new_buffer(1024); - FzDevice dev = mupdf.mupdf.pdf_new_pdf_device(pdfDoc, new FzMatrix(), resources, contents); - - IntPtr pDevColor = Marshal.AllocHGlobal(devColor.Length * sizeof(float)); - Marshal.Copy(devColor, 0, pDevColor, devColor.Length); - SWIGTYPE_p_float swigDevColor = new SWIGTYPE_p_float(pDevColor, true); - - dev.fz_fill_text(_nativeText, new FzMatrix(), colorSpace, swigDevColor, alpha, new FzColorParams(mupdf.mupdf.fz_default_color_params)); - dev.fz_close_device(); - dev.Dispose(); - - (int, int) maxNums = Utils.MergeResources(pdfPage, resources); - string cont = Utils.EscapeStrFromBuffer(contents); - (int maxAlp, int maxFont) = maxNums; - string[] oldLines = cont.Split('\n'); - - string optCont = page.GetOptionalContent(oc); - string bdc = ""; - string emc = ""; - if (!string.IsNullOrEmpty(optCont)) - { - bdc = $"/OC /{optCont} BDC"; - emc = "EMC"; - } - - List newContLines = new List() { "q" }; - if (!string.IsNullOrEmpty(bdc)) - newContLines.Add(bdc); - - Point cb = page.CropBoxPosition; - float delta = 0; - if (page.Rotation == 90 || page.Rotation == 270) - delta = page.Rect.Height - page.Rect.Width; - - Rect mb = page.MediaBox; - if (!cb.IsZero() || mb.Y0 != 0 || delta != 0) - newContLines.Add($"1 0 0 1 {Utils.FloatToString(cb.X)} {Utils.FloatToString(cb.Y + mb.Y0 - delta)} cm"); - - Matrix matrix_ = new Matrix(); - if (morph != null) - { - Point p = morph.P * ICtm; - Matrix matrixDelta = (new Matrix(1f, 1f)).Pretranslate(p.X, p.Y); - matrix_ = ~matrixDelta * morph.M * matrixDelta; - } - - if (morph != null || matrix != null) - newContLines.Add($"{Utils.FloatToString(matrix_.A)} {Utils.FloatToString(matrix_.B)} {Utils.FloatToString(matrix_.C)} {Utils.FloatToString(matrix_.D)} {Utils.FloatToString(matrix_.E)} {Utils.FloatToString(matrix_.F)} cm"); + lheight = lineheight.Value; + float LINEHEIGHT = fontsize * lheight; - foreach (string line in oldLines) + float startY; + float startX; + if (pos != null) { - string line_ = line; - if (line_.EndsWith(" cm") || string.IsNullOrEmpty(line_)) - continue; - if (line_ == "BT") - { - newContLines.Add(line_); - newContLines.Add($"{renderMode} Tr"); - continue; - } - if (line_.EndsWith(" gs")) - { - int alp = Convert.ToInt32(line_.Split(' ')[0].Substring(4)) + maxAlp; - line_ = $"/Alp{alp} gs"; - } - else if (line_.EndsWith(" Tf")) - { - string[] temp = line_.Split(' '); - float fSize = (float)Convert.ToDouble(temp[1], System.Globalization.CultureInfo.InvariantCulture); - float w = 1f; - if (renderMode != 0) - w = fSize * 0.05f; - newContLines.Add($"{w} w"); - int font = Convert.ToInt32(temp[0].Substring(2)) + maxFont; - line_ = string.Join(" ", (new List() { $"/F{font}" }).Concat(temp.Skip(1))); - } - else if (line_.EndsWith(" rg")) - newContLines.Add(line_.Replace("rg", "RG")); - else if (line_.EndsWith(" g")) - newContLines.Add(line_.Replace(" g", " G")); - else if (line_.EndsWith(" k")) - newContLines.Add(line_.Replace(" k", " K")); - newContLines.Add(line_); + startX = (float)pos.X; + startY = (float)pos.Y; } - if (!string.IsNullOrEmpty(emc)) - newContLines.Add(emc); - newContLines.Add("Q\n"); - - byte[] content = Encoding.UTF8.GetBytes(string.Join("\n", newContLines)); - Utils.InsertContents(page, content, overlay); - foreach (Font font in UsedFonts) - Utils.RepairMonoFont(page, font); - - pdfDoc.Dispose(); - } - - /// - /// Fill a given rectangle with text in horizontal writing mode. - /// - /// the area to fill. No part of the text will appear outside of this. - /// the text. - /// start storing at this point. Default is a point near rectangle top-left. - /// the Font. - /// the fontsize. - /// - /// text alignment. Use one of TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT or TEXT_ALIGN_JUSTIFY. - /// on text overflow do nothing, warn, or raise an exception. Overflow text will never be written. - /// - /// - /// List of lines that did not fit in the rectangle. - /// - public List<(string, float)> FillTextbox( - Rect rect, - string text, - Font font, - Point pos = null, - float fontSize = 11, - float lineHeight = 0, - int align = 0, - bool warn = true, - bool rtl = false, - bool smallCaps = false) - { - if (rect.IsEmpty) - throw new Exception("fill rect must not empty"); - if (font.IsNull) - font = new Font("helv"); - - // Return length of a string. - float TextLen(string x) - { - return font.TextLength(x, fontSize: fontSize, smallCaps: smallCaps ? 1 : 0); - } - - // Return list of single character lengths for a string. - List CharLengths(string x) - { - return font.GetCharLengths(x, fontSize: fontSize, smallCaps: smallCaps ? 1 : 0); - } - - (Rect, Point) AppendThis(Point _pos, string _text) - { - return Append(_pos, _text, font: font, fontSize: fontSize, smallCaps: smallCaps ? 1 : 0); - } - - float tolerance = fontSize * 0.2f; // extra distance to left border - float spaceLen = TextLen(" "); - float stdWidth = rect.Width - tolerance; - float stdStart = rect.X0 + tolerance; - - // Cut any word in pieces no longer than 'width'. - (List, List) NormWords(float _width, List _words) - { - List nwords = new List(); - List wordLengths = new List(); - - foreach (string word in _words) - { - List charLengths = CharLengths(word); - float wl = charLengths.Sum(x => x); - if (wl <= _width) // nothing to do - copy over - { - nwords.Add(word); - wordLengths.Add(wl); - continue; - } - - // word longer than rect width - split it in parts - int n = charLengths.Count; - string word_ = word; - while (n > 0) - { - wl = charLengths.Take(n).Sum(x => x); - if (wl <= _width) - { - nwords.Add(word_.Substring(0, n)); - wordLengths.Add(wl); - word_ = word_.Substring(n); - charLengths = charLengths.Skip(n).ToList(); - n = charLengths.Count; - } - else - n -= 1; - } - } - - return (nwords, wordLengths); - } - - // Justified output of a line. - void OutputJustify(Point _start, string line) - { - // ignore leading / trailing / multiple spaces - string[] words = line.Split(' ').Where(x => x != "").ToArray(); - int nwords = words.Length; - if (nwords == 0) - return; - if (nwords == 1) // single word cannot be justified - { - AppendThis(_start, words[0]); - return; - } - float tl = words.Sum(x => TextLen(x)); // total word lengths - int gaps = nwords - 1; // number of word gaps - float gapl = (stdWidth - tl) / gaps; // width of each gap - foreach (string w in words) - { - (Rect _, Point lp) = AppendThis(_start, w); // output one word - _start.X = lp.X + gapl; // next start at word end plus gap - } - } - - float asc = font.Ascender; - float dsc = font.Descender; - float lheight = 0; - if (lineHeight == 0) + else { - if (asc - dsc <= 1) - lheight = 1.2f; - else - lheight = asc - dsc; + startX = stdStart; + startY = (float)rect.Y0 + fontsize * asc; } - else - lheight = lineHeight; - - float LineHeight = fontSize * lheight; // effective line height - float width = stdWidth; // available horizontal space - // starting point of text - if (pos == null) - pos = rect.TopLeft + new Point(tolerance, fontSize * asc); - if (!(rect.IncludePoint(pos).EqualTo(rect))) + float factor; + switch (align) { - List<(string, float)> retLines = new List<(string, float)>(); - retLines.Add((text, TextLen(text))); - return retLines; + case 1: factor = 0.5f; break; + case 2: factor = 1.0f; break; + default: factor = 0; break; } - float factor = 0; - if (align == (int)TextAlign.TEXT_ALIGN_CENTER) - factor = 0.5f; - else if (align == (int)TextAlign.TEXT_ALIGN_RIGHT) - factor = 1.0f; + string[] textlines = text.Split('\n'); + int maxLines = (int)(((float)rect.Y1 - startY) / LINEHEIGHT) + 1; - // split in lines if just a string was given - string[] textLines = text.Split('\n'); - int maxLines = (int)((rect.Y1 - pos.Y) / LineHeight) + 1; + var newLines = new List<(string text, float tl)>(); + var noJustify = new HashSet(); - List<(string, float)> newLines = new List<(string, float)>(); - List noJustify = new List(); - - int i = -1; - foreach (string line in textLines) + for (int i = 0; i < textlines.Length; i++) { - i += 1; - if (line == "" || line == " ") + string line = textlines[i]; + if (string.IsNullOrEmpty(line) || line == " ") { newLines.Add((line, spaceLen)); - width = rect.Width - tolerance; noJustify.Add(newLines.Count - 1); continue; } - if (i == 0) - width = rect.X1 - pos.X; - else - width = rect.Width - tolerance; - string line_ = line; - if (rtl) - line_ = CleanRtl(line_); + float width = (i == 0) ? (float)rect.X1 - startX : stdWidth; - float tl = TextLen(line_); + float tl = f.TextLength(line, fontsize); if (tl <= width) { newLines.Add((line, tl)); - noJustify.Add((newLines.Count - 1)); + noJustify.Add(newLines.Count - 1); continue; } string[] words = line.Split(' '); - (List words_, List wordLengths_) = NormWords(stdWidth, new List(words)); + var wordLengths = new List(); + foreach (string w in words) + wordLengths.Add(f.TextLength(w, fontsize)); - int n = words_.Count; - while (n > 0) + int n = words.Length; + var wordsList = new List(words); + var wlList = new List(wordLengths); + + while (wordsList.Count > 0) { - string line0 = string.Join(" ", words_.Take(n).ToArray()); - float wl = wordLengths_.Take(n).Sum() + spaceLen * (n - 1); - if (wl <= width) + n = wordsList.Count; + while (n > 0) { - newLines.Add((line0, wl)); - //words_ = words_.Take(n).ToList(); - words_.RemoveRange(0, n); - //wordLengths_ = wordLengths_.Take(n).ToList(); - wordLengths_.RemoveRange(0, n); - n = words_.Count; - line0 = null; + float wl = 0; + for (int j = 0; j < n; j++) wl += wlList[j]; + wl += spaceLen * (n - 1); + if (wl <= width) + { + string line0 = string.Join(" ", wordsList.GetRange(0, n)); + newLines.Add((line0, wl)); + wordsList.RemoveRange(0, n); + wlList.RemoveRange(0, n); + width = stdWidth; + break; + } + n--; } - else + if (n == 0 && wordsList.Count > 0) { - n -= 1; + newLines.Add((wordsList[0], wlList[0])); + wordsList.RemoveAt(0); + wlList.RemoveAt(0); + width = stdWidth; } + } + } + + int nlines = newLines.Count; + if (nlines > maxLines && warn) + throw new InvalidOperationException($"Only fitting {maxLines} of {nlines} lines."); - if (words_.Count == 0) + noJustify.Add(newLines.Count - 1); + float x = startX; + float y = startY; + + var rest = new List(); + for (int i = 0; i < Math.Min(maxLines, newLines.Count); i++) + { + var (lineText, tl2) = newLines[i]; + float lineX = (i == 0) ? startX : stdStart; + float lineWidth = (i == 0) ? (float)rect.X1 - startX : stdWidth; + + if (align == 3 && !noJustify.Contains(i) && tl2 < stdWidth) + { + string[] jwords = lineText.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (jwords.Length > 1) { - break; + float totalWordLen = 0; + foreach (string w in jwords) totalWordLen += f.TextLength(w, fontsize); + float gapLen = (stdWidth - totalWordLen) / (jwords.Length - 1); + float jx = lineX; + foreach (string w in jwords) + { + Append(new Point(jx, y), w, f, fontsize); + jx += f.TextLength(w, fontsize) + gapLen; + } + y += LINEHEIGHT; + continue; } } + + lineX += (lineWidth - tl2) * factor; + Append(new Point(lineX, y), lineText, f, fontsize); + y += LINEHEIGHT; } - // List of lines created. Each item is (text, tl), where 'tl' is the PDF - // output length (float) and 'text' is the text. Except for justified text, - // this is output-ready. - int nLines = newLines.Count; - if (nLines > maxLines) + for (int i = maxLines; i < newLines.Count; i++) + rest.Add(newLines[i].text); + + return rest; + } + + /// + /// Write all accumulated text to a PDF page. + /// + public void WriteText(Page page, float opacity = -1, float[] color = null, int overlay = 1, int oc = 0, int renderMode = 0) + { + if (_textSpans.Count == 0) return; + + var pdf = page.Parent.NativePdfDocument; + var pdfPage = page.NativePdfPage; + + // Register fonts under /Resources/Font (names F) as we encounter them. + var fontResByName = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // Build content stream + var sb = new System.Text.StringBuilder(); + sb.AppendLine("q"); + float op = opacity >= 0 ? opacity : _opacity; + var col = color ?? _color; + + if (col != null && col.Length > 0) { - if (warn) - Console.WriteLine($"Only fitting {maxLines} of {nLines} lines"); - else - throw new Exception($"Only fitting {maxLines} of {nLines} lines"); + if (col.Length == 1) sb.AppendLine($"{col[0]:F4} g"); + else if (col.Length == 3) sb.AppendLine($"{col[0]:F4} {col[1]:F4} {col[2]:F4} rg"); + else if (col.Length == 4) sb.AppendLine($"{col[0]:F4} {col[1]:F4} {col[2]:F4} {col[3]:F4} k"); } - Point start = new Point(); - noJustify.Add(newLines.Count - 1); // no justifying of last line - for (i = 0; i < maxLines; i++) + foreach (var (pos, text, font, fontsize) in _textSpans) { - string line; - float tl; - try - { - (line, tl) = newLines[0]; - newLines.RemoveAt(0); - } - catch(Exception) + var fname = font?.Name; + if (string.IsNullOrEmpty(fname)) fname = "helv"; + if (!fontResByName.TryGetValue(fname, out var resKey)) { - break; + int xref = page.InsertFont(fname); + resKey = $"F{xref}"; + fontResByName[fname] = resKey; } - if (rtl) // Arabic, Hebrew - line = string.Join("", line.Reverse().ToArray()); - - if (i == 0) // may have different start for first line - start = pos; + sb.AppendLine("BT"); + sb.AppendLine($"/{resKey} {fontsize:F1} Tf"); + sb.AppendLine($"{pos.X:F2} {_rect.Height - pos.Y:F2} Td"); + // GetPdfStr already returns a parenthesized PDF string literal. + sb.AppendLine($"{Helpers.GetPdfStr(text)} Tj"); + sb.AppendLine("ET"); + } + sb.AppendLine("Q"); - if (align == (int)TextAlign.TEXT_ALIGN_JUSTIFY && !noJustify.Contains(i) && tl < stdWidth) - { - OutputJustify(start, line); - start.X = stdStart; - start.Y += LineHeight; - continue; - } + var content = System.Text.Encoding.UTF8.GetBytes(sb.ToString()); + var buf = Helpers.BufferFromBytes(content); + Helpers.JM_insert_contents(pdf, pdfPage.obj(), buf, overlay == 1); + } - if (i > 0 || pos.X == stdStart) // left, center, right alignments - start.X += (width - tl) * factor; + /// + /// Empty the TextWriter. + /// + public void Reset() + { + _textSpans.Clear(); + TextRect = new Rect(); + LastPoint = new Point(0, 0); + } - AppendThis(start, line); - start.X = stdStart; - start.Y += LineHeight; - } + // ─── IDisposable ──────────────────────────────────────────────── - return newLines; // return non-written lines + public void Dispose() + { + if (!_disposed) { _disposed = true; } + GC.SuppressFinalize(this); } + + ~TextWriter() { Dispose(); } + + public override string ToString() => $"TextWriter(spans={_textSpans.Count}, rect={TextRect})"; + + // Python/legacy compatibility aliases (mirrors _alias(TextWriter, ...)). + public List fill_textbox(Rect rect, string text, Point pos = null, + Font font = null, float fontsize = 11, int align = 0, bool rightToLeft = false, + bool warn = false, float[] color = null, string fontname = null, float? lineheight = null) + => FillTextbox(rect, text, pos, font, fontsize, align, rightToLeft, warn, color, fontname, lineheight); + public List fillTextbox(Rect rect, string text, Point pos = null, + Font font = null, float fontsize = 11, int align = 0, bool rightToLeft = false, + bool warn = false, float[] color = null, string fontname = null, float? lineheight = null) + => fill_textbox(rect, text, pos, font, fontsize, align, rightToLeft, warn, color, fontname, lineheight); + public void write_text(Page page, float opacity = -1, float[] color = null, int overlay = 1, int oc = 0, int renderMode = 0) + => WriteText(page, opacity, color, overlay, oc, renderMode); + public void writeText(Page page, float opacity = -1, float[] color = null, int overlay = 1, int oc = 0, int renderMode = 0) + => write_text(page, opacity, color, overlay, oc, renderMode); } -} \ No newline at end of file +} diff --git a/MuPDF.NET/Utils.cs b/MuPDF.NET/Utils.cs index 1feab8f7..73ea8620 100644 --- a/MuPDF.NET/Utils.cs +++ b/MuPDF.NET/Utils.cs @@ -1,8643 +1,879 @@ using System; using System.Collections.Generic; -using System.Data; -using System.Diagnostics; -using System.Globalization; using System.IO; -using System.IO.Compression; using System.Linq; -using System.Net.NetworkInformation; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using Microsoft.VisualBasic; -using mupdf; -using MuPDF.NET; -using Newtonsoft.Json; -using SkiaSharp; -using static System.Net.Mime.MediaTypeNames; namespace MuPDF.NET { + /// + /// Static utility functions ported from PyMuPDF's utils.py module. + /// Provides color lookup, page label handling, quad recovery, and font helpers. + /// public static class Utils { - public static (string, string) VERSION = ("1.26.1", "3.2.8-rc.6"); - - public static int FZ_MIN_INF_RECT = (int)(-0x80000000); - - public static int FZ_MAX_INF_RECT = (int)0x7fffff80; - - public static double FLT_EPSILON = 1e-5; - - public static bool IsInitialized = false; + // ─── Color Functions ───────────────────────────────────────────── /// - /// Global lock for thread-safe access to native MuPDF library. - /// MuPDF is not thread-safe, so all P/Invoke calls must be synchronized with this lock. + /// Returns a list of upper-case colour names from the wx colour database. /// - public static readonly object MuPDFLock = new object(); - - public static string ANNOT_ID_STEM = "fitz"; - - public static int SigFlag_SignaturesExist = 1; - public static int SigFlag_AppendOnly = 2; - - public static bool SmallGlyphHeights = false; - - public static int UNIQUE_ID = 0; - - public static Dictionary AdobeUnicodes = new Dictionary(); - - public static Dictionary AdobeGlyphs = new Dictionary(); - - public static string TESSDATA_PREFIX = Environment.GetEnvironmentVariable( - "TESSDATA_PREFIX" - ); - - public static int trace_device_FILL_PATH = 1; - public static int trace_device_STROKE_PATH = 2; - public static int trace_device_CLIP_PATH = 3; - public static int trace_device_CLIP_STROKE_PATH = 4; - - public static Dictionary AnnotSkel = new Dictionary() + public static List GetColorList() { - { - "goto1", - "<>/Rect[{4}]/BS<>/Subtype/Link>>" - }, - { "goto2", "<>/Rect[{1}]/BS<>/Subtype/Link>>" }, - { - "gotor1", - "<>>>/Rect[{6}]/BS<>/Subtype/Link>>" - }, - { "gotor2", "<>/Rect[{2}]/BS<>/Subtype/Link>>" }, - { - "launch", - "<>>>/Rect[{2}]/BS<>/Subtype/Link>>" - }, - { "uri", "<>/Rect[{1}]/BS<>/Subtype/Link>>" }, - { "named", "<>/Rect[{1}]/BS<>/Subtype/Link>>" } - }; - - public static List MUPDF_WARNINGS_STORE = new List(); + return WxColors.ColorList.Select(c => c.Name).ToList(); + } - private static Dictionary PaperSizes = new Dictionary< - string, - (int, int) - >() + /// + /// Returns a list of (name, red, green, blue) tuples where RGB values + /// are integers in the range 0–255. + /// + public static List<(string name, int r, int g, int b)> GetColorInfoList() { - { "a0", (2384, 3370) }, - { "a1", (1684, 2384) }, - { "a10", (74, 105) }, - { "a2", (1191, 1684) }, - { "a3", (842, 1191) }, - { "a4", (595, 842) }, - { "a5", (420, 595) }, - { "a6", (298, 420) }, - { "a7", (210, 298) }, - { "a8", (147, 210) }, - { "a9", (105, 147) }, - { "b0", (2835, 4008) }, - { "b1", (2004, 2835) }, - { "b10", (88, 125) }, - { "b2", (1417, 2004) }, - { "b3", (1001, 1417) }, - { "b4", (709, 1001) }, - { "b5", (499, 709) }, - { "b6", (354, 499) }, - { "b7", (249, 354) }, - { "b8", (176, 249) }, - { "b9", (125, 176) }, - { "c0", (2599, 3677) }, - { "c1", (1837, 2599) }, - { "c10", (79, 113) }, - { "c2", (1298, 1837) }, - { "c3", (918, 1298) }, - { "c4", (649, 918) }, - { "c5", (459, 649) }, - { "c6", (323, 459) }, - { "c7", (230, 323) }, - { "c8", (162, 230) }, - { "c9", (113, 162) }, - { "card-4x6", (288, 432) }, - { "card-5x7", (360, 504) }, - { "commercial", (297, 684) }, - { "executive", (522, 756) }, - { "invoice", (396, 612) }, - { "ledger", (792, 1224) }, - { "legal", (612, 1008) }, - { "legal-13", (612, 936) }, - { "letter", (612, 792) }, - { "monarch", (279, 540) }, - { "tabloid-extra", (864, 1296) } - }; + return WxColors.ColorList.ToList(); + } - public static List<(int, double)> zapf_glyphs = new List<(int, double)>() + /// + /// Retrieve an RGB color in PDF format (0–1 range) by name. + /// Returns white (1, 1, 1) when the name is not found. + /// + /// Colour name (case-insensitive). + public static (float r, float g, float b) GetColor(string name) { - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (32, 0.278), - (33, 0.974), - (34, 0.961), - (35, 0.974), - (36, 0.98), - (37, 0.719), - (38, 0.789), - (39, 0.79), - (40, 0.791), - (41, 0.69), - (42, 0.96), - (43, 0.939), - (44, 0.549), - (45, 0.855), - (46, 0.911), - (47, 0.933), - (48, 0.911), - (49, 0.945), - (50, 0.974), - (51, 0.755), - (52, 0.846), - (53, 0.762), - (54, 0.761), - (55, 0.571), - (56, 0.677), - (57, 0.763), - (58, 0.76), - (59, 0.759), - (60, 0.754), - (61, 0.494), - (62, 0.552), - (63, 0.537), - (64, 0.577), - (65, 0.692), - (66, 0.786), - (67, 0.788), - (68, 0.788), - (69, 0.79), - (70, 0.793), - (71, 0.794), - (72, 0.816), - (73, 0.823), - (74, 0.789), - (75, 0.841), - (76, 0.823), - (77, 0.833), - (78, 0.816), - (79, 0.831), - (80, 0.923), - (81, 0.744), - (82, 0.723), - (83, 0.749), - (84, 0.79), - (85, 0.792), - (86, 0.695), - (87, 0.776), - (88, 0.768), - (89, 0.792), - (90, 0.759), - (91, 0.707), - (92, 0.708), - (93, 0.682), - (94, 0.701), - (95, 0.826), - (96, 0.815), - (97, 0.789), - (98, 0.789), - (99, 0.707), - (100, 0.687), - (101, 0.696), - (102, 0.689), - (103, 0.786), - (104, 0.787), - (105, 0.713), - (106, 0.791), - (107, 0.785), - (108, 0.791), - (109, 0.873), - (110, 0.761), - (111, 0.762), - (112, 0.762), - (113, 0.759), - (114, 0.759), - (115, 0.892), - (116, 0.892), - (117, 0.788), - (118, 0.784), - (119, 0.438), - (120, 0.138), - (121, 0.277), - (122, 0.415), - (123, 0.392), - (124, 0.392), - (125, 0.668), - (126, 0.668), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (183, 0.788), - (161, 0.732), - (162, 0.544), - (163, 0.544), - (164, 0.91), - (165, 0.667), - (166, 0.76), - (167, 0.76), - (168, 0.776), - (169, 0.595), - (170, 0.694), - (171, 0.626), - (172, 0.788), - (173, 0.788), - (174, 0.788), - (175, 0.788), - (176, 0.788), - (177, 0.788), - (178, 0.788), - (179, 0.788), - (180, 0.788), - (181, 0.788), - (182, 0.788), - (183, 0.788), - (184, 0.788), - (185, 0.788), - (186, 0.788), - (187, 0.788), - (188, 0.788), - (189, 0.788), - (190, 0.788), - (191, 0.788), - (192, 0.788), - (193, 0.788), - (194, 0.788), - (195, 0.788), - (196, 0.788), - (197, 0.788), - (198, 0.788), - (199, 0.788), - (200, 0.788), - (201, 0.788), - (202, 0.788), - (203, 0.788), - (204, 0.788), - (205, 0.788), - (206, 0.788), - (207, 0.788), - (208, 0.788), - (209, 0.788), - (210, 0.788), - (211, 0.788), - (212, 0.894), - (213, 0.838), - (214, 1.016), - (215, 0.458), - (216, 0.748), - (217, 0.924), - (218, 0.748), - (219, 0.918), - (220, 0.927), - (221, 0.928), - (222, 0.928), - (223, 0.834), - (224, 0.873), - (225, 0.828), - (226, 0.924), - (227, 0.924), - (228, 0.917), - (229, 0.93), - (230, 0.931), - (231, 0.463), - (232, 0.883), - (233, 0.836), - (234, 0.836), - (235, 0.867), - (236, 0.867), - (237, 0.696), - (238, 0.696), - (239, 0.874), - (183, 0.788), - (241, 0.874), - (242, 0.76), - (243, 0.946), - (244, 0.771), - (245, 0.865), - (246, 0.771), - (247, 0.888), - (248, 0.967), - (249, 0.888), - (250, 0.831), - (251, 0.873), - (252, 0.927), - (253, 0.97), - (183, 0.788), - (183, 0.788), - }; + if (WxColors.PdfColorDict.TryGetValue(name.ToLower(), out var c)) + return c; + return (1f, 1f, 1f); + } - public static List<(int, double)> symbol_glyphs = new List<(int, double)>() + /// + /// Retrieve the Hue / Saturation / Value triple for a named colour. + /// Hue is in degrees (0–360), Saturation in percent (0–100), + /// Value in percent (0–100). Returns (-1, -1, -1) when the name is + /// not found. + /// + /// Colour name (case-insensitive). + public static (int H, int S, float V) GetColorHSV(string name) { - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (32, 0.25), - (33, 0.333), - (34, 0.713), - (35, 0.5), - (36, 0.549), - (37, 0.833), - (38, 0.778), - (39, 0.439), - (40, 0.333), - (41, 0.333), - (42, 0.5), - (43, 0.549), - (44, 0.25), - (45, 0.549), - (46, 0.25), - (47, 0.278), - (48, 0.5), - (49, 0.5), - (50, 0.5), - (51, 0.5), - (52, 0.5), - (53, 0.5), - (54, 0.5), - (55, 0.5), - (56, 0.5), - (57, 0.5), - (58, 0.278), - (59, 0.278), - (60, 0.549), - (61, 0.549), - (62, 0.549), - (63, 0.444), - (64, 0.549), - (65, 0.722), - (66, 0.667), - (67, 0.722), - (68, 0.612), - (69, 0.611), - (70, 0.763), - (71, 0.603), - (72, 0.722), - (73, 0.333), - (74, 0.631), - (75, 0.722), - (76, 0.686), - (77, 0.889), - (78, 0.722), - (79, 0.722), - (80, 0.768), - (81, 0.741), - (82, 0.556), - (83, 0.592), - (84, 0.611), - (85, 0.69), - (86, 0.439), - (87, 0.768), - (88, 0.645), - (89, 0.795), - (90, 0.611), - (91, 0.333), - (92, 0.863), - (93, 0.333), - (94, 0.658), - (95, 0.5), - (96, 0.5), - (97, 0.631), - (98, 0.549), - (99, 0.549), - (100, 0.494), - (101, 0.439), - (102, 0.521), - (103, 0.411), - (104, 0.603), - (105, 0.329), - (106, 0.603), - (107, 0.549), - (108, 0.549), - (109, 0.576), - (110, 0.521), - (111, 0.549), - (112, 0.549), - (113, 0.521), - (114, 0.549), - (115, 0.603), - (116, 0.439), - (117, 0.576), - (118, 0.713), - (119, 0.686), - (120, 0.493), - (121, 0.686), - (122, 0.494), - (123, 0.48), - (124, 0.2), - (125, 0.48), - (126, 0.549), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (183, 0.46), - (160, 0.25), - (161, 0.62), - (162, 0.247), - (163, 0.549), - (164, 0.167), - (165, 0.713), - (166, 0.5), - (167, 0.753), - (168, 0.753), - (169, 0.753), - (170, 0.753), - (171, 1.042), - (172, 0.713), - (173, 0.603), - (174, 0.987), - (175, 0.603), - (176, 0.4), - (177, 0.549), - (178, 0.411), - (179, 0.549), - (180, 0.549), - (181, 0.576), - (182, 0.494), - (183, 0.46), - (184, 0.549), - (185, 0.549), - (186, 0.549), - (187, 0.549), - (188, 1), - (189, 0.603), - (190, 1), - (191, 0.658), - (192, 0.823), - (193, 0.686), - (194, 0.795), - (195, 0.987), - (196, 0.768), - (197, 0.768), - (198, 0.823), - (199, 0.768), - (200, 0.768), - (201, 0.713), - (202, 0.713), - (203, 0.713), - (204, 0.713), - (205, 0.713), - (206, 0.713), - (207, 0.713), - (208, 0.768), - (209, 0.713), - (210, 0.79), - (211, 0.79), - (212, 0.89), - (213, 0.823), - (214, 0.549), - (215, 0.549), - (216, 0.713), - (217, 0.603), - (218, 0.603), - (219, 1.042), - (220, 0.987), - (221, 0.603), - (222, 0.987), - (223, 0.603), - (224, 0.494), - (225, 0.329), - (226, 0.79), - (227, 0.79), - (228, 0.786), - (229, 0.713), - (230, 0.384), - (231, 0.384), - (232, 0.384), - (233, 0.384), - (234, 0.384), - (235, 0.384), - (236, 0.494), - (237, 0.494), - (238, 0.494), - (239, 0.494), - (183, 0.46), - (241, 0.329), - (242, 0.274), - (243, 0.686), - (244, 0.686), - (245, 0.686), - (246, 0.384), - (247, 0.549), - (248, 0.384), - (249, 0.384), - (250, 0.384), - (251, 0.384), - (252, 0.494), - (253, 0.494), - (254, 0.494), - (183, 0.46), - }; + string upper = name.ToUpper(); + var list = WxColors.ColorList; + int idx = -1; + for (int i = 0; i < list.Count; i++) + { + if (list[i].Name == upper) + { + idx = i; + break; + } + } + if (idx < 0) + return (-1, -1, -1f); - public static string GetImageExtension(int type) - { - if (type == (int)ImageType.FZ_IMAGE_FAX) - return "fax"; - if (type == (int)ImageType.FZ_IMAGE_RAW) - return "raw"; - if (type == (int)ImageType.FZ_IMAGE_FLATE) - return "flate"; - if (type == (int)ImageType.FZ_IMAGE_RLD) - return "rld"; - if (type == (int)ImageType.FZ_IMAGE_BMP) - return "bmp"; - if (type == (int)ImageType.FZ_IMAGE_GIF) - return "gif"; - if (type == (int)ImageType.FZ_IMAGE_LZW) - return "lzw"; - if (type == (int)ImageType.FZ_IMAGE_JBIG2) - return "jb2"; - if (type == (int)ImageType.FZ_IMAGE_JPEG) - return "jpeg"; - if (type == (int)ImageType.FZ_IMAGE_JPX) - return "jpx"; - if (type == (int)ImageType.FZ_IMAGE_JXR) - return "jxr"; - if (type == (int)ImageType.FZ_IMAGE_PNG) - return "png"; - if (type == (int)ImageType.FZ_IMAGE_PNM) - return "pnm"; - if (type == (int)ImageType.FZ_IMAGE_TIFF) - return "tiff"; - return "n/a"; - } + var entry = list[idx]; + float r = entry.R / 255f; + float g = entry.G / 255f; + float b = entry.B / 255f; - public static Dictionary Base14_fontdict = new Dictionary() - { - { "helv", "Helvetica" }, - { "heit", "Helvetica-Oblique" }, - { "hebo", "Helvetica-Bold" }, - { "hebi", "Helvetica-BoldOblique" }, - { "cour", "Courier" }, - { "coit", "Courier-Obliqu" }, - { "cobo", "Courier-Bold" }, - { "cobi", "Courier-BoldOblique" }, - { "tiro", "Times-Roman" }, - { "tibo", "Times-Bold" }, - { "tiit", "Times-Italic" }, - { "tibi", "Times-BoldItalic" }, - { "symb", "Symbol" }, - { "zadb", "ZapfDingbats" } - }; + float cmax = Math.Max(r, Math.Max(g, b)); + float V = (float)Math.Round(cmax * 100, 1); + float cmin = Math.Min(r, Math.Min(g, b)); + float delta = cmax - cmin; - public static Rect INFINITE_RECT() - { - return new Rect( - Utils.FZ_MIN_INF_RECT, - Utils.FZ_MIN_INF_RECT, - Utils.FZ_MAX_INF_RECT, - Utils.FZ_MAX_INF_RECT - ); - } + float hue; + if (delta == 0f) + hue = 0f; + else if (cmax == r) + hue = 60f * (((g - b) / delta) % 6f); + else if (cmax == g) + hue = 60f * (((b - r) / delta) + 2f); + else + hue = 60f * (((r - g) / delta) + 4f); - public static Matrix HorMatrix(Point c, Point p) - { - FzPoint s = mupdf.mupdf.fz_normalize_vector( - mupdf.mupdf.fz_make_point(p.X - c.X, p.Y - c.Y) - ); + int H = (int)Math.Round(hue); + + float sat = cmax == 0f ? 0f : delta / cmax; + int S = (int)Math.Round(sat * 100); - FzMatrix m1 = mupdf.mupdf.fz_make_matrix(1, 0, 0, 1, -c.X, -c.Y); - FzMatrix m2 = mupdf.mupdf.fz_make_matrix(s.x, -s.y, s.y, s.x, 0, 0); - return new Matrix(mupdf.mupdf.fz_concat(m1, m2)); + return (H, S, V); } - public static (int, Matrix) InvertMatrix(Matrix m) + // ─── Page Label Functions ──────────────────────────────────────── + + /// + /// Convert a PDF page-label rule (page number + raw rule string) into a + /// dictionary with keys: startpage, prefix, style, firstpagenum. + /// + /// Tuple of (page number, raw rule string like "<</S/D…>>"). + public static Dictionary RuleDict((int pno, string rule) item) { - /*if (false) + string rule = item.rule; + rule = rule.Substring(2, rule.Length - 4); + string[] parts = rule.Split('/'); + string[] entries = parts.Skip(1).ToArray(); + + var d = new Dictionary { - FzMatrix ret = m.ToFzMatrix().fz_invert_matrix(); - if (false || Math.Abs(m.A - 1) >= float.Epsilon - || Math.Abs(m.B - 0) >= float.Epsilon - || Math.Abs(m.C - 0) >= float.Epsilon - || Math.Abs(m.D - 1) >= float.Epsilon - ) - return (1, null); - return (0, new Matrix(ret)); - }*/ - FzMatrix src = m.ToFzMatrix(); - float a = src.a; - float det = a * src.d - src.b * src.c; - if (det < -float.Epsilon || det > float.Epsilon) + ["startpage"] = item.pno, + ["prefix"] = "", + ["firstpagenum"] = 1 + }; + + bool skip = false; + for (int i = 0; i < entries.Length; i++) { - FzMatrix dst = new FzMatrix(); - float rdet = 1 / det; - dst.a = src.d * rdet; - dst.b = -src.b * rdet; - dst.c = -src.c * rdet; - dst.d = a * rdet; - a = -src.e * dst.a - src.f * dst.c; - dst.f = -src.e * dst.b - src.f * dst.d; - dst.e = a; - return (0, new Matrix(dst)); - } - return (1, null); - } + if (skip) + { + skip = false; + continue; + } - public static Matrix PlanishLine(Point a, Point b) - { - return Utils.HorMatrix(a, b); + string entry = entries[i]; + if (entry == "S") + { + d["style"] = entries[i + 1]; + skip = true; + } + else if (entry.StartsWith("P")) + { + string x = entry.Substring(1).Replace("(", "").Replace(")", ""); + d["prefix"] = x; + } + else if (entry.StartsWith("St")) + { + int x = int.Parse(entry.Substring(2)); + d["firstpagenum"] = x; + } + } + return d; } - public static float SineBetween(Point c, Point p, Point q) + /// + /// Return the page label string for a given 0-based page number. + /// + /// 0-based page number. + /// List of (page-number, rule-string) pairs from the document. + public static string GetLabelPno(int pageNo, List<(int pno, string rule)> labels) { - FzPoint s = mupdf.mupdf.fz_normalize_vector( - mupdf.mupdf.fz_make_point(q.X - p.X, q.Y - p.Y) - ); - FzMatrix m1 = mupdf.mupdf.fz_make_matrix(1, 0, 0, 1, -p.X, -p.Y); - FzMatrix m2 = mupdf.mupdf.fz_make_matrix(s.x, -s.y, s.y, s.x, 0, 0); - m1 = mupdf.mupdf.fz_concat(m1, m2); - return mupdf.mupdf.fz_transform_point(c.ToFzPoint(), m1).fz_normalize_vector().y; + var item = labels.Last(x => x.pno <= pageNo); + var rule = RuleDict(item); + string prefix = rule.ContainsKey("prefix") ? (string)rule["prefix"] : ""; + string style = rule.ContainsKey("style") ? (string)rule["style"] : ""; + int delta = (style == "a" || style == "A") ? -1 : 0; + int pagenumber = pageNo - (int)rule["startpage"] + (int)rule["firstpagenum"] + delta; + return ConstructLabel(style, prefix, pagenumber); } - /// Follow a chain of dictionary keys (e.g. Root → AcroForm → Fields). - public static PdfObj pdf_dict_getl(PdfObj obj, string[] keys) + /// + /// Build a page label string from style, prefix and page number. + /// + /// One of "D" (decimal), "r"/"R" (roman), "a"/"A" (letter), or empty. + /// Prefix string prepended to the number part. + /// Logical page number. + public static string ConstructLabel(string style, string prefix, int pno) { - foreach (string key in keys) + string nStr = ""; + switch (style) { - if (obj.m_internal == null) + case "D": + nStr = pno.ToString(); + break; + case "r": + nStr = IntegerToRoman(pno).ToLower(); + break; + case "R": + nStr = IntegerToRoman(pno).ToUpper(); + break; + case "a": + nStr = IntegerToLetter(pno).ToLower(); + break; + case "A": + nStr = IntegerToLetter(pno).ToUpper(); break; - obj = obj.pdf_dict_get(new PdfObj(key)); } - return obj; + return prefix + nStr; } - internal static void pdf_dict_putl(PdfObj obj, PdfObj val, string[] keys) + /// + /// Convert a positive integer to an alphabetic sequence string + /// (0→A, 1→B, … 25→Z, 26→AA, …). + /// + public static string IntegerToLetter(int i) { - if (obj.pdf_is_indirect() != 0) - obj = obj.pdf_resolve_indirect_chain(); - if (obj.pdf_is_dict() == 0) - throw new Exception(string.Format("Not a dict: {0}", obj)); - if (keys.Length == 0) - return; + const string ls = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + int n = 1; + int a = i; + while (Math.Pow(26, n) <= a) + { + a -= (int)Math.Pow(26, n); + n++; + } - PdfDocument doc = obj.pdf_get_bound_document(); - for (int i = 0; i < keys.Length - 1; i++) + string result = ""; + for (int j = n - 1; j >= 0; j--) { - PdfObj nextObj = obj.pdf_dict_get(new PdfObj(keys[i])); - if (nextObj.m_internal == null) - { - nextObj = doc.pdf_new_dict(1); - obj.pdf_dict_put(new PdfObj(keys[i]), nextObj); - } - obj = nextObj; + int power = (int)Math.Pow(26, j); + int f = a / power; + int g = a % power; + result += ls[f]; + a = g; } - string key = keys[keys.Length - 1]; - obj.pdf_dict_put(new PdfObj(key), val); + return result; } - public static (int, int, int) MUPDF_VERSION = (1, 26, 1); - - public static Dictionary ErrorMessages = new Dictionary() - { - { "MSG_BAD_ANNOT_TYPE", "bad annot type" }, - { "MSG_BAD_APN", "bad or missing annot AP/N" }, - { "MSG_BAD_ARG_INK_ANNOT", "arg must be seq of seq of float pairs" }, - { "MSG_BAD_ARG_POINTS", "bad seq of points" }, - { "MSG_BAD_BUFFER", "bad type: 'buffer'" }, - { "MSG_BAD_COLOR_SEQ", "bad color sequence" }, - { "MSG_BAD_DOCUMENT", "cannot open broken document" }, - { "MSG_BAD_FILETYPE", "bad filetype" }, - { "MSG_BAD_LOCATION", "bad location" }, - { "MSG_BAD_OC_CONFIG", "bad config number" }, - { "MSG_BAD_OC_LAYER", "bad layer number" }, - { "MSG_BAD_OC_REF", "bad 'oc' reference" }, - { "MSG_BAD_PAGEID", "bad page id" }, - { "MSG_BAD_PAGENO", "bad page number(s)" }, - { "MSG_BAD_PDFROOT", "PDF has no root" }, - { "MSG_BAD_RECT", "rect is infinite or empty" }, - { "MSG_BAD_TEXT", "bad type: 'text'" }, - { "MSG_BAD_XREF", "bad xref" }, - { "MSG_COLOR_COUNT_FAILED", "color count failed" }, - { "MSG_FILE_OR_BUFFER", "need font file or buffer" }, - { "MSG_FONT_FAILED", "cannot create font" }, - { "MSG_IS_NO_ANNOT", "is no annotation" }, - { "MSG_IS_NO_IMAGE", "is no image" }, - { "MSG_IS_NO_PDF", "is no PDF" }, - { "MSG_IS_NO_DICT", "object is no PDF dict" }, - { "MSG_PIX_NOALPHA", "source pixmap has no alpha" }, - { "MSG_PIXEL_OUTSIDE", "pixel(s) outside image" } - }; - /// - /// ColorSpace types + /// Convert a positive integer to its Roman numeral representation. /// - public static int CS_RGB = 1; - public static int CS_GRAY = 2; - public static int CS_CMYK = 3; - - //public static ColorSpace csRGB = new ColorSpace(Utils.CS_RGB); - //public static ColorSpace csGRAY = new ColorSpace(Utils.CS_GRAY); - //public static ColorSpace csCMYK = new ColorSpace(Utils.CS_CMYK); - - public static byte[] BinFromBuffer(FzBuffer buffer) + public static string IntegerToRoman(int num) { - //return buffer.fz_buffer_extract(); - var outparams = new mupdf.ll_fz_buffer_storage_outparams(); - uint n = mupdf.mupdf.ll_fz_buffer_storage_outparams_fn(buffer.m_internal, outparams); - var raw1 = mupdf.SWIGTYPE_p_unsigned_char.getCPtr(outparams.datap); - System.IntPtr raw2 = System.Runtime.InteropServices.HandleRef.ToIntPtr(raw1); - byte[] ret = new byte[n]; - // Marshal.Copy() raises exception if is null even if is zero. - if (n > 0) + var romanValues = new (int value, string numeral)[] + { + (1000, "M"), (900, "CM"), (500, "D"), (400, "CD"), + (100, "C"), (90, "XC"), (50, "L"), (40, "XL"), + (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), + (1, "I") + }; + + string result = ""; + foreach (var (value, numeral) in romanValues) { - System.Runtime.InteropServices.Marshal.Copy(raw2, ret, 0, (int)n); - outparams.Dispose(); + int count = num / value; + for (int k = 0; k < count; k++) + result += numeral; + num -= value * count; + if (num <= 0) + break; } - - return ret; + return result; } - internal static FzBuffer BufferFromBytes(byte[] bytes) - { - if (bytes == null) - return new FzBuffer(); - return Utils.fz_new_buffer_from_data(bytes); - } + // ─── Quad Recovery Functions ───────────────────────────────────── - internal static FzBuffer CompressBuffer(FzBuffer buffer) + /// + /// Compute the quadrilateral located inside a bounding box. + /// The bbox may be the span bbox or any character bbox within the span. + /// + /// The line's direction vector (cos, sin). + /// The span dictionary from text extraction. + /// The bounding box as a . + /// + /// When true, use only font-size as glyph height (matching TOOLS.set_small_glyph_heights()). + /// + public static Quad RecoverBboxQuad( + (double cos, double sin) lineDir, + Dictionary span, + Rect bbox, + bool smallGlyphHeights = false) { - ll_fz_new_deflated_data_from_buffer_outparams outparams = - new ll_fz_new_deflated_data_from_buffer_outparams(); - SWIGTYPE_p_unsigned_char data = - mupdf.mupdf.ll_fz_new_deflated_data_from_buffer_outparams_fn( - buffer.m_internal, - fz_deflate_level.FZ_DEFLATE_BEST, - outparams - ); + double cos = lineDir.cos; + double sin = lineDir.sin; - if (data == null || outparams.compressed_length == 0) - return null; - FzBuffer buf = new FzBuffer( - mupdf.mupdf.fz_new_buffer_from_data(data, outparams.compressed_length) - ); + double d; + if (smallGlyphHeights) + d = 1.0; + else + d = Convert.ToDouble(span["ascender"]) - Convert.ToDouble(span["descender"]); - buf.fz_resize_buffer(outparams.compressed_length); - return buf; - } + double height = d * Convert.ToDouble(span["size"]); - internal static void UpdateStream( - PdfDocument doc, - PdfObj obj, - FzBuffer buffer, - int compress - ) - { - if (compress != 0) + double hs = height * sin; + double hc = height * cos; + + Point ul, ur, ll, lr; + if (hc >= 0 && hs <= 0) { - uint len = buffer.fz_buffer_storage(new SWIGTYPE_p_p_unsigned_char(IntPtr.Zero, false)); - if (len > 30) - { - FzBuffer res = Utils.CompressBuffer(buffer); - if (res != null) - { - uint nlen = res.fz_buffer_storage(new SWIGTYPE_p_p_unsigned_char(IntPtr.Zero, false)); - if (nlen < len) - { - obj.pdf_dict_put(new PdfObj("Filter"), new PdfObj("FlateDecode")); - doc.pdf_update_stream(obj, res, 1); - return; - } - } - } + ul = bbox.BL - new Point(0, hc); + ur = bbox.TR + new Point(hs, 0); + ll = bbox.BL - new Point(hs, 0); + lr = bbox.TR + new Point(0, hc); } - - doc.pdf_update_stream(obj, buffer, 0); + else if (hc <= 0 && hs <= 0) + { + ul = bbox.BR + new Point(hs, 0); + ur = bbox.TL - new Point(0, hc); + ll = bbox.BR + new Point(0, hc); + lr = bbox.TL - new Point(hs, 0); + } + else if (hc <= 0 && hs >= 0) + { + ul = bbox.TR - new Point(0, hc); + ur = bbox.BL + new Point(hs, 0); + ll = bbox.TR - new Point(hs, 0); + lr = bbox.BL + new Point(0, hc); + } + else + { + ul = bbox.TL + new Point(hs, 0); + ur = bbox.BR - new Point(0, hc); + ll = bbox.TL + new Point(0, hc); + lr = bbox.BR - new Point(hs, 0); + } + return new Quad(ul, ur, ll, lr); } - public static bool INRANGE(int v, int low, int high) + /// + /// Recover the quadrilateral of a text span from its bbox and direction. + /// + /// The line's direction vector (cos, sin). + /// The span dictionary (must contain "bbox"). + /// Use font-size only as glyph height. + public static Quad RecoverQuad( + (double cos, double sin) lineDir, + Dictionary span, + bool smallGlyphHeights = false) { - return low <= v && v <= high; + Rect bbox = DictToRect(span["bbox"]); + return RecoverBboxQuad(lineDir, span, bbox, smallGlyphHeights); } - public static bool INRANGE(float v, float low, float high) + /// + /// Calculate the quad covering a text line (or a subset of its spans) + /// from "dict" / "rawdict" text extraction output. + /// + /// The line dictionary (must contain "dir" and "spans"). + /// Optional sub-list of spans; defaults to all spans in the line. + /// Use font-size only as glyph height. + public static Quad RecoverLineQuad( + Dictionary line, + List> spans = null, + bool smallGlyphHeights = false) { - return low <= v && v <= high; - } + if (spans == null) + spans = (List>)line["spans"]; + if (spans.Count == 0) + throw new ArgumentException("bad span list"); - internal static Matrix RotatePageMatrix(PdfPage page) - { - if (page.m_internal == null) - return new Matrix(new FzMatrix()); - int rotation = Utils.PageRotation(page); - if (rotation == 0) - return new Matrix(new FzMatrix()); + var lineDir = DictToDir(line["dir"]); - Point cbSize = GetCropBoxSize(page.obj()); - float w = cbSize.X; - float h = cbSize.Y; + Quad q0 = RecoverQuad(lineDir, spans[0], smallGlyphHeights); + Quad q1 = spans.Count > 1 + ? RecoverQuad(lineDir, spans[spans.Count - 1], smallGlyphHeights) + : q0; - FzMatrix m = new FzMatrix(); - if (rotation == 90) - m = mupdf.mupdf.fz_make_matrix(0, 1, -1, 0, h, 0); - else if (rotation == 180) - m = mupdf.mupdf.fz_make_matrix(-1, 0, 0, -1, w, h); - else - m = mupdf.mupdf.fz_make_matrix(0, -1, 1, 0, 0, w); + Point lineLl = q0.LL; + Point lineLr = q1.LR; - return new Matrix(m); - } + Matrix mat0 = PlanishLineCore(lineLl, lineLr); - internal static Point GetCropBoxSize(PdfObj pageObj) - { - FzRect rect = GetCropBox(pageObj).ToFzRect(); - float width = Math.Abs(rect.x1 - rect.x0); - float height = Math.Abs(rect.y1 - rect.y0); + Point xLr = new Point(lineLr); + xLr.Transform(mat0); - FzPoint size = mupdf.mupdf.fz_make_point(width, height); - return new Point(size); - } + double h = 0; + foreach (var s in spans) + { + double size = Convert.ToDouble(s["size"]); + double spanH; + if (smallGlyphHeights) + spanH = size; + else + spanH = size * (Convert.ToDouble(s["ascender"]) - Convert.ToDouble(s["descender"])); + if (spanH > h) h = spanH; + } - internal static Rect GetCropBox(PdfObj pageObj) - { - FzRect mediabox = Utils.GetMediaBox(pageObj).ToFzRect(); - FzRect cropBox = pageObj.pdf_dict_get_inheritable(new PdfObj("CropBox")).pdf_to_rect(); - if (cropBox.fz_is_infinite_rect() != 0 || cropBox.fz_is_empty_rect() != 0) - cropBox = mediabox; - float y0 = mediabox.y1 - cropBox.y1; - float y1 = mediabox.y1 - cropBox.y0; - cropBox.y0 = y0; - cropBox.y1 = y1; + var lineRect = new Rect(0, -h, xLr.X, 0); + var lineQuad = lineRect.Quad; + + Matrix inv = mat0.Inverted(); + if (inv != null) + lineQuad.Transform(inv); - return new Rect(cropBox); + return lineQuad; } - internal static Rect GetMediaBox(PdfObj pageObj) + /// + /// Calculate the quad of a text span (or a sub-selection of its characters) + /// from "dict" / "rawdict" text extraction output. + /// When is null the full span quad is returned. + /// Sub-selecting characters requires the "rawdict" extraction option. + /// + /// The line's direction vector (cos, sin). + /// The span dictionary. + /// Optional sub-list of character dictionaries. + /// Use font-size only as glyph height. + public static Quad RecoverSpanQuad( + (double cos, double sin) lineDir, + Dictionary span, + List> chars = null, + bool smallGlyphHeights = false) { - FzRect pageMediaBox = new FzRect(FzRect.Fixed.Fixed_UNIT); - FzRect mediaBox = pageObj - .pdf_dict_get_inheritable(new PdfObj("MediaBox")) - .pdf_to_rect(); - if (mediaBox.fz_is_empty_rect() != 0 || mediaBox.fz_is_infinite_rect() != 0) - { - mediaBox.x0 = 0; - mediaBox.y0 = 0; - mediaBox.x1 = 612; - mediaBox.y1 = 792; - } - pageMediaBox = new FzRect( - Math.Min(mediaBox.x0, mediaBox.x1), - Math.Min(mediaBox.y0, mediaBox.y1), - Math.Max(mediaBox.x0, mediaBox.x1), - Math.Max(mediaBox.y0, mediaBox.y1) - ); + if (chars == null) + return RecoverQuad(lineDir, span, smallGlyphHeights); + if (!span.ContainsKey("chars")) + throw new ArgumentException("need 'rawdict' option to sub-select chars"); - if (pageMediaBox.x1 - pageMediaBox.x0 < 1 || pageMediaBox.y1 - pageMediaBox.y0 < 1) - { - pageMediaBox = new FzRect(FzRect.Fixed.Fixed_UNIT); - } - return new Rect(pageMediaBox); - } + Quad q0 = RecoverCharQuad(lineDir, span, chars[0], smallGlyphHeights); + Quad q1 = chars.Count > 1 + ? RecoverCharQuad(lineDir, span, chars[chars.Count - 1], smallGlyphHeights) + : q0; - internal static FzMatrix DerotatePageMatrix(PdfPage page) - { - Matrix mp = RotatePageMatrix(page); - return mp.ToFzMatrix().fz_invert_matrix(); - } + Point spanLl = q0.LL; + Point spanLr = q1.LR; + Matrix mat0 = PlanishLineCore(spanLl, spanLr); - internal static int PageRotation(PdfPage page) - { - int rotate; + Point xLr = new Point(spanLr); + xLr.Transform(mat0); - PdfObj obj = page.obj().pdf_dict_get_inheritable(new PdfObj("Rotate")); - rotate = obj.pdf_to_int(); - rotate = NormalizeRotation(rotate); - return rotate; - } + double size = Convert.ToDouble(span["size"]); + double h; + if (smallGlyphHeights) + h = size; + else + h = size * (Convert.ToDouble(span["ascender"]) - Convert.ToDouble(span["descender"])); - public static int NormalizeRotation(int rotate) - { - while (rotate < 0) - { - rotate += 360; - } - while (rotate >= 360) - { - rotate -= 360; - } - if (rotate % 90 != 0) - { - return 0; - } - return rotate; - } + var spanRect = new Rect(0, -h, xLr.X, 0); + var spanQuad = spanRect.Quad; - internal static FzRect RectFromObj(dynamic r) - { - if (r is FzRect) - return r; - if (r is FzIrect) - return new FzRect(r); - if (r is Rect || r is IRect) - return mupdf.mupdf.fz_make_rect(r.X0, r.Y0, r.X1, r.Y1); - if (r == null || r.Length != 4) - return new FzRect(FzRect.Fixed.Fixed_INFINITE); - if (r is float[] || r is Tuple || r is List) - { - for (int i = 0; i < 4; i++) - { - try - { - if (r[i] < Utils.FZ_MIN_INF_RECT) - r[i] = Utils.FZ_MIN_INF_RECT; - if (r[i] > Utils.FZ_MAX_INF_RECT) - r[i] = Utils.FZ_MAX_INF_RECT; - } - catch (Exception) - { - return new FzRect(FzRect.Fixed.Fixed_INFINITE); - } - } - } - return mupdf.mupdf.fz_make_rect(r[0], r[1], r[2], r[3]); - } + Matrix inv = mat0.Inverted(); + if (inv != null) + spanQuad.Transform(inv); - internal static string ReadSamples(byte[] buff, int offset, int n) - { - if ((offset + n) > buff.Length) - return null; - List ret = new List(); - for (int i = 0; i < n; i++) - ret.Add((byte)buff[offset + i]); - return Utils.Bytes2Str(ret); + return spanQuad; } - internal static string ReadSamples(FzPixmap pixmap, int offset, int n) + /// + /// Recover the quadrilateral of a single text character. + /// Requires the "rawdict" extraction option. + /// + /// The line's direction vector (cos, sin). + /// The span dictionary. + /// The character dictionary (must contain "bbox"). + /// Use font-size only as glyph height. + public static Quad RecoverCharQuad( + (double cos, double sin) lineDir, + Dictionary span, + Dictionary charDict, + bool smallGlyphHeights = false) { - List ret = new List(); - for (int i = 0; i < n; i++) - ret.Add((byte)pixmap.fz_samples_get(offset + i)); - return Utils.Bytes2Str(ret); + Rect bbox = DictToRect(charDict["bbox"]); + return RecoverBboxQuad(lineDir, span, bbox, smallGlyphHeights); } - public static string Bytes2Str(List bytes) - { - //return string.Join(',', bytes.Select(b => $"{b}")); - return string.Join(",", bytes.Select(b => $"{b}")); - } + // ─── Font Helper ──────────────────────────────────────────────── - internal static Dictionary ColorCount(FzPixmap pm, IRect iRect, int iStride, int iN, int iX, int iY, byte[] samples, dynamic clip) + /// + /// Retrieve font properties (name, file extension, subtype, ascender, + /// descender) for the font at the given xref. + /// Ascender and descender default to 0.8 / -0.2 when the font cannot + /// be inspected. + /// + /// The parent document. + /// The font object's xref number. + public static (string fontname, string ext, string stype, float asc, float dsc) GetFontProperties( + Document doc, int xref) { - Dictionary ret = new Dictionary(); - int count = 0; - if (iRect == null) - throw new ArgumentNullException(nameof(iRect)); - if (samples == null) - throw new ArgumentNullException(nameof(samples)); - FzIrect irect = iRect.ToFzIrect(); - irect = irect.fz_intersect_irect(new FzIrect(RectFromObj(clip))); - int stride = iStride; - int width = irect.x1 - irect.x0; - int height = irect.y1 - irect.y0; - int n = iN; - - int substride = width * n; - int s = stride * (irect.y0 - iY) + (irect.x0 - iX) * n; - - string oldPix = Utils.ReadSamples(samples, s, n); + var (fontname, ext, stype, buffer) = doc.ExtractFont(xref); + float asc = 0.8f; + float dsc = -0.2f; - count = 0; - if (irect.fz_is_empty_irect() != 0) - return ret; + if (string.IsNullOrEmpty(ext)) + return (fontname, ext, stype, asc, dsc); - string pixel = null; - int c = 0; - for (int i = 0; i < height; i++) + if (buffer != null && buffer.Length > 0) { - for (int j = 0; j < substride; j += n) + try { - string newPix = Utils.ReadSamples(samples, s + j, n); - if (!newPix.SequenceEqual(oldPix)) + using var font = new Font(fontbuffer: buffer); + asc = font.Ascender; + dsc = font.Descender; + var bbox = font.BBox; + if (asc - dsc < 1) { - pixel = oldPix; - //c = ret.GetValueOrDefault(pixel, 0); - c = 0; - ret.TryGetValue(pixel, out c); - if (c != 0) - { - count += c; - } - ret[pixel] = count; - count = 1; - oldPix = newPix; + if (bbox.Y0 < dsc) + dsc = (float)bbox.Y0; + asc = 1 - dsc; } - else - count += 1; } - s += stride; + catch + { + asc *= 1.2f; + dsc *= 1.2f; + } + return (fontname, ext, stype, asc, dsc); + } + + if (ext != "n/a") + { + try + { + using var font = new Font(fontname: fontname); + asc = font.Ascender; + dsc = font.Descender; + } + catch + { + asc *= 1.2f; + dsc *= 1.2f; + } } - pixel = oldPix; - //c = ret.GetValueOrDefault(pixel, 0); - c = 0; - ret.TryGetValue(pixel, out c); - if (c != 0) + else { - count += c; + asc *= 1.2f; + dsc *= 1.2f; } - ret[pixel] = count; - return ret; + return (fontname, ext, stype, asc, dsc); } - public static void GetWidgetProperties(Annot annot, Widget widget) + /// + /// "Now" timestamp in PDF Format. + /// + public static string get_pdf_now() => Helpers.GetPdfNow(); + public static string getPDFnow() => get_pdf_now(); + /// + /// Return a PDF string depending on its coding. + /// Notes: + /// Returns a string bracketed with either "()" or "<>" for hex values. + /// If only ascii then "(original)" is returned, else if only 8 bit chars + /// then "(original)" with interspersed octal strings \nnn is returned, + /// else a string "<FEFF[hexstring]>" is returned, where [hexstring] is the + /// UTF-16BE encoding of the original. + /// + public static string get_pdf_str(string s) => Helpers.GetPdfStr(s); + public static string getPDFstr(string s) => get_pdf_str(s); + /// + /// Calculate length of a string for a built-in font. + /// Args: + /// fontname: name of the font. + /// fontsize: font size points. + /// encoding: encoding to use, 0=Latin (default), 1=Greek, 2=Cyrillic. + /// Returns: + /// (float) length of text. + /// + public static float get_text_length(string text, string fontname = "helv", float fontsize = 11, int encoding = 0) { - PdfObj annotObj = mupdf.mupdf.pdf_annot_obj(annot.ToPdfAnnot()); - PdfPage page = mupdf.mupdf.pdf_annot_page(annot.ToPdfAnnot()); - PdfDocument pageDoc = page.doc(); - PdfAnnot tw = annot.ToPdfAnnot(); + if (string.IsNullOrEmpty(text)) + return 0f; - pdf_widget_type fieldType = tw.pdf_widget_type(); - widget.FieldType = (int)fieldType; - if (fieldType == (pdf_widget_type)PdfWidgetType.PDF_WIDGET_TYPE_SIGNATURE) + // fontname = fontname.lower() + string lower = (fontname ?? "helv").ToLowerInvariant(); + // basename = Base14_fontdict.get(fontname, None) + string resolved; + if (Constants.Base14FontDict.TryGetValue(lower, out resolved)) { - if (pageDoc.pdf_signature_is_signed(annotObj) != 0) - widget.IsSigned = true; - else - widget.IsSigned = false; + // if fontname in Base14_fontdict.keys(): + // return util_measure_string(text, Base14_fontdict[fontname], fontsize, encoding) + using (var font = new Font(lower)) + { + return font.TextLength(text, fontsize, script: encoding); + } } - else - widget.IsSigned = false; - widget.BorderStyle = Utils.UnicodeFromStr(annotObj.pdf_field_border_style()); - widget.FieldTypeString = Utils.UnicodeFromStr(Utils.GetFieldTypeText((int)fieldType)); - string fieldName = annotObj.pdf_load_field_name(); - widget.FieldName = fieldName; + // if fontname in ("china-t","china-s","china-ts","china-ss","japan","japan-s","korea","korea-s"): + // return len(text) * fontsize + if (lower == "china-t" || lower == "china-s" || lower == "china-ts" || lower == "china-ss" || + lower == "japan" || lower == "japan-s" || lower == "korea" || lower == "korea-s") + return text.Length * fontsize; - PdfObj obj = annotObj.pdf_dict_get(new PdfObj("TU")); - if (obj.m_internal != null) - { - string label = obj.pdf_to_text_string(); - widget.FieldLabel = label; - } + // raise ValueError(f"Font '{fontname}' is unsupported") + throw new ArgumentException($"Font '{fontname}' is unsupported"); + } + public static float getTextlength(string text, string fontname = "helv", float fontsize = 11, int encoding = 0) + => get_text_length(text, fontname, fontsize, encoding); + /// + /// Return basic properties of an image. + /// Args: + /// img: bytes, bytearray, io.BytesIO object or an opened image file. + /// Returns: + /// A dictionary with keys width, height, colorspace.n, bpc, type, ext and size, + /// where 'type' is the MuPDF image type (0 to 14) and 'ext' the suitable + /// file extension. + /// + public static Dictionary image_profile(object img, int keep_image = 0) + { + if (img == null) + throw new ArgumentException("bad argument 'img'"); - string fVal = ""; - if (fieldType == (pdf_widget_type)PdfWidgetType.PDF_WIDGET_TYPE_RADIOBUTTON) - { - obj = annotObj.pdf_dict_get(new PdfObj("Parent")); - if (obj.m_internal != null) - widget.RbParent = obj.pdf_to_num(); - obj = annotObj.pdf_dict_get(new PdfObj("AS")); - if (obj.m_internal != null) - fVal = obj.pdf_to_name(); - } - if (string.IsNullOrEmpty(fVal)) + byte[] stream; + // if type(img) is io.BytesIO: stream = img.getvalue() + if (img != null && img.GetType() == typeof(MemoryStream)) { - fVal = annotObj.pdf_field_value(); + var ms = (MemoryStream)img; + stream = ms.ToArray(); } - widget.FieldValue = Utils.UnicodeFromStr(fVal); - widget.FieldDisplay = annotObj.pdf_field_display(); - float borderWidth = Utils - .pdf_dict_getl(annotObj, new string[] { "BS", "W" }) - .pdf_to_real(); - if (borderWidth == 0) - borderWidth = 1; - widget.BorderWidth = borderWidth; - - obj = Utils.pdf_dict_getl(annotObj, new string[] { "BS", "D" }); - if (obj.pdf_is_array() != 0) + // elif hasattr(img, "read"): stream = img.read() + else if (img is Stream s) { - int n = obj.pdf_array_len(); - int[] d = new int[n]; - for (int i = 0; i < n; i++) - d[i] = obj.pdf_array_get(i).pdf_to_int(); - widget.BorderDashes = d; + using (var mem = new MemoryStream()) + { + s.CopyTo(mem); + stream = mem.ToArray(); + } } - - widget.TextMaxLen = tw.pdf_text_widget_max_len(); - widget.TextFormat = tw.pdf_text_widget_format(); - - obj = Utils.pdf_dict_getl(annotObj, new string[] { "MK", "BG" }); - if (obj.pdf_is_array() != 0) + // elif type(img) in (bytes, bytearray): stream = img + else if (img is byte[] bytes) { - int n = obj.pdf_array_len(); - float[] col = new float[n]; - for (int i = 0; i < n; i++) - col[i] = obj.pdf_array_get(i).pdf_to_real(); - widget.FillColor = col; + stream = bytes; } - - obj = Utils.pdf_dict_getl(annotObj, new string[] { "MK", "BC" }); - if (obj.pdf_is_array() != 0) + // else: raise ValueError("bad argument 'img'") + else { - int n = obj.pdf_array_len(); - float[] col = new float[n]; - for (int i = 0; i < n; i++) - col[i] = obj.pdf_array_get(i).pdf_to_real(); - widget.BorderColor = col; + throw new ArgumentException("bad argument 'img'"); } - widget.ChoiceValues = Utils.GetChoiceOptions(annot.ToPdfAnnot()); - - string da = annotObj.pdf_dict_get_inheritable(new PdfObj("DA")).pdf_to_text_string(); - widget.TextDa = Utils.UnicodeFromStr(da); + // JM_image_profile: if not imagedata: return None + if (stream.Length == 0) + return null; + // len_ = len(imagedata) + // JM_image_profile: if len_ < 8 -> return None. + // message("bad image data") + if (stream.Length < 8) + return null; - obj = Utils.pdf_dict_getl(annotObj, new string[] { "MK", "CA" }); - if (obj.m_internal != null) - widget.ButtonCaption = Utils.UnicodeFromStr(obj.pdf_to_text_string()); - widget.FieldFlags = annotObj.pdf_field_flags(); + // c = imagedata + // if keep_image: + // res = mupdf.fz_new_buffer_from_copied_data(c, len_) + // else: + // res = mupdf.fz_new_buffer_from_shared_data(c, len_) + var buf = Helpers.BufferFromBytes(stream); + int type = RecognizeImageType(buf); + // if type_ == mupdf.FZ_IMAGE_UNKNOWN: return None + if (type == mupdf.mupdf.FZ_IMAGE_UNKNOWN) + return null; - widget.ParseDa(); + string ext = ImageExtensionFromType(type); + // image = mupdf.fz_new_image_from_buffer(res) + var image = mupdf.mupdf.fz_new_image_from_buffer(buf); + if (image == null || image.m_internal == null) + return null; - PdfObj s = annotObj.pdf_dict_get(new PdfObj("A")); - string ss = Utils.GetScript(s); - widget.Script = ss; - widget.ScriptStroke = Utils.GetScript( - Utils.pdf_dict_getl(annotObj, new string[] { "AA", "K" }) - ); - widget.ScriptFormat = Utils.GetScript( - Utils.pdf_dict_getl(annotObj, new string[] { "AA", "F" }) - ); - widget.ScriptChange = Utils.GetScript( - Utils.pdf_dict_getl(annotObj, new string[] { "AA", "V" }) - ); - widget.ScriptCalc = Utils.GetScript( - Utils.pdf_dict_getl(annotObj, new string[] { "AA", "C" }) - ); - widget.ScriptBlur = Utils.GetScript( - Utils.pdf_dict_getl(annotObj, new string[] { "AA", "Bl" }) - ); - widget.ScriptFocus = Utils.GetScript( - Utils.pdf_dict_getl(annotObj, new string[] { "AA", "Fo" }) - ); + // ctm = mupdf.fz_image_orientation_matrix(image) + var orientationMatrix = image.fz_image_orientation_matrix(); + // xres, yres = mupdf.fz_image_resolution(image) + var resolution = image.fz_image_resolution(); + // orientation = mupdf.fz_image_orientation(image) + byte orientation = image.fz_image_orientation(); + // cs_name = mupdf.fz_colorspace_name(image.colorspace()) + var cs = image.colorspace(); + string csName = cs != null && cs.m_internal != null ? mupdf.mupdf.fz_colorspace_name(cs) : null; + // result = dict() + + var result = new Dictionary + { + // result[dictkey_width] = image.w() + ["width"] = image.w(), + // result[dictkey_height] = image.h() + ["height"] = image.h(), + // result["orientation"] = orientation + ["orientation"] = orientation, + // result[dictkey_matrix] = JM_py_from_matrix(ctm) + ["transform"] = new Matrix(orientationMatrix), + // result[dictkey_xres] = xres + ["xres"] = resolution.xres, + // result[dictkey_yres] = yres + ["yres"] = resolution.yres, + // result[dictkey_colorspace] = image.n() + ["colorspace"] = image.n(), + // result[dictkey_bpc] = image.bpc() + ["bpc"] = image.bpc(), + // result[dictkey_ext] = JM_image_extension(type_) + ["ext"] = ext, + // result[dictkey_cs_name] = cs_name + ["cs-name"] = csName, + }; - pageDoc.Dispose(); + // if keep_image: result[dictkey_image] = image + if (keep_image != 0) + result["image"] = image; + return result; } + public static Dictionary ImageProperties(object img, int keep_image = 0) => image_profile(img, keep_image); + /// + /// Return a Rect for the paper size indicated in string 's'. + /// Must conform to the argument of method 'PaperSize', which will be invoked. + /// + public static Rect paper_rect(string s) => Helpers.PaperRect(s); + public static Rect PaperRect(string s) => paper_rect(s); + /// + /// Return a tuple (width, height) for a given paper format string. + /// Notes: + /// 'A4-L' will return (842, 595), the values for A4 landscape. + /// Suffix '-P' and no suffix return the portrait tuple. + /// + public static (float w, float h) paper_size(string s) => Helpers.PaperSize(s); + public static (float w, float h) PaperSize(string s) => paper_size(s); + /// + /// Known paper formats @ 72 dpi as a dictionary. Key is the format string + /// like "a4" for ISO-A4. Value is the tuple (width, height). + /// Information taken from: + /// www.din-formate.de + /// www.din-formate.info/amerikanische-formate.html + /// www.directtools.de/wissen/normen/iso.htm + /// + public static Dictionary paper_sizes() => Helpers.GetPaperSizes(); + /// + /// Compute matrix which maps line from p1 to p2 to the x-axis, such that it + /// maintains its length and p1 * matrix = Point(0, 0). + /// Returns: + /// Matrix which maps p1 to Point(0, 0) and p2 to a point on the x axis at + /// the same distance to Point(0,0). Will always combine a rotation and a + /// transformation. + /// + public static Matrix planish_line(Point p1, Point p2) => PlanishLineCore(p1, p2); - internal static List GetChoiceOptions(PdfAnnot annot) + /// + /// Compute matrix which maps line from p1 to p2 to the x-axis, such that it + /// maintains its length and p1 * matrix = Point(0, 0). + /// + /// Args: + /// p1, p2: point_like + /// Returns: + /// Matrix which maps p1 to Point(0, 0) and p2 to a point on the x axis at + /// the same distance to Point(0,0). Will always combine a rotation and a + /// transformation. + /// + private static Matrix PlanishLineCore(Point p1, Point p2) { - PdfObj annotObj = annot.pdf_annot_obj(); - vectors opts = mupdf.mupdf.pdf_choice_widget_options2(annot, 0); - int n = opts.Capacity; + // p1 = Point(p1) + // p2 = Point(p2) + double dx = p2.X - p1.X; + double dy = p2.Y - p1.Y; + double length = Math.Sqrt(dx * dx + dy * dy); + if (length < Constants.Epsilon) + return new Matrix(1, 0, 0, 1, -p1.X, -p1.Y); - if (n == 0) - return null; - PdfObj optArr = annotObj.pdf_dict_get(new PdfObj("Opt")); - List ret = new List(); - - for (int i = 0; i < n; i++) - { - int m = optArr.pdf_array_get(i).pdf_array_len(); - if (m == 2) - { - ret.Add( - new List() - { - optArr.pdf_array_get(i).pdf_array_get(0).pdf_to_text_string(), - optArr.pdf_array_get(i).pdf_array_get(1).pdf_to_text_string() - } - ); - } - else - { - ret.Add(new List() { optArr.pdf_array_get(i).pdf_to_text_string() }); - } - } - return ret; - } - - internal static void AddAnnotId(PdfAnnot annot, string stem) - { - PdfPage page = annot.pdf_annot_page(); - PdfObj annotObj = annot.pdf_annot_obj(); - List names = GetAnnotIDList(page); - int i = 0; - string stemId = ""; - while (true) - { - stemId = $"{ANNOT_ID_STEM}-{stem}{i}"; - if (!names.Contains(stemId)) - break; - i += 1; - } - PdfObj name = mupdf.mupdf.pdf_new_string(stemId, (uint)stemId.Length); - annotObj.pdf_dict_puts("NM", name); - PdfDocument pageDoc = page.doc(); - pageDoc.m_internal.resynth_required = 0; - pageDoc.Dispose(); - } - - internal static List GetAnnotIDList(PdfPage page) - { - List ids = new List(); - PdfObj annots = page.obj().pdf_dict_get(new PdfObj("Annots")); - if (annots.m_internal == null) - return ids; - for (int i = 0; i < annots.pdf_array_len(); i++) - { - PdfObj annotObj = annots.pdf_array_get(i); - PdfObj name = annotObj.pdf_dict_gets("NM"); - if (name.m_internal != null) - ids.Add(name.pdf_to_text_string()); - } - return ids; - } - - public static byte[] ToByte(string s) - { - UTF8Encoding utf8 = new UTF8Encoding(); - return utf8.GetBytes(s); - } - - public static byte[] ReplaceBytes(byte[] src, byte[] search, byte[] replace, int limit=1) - { - if (limit <= 0) - return src; // no replacement - using (var ms = new MemoryStream()) - { - int matchCount = 0; - for (int i = 0; i < src.Length;) - { - bool match = i + search.Length <= src.Length && - src.Skip(i).Take(search.Length).SequenceEqual(search); - if (match && matchCount < limit) - { - ms.Write(replace, 0, replace.Length); - i += search.Length; - matchCount++; - } - else - { - ms.WriteByte(src[i]); - i++; - } - } - return ms.ToArray(); - } - } - - internal static PdfObj EmbedFile( - PdfDocument pdf, - FzBuffer buf, - string fileName, - string uFilename, - string desc, - int compress - ) - { - int len = 0; - PdfObj val = pdf.pdf_new_dict(6); - val.pdf_dict_put_dict(new PdfObj("CI"), 4); - PdfObj ef = val.pdf_dict_put_dict(new PdfObj("EF"), 4); - val.pdf_dict_put_text_string(new PdfObj("F"), fileName); - val.pdf_dict_put_text_string(new PdfObj("UF"), uFilename); - val.pdf_dict_put_text_string(new PdfObj("Desc"), desc); - val.pdf_dict_put(new PdfObj("Type"), new PdfObj("Filespec")); - byte[] bs = Utils.ToByte(" "); - - PdfObj f = pdf.pdf_add_stream(Utils.fz_new_buffer_from_data(bs), new PdfObj(), 0); - - ef.pdf_dict_put(new PdfObj("F"), f); - Utils.UpdateStream(pdf, f, buf, compress); - len = (int)buf.fz_buffer_storage(new SWIGTYPE_p_p_unsigned_char(IntPtr.Zero, false)); - f.pdf_dict_put_int(new PdfObj("DL"), len); - f.pdf_dict_put_int(new PdfObj("Length"), len); - PdfObj param = f.pdf_dict_put_dict(new PdfObj("Params"), 4); - param.pdf_dict_put_int(new PdfObj("Size"), len); - - return val; - } - - internal static void MakeAnnotDA( - PdfAnnot annot, - int nCol, - float[] col, - string fontName, - float fontSize - ) - { - string buf = ""; - if (nCol < 1) - buf += "0 g "; - else if (nCol == 1) - buf += $"{FloatToString(col[0])} g "; - else if (nCol == 2) - Debug.Assert(false); - else if (nCol == 3) - buf += $"{FloatToString(col[0])} {FloatToString(col[1])} {FloatToString(col[2])} rg "; - else - buf += $"{FloatToString(col[0])} {FloatToString(col[1])} {FloatToString(col[2])} {FloatToString(col[3])} k "; - buf += $"/{ExpandFontName(fontName)} {FloatToString(fontSize)} Tf"; - annot.pdf_annot_obj().pdf_dict_put_text_string(new PdfObj("DA"), buf); - } - - public static string ExpandFontName(string fontname) - { - if (fontname == null) - return "Helv"; - if (fontname.StartsWith("Co")) - return "Cour"; - if (fontname.StartsWith("co")) - return "Cour"; - if (fontname.StartsWith("Ti")) - return "TiRo"; - if (fontname.StartsWith("ti")) - return "TiRo"; - if (fontname.StartsWith("Sy")) - return "Symb"; - if (fontname.StartsWith("sy")) - return "Symb"; - if (fontname.StartsWith("Za")) - return "ZaDb"; - if (fontname.StartsWith("za")) - return "ZaDb"; - return "Helv"; - } - - /// - /// Return the text words as a list with the bbox for each word - /// - /// - /// area on page to consider - /// control the amount of data parsed into the textpage - /// either passed-in or null - /// sort the words in reading sequence - /// characters to use as word delimiters - /// consider words to be part of the same line - /// Word Tuples (x0, y0, x1, y1, "word", bno, lno, wno) - /// - public static List GetTextWords( - Page page, - Rect clip = null, - int flags = 0, - TextPage stPage = null, - bool sort = false, - char[] delimiters = null, - int tolerance = 3 - ) - { - List SortWords(List _words) - { - _words.Sort((w1, w2) => - { - if (w1.Y1 == w2.Y1) - { - return w1.X0.CompareTo(w2.X0); - } - else - { - return w1.Y1.CompareTo(w2.Y1); - } - }); - - List nWords = new List(); - List line = new List() { _words[0] }; - Rect lrect = new Rect(_words[0].X0, _words[0].Y0, _words[0].X1, _words[0].Y1); - - foreach (WordBlock w in _words.Skip(1)) - { - Rect wrect = new Rect(w.X0, w.Y0, w.X1, w.Y1); - if (Math.Abs(wrect.Y0 - lrect.Y0) <= tolerance || Math.Abs(wrect.Y1 - lrect.Y1) <= tolerance) - { - line.Add(w); - lrect |= wrect; - } - else - { - line.Sort((w1, w2) => { return w1.X0.CompareTo(w2.X0); }); - nWords.AddRange(line); - line = new List { w }; - lrect = wrect; - } - } - line.Sort((w1, w2) => { return w1.X0.CompareTo(w2.X0); }); - nWords.AddRange(line); - - return nWords; - } - if (flags == 0) - flags = (int)(0 - | TextFlags.TEXT_PRESERVE_WHITESPACE - | TextFlags.TEXT_PRESERVE_LIGATURES - | TextFlags.TEXT_MEDIABOX_CLIP - | TextFlags.TEXT_CID_FOR_UNKNOWN_UNICODE - ); - - TextPage tp = stPage; - - if (tp == null) - tp = page.GetTextPage(clip, flags); - else if (tp.Parent != page) - throw new Exception("not a textpage of this page"); - - List words = tp.ExtractWords(delimiters); - if (stPage != null) - { - words = words.Where(w => - { - Rect wRect = new Rect(w.X0, w.Y0, w.X1, w.Y1); - return (clip & wRect).Abs() >= (0.5f * wRect.Abs()); - }).ToList(); - } - - if (stPage is null) - tp = null; - - if (words.Count > 0 && sort) - words = SortWords(words); - - return words; - } - - public static List GetTextBlocks( - Page page, - Rect clip = null, - int flags = 0, - TextPage textPage = null, - bool sort = false - ) - { - if (flags == 0) - { - flags = (int)( - TextFlags.TEXT_PRESERVE_WHITESPACE - | TextFlags.TEXT_PRESERVE_IMAGES - | TextFlags.TEXT_PRESERVE_LIGATURES - | TextFlags.TEXT_MEDIABOX_CLIP - ); - } - TextPage tp = textPage; - if (tp == null) - tp = page.GetTextPage(clip, flags); - else if (tp.Parent != page) - throw new Exception("not a textpage of this page"); - - List blocks = tp.ExtractBlocks(); - if (textPage == null) - tp = null; - if (sort == true) - blocks.Sort( - (TextBlock t1, TextBlock t2) => - { - var result = t1.Y1.CompareTo(t2.Y1); - if (result == 0) - { - result = t1.X0.CompareTo(t2.X0); - } - return result; - } - ); - return blocks; - } - - public static List
GetTables( - Page page, - Rect clip = null, - string vertical_strategy = "lines", - string horizontal_strategy = "lines", - List vertical_lines = null, - List horizontal_lines = null, - float snap_tolerance = TableFlags.TABLE_DEFAULT_SNAP_TOLERANCE, - float snap_x_tolerance = 0.0f, - float snap_y_tolerance = 0.0f, - float join_tolerance = TableFlags.TABLE_DEFAULT_JOIN_TOLERANCE, - float join_x_tolerance = 0.0f, - float join_y_tolerance = 0.0f, - float edge_min_length = 3.0f, - float min_words_vertical = TableFlags.TABLE_DEFAULT_MIN_WORDS_VERTICAL, - float min_words_horizontal = TableFlags.TABLE_DEFAULT_MIN_WORDS_HORIZONTAL, - float intersection_tolerance = 3.0f, - float intersection_x_tolerance = 0.0f, - float intersection_y_tolerance = 0.0f, - float text_tolerance = 3.0f, - float text_x_tolerance = 3.0f, - float text_y_tolerance = 3.0f, - string strategy = null, // offer abbreviation - List add_lines = null - ) - { - // Convert List to List for FindTables - List verticalLinesObj = vertical_lines?.Cast().ToList(); - List horizontalLinesObj = horizontal_lines?.Cast().ToList(); - - // Note: add_lines parameter in GetTables is List (text lines), but FindTables expects - // List> (geometric line segments). Since Line is a text line structure - // and not a geometric line, we cannot properly convert it. Pass null for now. - // If geometric line segments are needed, they should be passed directly to FindTables. - List> addLinesTuple = null; - - // Call FindTables with nullable tolerances (0.0f means use default, null means UNSET) - var finder = TableFinderHelper.FindTables( - page: page, - clip: clip, - vertical_strategy: vertical_strategy, - horizontal_strategy: horizontal_strategy, - vertical_lines: verticalLinesObj, - horizontal_lines: horizontalLinesObj, - snap_tolerance: snap_tolerance, - snap_x_tolerance: snap_x_tolerance == 0.0f ? (float?)null : snap_x_tolerance, - snap_y_tolerance: snap_y_tolerance == 0.0f ? (float?)null : snap_y_tolerance, - join_tolerance: join_tolerance, - join_x_tolerance: join_x_tolerance == 0.0f ? (float?)null : join_x_tolerance, - join_y_tolerance: join_y_tolerance == 0.0f ? (float?)null : join_y_tolerance, - edge_min_length: edge_min_length, - min_words_vertical: min_words_vertical, - min_words_horizontal: min_words_horizontal, - intersection_tolerance: intersection_tolerance, - intersection_x_tolerance: intersection_x_tolerance == 0.0f ? (float?)null : intersection_x_tolerance, - intersection_y_tolerance: intersection_y_tolerance == 0.0f ? (float?)null : intersection_y_tolerance, - text_tolerance: text_tolerance, - text_x_tolerance: text_x_tolerance, - text_y_tolerance: text_y_tolerance, - strategy: strategy, - add_lines: addLinesTuple - ); - - if (finder == null) - return new List
(); - - return finder.tables; - } - - /// - /// Read barcodes from page. - /// - /// - /// Decode barcodes only from embedded images in the PDF resources. - /// Barcode format to decode. - /// Spend more time to try to find a barcode; optimize for accuracy, not speed. - /// Try to decode as inverted image. - /// Image is a pure monochrome image of a barcode. - /// Try to read multi barcodes on page. - /// Indicate whether the image should be automatically rotated. - /// Rotation is supported for 90, 180 and 270 degrees. - public static List ReadBarcodes( - Page page, - Rect clip = null, - bool decodeEmbeddedOnly = false, - BarcodeFormat barcodeFormat = BarcodeFormat.ALL, - bool tryHarder = true, - bool tryInverted = false, - bool pureBarcode = false, - bool multi = true, - bool autoRotate = true - ) - { - // if clip is null, use the whole page rect - if (clip == null) - { - clip = new Rect(0, 0, page.Rect.Width, page.Rect.Height); - } - - List barcodes = new List(); - - List blockRects = new List(); - - if (!decodeEmbeddedOnly) - { - blockRects.Add(clip); // add the clip rect to the list - } - else - { - foreach (Block block in page.GetImageInfo()) - { - Rect blockRectItem = block.Bbox; - if (clip.Contains(blockRectItem)) - { - blockRects.Add(blockRectItem); - } - } - } - - foreach (Rect blockRect in blockRects) - { - Rect newRect = blockRect; - // make space around of clip - if (blockRect.X0 > 5 && blockRect.Y0 > 5 && - blockRect.X1 < page.Rect.Width - 5 && blockRect.Y1 < page.Rect.Height - 5) - { - newRect = new Rect(blockRect.X0 - 5, blockRect.Y0 - 5, blockRect.X1 + 5, blockRect.Y1 + 5); - } - - // save the start x and y of the clip - float startX = newRect.X0; - float startY = newRect.Y0; - - Config config = new Config(); - - Pixmap pxmp = page.GetPixmap(dpi: 200, clip: newRect); - byte[] pmBuf = pxmp.ToBytes(); - - // Calculate Rect ratio between PDF page and image. - float imageWidth = pxmp.IRect.Width; - float imageHeight = pxmp.IRect.Height + 0.01f; - float pageWidth = newRect.Width; - float pageHeight = newRect.Height + 0.01f; - float widthRatio = imageWidth / pageWidth; - float heightRatio = imageHeight / pageHeight; - - pxmp.Dispose(); // free pixmap memory - - SKBitmap bitmap; - try - { - bitmap = SKBitmap.Decode(pmBuf); - } - catch (Exception e) - { - throw new FileNotFoundException("Resource invalid: " + "(" + e.Message + ")"); - } - - List barcodes2 = ReadBarcodes2(bitmap, barcodeFormat); - - bitmap.Dispose(); // free bitmap memory - - foreach (var result in barcodes2) - { - BarcodePoint[] points = new BarcodePoint[result.ResultPoints.Length]; - for (int i = 0; i < result.ResultPoints.Length; i++) - { - // revert the original pdf page ratio - points[i] = new BarcodePoint(startX + result.ResultPoints[i].X / widthRatio, startY + result.ResultPoints[i].Y / heightRatio); - } - - Barcode newBarcode = new Barcode( - result.Text, - result.RawBytes, - result.NumBits, - points, - (BarcodeFormat)result.BarcodeFormat, - result.Timestamp - ); - barcodes.Add(newBarcode); - } - } - - return barcodes; - } - - public static bool Intersect(ref SKRectI rect1, SKRectI rect2) - { - int left = Math.Max(rect1.Left, rect2.Left); - int top = Math.Max(rect1.Top, rect2.Top); - int right = Math.Min(rect1.Right, rect2.Right); - int bottom = Math.Min(rect1.Bottom, rect2.Bottom); - - if (left < right && top < bottom) - { - rect1 = new SKRectI(left, top, right, bottom); - return true; - } - else - { - rect1 = new SKRectI(); // Empty rectangle - return false; - } - } - - /// - /// Read barcodes from image file. - /// - /// - /// Barcode format to decode. - /// Spend more time to try to find a barcode; optimize for accuracy, not speed. - /// Try to decode as inverted image. - /// Image is a pure monochrome image of a barcode. - /// Try to read multi barcodes on page. - /// Indicate whether the image should be automatically rotated. - /// Rotation is supported for 90, 180 and 270 degrees. - public static List ReadBarcodes( - string imageFile, - Rect clip = null, - BarcodeFormat barcodeFormat = BarcodeFormat.ALL, - bool tryHarder = true, - bool tryInverted = false, - bool pureBarcode = false, - bool multi = true, - bool autoRotate = true - ) - { - SKBitmap bitmap = SKBitmap.Decode(imageFile); - - if (clip != null) - { - // Define the region you want to crop (x, y, width, height) - SKRectI cropRect = new SKRectI((int)clip.X0, (int)clip.Y0, (int)clip.X1, (int)clip.Y1); - - // Ensure cropRect is within bounds - bool didIntersect = Intersect(ref cropRect, new SKRectI(0, 0, bitmap.Width, bitmap.Height)); - - // Create a new bitmap to hold the clipped region - var clippedBitmap = new SKBitmap(cropRect.Width, cropRect.Height); - - // Copy the subset into the new bitmap - if (bitmap.ExtractSubset(clippedBitmap, cropRect)) - { - List clippedResult = ReadBarcodes2(clippedBitmap, barcodeFormat); - clippedBitmap.Dispose(); // Dispose of the clipped bitmap - bitmap.Dispose(); // Dispose of the original bitmap - return clippedResult; - } - } - - List barcodes = ReadBarcodes2(bitmap, barcodeFormat); - - bitmap.Dispose(); - - return barcodes; - } - - public static List ReadBarcodes2(SKBitmap bitmap, BarcodeFormat barcodeFormat = BarcodeFormat.ALL) - { - List barcodeTypeList = new List(); - string barcodeType = null; - - List barcodes = new List(); - - switch (barcodeFormat) - { - case BarcodeFormat.AZTEC: barcodeType = "AZTEC"; break; - case BarcodeFormat.BOXES: barcodeType = "BOXES"; break; - case BarcodeFormat.CODABAR: barcodeType = "CODABAR"; break; - case BarcodeFormat.CODABLOCKF: barcodeType = "CODABLOCKF"; break; - case BarcodeFormat.CODE128: barcodeType = "CODE128"; break; - case BarcodeFormat.CODE16K: barcodeType = "CODE16K"; break; - case BarcodeFormat.CODE39: barcodeType = "CODE39"; break; - case BarcodeFormat.CODE39_LINEARREADER: barcodeType = "CODE39_LINEARREADER"; break; - case BarcodeFormat.CODE39_EX: barcodeType = "CODE39_EX"; break; - case BarcodeFormat.CODE39_NOISE1: barcodeType = "CODE39_NOISE1"; break; - case BarcodeFormat.CODE93: barcodeType = "CODE93"; break; - case BarcodeFormat.DM: barcodeType = "DM"; break; - case BarcodeFormat.DM_DPM: barcodeType = "DM_DPM"; break; - case BarcodeFormat.EAN13: barcodeType = "EAN13"; break; - case BarcodeFormat.EAN2: barcodeType = "EAN2"; break; - case BarcodeFormat.EAN5: barcodeType = "EAN5"; break; - case BarcodeFormat.EAN8: barcodeType = "EAN8"; break; - case BarcodeFormat.EAN_UPC_OLD: barcodeType = "EAN_UPC_OLD"; break; - case BarcodeFormat.GS1DATABAREXP: barcodeType = "GS1DATABAREXP"; break; - case BarcodeFormat.GS1DATABAREXPSTACKED: barcodeType = "GS1DATABAREXPSTACKED"; break; - case BarcodeFormat.GS1DATABAROMNI: barcodeType = "GS1DATABAROMNI"; break; - case BarcodeFormat.GS1DATABARSTACKED: barcodeType = "GS1DATABARSTACKED"; break; - case BarcodeFormat.GS1DATABARSTACKEDOMNI: barcodeType = "GS1DATABARSTACKEDOMNI"; break; - case BarcodeFormat.GS1DATABARLIMITED: barcodeType = "GS1DATABARLIMITED"; break; - case BarcodeFormat.HORIZONTALLINES: barcodeType = "HORIZONTALLINES"; break; - case BarcodeFormat.I2OF5: barcodeType = "I2OF5"; break; - case BarcodeFormat.IM: barcodeType = "IM"; break; - case BarcodeFormat.KIX: barcodeType = "KIX"; break; - case BarcodeFormat.LINETABLES: barcodeType = "LINETABLES"; break; - case BarcodeFormat.MAXICODE: barcodeType = "MAXICODE"; break; - case BarcodeFormat.MICR: barcodeType = "MICR"; break; - case BarcodeFormat.MICROPDF: barcodeType = "MICROPDF"; break; - case BarcodeFormat.MSI: barcodeType = "MSI"; break; - case BarcodeFormat.OMRCIRCLE: barcodeType = "OMRCIRCLE"; break; - case BarcodeFormat.OMRCIRCLE_EXT: barcodeType = "OMRCIRCLE_EXT"; break; - case BarcodeFormat.OMROVAL: barcodeType = "OMROVAL"; break; - case BarcodeFormat.OMROVAL_EXT: barcodeType = "OMROVAL_EXT"; break; - case BarcodeFormat.OMRSQUARE: barcodeType = "OMRSQUARE"; break; - case BarcodeFormat.OMRSQUARE_EXT: barcodeType = "OMRSQUARE_EXT"; break; - case BarcodeFormat.OMRSQUARELPATTERN: barcodeType = "OMRSQUARELPATTERN"; break; - case BarcodeFormat.OMRRECTANGLE: barcodeType = "OMRRECTANGLE"; break; - case BarcodeFormat.OMRRECTANGLE_EXT: barcodeType = "OMRRECTANGLE_EXT"; break; - case BarcodeFormat.OMRRECTANGLELPATTERNVERT: barcodeType = "OMRRECTANGLELPATTERNVERT"; break; - case BarcodeFormat.OMRRECTANGLELPATTERNHORIZ: barcodeType = "OMRRECTANGLELPATTERNHORIZ"; break; - case BarcodeFormat.PATCH: barcodeType = "PATCH"; break; - case BarcodeFormat.PHARMA: barcodeType = "PHARMA"; break; - case BarcodeFormat.PDF417: barcodeType = "PDF417"; break; - case BarcodeFormat.POSTCODE: barcodeType = "POSTCODE"; break; - case BarcodeFormat.POSTNET: barcodeType = "POSTNET"; break; - case BarcodeFormat.QR: barcodeType = "QR"; break; - case BarcodeFormat.RAWOMR: barcodeType = "RAWOMR"; break; - case BarcodeFormat.RM: barcodeType = "RM"; break; - case BarcodeFormat.VERTICALLINES: barcodeType = "VERTICALLINES"; break; - case BarcodeFormat.UPC_A: barcodeType = "UPC_A"; break; - case BarcodeFormat.UPC_E: barcodeType = "UPC_E"; break; - case BarcodeFormat.TRIOPTIC: barcodeType = "TRIOPTIC"; break; - case BarcodeFormat.ALL: barcodeType = ""; break; - default: - throw new NotSupportedException($"Barcode format {barcodeFormat} is not supported."); - } - - if (string.IsNullOrEmpty(barcodeType)) - { - // Add all supported formats when ALL is selected - foreach (BarcodeFormat val in Enum.GetValues(typeof(BarcodeFormat))) - { - if (val != BarcodeFormat.ALL && - val != BarcodeFormat.BOXES && - val != BarcodeFormat.RAWOMR && - val != BarcodeFormat.HORIZONTALLINES && - val != BarcodeFormat.VERTICALLINES) - barcodeTypeList.Add(val.ToString()); - } - } - else - { - barcodeTypeList.Add(barcodeType); - } - - foreach (string barcodetype in barcodeTypeList) - { - BarcodeReader reader = new BarcodeReader(barcodetype, null); - - try - { - string[] foundBarcodes = null; - SKRect[] foundBarcodesRectangles = null; - - // string with all barcode results for this image - // 2nd and further barcodes are added separated by the new line - StringBuilder decodingResults = new StringBuilder(); - - bool decoderSuccess = reader.Decode(bitmap); - - foundBarcodes = reader.GetFoundBarcodesAsStrings(); - foundBarcodesRectangles = reader.GetFoundBarcodesAsRectangles(); - - if (decoderSuccess && foundBarcodes != null && foundBarcodes.Length > 0) - { - for (int i = 0; i < foundBarcodes.Length; i++) - { - string text = foundBarcodes[i]; - byte[] rawBytes = Encoding.UTF8.GetBytes(text); - SKRect rect = foundBarcodesRectangles[i]; - BarcodePoint[] resultPoints = new BarcodePoint[] - { - new BarcodePoint(rect.Left, rect.Left), - new BarcodePoint(rect.Left + rect.Width, rect.Top), - new BarcodePoint(rect.Left + rect.Width, rect.Top + rect.Height), - new BarcodePoint(rect.Left, rect.Top + rect.Height) - }; - - BarcodeFormat resultFormat = (BarcodeFormat)Enum.Parse(typeof(BarcodeFormat), barcodetype); - Barcode barcode = new Barcode(text, rawBytes, resultPoints, resultFormat); - - barcodes.Add(barcode); - } - } - else - { - decodingResults.AppendLine("No barcodes found or decoding failed."); - } - } - catch (Exception ex) - { - Console.WriteLine("Error decoding barcode: " + ex.Message); - } - } - - return barcodes; - } - - /// - /// Write barcode to pdf page. - /// - /// Rect area on page to write - /// Contents to write - /// Barcode format to encode; Supported types: QR_CODE, EAN_8, EAN_13, UPC_A, CODE_39, CODE_128, ITF, PDF_417, CODABAR - /// Use a specific character set for binary encoding (if supported by the selected barcode format) - /// Don't generate ECI segment if non-default character set is used - /// Resize output barcode image width/height into clip region - /// Don't put the content string into the output image - /// Specifies margin left, in pixels, to use when generating the barcode - /// Specifies margin top, in pixels, to use when generating the barcode - /// Specifies margin right, in pixels, to use when generating the barcode - /// Specifies margin bottom, in pixels, to use when generating the barcode - /// The width of the narrow bar in pixels - public static void WriteBarcode( - Page page, - Rect clip, - string text, - BarcodeFormat barcodeFormat, - string characterSet = null, - bool disableEci = false, - bool forceFitToRect = false, - bool pureBarcode = false, - int marginLeft = 0, - int marginTop = 0, - int marginRight = 0, - int marginBottom = 0, - int narrowBarWidth = 0 - ) - { - if (clip == null) - { - throw new Exception("Rect is required"); - } - if (text == null) - { - throw new Exception("Text is required"); - } - - int width = (int)clip.Width; - int height = (int)clip.Height; - - if (width <= 0) - { - throw new Exception("Invalid width"); - } - - // get image format from file extension - SKEncodedImageFormat imageFormat = SKEncodedImageFormat.Png; - - // barcode format - string barcodeType = null; - switch (barcodeFormat) - { - case BarcodeFormat.AZTEC: barcodeType = "AZTEC"; break; - case BarcodeFormat.CODABAR: barcodeType = "CODABAR"; break; - case BarcodeFormat.CODE128: barcodeType = "CODE128"; break; - case BarcodeFormat.CODE39: barcodeType = "CODE39"; break; - case BarcodeFormat.CODE93: barcodeType = "CODE93"; break; - case BarcodeFormat.DM: barcodeType = "DM"; break; - case BarcodeFormat.EAN2: barcodeType = "EAN2"; break; - case BarcodeFormat.EAN5: barcodeType = "EAN5"; break; - case BarcodeFormat.EAN8: barcodeType = "EAN8"; break; - case BarcodeFormat.EAN13: barcodeType = "EAN14"; break; - case BarcodeFormat.GS1DATABAREXP: barcodeType = "GS1DATABAREXP"; break; - case BarcodeFormat.GS1DATABAREXPSTACKED: barcodeType = "GS1DATABAREXPSTACKED"; break; - case BarcodeFormat.GS1DATABAROMNI: barcodeType = "GS1DATABAROMNI"; break; - case BarcodeFormat.GS1DATABARSTACKED: barcodeType = "GS1DATABARSTACKED"; break; - case BarcodeFormat.GS1DATABARSTACKEDOMNI: barcodeType = "GS1DATABARSTACKEDOMNI"; break; - case BarcodeFormat.GS1DATABARLIMITED: barcodeType = "GS1DATABARLIMITED"; break; - case BarcodeFormat.I2OF5: barcodeType = "I2OF5"; break; - case BarcodeFormat.IM: barcodeType = "IM"; break; - case BarcodeFormat.MAXICODE: barcodeType = "MAXICODE"; break; - case BarcodeFormat.MSI: barcodeType = "MSI"; break; - case BarcodeFormat.PHARMA: barcodeType = "PHARMA"; break; - case BarcodeFormat.PDF417: barcodeType = "PDF417"; break; - case BarcodeFormat.POSTNET: barcodeType = "POSTNET"; break; - case BarcodeFormat.QR: barcodeType = "QR"; break; - case BarcodeFormat.RM: barcodeType = "RM"; break; - case BarcodeFormat.UPC_A: barcodeType = "UPC_A"; break; - case BarcodeFormat.UPC_E: barcodeType = "UPC_E"; break; - default: - throw new NotSupportedException($"Barcode format {barcodeFormat} is not supported."); - } - - BarcodeWriter barcodeWriter = new BarcodeWriter(barcodeType); - - SKBitmap barcodeImage = barcodeWriter.Encode( - text, - imageFormat, - width, - height, - characterSet, - disableEci, - false, - pureBarcode, - marginLeft, marginTop, marginRight, marginBottom, 0, narrowBarWidth); - - // resize image to fit into clip region - if (forceFitToRect) - { - int newHeigth = barcodeImage.Height * width / barcodeImage.Width; - SKBitmap resizedBitmap = new SKBitmap(width, newHeigth); - using (SKCanvas canvas = new SKCanvas(resizedBitmap)) - { - canvas.DrawBitmap(barcodeImage, new SKRect(0, 0, width, newHeigth)); - } - barcodeImage.Dispose(); - barcodeImage = resizedBitmap; - } - - Rect rect = new Rect(clip.X0, clip.Y0, clip.X0 + barcodeImage.Width, clip.Y0 + barcodeImage.Height); - - MemoryStream ms = new MemoryStream(); - using (SKData data = barcodeImage.Encode(SKEncodedImageFormat.Png, 100)) - { - data.SaveTo(ms); - ms.Position = 0; // Reset stream position - page.InsertImage(rect, stream: ms.ToArray()); - } - - barcodeImage.Dispose(); - } - - /// - /// Return pixmap of barcode image. - /// - /// Contents to write - /// Barcode format to encode; Supported types: QR_CODE, EAN_8, EAN_13, UPC_A, CODE_39, CODE_128, ITF, PDF_417, CODABAR - /// Width of barcode - /// Use a specific character set for binary encoding (if supported by the selected barcode format) - /// Don't generate ECI segment if non-default character set is used - /// Don't put the content string into the output image - /// Specifies margin left, in pixels, to use when generating the barcode - /// Specifies margin top, in pixels, to use when generating the barcode - /// Specifies margin right, in pixels, to use when generating the barcode - /// Specifies margin bottom, in pixels, to use when generating the barcode - /// The width of the narrow bar in pixels - public static Pixmap GetBarcodePixmap( - string text, - BarcodeFormat barcodeFormat, - int width = 0, - string characterSet = null, - bool disableEci = false, - bool pureBarcode = false, - int marginLeft = 0, - int marginTop = 0, - int marginRight = 0, - int marginBottom = 0, - int narrowBarWidth = 0 - ) - { - if (text == null) - { - throw new Exception("Text is required"); - } - - // get image format from file extension - SKEncodedImageFormat imageFormat = SKEncodedImageFormat.Png; - - // barcode format - string barcodeType = null; - switch (barcodeFormat) - { - case BarcodeFormat.AZTEC: barcodeType = "AZTEC"; break; - case BarcodeFormat.CODABAR: barcodeType = "CODABAR"; break; - case BarcodeFormat.CODE128: barcodeType = "CODE128"; break; - case BarcodeFormat.CODE39: barcodeType = "CODE39"; break; - case BarcodeFormat.CODE93: barcodeType = "CODE93"; break; - case BarcodeFormat.DM: barcodeType = "DM"; break; - case BarcodeFormat.EAN2: barcodeType = "EAN2"; break; - case BarcodeFormat.EAN5: barcodeType = "EAN5"; break; - case BarcodeFormat.EAN8: barcodeType = "EAN8"; break; - case BarcodeFormat.EAN13: barcodeType = "EAN14"; break; - case BarcodeFormat.GS1DATABAREXP: barcodeType = "GS1DATABAREXP"; break; - case BarcodeFormat.GS1DATABAREXPSTACKED: barcodeType = "GS1DATABAREXPSTACKED"; break; - case BarcodeFormat.GS1DATABAROMNI: barcodeType = "GS1DATABAROMNI"; break; - case BarcodeFormat.GS1DATABARSTACKED: barcodeType = "GS1DATABARSTACKED"; break; - case BarcodeFormat.GS1DATABARSTACKEDOMNI: barcodeType = "GS1DATABARSTACKEDOMNI"; break; - case BarcodeFormat.GS1DATABARLIMITED: barcodeType = "GS1DATABARLIMITED"; break; - case BarcodeFormat.I2OF5: barcodeType = "I2OF5"; break; - case BarcodeFormat.IM: barcodeType = "IM"; break; - case BarcodeFormat.MAXICODE: barcodeType = "MAXICODE"; break; - case BarcodeFormat.MSI: barcodeType = "MSI"; break; - case BarcodeFormat.PHARMA: barcodeType = "PHARMA"; break; - case BarcodeFormat.PDF417: barcodeType = "PDF417"; break; - case BarcodeFormat.POSTNET: barcodeType = "POSTNET"; break; - case BarcodeFormat.QR: barcodeType = "QR"; break; - case BarcodeFormat.RM: barcodeType = "RM"; break; - case BarcodeFormat.UPC_A: barcodeType = "UPC_A"; break; - case BarcodeFormat.UPC_E: barcodeType = "UPC_E"; break; - default: - throw new NotSupportedException($"Barcode format {barcodeFormat} is not supported."); - } - - BarcodeWriter barcodeWriter = new BarcodeWriter(barcodeType); - - SKBitmap barcodeImage = barcodeWriter.Encode( - text, - imageFormat, - width, - 0, - characterSet, - disableEci, - false, - pureBarcode, - marginLeft, marginTop, marginRight, marginBottom, 0, narrowBarWidth); - - // resize image to fit into clip region - if (width > 0) - { - int newHeight = barcodeImage.Height * width / barcodeImage.Width; - SKBitmap resizedBitmap = new SKBitmap(width, newHeight); - using (SKCanvas canvas = new SKCanvas(resizedBitmap)) - { - canvas.DrawBitmap(barcodeImage, new SKRect(0, 0, width, newHeight)); - } - barcodeImage.Dispose(); - barcodeImage = resizedBitmap; - } - - MemoryStream ms = new MemoryStream(); - using (SKData data = barcodeImage.Encode(SKEncodedImageFormat.Png, 100)) - { - data.SaveTo(ms); - } - - // Reset position if you want to read from stream - ms.Position = 0; - - // Example: convert to byte[] - byte[] bytes = ms.ToArray(); - - Pixmap pixmap = new Pixmap(bytes); - - barcodeImage.Dispose(); - - return pixmap; - } - - /// - /// Write barcode to image file. - /// - /// Full path of being created barcode image file - /// Contents to write - /// Format to encode; Supported formats: QR_CODE, EAN_8, EAN_13, UPC_A, CODE_39, CODE_128, ITF, PDF_417, CODABAR - /// width of image - /// height of image - /// Use a specific character set for binary encoding (if supported by the selected barcode format) - /// don't generate ECI segment if non-default character set is used - /// Resize output barcode image width/height with params - /// Don't put the content string into the output image - /// Specifies margin left, in pixels, to use when generating the barcode - /// Specifies margin top, in pixels, to use when generating the barcode - /// Specifies margin right, in pixels, to use when generating the barcode - /// Specifies margin bottom, in pixels, to use when generating the barcode - /// The width of the narrow bar in pixels - public static void WriteBarcode( - string imageFile, - string text, - BarcodeFormat barcodeFormat, - int width = 300, - int height = 300, - string characterSet = null, - bool disableEci = false, - bool forceFitToRect = false, - bool pureBarcode = false, - int marginLeft = 0, - int marginTop = 0, - int marginRight = 0, - int marginBottom = 0, - int narrowBarWidth = 0 - ) - { - if (width <= 0) - { - throw new Exception("Invalid width"); - } - if (text == null) - { - throw new Exception("Text is required"); - } - - // get image format from file extension - SKEncodedImageFormat imageFormat = SKEncodedImageFormat.Png; - string extension = System.IO.Path.GetExtension(imageFile).ToLower(); - switch (extension) - { - case ".bmp": - imageFormat = SKEncodedImageFormat.Bmp; - break; - case ".gif": - imageFormat = SKEncodedImageFormat.Gif; - break; - case ".ico": - case ".icon": - imageFormat = SKEncodedImageFormat.Ico; - break; - case ".jpeg": - case ".jpg": - imageFormat = SKEncodedImageFormat.Jpeg; - break; - case ".png": - imageFormat = SKEncodedImageFormat.Png; - break; - case ".webp": - imageFormat = SKEncodedImageFormat.Webp; - break; - default: - throw new Exception("Unsupported image format"); - } - - // barcode format - string barcodeType = null; - switch (barcodeFormat) - { - case BarcodeFormat.AZTEC: barcodeType = "AZTEC"; break; - case BarcodeFormat.CODABAR: barcodeType = "CODABAR"; break; - case BarcodeFormat.CODE128: barcodeType = "CODE128"; break; - case BarcodeFormat.CODE39: barcodeType = "CODE39"; break; - case BarcodeFormat.CODE93: barcodeType = "CODE93"; break; - case BarcodeFormat.DM: barcodeType = "DM"; break; - case BarcodeFormat.EAN2: barcodeType = "EAN2"; break; - case BarcodeFormat.EAN5: barcodeType = "EAN5"; break; - case BarcodeFormat.EAN8: barcodeType = "EAN8"; break; - case BarcodeFormat.EAN13: barcodeType = "EAN14"; break; - case BarcodeFormat.GS1DATABAREXP: barcodeType = "GS1DATABAREXP"; break; - case BarcodeFormat.GS1DATABAREXPSTACKED: barcodeType = "GS1DATABAREXPSTACKED"; break; - case BarcodeFormat.GS1DATABAROMNI: barcodeType = "GS1DATABAROMNI"; break; - case BarcodeFormat.GS1DATABARSTACKED: barcodeType = "GS1DATABARSTACKED"; break; - case BarcodeFormat.GS1DATABARSTACKEDOMNI: barcodeType = "GS1DATABARSTACKEDOMNI"; break; - case BarcodeFormat.GS1DATABARLIMITED: barcodeType = "GS1DATABARLIMITED"; break; - case BarcodeFormat.I2OF5: barcodeType = "I2OF5"; break; - case BarcodeFormat.IM: barcodeType = "IM"; break; - case BarcodeFormat.MAXICODE: barcodeType = "MAXICODE"; break; - case BarcodeFormat.MSI: barcodeType = "MSI"; break; - case BarcodeFormat.PHARMA: barcodeType = "PHARMA"; break; - case BarcodeFormat.PDF417: barcodeType = "PDF417"; break; - case BarcodeFormat.POSTNET: barcodeType = "POSTNET"; break; - case BarcodeFormat.QR: barcodeType = "QR"; break; - case BarcodeFormat.RM: barcodeType = "RM"; break; - case BarcodeFormat.UPC_A: barcodeType = "UPC_A"; break; - case BarcodeFormat.UPC_E: barcodeType = "UPC_E"; break; - default: - throw new NotSupportedException($"Barcode format {barcodeFormat} is not supported."); - } - - BarcodeWriter barcodeWriter = new BarcodeWriter(barcodeType); - - barcodeWriter.Encode( - imageFile, - text, - imageFormat, - width, - height, - characterSet, - disableEci, - forceFitToRect, - pureBarcode, - marginLeft, marginTop, marginRight, marginBottom, 0, narrowBarWidth); - } - - public static dynamic GetText( - Page page, - string option = "text", - Rect clip = null, - int flags = 0, - TextPage stPage = null, - bool sort = false, - char[] delimiters = null - ) - { - Dictionary formats = new Dictionary() - { - { "text", 0 }, - { "html", 1 }, - { "json", 1 }, - { "rawjson", 1 }, - { "xml", 0 }, - { "xhtml", 1 }, - { "dict", 1 }, - { "rawdict", 1 }, - { "words", 0 }, - { "blocks", 1 }, - }; - - option = option.ToLower(); - if (!formats.Keys.Contains(option)) - option = "text"; - if (flags == 0) - { - flags = (int)( - TextFlags.TEXT_PRESERVE_WHITESPACE - | TextFlags.TEXT_PRESERVE_LIGATURES - | TextFlags.TEXT_MEDIABOX_CLIP - | TextFlags.TEXT_PRESERVE_IMAGES - | TextFlags.TEXT_INHIBIT_SPACES - | TextFlags.TEXT_DEHYPHENATE - | TextFlags.TEXT_PRESERVE_SPANS - | TextFlags.TEXT_CID_FOR_UNKNOWN_UNICODE - | TextFlags.TEXT_COLLECT_STRUCTURE - | TextFlags.TEXT_ACCURATE_BBOXES - ); - if (formats[option] == 1) - flags = flags | (int)TextFlags.TEXT_PRESERVE_IMAGES; - } - - if (option == "words") - { - return Utils.GetTextWords(page, clip, flags, stPage, sort, delimiters); - } - - if (option == "blocks") - { - return Utils.GetTextBlocks(page, clip, flags, stPage, sort); - } - - Rect cb = null; - if ((new List() { "html", "xml", "xhtml" }).Contains(option)) - clip = page.CropBox; - if (clip != null) - cb = null; - else if (page is Page) - cb = page.CropBox; - if (clip == null) - clip = page.CropBox; - - TextPage tp = stPage; - if (tp == null) - tp = page.GetTextPage(clip, flags); - else if (tp.Parent != page) - throw new Exception("not a textpage of this page"); - - dynamic t = null; - if (option == "json") - t = tp.ExtractJSON(cb, sort); - else if (option == "rawjson") - t = tp.ExtractRawJSON(cb, sort); - else if (option == "dict") - t = tp.ExtractDict(cb, sort); - else if (option == "rawdict") - t = tp.ExtractRAWDict(cb, sort); - else if (option == "html") - t = tp.ExtractHtml(); - else if (option == "xml") - t = tp.ExtractXML(); - else if (option == "xhtml") - t = tp.ExtractText(sort); - else - t = tp.ExtractText(); - - if (stPage == null) - tp = null; - return t; - } - - internal static void SetFieldType(PdfDocument doc, PdfObj annotObj, PdfWidgetType type) - { - PdfFieldFlags setBits = 0; - PdfFieldFlags clearBits = 0; - PdfObj typeName = null; - - if (type == PdfWidgetType.PDF_WIDGET_TYPE_BUTTON) - { - typeName = new PdfObj("Btn"); - setBits = PdfFieldFlags.PDF_BTN_FIELD_IS_PUSHBUTTON; - } - else if (type == PdfWidgetType.PDF_WIDGET_TYPE_RADIOBUTTON) - { - typeName = new PdfObj("Btn"); - clearBits = PdfFieldFlags.PDF_BTN_FIELD_IS_PUSHBUTTON; - setBits = PdfFieldFlags.PDF_BTN_FIELD_IS_RADIO; - } - else if (type == PdfWidgetType.PDF_WIDGET_TYPE_CHECKBOX) - { - typeName = new PdfObj("Btn"); - clearBits = ( - PdfFieldFlags.PDF_BTN_FIELD_IS_PUSHBUTTON | PdfFieldFlags.PDF_BTN_FIELD_IS_RADIO - ); - } - else if (type == PdfWidgetType.PDF_WIDGET_TYPE_TEXT) - { - typeName = new PdfObj("Tx"); - } - else if (type == PdfWidgetType.PDF_WIDGET_TYPE_LISTBOX) - { - typeName = new PdfObj("Ch"); - clearBits = PdfFieldFlags.PDF_CH_FIELD_IS_COMBO; - } - else if (type == PdfWidgetType.PDF_WIDGET_TYPE_COMBOBOX) - { - typeName = new PdfObj("Ch"); - setBits = PdfFieldFlags.PDF_CH_FIELD_IS_COMBO; - } - else if (type == PdfWidgetType.PDF_WIDGET_TYPE_SIGNATURE) - { - typeName = new PdfObj("Sig"); - } - - if (typeName != null) - annotObj.pdf_dict_put(new PdfObj("FT"), typeName); - - int bits = 0; - if ((int)setBits != 0 || (int)setBits != 0) - { - bits = annotObj.pdf_dict_get_int(new PdfObj("Ff")); - bits &= ~(int)clearBits; - bits |= (int)setBits; - annotObj.pdf_dict_put_int(new PdfObj("Ff"), bits); - } - } - - internal static PdfAnnot CreateWidget( - PdfDocument doc, - PdfPage page, - PdfWidgetType type, - string fieldName - ) - { - int oldSigFlags = doc.pdf_trailer() - .pdf_dict_getp("Root/AcroForm/SigFlags") - .pdf_to_int(); - PdfAnnot annot = page.pdf_create_annot_raw(pdf_annot_type.PDF_ANNOT_WIDGET); - PdfObj annotObj = annot.pdf_annot_obj(); - try - { - Utils.SetFieldType(doc, annotObj, type); - annotObj.pdf_dict_put_text_string(new PdfObj("T"), fieldName); - - if (type == PdfWidgetType.PDF_WIDGET_TYPE_SIGNATURE) - { - int sigFlags = - oldSigFlags | (Utils.SigFlag_SignaturesExist | Utils.SigFlag_AppendOnly); - Utils.pdf_dict_putl( - doc.pdf_trailer(), - mupdf.mupdf.pdf_new_int(sigFlags), - new string[] { "Root", "AcroForm", "SigFlags" } - ); - } - - PdfObj form = doc.pdf_trailer().pdf_dict_getp("Root/AcroForm/Fields"); - if (form.m_internal == null) - { - form = doc.pdf_new_array(1); - Utils.pdf_dict_putl( - doc.pdf_trailer(), - form, - new string[] { "Root", "AcroForm", "Fields" } - ); - } - - form.pdf_array_push(annotObj); - } - catch (Exception) - { - page.pdf_delete_annot(annot); - - if (type == PdfWidgetType.PDF_WIDGET_TYPE_SIGNATURE) - { - Utils.pdf_dict_putl( - doc.pdf_trailer(), - mupdf.mupdf.pdf_new_int(oldSigFlags), - new string[] { "Root", "AcroForm", "SigFlags" } - ); - } - } - return annot; - } - - internal static List<(string, int)> GetResourceProperties(PdfObj refer) - { - PdfObj properties = Utils.pdf_dict_getl( - refer, - new string[] { "Resource", "Properties" } - ); - List<(string, int)> rc = new List<(string, int)>(); - if (properties.m_internal == null) - return new List<(string, int)>(); - else - { - int n = properties.pdf_dict_len(); - if (n < 1) - return new List<(string, int)>(); - - for (int i = 0; i < n; i++) - { - PdfObj key = properties.pdf_dict_get_key(i); - PdfObj val = properties.pdf_dict_get_val(i); - string c = key.pdf_to_name(); - int xref = val.pdf_to_num(); - rc.Add((c, xref)); - } - } - return rc; - } - - internal static void SetResourceProperty(PdfObj refer, string name, int xref) - { - PdfDocument pdf = refer.pdf_get_bound_document(); - PdfObj ind = pdf.pdf_new_indirect(xref, 0); - - if (ind.m_internal == null) - Console.WriteLine(Utils.ErrorMessages["MSG_BAD_XREF"]); - - PdfObj resource = refer.pdf_dict_get(new PdfObj("Resource")); - if (resource.m_internal == null) - resource = refer.pdf_dict_put_dict(new PdfObj("Resources"), 1); - - PdfObj properties = resource.pdf_dict_get(new PdfObj("Properties")); - if (properties.m_internal == null) - properties = resource.pdf_dict_put_dict(new PdfObj("Properties"), 1); - - properties.pdf_dict_put(mupdf.mupdf.pdf_new_name(name), ind); - } - - public static int GenID() - { - UNIQUE_ID += 1; - return UNIQUE_ID; - } - - internal static void EmbeddedClean(PdfDocument pdf) - { - PdfObj root = pdf.pdf_trailer().pdf_dict_get(new PdfObj("Root")); - PdfObj coll = root.pdf_dict_get(new PdfObj("Collection")); - if (coll.m_internal != null && coll.pdf_dict_len() == 0) - { - root.pdf_dict_del(new PdfObj("Collection")); - } - - PdfObj efiles = Utils.pdf_dict_getl( - root, - new string[] { "Names", "EmbeddedFiles", "Names" } - ); - if (efiles.m_internal != null) - root.pdf_dict_put_name(new PdfObj("PageMode"), "UseAttachments"); - } - - public static void EnsureIdentity(Document pdf) - { - PdfObj id = Document.AsPdfDocument(pdf).pdf_trailer().pdf_dict_get(new PdfObj("ID")); - if (id.m_internal == null) - { - IntPtr p_block = Marshal.AllocHGlobal(16); - SWIGTYPE_p_unsigned_char swigBlock = new SWIGTYPE_p_unsigned_char(p_block, true); - mupdf.mupdf.fz_memrnd(swigBlock, 16); - - //string rnd0 = Marshal.PtrToStringUTF8(p_block); - byte[] byteArray = new byte[16]; - Marshal.Copy(p_block, byteArray, 0, byteArray.Length); - string rnd0 = System.Text.Encoding.UTF8.GetString(byteArray); - - Marshal.FreeHGlobal(p_block); - - id = Document.AsPdfDocument(pdf).pdf_trailer().pdf_dict_put_array(new PdfObj("ID"), 2); - id.pdf_array_push(mupdf.mupdf.pdf_new_string(rnd0, (uint)rnd0.Length)); - id.pdf_array_push(mupdf.mupdf.pdf_new_string(rnd0, (uint)rnd0.Length)); - } - id.Dispose(); - } - - public static FontInfo CheckFont(Page page, string fontName) - { - foreach (Entry f in page.GetFonts()) - { - if (f.RefName == fontName) - { - return new FontInfo() - { - Xref = f.Xref, - Ext = f.Ext, - Type = f.Type, - Name = f.Name, - RefName = f.RefName, - Encoding = f.Encoding, - StreamXref = f.StreamXref - }; - } - } - return null; - } - - public static FontInfo CheckFontInfo(Document doc, int xref) - { - foreach (FontInfo f in doc.FontInfos) - { - if (xref == f.Xref) - return f; - } - return null; - } - - internal static void ScanResources( - PdfDocument pdf, - PdfObj rsrc, - List liste, - int what, - int streamXRef, - List tracer - ) - { - if (rsrc.pdf_mark_obj() != 0) - { - mupdf.mupdf.fz_warn("Circular dependencies! Consider page cleaning."); - return; - } - try - { - PdfObj xObj = rsrc.pdf_dict_get(new PdfObj("XObject")); - - if (what == 1) - { - PdfObj font = rsrc.pdf_dict_get(new PdfObj("Font")); - GatherFonts(pdf, font, liste, streamXRef); - } - else if (what == 2) - { - GatherIamges(pdf, xObj, liste, streamXRef); - } - else if (what == 3) - GatherForms(pdf, xObj, liste, streamXRef); - else - return; - - int n = xObj.pdf_dict_len(); - for (int i = 0; i < n; i++) - { - PdfObj obj = xObj.pdf_dict_get_val(i); - int sxref = 0; - if (obj.pdf_is_stream() != 0) - sxref = obj.pdf_to_num(); - else - sxref = 0; - PdfObj subrsrc = obj.pdf_dict_get(new PdfObj("Resources")); - if (subrsrc != null) - { - int sxref_t = sxref; - if (!tracer.Contains(sxref_t)) - { - tracer.Add(sxref_t); - Utils.ScanResources(pdf, subrsrc, liste, what, streamXRef, tracer); - } - else - { - mupdf.mupdf.fz_warn("Circular dependencies! Consider page cleaning."); - return; - } - } - } - } - finally - { - mupdf.mupdf.pdf_unmark_obj(rsrc); - } - } - - internal static int GatherFonts( - PdfDocument pdf, - PdfObj dict, - List fontList, - int streamXRef - ) - { - int rc = 1; - int n = dict.pdf_dict_len(); - - for (int i = 0; i < n; i++) - { - PdfObj refName = dict.pdf_dict_get_key(i); - PdfObj fontDict = dict.pdf_dict_get_val(i); - if (fontDict.pdf_is_dict() == 0) - { - mupdf.mupdf.fz_warn( - $"'{refName.pdf_to_name()}' is no font dict ({fontDict.pdf_to_num()} 0 R)" - ); - continue; - } - - PdfObj subType = fontDict.pdf_dict_get(new PdfObj("Subtype")); - PdfObj baseFont = fontDict.pdf_dict_get(new PdfObj("Base")); - PdfObj name = null; - if (baseFont == null || baseFont.pdf_is_null() != 0) - name = fontDict.pdf_dict_get(new PdfObj("Name")); - else - name = baseFont; - PdfObj encoding = fontDict.pdf_dict_get(new PdfObj("Encoding")); - if (encoding.pdf_is_dict() != 0) - encoding = encoding.pdf_dict_get(new PdfObj("BaseEncoding")); - int xref = fontDict.pdf_to_num(); - string ext = "n/a"; - - if (xref != 0) - ext = Utils.GetFontExtension(pdf, xref); - - Entry entry = new Entry - { - Xref = xref, - Ext = ext, - Type = subType.pdf_to_name(), - Name = Utils.EscapeStrFromStr(name.pdf_to_name()), - RefName = refName.pdf_to_name(), - Encoding = encoding.pdf_to_name(), - StreamXref = streamXRef - }; - fontList.Add(entry); - } - - return rc; - } - - internal static string GetFontExtension(PdfDocument doc, int xref) - { - if (xref < 1) - return "n/a"; - PdfObj o = mupdf.mupdf.pdf_load_object(doc, xref); - PdfObj desft = o.pdf_dict_get(new PdfObj("DescendantFonts")); - PdfObj obj = null; - if (desft.m_internal != null) - { - obj = desft.pdf_array_get(0).pdf_resolve_indirect(); - obj = obj.pdf_dict_get(new PdfObj("FontDescriptor")); - } - else - obj = o.pdf_dict_get(new PdfObj("FontDescriptor")); - if (obj.m_internal == null) - return "n/a"; - - o = obj; - obj = o.pdf_dict_get(new PdfObj("FontFile")); - if (obj.m_internal != null) - return "pfa"; - - obj = o.pdf_dict_get(new PdfObj("FontFile2")); - if (obj.m_internal != null) - return "ttf"; - - obj = o.pdf_dict_get(new PdfObj("FontFile3")); - if (obj.m_internal != null) - { - obj = obj.pdf_dict_get(new PdfObj("Subtype")); - if (obj.m_internal != null && obj.pdf_is_name() == 0) - return "n/a"; - if (obj.pdf_name_eq(new PdfObj("Type1C")) != 0) - return "cff"; - else if (obj.pdf_name_eq(new PdfObj("CIDFontType0C")) != 0) - return "cid"; - else if (obj.pdf_name_eq(new PdfObj("OpenType")) != 0) - return "otf"; - else - Console.WriteLine("unhandled font type '%s'", obj.pdf_to_name()); - } - - return "n/a"; - } - - internal static string EscapeStrFromStr(string c) - { - if (c == null) - return ""; - byte[] b = Encoding.UTF8.GetBytes(c); - string ret = ""; - foreach (byte bb in b) - { - ret += (char)bb; - } - - return ret; - } - - internal static int GatherForms( - PdfDocument doc, - PdfObj dict, - List imageList, - int streamXRef - ) - { - int rc = 1; - int n = dict.pdf_dict_len(); - for (int i = 0; i < n; i++) - { - PdfObj refName = dict.pdf_dict_get_key(i); - PdfObj imageDict = dict.pdf_dict_get_val(i); - if (imageDict == null) - { - mupdf.mupdf.fz_warn( - $"'{refName.pdf_to_name()}' is no form dict ({imageDict.pdf_to_num()} 0 R)" - ); - continue; - } - - PdfObj type = imageDict.pdf_dict_get(new PdfObj("BBox")); - if (type.pdf_name_eq(new PdfObj("Form")) != 0) - continue; - - PdfObj o = imageDict.pdf_dict_get(new PdfObj("BBox")); - PdfObj m = imageDict.pdf_dict_get(new PdfObj("Matrix")); - FzMatrix mat = null; - if (m != null) - mat = m.pdf_to_matrix(); - else - mat = new FzMatrix(); - - FzRect bbox; - if (o != null) - bbox = o.pdf_to_rect().fz_transform_rect(mat); - else - bbox = new FzRect(FzRect.Fixed.Fixed_INFINITE); - int xref = imageDict.pdf_to_num(); - - Entry entry = new Entry() - { - Xref = xref, - RefName = refName.pdf_to_name(), - StreamXref = streamXRef, - Bbox = new Rect(bbox) - }; - imageList.Add(entry); - } - return rc; - } - - /// - /// Store info of an image in list - /// - /// - /// - /// - /// - /// - internal static int GatherIamges( - PdfDocument doc, - PdfObj dict, - List imageList, - int streamXRef - ) - { - int rc = 1; - int n = dict.pdf_dict_len(); - for (int i = 0; i < n; i++) - { - PdfObj refName = dict.pdf_dict_get_key(i); - PdfObj imageDict = dict.pdf_dict_get_val(i); - if (imageDict.pdf_is_dict() == 0) - { - mupdf.mupdf.fz_warn( - $"'{refName.pdf_to_name()}' is no image dict ({imageDict.pdf_to_name()} 0 R)" - ); - continue; - } - - PdfObj type = imageDict.pdf_dict_get(new PdfObj("Subtype")); - if (type.pdf_name_eq(new PdfObj("Image")) == 0) - continue; - - int xref = imageDict.pdf_to_num(); - int gen = 0; - PdfObj smask = imageDict.pdf_dict_geta(new PdfObj("SMask"), new PdfObj("Mask")); - if (smask != null) - gen = smask.pdf_to_num(); - - PdfObj filter = imageDict.pdf_dict_geta(new PdfObj("Filter"), new PdfObj("F")); - if (filter.pdf_is_array() != 0) - filter = filter.pdf_array_get(0); - - PdfObj altcs = new PdfObj(0); - PdfObj cs = imageDict.pdf_dict_geta(new PdfObj("ColorSpace"), new PdfObj("CS")); - if (cs.pdf_is_array() != 0) - { - PdfObj cses = new PdfObj(cs); - cs = cses.pdf_array_get(0); - if ( - cs.pdf_name_eq(new PdfObj("DeviceN")) != 0 - || cs.pdf_name_eq(new PdfObj("Separation")) != 0 - ) - { - altcs = cses.pdf_array_get(2); - if (altcs.pdf_is_array() != 0) - altcs = altcs.pdf_array_get(0); - } - } - - PdfObj width = imageDict.pdf_dict_geta(new PdfObj("Width"), new PdfObj("W")); - PdfObj height = imageDict.pdf_dict_geta(new PdfObj("Height"), new PdfObj("H")); - PdfObj bpc = imageDict.pdf_dict_geta( - new PdfObj("BitsPerComponent"), - new PdfObj("BPC") - ); - - Entry entry = new Entry() - { - Xref = xref, - Smask = gen, - Width = width.pdf_to_int(), - Height = height.pdf_to_int(), - Bpc = bpc.pdf_to_int(), - CsName = Utils.EscapeStrFromStr(cs.pdf_to_name()), - AltCsName = Utils.EscapeStrFromStr(altcs.pdf_to_name()), - RefName = Utils.EscapeStrFromStr(refName.pdf_to_name()), - Filter = Utils.EscapeStrFromStr(filter.pdf_to_name()), - StreamXref = streamXRef - }; - imageList.Add(entry); - } - return rc; - } - - public static int InsertContents(Page page, byte[] newCont, int overlay = 1) - { - PdfPage pdfpage = page.GetPdfPage(); - - using (PdfDocument pageDoc = pdfpage.doc()) - { - if (overlay != 0) - { - if (!page.IsWrapped) - { - PdfObj qConts = pageDoc.pdf_add_stream(Utils.BufferFromBytes(Encoding.UTF8.GetBytes("q\n")), new PdfObj(), 0); - PdfObj QConts = pageDoc.pdf_add_stream(Utils.BufferFromBytes(Encoding.UTF8.GetBytes("\nQ")), new PdfObj(), 0); - PdfObj contents = pdfpage.obj().pdf_dict_get(new PdfObj("Contents")); - if (contents.pdf_is_array() != 0) - { - contents.pdf_array_insert(qConts, 0); - contents.pdf_array_push(QConts); - } - else - { - PdfObj carr = pageDoc.pdf_new_array(5); - if (contents.m_internal != null) - carr.pdf_array_push(contents); - carr.pdf_array_insert(qConts, 0); - carr.pdf_array_push(QConts); - pdfpage.obj().pdf_dict_put(new PdfObj("Contents"), carr); - } - page.WasWrapped = true; - } - } - FzBuffer contbuf = Utils.BufferFromBytes(newCont); - int xref = Utils.InsertContents(pageDoc, pdfpage.obj(), contbuf, overlay); - return xref; - } - } - - internal static int InsertContents( - PdfDocument pdf, - PdfObj pageRef, - FzBuffer newcont, - int overlay - ) - { - PdfObj contents = pageRef.pdf_dict_get(new PdfObj("Contents")); - PdfObj newconts = pdf.pdf_add_stream(newcont, new PdfObj(), 0); - int xref = newconts.pdf_to_num(); - if (contents.pdf_is_array() != 0) - { - if (overlay != 0) - { - contents.pdf_array_push(newconts); - } - else - { - contents.pdf_array_insert(newconts, 0); - } - } - else - { - PdfObj carr = pdf.pdf_new_array(5); - if (overlay != 0) - { - if (contents.m_internal != null) - carr.pdf_array_push(contents); - carr.pdf_array_push(newconts); - } - else - { - carr.pdf_array_push(newconts); - if (contents.m_internal != null) - carr.pdf_array_push(contents); - } - pageRef.pdf_dict_put(new PdfObj("Contents"), carr); - } - return xref; - } - - public static (string, string, string, float, float) GetFontProperties( - Document doc, - int xref - ) - { - FontInfo res = doc.ExtractFont(xref); - float asc = 0.8f; - float dsc = -0.2f; - - if (res.Ext == "") - return (res.Name, res.Ext, res.Type, asc, dsc); - - if (res.Content != null) - { - /*try - { - Font font = new Font(res.Content); - asc = font.Ascender; - dsc = font.Descender; - Rect bbox = font.Bbox; - if (asc - dsc < 1) - { - if (bbox.Y0 < dsc) - dsc = bbox.Y0; - asc = 1 - dsc; - } - } - catch (Exception e) - { - asc *= 1.2f; - dsc *= 1.2f; - } - return (res.Name, res.Ext, res.Type, asc, dsc);*/ - } - if (res.Ext != "n/a") - { - try - { - Font font = new Font(res.Name); - asc = font.Ascender; - dsc = font.Descender; - } - catch (Exception) - { - asc *= 1.2f; - dsc *= 1.2f; - } - } - else - { - asc *= 1.2f; - dsc *= 1.2f; - } - return (res.Name, res.Ext, res.Type, asc, dsc); - } - - internal static FzBuffer GetFontBuffer(PdfDocument doc, int xref) - { - if (xref < 1) - return null; - - PdfObj o = doc.pdf_load_object(xref); - PdfObj desft = o.pdf_dict_get(new PdfObj("DescendantFonts")); - PdfObj obj; - if (desft.m_internal != null) - { - obj = desft.pdf_array_get(0).pdf_resolve_indirect(); - obj = obj.pdf_dict_get(new PdfObj("FontDescriptor")); - } - else - { - obj = o.pdf_dict_get(new PdfObj("FontDescriptor")); - } - - if (obj.m_internal == null) - { - Console.WriteLine("invalid font - FontDescriptor missing"); - return null; - } - - o = obj; - PdfObj stream = null; - obj = o.pdf_dict_get(new PdfObj("FontFile")); - if (obj.m_internal_value() != 0) - stream = obj; // pfa - - obj = o.pdf_dict_get(new PdfObj("FontFile2")); - if (obj.m_internal != null) - stream = obj; // ttf - - obj = o.pdf_dict_get(new PdfObj("FontFile3")); - if (obj.m_internal != null) - { - stream = obj; - obj = obj.pdf_dict_get(new PdfObj("Subtype")); - if (obj != null && obj.pdf_is_name() == 0) - { - Console.WriteLine("invalid font descriptor subtype"); - return null; - } - - if (obj.pdf_name_eq(new PdfObj("Type1C")) != 0) { } // cff - else if (obj.pdf_name_eq(new PdfObj("CIDFontType0C")) != 0) { } //cid - else if (obj.pdf_name_eq(new PdfObj("OpenType")) != 0) { } // otf - else - Console.WriteLine("warning: unhandled font type {pdf_to_name(ctx, obj)!r}"); - } - - if (stream == null) - { - Console.WriteLine("warning: unhandled font type"); - return null; - } - - return stream.pdf_load_stream(); - } - - public static string UnicodeFromStr(dynamic s) - { - if (s == null) - return ""; - - if (s is byte[]) - return Encoding.UTF8.GetString(s); - - if (s is string) - return s; - - return null; - } - - public static void UpdateFontInfo(Document doc, FontInfo info) - { - int xref = info.Xref; - bool found = false; - - int i = 0; - for (; i < doc.FontInfos.Count; i++) - { - if (doc.FontInfos[i].Xref == xref) - { - found = true; - break; - } - } - if (found) - doc.FontInfos[i] = info; - else - doc.FontInfos.Add(info); - } - - internal static FontInfo InsertFont( - PdfDocument pdf, - string bfName, - string fontFile, - byte[] fontBuffer, - bool setSimple, - int idx, - int wmode, - int serif, - int encoding, - int ordering - ) - { - FzFont font = new FzFont(); - FzBuffer res = null; - SWIGTYPE_p_unsigned_char data = null; - int ixref = 0; - int simple = 0; - FontInfo value = null; - string name = null; - string subt = null; - string exto = null; - ll_fz_lookup_cjk_font_outparams cjk_params = new ll_fz_lookup_cjk_font_outparams(); - PdfObj fontObj = null; - - if (ordering > -1) - data = mupdf.mupdf.ll_fz_lookup_cjk_font_outparams_fn(ordering, cjk_params); - if (data != null) - { - font = mupdf.mupdf.fz_new_font_from_memory( - null, - data, - cjk_params.len, - cjk_params.index, - 0 - ); - fontObj = pdf.pdf_add_simple_font(font, encoding); - exto = "n/a"; - simple = 1; - } - else - { - ll_fz_lookup_base14_font_outparams outparams = - new ll_fz_lookup_base14_font_outparams(); - if (!string.IsNullOrEmpty(bfName)) - { - data = mupdf.mupdf.ll_fz_lookup_base14_font_outparams_fn(bfName, outparams); - } - if (data != null) - { - font = mupdf.mupdf.fz_new_font_from_memory(bfName, data, outparams.len, 0, 0); - fontObj = pdf.pdf_add_simple_font(font, encoding); - exto = "n/a"; - simple = 1; - } - else - { - if (!string.IsNullOrEmpty(fontFile)) - { - font = mupdf.mupdf.fz_new_font_from_file(null, fontFile, idx, 0); - } - else - { - res = Utils.BufferFromBytes(fontBuffer); - if (res.m_internal == null) - throw new Exception(Utils.ErrorMessages["MSG_FILE_OR_BUFFER"]); - font = mupdf.mupdf.fz_new_font_from_buffer(null, res, idx, 0); - } - - if (!setSimple) - { - fontObj = mupdf.mupdf.pdf_add_cid_font(pdf, font); - simple = 0; - } - else - { - fontObj = pdf.pdf_add_simple_font(font, encoding); - simple = 2; - } - } - } - ixref = fontObj.pdf_to_num(); - name = Utils.EscapeStrFromStr( - fontObj.pdf_dict_get(new PdfObj("BaseFont")).pdf_to_name() - ); - subt = Utils.UnicodeFromStr(fontObj.pdf_dict_get(new PdfObj("Subtype")).pdf_to_name()); - - if (string.IsNullOrEmpty(exto)) - exto = Utils.GetFontExtension(pdf, ixref); - - float asc = font.fz_font_ascender(); - float dsc = font.fz_font_descender(); - - value = new FontInfo() - { - Xref = ixref, - Name = name, - Type = subt, - Ext = exto, - Simple = simple != 0, - Ordering = ordering, - Ascender = asc, - Descender = dsc - }; - - return value; - } - - public static string GetTJstr( - string text, - List<(int, double)> glyphs, - bool simple, - int ordering - ) - { - if (text.StartsWith("[<") && text.EndsWith(">]")) - return text; - if (string.IsNullOrEmpty(text)) - return "[<>]"; - - string otxt = ""; - if (simple) - { - if (glyphs == null) - { - foreach (char c in text) - { - if (Convert.ToInt32(c) < 256) - otxt += Convert.ToInt32(c).ToString("x2"); - else - otxt += Convert.ToInt32("b7", 16).ToString("x2"); - } - } - else - { - foreach (char c in text.ToCharArray()) - { - if (Convert.ToInt32(c) < 256) - otxt += (glyphs[Convert.ToInt32(c)].Item1).ToString("x2"); - else - otxt += (glyphs[Convert.ToInt32("b7", 16)].Item1).ToString("x2"); - } - } - return $"[<{otxt}>]"; - } - if (ordering < 0) - { - foreach (char c in text.ToCharArray()) - otxt += (glyphs[Convert.ToInt32(c)].Item1).ToString("x4"); - } - else - { - foreach (char c in text.ToCharArray()) - otxt += Convert.ToInt32(c).ToString("x4"); - } - return $"[<{otxt}>]"; - } - - public static void CheckColor(float[] c) - { - if (c != null) - { - if (c.Length != 1 && c.Length != 3 && c.Length != 4 || c.Min() < 0 || c.Max() > 1) - { - throw new Exception("need 1, 3 or 4 color components in range 0 to 1"); - } - } - } - - public static string GetColorCode(float[] c, string f) - { - if (c == null || c.Length == 0) - return ""; - - Utils.CheckColor(c); - string s = ""; - if (c.Length == 1) - { - s = $"{FloatToString(c[0])} "; - return s + (f == "c" ? "G " : "g "); - } - - if (c.Length == 3) - { - s = $"{FloatToString(c[0])} {FloatToString(c[1])} {FloatToString(c[2])} "; - return s + (f == "c" ? "RG " : "rg "); - } - - s = $"{FloatToString(c[0])} {FloatToString(c[1])} {FloatToString(c[2])} {FloatToString(c[3])} "; - return s + (f == "c" ? "K " : "k "); - } - - internal static string GetColorCode(float c, string f) - { - float[] color = { c, }; - Utils.CheckColor(color); - string s = ""; - s = $"{FloatToString(color[0])} "; - return s + (f == "c" ? "G " : "g "); - } - - internal static string EscapeStrFromBuffer(FzBuffer buf) - { - if (buf.m_internal == null) - return ""; - FzBuffer s = buf.fz_clone_buffer(); - return DecodeRawUnicodeEscape(s); - } - - /// - /// Decode Raw Unicode - /// - /// - /// - public static string DecodeRawUnicodeEscape(string s) - { - try - { - return System.Text.RegularExpressions.Regex.Unescape(s); - } - catch (Exception) - { - return s; - } - } - - /// - /// Decode Raw Unicode - /// - /// - /// - internal static string DecodeRawUnicodeEscape(FzBuffer s) - { - string ret = s.fz_string_from_buffer(); - return DecodeRawUnicodeEscape(ret); - } - - internal static FzBuffer Object2Buffer(PdfObj what, int compress, int ascii) - { - FzBuffer res = mupdf.mupdf.fz_new_buffer(512); - FzOutput output = new FzOutput(res); - output.pdf_print_obj(what, compress, ascii); - output.fz_close_output(); - output.Dispose(); - res.fz_terminate_buffer(); - - return res; - } - - internal static string UnicodeFromBuffer(FzBuffer buf) - { - //byte[] bufBytes = buf.fz_buffer_extract(); - byte[] bufBytes = Utils.BinFromBuffer(buf); - string val = Encoding.UTF8.GetString(bufBytes); - int z = val.IndexOf((char)0); - - if (z >= 0) - val = val.Substring(0, z); - return val; - } - - internal static void Recurse( - Document doc, - Outline olItem, - List liste, - int lvl, - bool simple - ) - { - int page = 0; - while (olItem != null && !olItem.IsExternal) - { - string title = ""; - if (olItem.Title != null) - title = olItem.Title; - else - title = " "; - - if (!olItem.IsExternal) - { - if (olItem.Uri != null) - { - if (olItem.Page == -1) - { - var resolve = doc.ResolveLink(olItem.Uri); - page = resolve.Item1[0] + 1; - } - else - page = olItem.Page + 1; - } - else - page = -1; - } - else - page = -1; - - if (!simple) { } - } - } - - internal static LinkInfo GetLinkDict(Link ln, Document doc = null) - { - return Utils._GetLinkDict(ln.Dest, ln.Rect, doc); - } - - internal static LinkInfo GetLinkDict(Outline ol, Document doc = null) - { - return Utils._GetLinkDict(ol.Dest, null, doc); - } - - internal static LinkInfo _GetLinkDict(LinkDest dest, Rect r, Document document) - { - LinkInfo nl = new LinkInfo(); - nl.Kind = dest.Kind; - nl.Xref = 0; - nl.From = (r == null) ? null : new Rect(r); - Point pnt = new Point(0, 0); - - if ((dest.Flags & (int)LinkFlags.LINK_FLAG_L_VALID) != 0) - pnt.X = dest.TopLeft.X; - if ((dest.Flags & (int)LinkFlags.LINK_FLAG_T_VALID) != 0) - pnt.Y = dest.TopLeft.Y; - - if (dest.Kind == LinkType.LINK_URI) - { - nl.Uri = dest.Uri; - } - else if (dest.Kind == LinkType.LINK_GOTO) - { - nl.Page = dest.Page; - nl.To = new Point(pnt); - if ((dest.Flags & (int)LinkFlags.LINK_FLAG_R_IS_ZOOM) != 0) - nl.Zoom = dest.BottomRight.X; - else - nl.Zoom = 0.0f; - } - else if (dest.Kind == LinkType.LINK_GOTOR) - { - nl.File = dest.FileSpec.Replace("\\", "/"); - nl.Page = dest.Page; - if (dest.Page < 0) - nl.ToStr = dest.Dest; - else - { - nl.To = pnt; - if ((dest.Flags & (int)LinkFlags.LINK_FLAG_R_IS_ZOOM) != 0) - nl.Zoom = dest.BottomRight.X; - else - nl.Zoom = 0.0f; - } - } - else if (dest.Kind == LinkType.LINK_LAUNCH) - { - nl.File = (string.IsNullOrEmpty(dest.FileSpec) ? dest.Uri : dest.FileSpec).Replace( - "\\", - "/" - ); - } - else if (dest.Kind == LinkType.LINK_NAMED) - { - bool andKeys = dest - .Named.Keys.Intersect(nl.GetType().GetFields().Select(e => e.Name)) - .Any(); - if (!andKeys) - throw new Exception("not same keys"); - } - else - nl.Page = dest.Page; - return nl; - } - - internal static Border GetAnnotBorder(PdfObj annotObj) - { - List dash = new List(); - float width = -1; - float clouds = -1; - PdfObj obj = null; - string style = null; - - obj = annotObj.pdf_dict_get(new PdfObj("Border")); - if (obj.pdf_is_array() != 0) - { - width = obj.pdf_array_get(2).pdf_to_real(); - if (obj.pdf_array_len() == 4) - { - PdfObj dashObj = obj.pdf_array_get(3); - for (int i = 0; i < dashObj.pdf_array_len(); i++) - { - int val = dashObj.pdf_array_get(i).pdf_to_int(); - dash.Add(val); - } - } - } - - PdfObj bsObj = annotObj.pdf_dict_get(new PdfObj("BS")); - if (bsObj != null) - { - width = bsObj.pdf_dict_get(new PdfObj("W")).pdf_to_real(); - style = bsObj.pdf_dict_get(new PdfObj("S")).pdf_to_name(); - if (style == "") - style = null; - obj = bsObj.pdf_dict_get(new PdfObj("D")); - if (obj != null) - { - for (int i = 0; i < obj.pdf_array_len(); i++) - { - int val = obj.pdf_array_get(i).pdf_to_int(); - dash.Add(val); - } - } - } - - obj = annotObj.pdf_dict_get(new PdfObj("BE")); - if (obj != null) - clouds = obj.pdf_dict_get(new PdfObj("I")).pdf_to_int(); - - Border res = new Border(); - res.Width = width; - res.Dashes = dash.ToArray(); - res.Style = style; - res.Clouds = clouds; - - return res; - } - - internal static Color GetAnnotColors(PdfObj annotObj) - { - Color res = new Color(); - List bc = new List(); - List fc = new List(); - - PdfObj obj = annotObj.pdf_dict_get(new PdfObj("C")); - if (obj.pdf_is_array() != 0) - { - int n = obj.pdf_array_len(); - for (int i = 0; i < n; i++) - { - float col = obj.pdf_array_get(i).pdf_to_real(); - bc.Add(col); - } - } - res.Stroke = bc.ToArray(); - - obj = annotObj.pdf_dict_gets("IC"); - if (obj.pdf_is_array() != 0) - { - int n = obj.pdf_array_len(); - for (int i = 0; i < n; i++) - { - float col = obj.pdf_array_get(i).pdf_to_real(); - fc.Add(col); - } - } - res.Fill = fc.ToArray(); - - return res; - } - - internal static void SetAnnotBorder(Border border, PdfDocument pdf, PdfObj linkObj) - { - /* - PdfObj obj = null; - int dashLen = 0; - float nWidth = border.Width; - int[] nDashes = border.Dashes; - string nStyle = border.Style; - float nClouds = border.Clouds; - */ - // get old border properties - } - - internal static List GetAnnotIdList(PdfPage page) - { - List names = new List(); - PdfObj annots = page.obj().pdf_dict_get(new PdfObj("Annots")); - if (annots == null) - return null; - - int n = annots.pdf_array_len(); - for (int i = 0; i < n; i++) - { - PdfObj annotObj = annots.pdf_array_get(i); - PdfObj name = annotObj.pdf_dict_gets("NM"); - if (name != null) - names.Add(name.pdf_to_text_string()); - } - - return names; - } - - internal static List GetAnnotXrefList(PdfObj pageObj) - { - List names = new List(); - PdfObj annots = pageObj.pdf_dict_get(new PdfObj("Annots")); - if (annots == null) - return null; - - int n = annots.pdf_array_len(); - for (int i = 0; i < n; i++) - { - PdfObj annotObj = annots.pdf_array_get(i); - int xref = annotObj.pdf_to_num(); - PdfObj subtype = annotObj.pdf_dict_get(new PdfObj("Subtype")); - if (subtype.m_internal == null) - continue; - - pdf_annot_type type = mupdf.mupdf.pdf_annot_type_from_string(subtype.pdf_to_name()); - if (type == pdf_annot_type.PDF_ANNOT_UNKNOWN) - continue; - PdfObj id_ = annotObj.pdf_dict_gets("NM"); - names.Add( - new AnnotXref() - { - Xref = xref, - AnnotType = (PdfAnnotType)type, - Id = id_.pdf_to_text_string() - } - ); - } - return names; - } - - /// - /// Convert sRGB color code to an RGB color triple. - /// - /// srgb: (int) RRGGBB (red, green, blue), each color in range(255). - /// Tuple (red, green, blue) each item in interval 0 <= item <= 255. - public static (int, int, int) sRGB2Rgb(int srgb) - { - int r = srgb >> 16; - int g = (srgb - (r << 16) >> 8); - int b = srgb - (r << 16) - (g << 8); - return (r, g, b); - } - - /// - /// Convert sRGB color code to a PDF color triple. - /// - /// (int) RRGGBB (red, green, blue), each color in range(255). - /// Tuple (red, green, blue) each item in interval 0 <= item <= 1. - public static (float, float, float) sRGB2Pdf(int srgb) - { - (int, int, int) t = sRGB2Rgb(srgb); - return (t.Item1 / 255.0f, t.Item2 / 255.0f, t.Item3 / 255.0f); - } - - public static string GetLinkText(Page page, LinkInfo link) - { - Matrix ctm = page.TransformationMatrix; - Matrix ictm = ~ctm; - if (link.From == null) - throw new Exception("should contain 'From' in Link"); - - Rect r = link.From * ictm; - string rectStr = $"{Utils.FloatToString(r.X0)} {Utils.FloatToString(r.Y0)} {Utils.FloatToString(r.X1)} {Utils.FloatToString(r.Y1)}"; - string txt; - - string annot = ""; - if (link.Kind == LinkType.LINK_GOTO) - if (link.Page >= 0) - { - txt = Utils.AnnotSkel["goto1"]; - int pno = link.Page; - int xref = page.Parent.GetPageXref(pno); - Point pnt = link.To == null ? new Point(0, 0) : link.To; - Page destPage = page.Parent[pno]; - Matrix destCtm = destPage.TransformationMatrix; - Matrix destIctm = ~destCtm; - Point ipnt = pnt * destIctm; - annot = string.Format(System.Globalization.CultureInfo.InvariantCulture, txt, xref, Utils.FloatToString(ipnt.X), Utils.FloatToString(ipnt.Y), Utils.FloatToString(link.Zoom), rectStr); - } - else - { - txt = Utils.AnnotSkel["goto2"]; - annot = string.Format(System.Globalization.CultureInfo.InvariantCulture, txt, Utils.GetPdfString(link.ToStr), rectStr); - } - else if (link.Kind == LinkType.LINK_GOTOR) - { - if (link.Page >= 0) - { - txt = Utils.AnnotSkel["gotor1"]; - Point pnt = link.To; - annot = string.Format( - System.Globalization.CultureInfo.InvariantCulture, - txt, - link.Page, - Utils.FloatToString(pnt.X), - Utils.FloatToString(pnt.Y), - Utils.FloatToString(link.Zoom), - link.File, - link.File, - rectStr - ); - } - else - { - txt = Utils.AnnotSkel["gotor2"]; - annot = string.Format(System.Globalization.CultureInfo.InvariantCulture, txt, Utils.GetPdfString(link.ToStr), link.File, rectStr); - } - } - else if (link.Kind == LinkType.LINK_LAUNCH) - { - txt = Utils.AnnotSkel["launch"]; - annot = string.Format(System.Globalization.CultureInfo.InvariantCulture, txt, link.File, link.File, rectStr); - } - else if (link.Kind == LinkType.LINK_URI) - { - txt = Utils.AnnotSkel["uri"]; - annot = string.Format(System.Globalization.CultureInfo.InvariantCulture, txt, link.Uri, rectStr); - } - else if (link.Kind == LinkType.LINK_NAMED) - { - txt = Utils.AnnotSkel["named"]; - annot = string.Format(System.Globalization.CultureInfo.InvariantCulture, txt, link.Name, rectStr); - } - if (annot == null) - return annot; - - Dictionary linkNames = new Dictionary(); - foreach (AnnotXref x in page.GetAnnotXrefs()) - { - if (x.AnnotType == PdfAnnotType.PDF_ANNOT_LINK) - linkNames.Add(x.Xref, x.Id); - } - - string oldName = link.Id; - string name; - if ( - oldName != null - && linkNames.Contains(new KeyValuePair(link.Xref, oldName)) - ) - name = oldName; - else - { - int i = 0; - string stem = Utils.SetAnnotStem() + "-L{0}"; - while (true) - { - name = string.Format(stem, i); - if (!linkNames.Values.Contains(name)) - break; - i += 1; - } - } - annot = annot.Replace("/Link", $"/Link/NM({name})"); - return annot; - } - - public static string SetAnnotStem(string stem = null) - { - if (stem == null) - return Utils.ANNOT_ID_STEM; - int len = stem.Length; - if (len > 50) - len = 50; - Utils.ANNOT_ID_STEM = stem.Substring(0, len); - return Utils.ANNOT_ID_STEM; - } - - public static PdfAnnot GetAnnotByName(Page page, string name) - { - if (string.IsNullOrEmpty(name)) - return null; - - PdfAnnot annot = mupdf.mupdf.pdf_first_annot(page.GetPdfPage()); - bool found = false; - while (true) - { - if (annot.m_internal == null) - break; - (string res, ulong len) = annot.pdf_annot_obj().pdf_dict_gets("NM").pdf_to_string(); - if (name == res) - { - found = true; - break; - } - annot = annot.pdf_next_annot(); - } - if (!found) - throw new Exception($"'{name}' is not an annot of this page"); - return annot; - } - - internal static PdfAnnot GetAnnotByXref(Page page, int xref) - { - bool found = false; - PdfAnnot annot = page.GetPdfPage().pdf_first_annot(); - while (true) - { - if (annot == null) - break; - if (xref == annot.pdf_annot_obj().pdf_to_num()) - { - found = true; - break; - } - annot = annot.pdf_next_annot(); - } - if (!found) - throw new Exception($"xref {xref} is not an annot of this page"); - return annot; - } - - public static void StoreShrink(int percent) - { - if (percent >= 100) - { - mupdf.mupdf.fz_empty_store(); - return; - } - if (percent > 0) - mupdf.mupdf.fz_shrink_store((uint)(100 - percent)); - } - - public static void GlyphCacheEmpty() - { - mupdf.mupdf.fz_purge_glyph_cache(); - } - - internal static void RefreshLinks(PdfPage page) - { - if (page == null) - return; - PdfObj obj = page.obj().pdf_dict_get(new PdfObj("Annots")); - if (obj.m_internal != null) - { - PdfDocument pageDoc = page.doc(); - int number = pageDoc.pdf_lookup_page_number(page.obj()); - FzRect pageMediabox = new FzRect(); - FzMatrix pageCtm = new FzMatrix(); - page.pdf_page_transform(pageMediabox, pageCtm); - FzLink link = pageDoc.pdf_load_link_annots(page, obj, number, pageCtm); - page.m_internal.links = mupdf.mupdf.ll_fz_keep_link(link.m_internal); - pageDoc.Dispose(); - } - } - - internal static void PageMerge( - Document docDes, - Document docSrc, - int pageFrom, - int pageTo, - int rotate, - bool links, - bool copyAnnots, - GraftMap graftmap - ) - { - PdfDocument pdfDes = Document.AsPdfDocument(docDes); - PdfDocument pdfSrc = Document.AsPdfDocument(docSrc); - List knownPageObjs = new List() - { - new PdfObj("Contents"), - new PdfObj("Resources"), - new PdfObj("MediaBox"), - new PdfObj("CropBox"), - new PdfObj("BleedBox"), - new PdfObj("TrimBox"), - new PdfObj("ArtBox"), - new PdfObj("Rotate"), - new PdfObj("UserUnit") - }; - - PdfObj pageRef = pdfSrc.pdf_lookup_page_obj(pageFrom); - PdfObj pageDict = pdfDes.pdf_new_dict(4); - pageDict.pdf_dict_put(new PdfObj("Type"), new PdfObj("Page")); - - foreach (PdfObj e in knownPageObjs) - { - PdfObj obj = pageRef.pdf_dict_get_inheritable(e); - if (obj.m_internal != null) - { - pageDict.pdf_dict_put( - e, - mupdf.mupdf.pdf_graft_mapped_object(graftmap.ToPdfGraftMap(), obj) - ); - } - } - - if (copyAnnots) - { - PdfObj oldAnnots = pageRef.pdf_dict_get(new PdfObj("Annots")); - int n = oldAnnots.pdf_array_len(); - if (n > 0) - { - PdfObj newAnnots = pageDict.pdf_dict_put_array(new PdfObj("Annots"), n); - for (int i = 0; i < n; i++) - { - PdfObj o = oldAnnots.pdf_array_get(i); - if (o.m_internal == null || o.pdf_is_dict() == 0) - continue; - if (o.pdf_dict_gets("IRT").m_internal != null) - continue; - PdfObj subtype = o.pdf_dict_get(new PdfObj("Subtype")); - if (subtype.pdf_name_eq(new PdfObj("Link")) != 0) - continue; - if (subtype.pdf_name_eq(new PdfObj("Popup")) != 0) - continue; - if (subtype.pdf_name_eq(new PdfObj("Widget")) != 0) - { - mupdf.mupdf.fz_warn("skipping widget annotation"); - continue; - } - - o.pdf_dict_del(new PdfObj("Popup")); - o.pdf_dict_del(new PdfObj("P")); - PdfObj copyO = graftmap.ToPdfGraftMap().pdf_graft_mapped_object(o); - PdfObj annot = pdfDes.pdf_new_indirect(copyO.pdf_to_num(), 0); - newAnnots.pdf_array_push(annot); - } - } - } - if (rotate != -1) - pageDict.pdf_dict_put_int(new PdfObj("Rotate"), rotate); - PdfObj ref_ = pdfDes.pdf_add_object(pageDict); - pdfDes.pdf_insert_page(pageTo, ref_); - - pdfSrc.Dispose(); - pdfDes.Dispose(); - } - - public static void MergeRange( - Document docDes, - Document docSrc, - int spage, - int epage, - int apage, - int rotate, - bool links, - bool annots, - int showProgress, - GraftMap graftmap - ) - { - int afterPage = apage; - int counter = 0; - int total = mupdf.mupdf.fz_absi(epage - spage) + 1; - - if (spage < epage) - { - int page = spage; - while (page <= epage) - { - Utils.PageMerge( - docDes, - docSrc, - page, - afterPage, - rotate, - links, - annots, - graftmap - ); - counter += 1; - if (showProgress > 0 && counter % showProgress == 0) - Console.WriteLine( - string.Format("Inserted {0} of {1} pages", counter, total) - ); - page += 1; - afterPage += 1; - } - } - else - { - int page = spage; - while (page >= epage) - { - Utils.PageMerge( - docDes, - docSrc, - page, - afterPage, - rotate, - links, - annots, - graftmap - ); - counter += 1; - if (showProgress > 0 && counter % showProgress == 0) - Console.WriteLine( - string.Format("Inserted {0} of {1} pages", counter, total) - ); - page -= 1; - afterPage += 1; - } - } - } - - /// - /// Insert links contained in copied page range into destination PDF. - /// - /// - /// - /// - /// - /// - /// - public static void DoLinks( - Document doc1, - Document doc2, - int fromPage = -1, - int toPage = -1, - int startAt = -1 - ) - { - string CreateAnnot(LinkInfo link, List xrefDest, List _pnoSrc, Matrix ctm) - { - Rect r = link.From * ctm; - string rStr = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} {2} {3}", Utils.FloatToString(r[0]), Utils.FloatToString(r[1]), Utils.FloatToString(r[2]), Utils.FloatToString(r[3])); - string annot = ""; - if (link.Kind == LinkType.LINK_GOTO) - { - string txt = Utils.AnnotSkel["goto1"]; - int idx = _pnoSrc.IndexOf(link.Page); - Point p = link.To * ctm; - annot = string.Format(System.Globalization.CultureInfo.InvariantCulture, txt, xrefDest[idx], Utils.FloatToString(p.X), Utils.FloatToString(p.Y), Utils.FloatToString(link.Zoom), rStr); - } - else if (link.Kind == LinkType.LINK_GOTOR) - { - if (link.Page >= 0) - { - string txt = Utils.AnnotSkel["gotor1"]; - Point pnt = link.To == null ? new Point(0, 0) : link.To; - annot = string.Format( - System.Globalization.CultureInfo.InvariantCulture, - txt, - link.Page, - Utils.FloatToString(pnt.X), - Utils.FloatToString(pnt.Y), - Utils.FloatToString(link.Zoom), - link.File, - link.File, - rStr - ); - } - else - { - string txt = Utils.AnnotSkel["gotor2"]; - string to = Utils.GetPdfString(link.ToStr); - to = to.Substring(1, -1); - string f = link.File; - annot = string.Format(System.Globalization.CultureInfo.InvariantCulture, txt, to, f, rStr); - } - } - else if (link.Kind == LinkType.LINK_LAUNCH) - { - string txt = Utils.AnnotSkel["launch"]; - annot = string.Format(System.Globalization.CultureInfo.InvariantCulture, txt, link.File, link.File, rStr); - } - else if (link.Kind == LinkType.LINK_URI) - { - string txt = Utils.AnnotSkel["uri"]; - annot = string.Format(System.Globalization.CultureInfo.InvariantCulture, txt, link.Uri, rStr); - } - else - annot = ""; - - return annot; - } - // --------------------validate & normalize parameters------------------------- - int fp, - tp; - if (fromPage < 0) - fp = 0; - else if (fromPage >= doc2.PageCount) - fp = doc2.PageCount - 1; - else - fp = fromPage; - - if (toPage < 0 || toPage >= doc2.PageCount) - tp = doc2.PageCount - 1; - else - tp = toPage; - - if (startAt < 0) - throw new Exception("'start_at' must be >= 0"); - int sa = startAt; - int incr = fp <= tp ? 1 : -1; - - // lists of source / destination page numbers - List pnoSrc = new List(); - List pnoDst = new List(); - for (int i = fp; i < tp + incr; i += incr) - pnoSrc.Add(i); - for (int i = 0; i < pnoSrc.Count; i++) - pnoDst.Add(sa + i); - - List xrefSrc = new List(); - List xrefDst = new List(); - for (int i = 0; i < pnoSrc.Count; i++) - { - int pSrc = pnoSrc[i]; - int pDst = pnoDst[i]; - int oldXref = doc2.GetPageXref(pSrc); - int newXref = doc1.GetPageXref(pDst); - xrefSrc.Add(oldXref); - xrefDst.Add(newXref); - } - - // create the links for each copied page in destination PDF - for (int i = 0; i < xrefSrc.Count; i++) - { - Page pageSrc = doc2[pnoSrc[i]]; - List links = pageSrc.GetLinks(); - if (links.Count == 0) - { - pageSrc = null; - continue; - } - - Matrix ctm = ~pageSrc.TransformationMatrix; - Page pageDst = doc1[pnoDst[i]]; - List linkTab = new List(); - foreach (LinkInfo l in links) - { - if (l.Kind == LinkType.LINK_GOTO && !pnoSrc.Contains(l.Page)) - continue; - string annotText = CreateAnnot(l, xrefDst, pnoSrc, ctm); - if (annotText != null || annotText != "") - linkTab.Add(annotText); - } - if (linkTab.Count != 0) - pageDst.AddAnnotFromString(linkTab); - } - } - - public static int FZ_LANG_TAG2(char c1, char c2) - { - return ((c1 - 'a' + 1) + ((c2 - 'a' + 1) * 27)); - } - - public static int FZ_LANG_TAG3(char c1, char c2, char c3) - { - return ((c1 - 'a' + 1) + ((c2 - 'a' + 1) * 27) + ((c3 - 'a' + 1) * 27 * 27)); - } - - internal static Pixmap GetPixmapFromDisplaylist( - FzDisplayList list, - Matrix ctm, - FzColorspace cs, - int alpha, - Rect clip, - FzSeparations seps = null - ) - { - var disposables = new List(); - - try - { - lock (Utils.MuPDFLock) - { - if (seps == null) - { - seps = new FzSeparations(); - disposables.Add(seps); - } - - FzRect rect = mupdf.mupdf.fz_bound_display_list(list); - //disposables.Add(rect); - - FzMatrix matrix = new FzMatrix(ctm.A, ctm.B, ctm.C, ctm.D, ctm.E, ctm.F); - //disposables.Add(matrix); - - FzRect rclip = clip == null ? new FzRect(FzRect.Fixed.Fixed_INFINITE) : clip.ToFzRect(); - //disposables.Add(rclip); - rect = FzRect.fz_intersect_rect(rect, rclip); - - rect = rect.fz_transform_rect(matrix); - FzIrect irect = rect.fz_round_rect(); - //disposables.Add(irect); - - FzPixmap pix = mupdf.mupdf.fz_new_pixmap_with_bbox(cs, irect, seps, alpha); - if (alpha != 0) - pix.fz_clear_pixmap(); - else - pix.fz_clear_pixmap_with_value(0xFF); - - FzDevice dev; - if (rclip.fz_is_infinite_rect() == 0) - { - dev = mupdf.mupdf.fz_new_draw_device_with_bbox(matrix, pix, irect); - list.fz_run_display_list(dev, new FzMatrix(), rclip, new FzCookie()); - } - else - { - dev = mupdf.mupdf.fz_new_draw_device(matrix, pix); - list.fz_run_display_list( - dev, - new FzMatrix(), - new FzRect(FzRect.Fixed.Fixed_INFINITE), - new FzCookie() - ); - } - disposables.Add(dev); - - dev.fz_close_device(); - - return new Pixmap(pix); - } - } - finally - { - foreach (var disposable in disposables) - { - disposable.Dispose(); - } - } - } - - internal static FzFont GetFont( - string fontName, - string fontFile, - byte[] fontBuffer, - int script, - int lang, - int ordering, - int isBold, - int isItalic, - int isSerif, - int embed - ) - { - FzFont Fertig(FzFont _font) - { - if (_font.m_internal == null) - throw new Exception(Utils.ErrorMessages["MSG_FONT_FAILED"]); - if (_font.m_internal.flags.never_embed == 0) - _font.fz_set_font_embedding(embed); - return _font; - } - - int index = 0; - FzFont font = null; - if (fontFile != null) - { - font = mupdf.mupdf.fz_new_font_from_file(null, fontFile, index, 0); - return Fertig(font); - } - - if (ordering > -1) - { - font = mupdf.mupdf.fz_new_cjk_font(ordering); - return Fertig(font); - } - - if (fontName != null) - { - font = mupdf.mupdf.fz_new_base14_font(fontName); - if (font.m_internal != null) - return Fertig(font); - font = mupdf.mupdf.fz_new_builtin_font(fontName, isBold, isItalic); - return Fertig(font); - } - - ll_fz_lookup_noto_font_outparams outparams = new ll_fz_lookup_noto_font_outparams(); - SWIGTYPE_p_unsigned_char data = mupdf.mupdf.ll_fz_lookup_noto_font_outparams_fn( - script, - lang, - outparams - ); - font = null; - if (data != null) - font = mupdf.mupdf.fz_new_font_from_memory( - null, - data, - outparams.len, - outparams.subfont, - 0 - ); - if (font.m_internal != null) - return Fertig(font); - font = mupdf.mupdf.fz_load_fallback_font(script, lang, isSerif, isBold, isItalic); - return Fertig(font); - } - - /// - /// Adobe Glyph List function - /// - /// - /// - public static int GlyphName2Unicode(string name) - { - if (Utils.AdobeUnicodes.Count == 0) - { - string[] lines = Utils.GetGlyphText(); - foreach (string line in lines) - { - if (line.StartsWith("#")) - continue; - string[] items = line.Split(';'); - if (items.Length != 2) - continue; - int c = Convert.ToInt32(items[1].Substring(0, 4), 16); - AdobeUnicodes[items[0]] = c; - } - } - //return AdobeUnicodes.GetValueOrDefault(name, 65533); - int value; - if (!AdobeUnicodes.TryGetValue(name, out value)) - { - value = 65533; // Default value if key doesn't exist - } - return value; - } - - public static string[] GetGlyphText() - { - string base64String = - "H4sIABmRaF8C/7W9SZfjRpI1useviPP15utzqroJgBjYWhEkKGWVl" + - "KnOoapVO0YQEYSCJEIcMhT569+9Ppibg8xevHdeSpmEXfPBfDZ3N3" + - "f/t7u//r//k/zb3WJ4eTv2T9vzXTaZZH/NJunsbr4Z7ru7/7s9n1" + - "/+6z//8/X19T/WRP7jYdj/57//R/Jv8Pax2/Sn87G/v5z74XC3Pm" + - "zuLqfurj/cnYbL8aEzyH1/WB/f7h6H4/70l7vX/ry9G47wzK/hcr" + - "7bD5v+sX9YM4i/3K2P3d1Ld9z353O3uXs5Dl/7DT7O2/UZ/3Tw9z" + - "jsdsNrf3i6exgOm57eTsbbvjv/1w2xTnfDo5fnYdjA3eV0vjt25z" + - "XkRJB36/vhKwN+kEw4DOf+ofsLuP3pboewGISO7bAxPkUU+EaUD7" + - "t1v++O/3FTCESmcsILgQRuLhDs/w857lz6NsPDZd8dzmtfSP85HO" + - "8GcI53+/W5O/br3QkeJa9NERmPKgE2Ue+73vgj97Ded5TH1pPDEF" + - "CT4/35RFFtAMORMezXb3dwiioCsYe77rABjjCOjHs/nLs7mx3wuY" + - "FYX+HsEQyTfHg/DY/nVxa0rzmnl+6BVQfeegTyemSlOdjqczqJ0J" + - "9/evfp7tOH1ed/zj+2d/j+9eOHf7xbtsu75jcw27vFh19/+/jux5" + - "8+3/304edl+/HT3fz9kq3iw/vPH981Xz5/APR/5p/g9/+Qhb+/3b" + - "X/8+vH9tOnuw8f79798uvP7xAcwv84f//5XfvpL/D97v3i5y/Ld+" + - "9//Msdgrh7/+Hz3c/vfnn3GQ4/f/iLifja492HFbz+0n5c/ARg3r" + - "z7+d3n30ycq3ef3zO+FSKc3/06//j53eLLz/OPd79++fjrh0/tHR" + - "IHr8t3nxY/z9/90i7/AxIg1rv2H+37z3effpr//PPN1CIF47Q2LU" + - "SdNz+3NjakdvnuY7v4/BcEGb4WyEPI+DMT++nXdvEOn8iWFomaf/z" + - "tL8wZhPqp/e8vcAbm3XL+y/xHpPH/xlnDejXKHJTQ4svH9hdK/mF1" + - "9+lL8+nzu89fPrd3P374sDSZ/qn9+I93i/bTD/D+8wcWxOruy6f2L" + - "4jl89xEjkCQaZ9+4Hfz5dM7k33v3n9uP3788uvndx/e/zu8/vThn8" + - "ggSDqH56XJ6Q/vTZKRVx8+/sZgmRemIP5y98+fWuAo8vc+z+bMjE/" + - "Iu8Vn7RBxIis/q7TevW9//Pndj+37RWuz/AND+ue7T+2/o+zefaKT" + - "dzbqf84R7xeTdJYYJLOf7z4xq11N/osp2bt3q7v58h/vKLxzjtrw6" + - "Z2rOSbzFj+5rEd7+P84ULxH8/6vO/lj2/6Pu7eX7d3P6C3Y2tb3u+" + - "7ua3dkA/yvu+w/JqyV6GeUt0/dy7nb36MjySZ/MUMO3Hz5+LNycsd" + - "x54SB5wmN/XJvRh0z/vz1/PaCf4Zhd/rP9dPur/j7eDDtfIV+dX3+r" + - "7vz63B36vb9w7AbDn/ddLseown7kr7bbU4YIhD6/03//e7JiM0O669" + - "/vbyg1/hPdKLd8WGNPmnXoSs52h5200OGk/WW/fvdl0NvhpHTw3q3P" + - "t59Xe8uCOARA8ydCcX433Z/rjfonfbrnfhP5j9MJtM0mbf4XZT4XT9" + - "czt0Pk3S1ALFfPxyHA6g2A3WCz90Pq6qFO+dsskjdtzAB3B+7rwwDe" + - "Wi/reu0nbcOeMBostv1Dz9MpsuJwzbD+b5DcuGuKR32dFx/pcfGO9o" + - "Ow7MZlAj64M/9bmOAaTJ/WFuJF0t898eHXfdDNmV4JC77x133J8XON" + - "CDiTTWq5JkvNMMLNY9C1ZLNa82RrIki9ULP50AZ/6pczOyn92DSE3I" + - "qRSZs7nc2+gmqKMi+O3an/sQkTQOpszcLsBTnsg2gSEf/KskTQ4YaA" + - "NrFPFn4b/ELIEo/Iu2jQkbg/QEtEJXe1Y6MtWP3sl3/MMlnqf08D4c" + - "Baclr5KzEzHTuyXhZPyCXVhkcD0/DoXsmEwEfoWVQqsJ+Sg2eW9qniO" + - "GQFqHh3n+XCNMWCMLJ3bc4BPB2vz5CYenXkKjI06Rhu8mSJlSxKmmQX" + - "+uHB6g1jC0ztEQ+TRqdISmC6A46TLiH/sfMwBczE0mo4WrXHzoJpUyaK" + - "CvglLnpJC1XiEWSBN55eIHcDChLFpQ4TxZrHWkL2mUXwl6YtoN6OLefE" + - "myRLHy7mizwDT1yt1szryqhfCOa1AJJBtKVZFRtCd8WU3pATvFrbr5cH" + - "lo6DometzoF0xmAbn3/vF2fgKgcbhbkKCCrCKBYETp0uZt+2siJ5pSGc" + - "92+kOVgbLVIOREE/rw+jcJfNGSxGWBysYMmOzxrCU3qelSBOUV1VQCf4" + - "56kXEGaqB4gykGJUKTJQupBnixZ9NNk+S+2ihS/0kkCjOoD6ccjhCO3n" + - "iVLKfYW367Y0xY90TIU6MwSVkRfVdMM6HFYsxzpPGobc0NLrV4ky6htQI" + - "oOA9rLmWTeIupuh6aRZaij5vPp2LH15zO49PmEMH1niBrcCCWd60KgH00" + - "/BmgpkM8t9NzL/mm930scS/j7XYuHlr2MGiXkiwoDQvnESoFVyfKEarx1" + - "uSGFA7ehkULobywiRPBNiqgAcbOCo9MFRwtGp1GVn6wSDuzTImllwJ65b" + - "2mcAPyAjZxvfcTpHN+2xC0bZboApKt6joBDPZhbIgyyEeD7B7Sx9kZ1qT" + - "WqKgeUkvZ66MUI1N4eejGytzeG3kgUP/QumFyVWyD1+EpSja9NICVYYqb" + - "rSkvzJV2Xo0WhQfIedV+EsGU0rd23hAogyuUKtNZ7kBjOxTEPBT9LS/Cv" + - "BlfE32OqDgVzo+JFfWt3uqkhATv4OEhYCFtGXrRhR/jCY7Is4kuCVWavQ" + - "0QdiVoDqoiutekS9K0eFjpDy3E8nc75EdVjKGbtgVmg+1KkWtQAVp/hpa" + - "PQM1SNl1O/YwryWeEJUS3gUkebwTnzDLP+DdtgG0jtClLrXh86SHu6mQo" + - "Ib1r5HM1KWjmksEN7xQ9VsjVpEQ1ezvA7gUqMD+97RcpruAv3Le0G8V2O" + - "ww/ZBDpq+40xQxPBh2/G6D1BqRSiKq7YJ5TJKjTdJlnpDjptk1U0phVwr" + - "bvkabJy/S5Ut1UPnyELqgwIovM1Cm6jCoGgMDERdp6sJJ/K5EeKViU/Nq" + - "c/Lutj90OeYwD8UVS6Kb7RNzMrc/sZhqsZmYenfh3EnCc/StfWJj9KniA" + - "e0WFSKFE/hpxYWEK0k5TAwIh806Z72+hRd37UjZ50NJBBxu16o3UD+N1i" + - "HrjZ7LpRfab42+5KJ5gZH5eX8+WomxFq+Y++BBALJnWqVgGIRywArlFjJ" + - "gefUXkgf/142NpPKQ84le/KfdtYs1kD2gjLDJ0mP7Hg6uSntEb8P2TFYm" + - "W+p/xGo+B3kfK7SX7CQF4ZPE1++lUKGh3sT+tbAx3G5J/WN5WyDIzj5tQ" + - "/aecZYrMDKqraT6b8fWshK2gxGcINBb+0hBQ8uuifpPuHY4SlmwhqwU+q" + - "g6frKFcRttbIphPQR9WCwJesxfcF85bjZb9bX84siFWEiBYBh98kv1AF3" + - "jHTZ8k7PUvMVsm7v0F+TCjefdF4m7wTJWDpvmXIAeBbSrZI3on2gcBCFr" + - "WWCAN8BEhYRFXlK5N3elStQapRdRVIP8hQ0huaNirZu6sBmN5NW8wn5kv" + - "aoqNFjZgn77qrpQeIFrXXInn3eFw/o62hZ8IU7Z2M0Qv3LREDiNQOJKvX" + - "QZEej8mQoT9th+NZO0TxyYCL+ukInW4UZFS14AO1SrX3Jnk36ByH4DIyM" + - "jMHO/jMzJfqMEsDhNLI0VCJyIAEUiopfEt7xzj2zk2XU9T0d9GQxPrzbd" + - "ufT9GgMPWgrwuaWSZ/Y02eJ3+L5nZp8rdQ+VaWkPaJucrfok6uTv42mog" + - "1yd+ijEP4kpx58ndG2SR/V0NNkfz976E/WiZ/X99DZ3/uoxF+AtjV1Nx8" + - "q8JEqDd7qhkZYwUmB/byYoqG7OuuvwX63cnibJH8XQa0Gt8yoOUlKJ9v0" + - "JT/Ho9fZKuWgX7i7/FYPwUQLU2skr9vdTKh0/19q9UBhOgHI0gSjz0QU8" + - "+WUGx/jwoFJTAgF5SXemIhmYEhH066cZUEfEE2yc8syEXyM3s9aIU//4y" + - "uEtXlZ6815DN87+83Jqfh3OdavsR3yDVyJNdSS8STlByRjPISnlz/szJf" + - "gWNp8VoGUoZiqH8/969RViOG35kMcOJsRBqibJwnP0fZCI9+gol2Y79l3" + - "IBnya9F8gvza5n8oip+mfxihVqVUD7tt0yJVwRchW+TX0ImZckvekjEGP" + - "eLSjJ0nV+iejSdJr9EMkMGEQvfVHGMioqq/cuFhbVI3lPWNnlvynaevPd" + - "lOs2T974coS++D+WIye77IGJuibgc0dG8j8uRnqKkTA0tHsrkPSv4rnuk" + - "69kyeY+yEBW2Tt6bQmvwGxUa4tGFBv3ofZQBSNjwqnMI8UiOgOmXJJep+" + - "5Y5AQCTQ8vkA3NolXzARD8tMvxKqc+TD37AX+buWwIAACXpGM1y0I048N" + - "bwi+C8ioAS+eBzH7J9YK7Bw8aPCTPIE8pgaglRG5YR4KsW6t2HmysAy1o" + - "z/LxzmWlUD8Vx8JLgCPXzKWgAH3T/jXRhfPKVrJgYUlSXBcigutDvrXxS" + - "sEROTCkjCMiMz1JUDQCnajBhkaqxAhD1zwXoPeodVNIPkQ7Skj6yUDBIm" + - "U/J3LmllRBtZiHJ0IWlo6x0IfrsahmsVlVtHvWMEcFdKTzwLroNeugP8W" + - "ICa2u8mMDA9t3T2iWOn7rbd1w/LmCKbejjcDnoalzNLX7uzzutF1ULh3v" + - "1BrV031vx8pkQwqZz3VrhQjV6CCNKFtuGJcJ+CXy7FQn0rh9c3zxhZTbf" + - "MqVtHSDFTRe+D0CUduDXzrX6WJH2vUThvn0GM8sNoOYxU+9B4iuSX+EZW" + - "f+rFMw0+TU0X/B111iUya+R0rwCHaldcwA3p7hzeLXr2/ywCsMccRkI8f" + - "evR13P8+RXnf9Qtn49Gac1P3QmkOOSg+//ZnLS5L9DEsrkv6OQwBT3afK" + - "R7rPkY6R7LkD7bmCafPS9XVHjW8Ya5MXHEEsFIhpVyFb9RzoBqXOyNrRv" + - "kMU8kKIiFJAj1s4QiJqjgL0dmCdIRtjbKlcLknFrTJFEPRoVbfIxyhXwJ" + - "Vf8tw8E/ut0hJ0uLx2tXMBryuQTczFPPq24YzeZYHqP/hJU5qh0Sir31I" + - "TU1FM1qcJRufFXOiozVOV5JpTa+zO8mXdJnoncxM4YUpElI+VdlimozLs" + - "sycu8SxQaKC81OltQXuqS6cu81IUJxUtdVKS81MWSlJe6oJyZl7poQOXi" + - "siUlLlekxOWclJe6YPqmIvWMlJe6pNRTL3XJtE+91IWhvNQlZZl6qUtKP" + - "fWylCyHqZelNPF5WUrmxFRkYeyFl6Wgv0JykPlZSA4yzwrJQaa9EFmQPm" + - "ll/ls3EYqw3r/0vsvHAPTJN8XSf0ceSgdKS0BBqAaLzH7YvvITvb/51Os" + - "BtYVubaNDutDSa0vIXJTlGzX9jDU6kmtiaN/2WOU8GTmDt7gzhfjR+jzS" + - "F2+AVgT05AxBbB9iCIUVzdcQ+zZy0SB5236vlk6Rov7JrLTOUYD9nyIAq" + - "kHUa4A7PJ7Ha3DwLn0JXJwZlszn5slndhbT5POaSiyGgM92wQ6p+yzFCz" + - "QUHDLsc8j/mSVirR49/+e4/6WnKHfnhpZCWCSfow1iOL+5+Tunw1AEiL0" + - "7n6KNW8i6dbv3NT7d0LbgJ/WxCRQp8ymDLmlkh4SJqNWgXJIfzwyh4n/W" + - "vTemB5+jcoAIesERk97PUEgee6OwNwtDnXrW1npqiPPrQCGr5POxg47h1" + - "WhiCDtKH5Sxz6d4Z7EB4gsY4b12O7XkD+brIFSafGFxF8kXmY7M3bfkBw" + - "A/uUCxfJHJRY5vKfa5JcJEotGA1INSoxID3aoUIWCl6aPufNEj9RSk0vQ" + - "XgfQ+llXAJOYsYJKCmcKU2cAkwC7WlMm5NtUpAihpoTxKk4e0MnuYuW9x" + - "C0Cr9JiefPGThJX99Gofpn9fRpMEiqknCVB0v4wnCegqvkSThBZ0PElg9" + - "mpIZwTy7EpTgYxab6wgmGQIGvGX6zXS1oNK1a3oUjcRZKWo7Cwr2SacF5" + - "5I2T8Jy+QM03p6298PO+nAcnEgi6lN6jG9ntqMwRuBTb2bwIuEkPkI0mh" + - "NnVI0/i/jheQJMd8ikR7MG9bcJdb9WBvga+MTlJGfv2MY+hLNJCoPSFWf" + - "Jv9goy6Tf4T22ST/UHUHU5N/RBOFDHS02gEHrsdpwIuKCuFG2yd18g9JH" + - "Hi+rmFK90+KUSX/9KLWWfLPINLCEjJSQ+5/qipSk1QjBKZq/1RJqOvkn7" + - "7q15Pkn5GIiFNEqpL/oRh18j8h6mXyPzqmBUgd0zz5n2ikz+Ges5tZm/x" + - "PFA8ClXjq5DfGM0t+k6506b6lwRPQpY6x5bcgVWuJkCFl8luosSljuOpu" + - "VsC06K2hpY+YJr9hHqA714bI5Va3h+B9hqLl/+aLP7efvktZQSi9wzEtQ" + - "Ou6XoGOhkfonL9FuYYsklzDt68wFOByuu+fdAbNHXbLYGJB3q4/n3e6Lk" + - "NREfiWrzr5F8tpnvwrMq8qQfsRZ5aIGVa1dN8y/K8ASJE5whVZ2s4myb/" + - "sonPVmC9ReBztS2aWJf+KWmAF+ub2RE3GDa23BW7VGoi+7XRa5gTGO2qL" + - "lKiO0vi7Gafl3Ih0kfxLazqzafKvqGgRsxQtv/2uVFMktEmEvrFe33cYb" + - "XZoTzM06bVvLC1Zm+4rnM0mxJ8uv6+P6zPczWtLH/eXZ65RzA1/v0Z3qc" + - "C8BXi8yML5JAf9dYD2QwU4RNq0Gncx5hGooqbre2Zlb87D7NfHZ121VxF" + - "XBYhhVScUyb8fXob98Dj8kNN+ay2G2Ln7FkvnlQN0vqcO03ZLlcPEENs7" + - "igySfPBipgJRZAsZiZO6vJxYQlQ4TEXWNwyxC41qq+SlZoghdqXRyBB5p" + - "jlict0kvkZAczefJoKH/T2qelpZyFKT1FFDRLoSKJx3LtkMXCRBYzUABm" + - "0XwJQ+Qi7nyAG9pgzuZrN+VnWsIuTqKPJB6aFQ9G7OTfMAB70RguiMSw0" + - "ZlidBmxaBWh4WF5G73fNw7FDvcq7srrvgAZE89v2EO/g/QOzCkvVsmtL4" + - "aGrIdII+yFqqe7K2xs6enFlFwJHZxFrJeDK11p+ezOyevCdzu7ftyantX" + - "jxZ2A7Ok6XdhPdkZbfaPVnbzVpPzqwpnCPzibVj82RqzdY8mdmNAk/mdg" + - "3Uk1NrU+bJwhqLebK000xPVnYm4snaWgZ6cma3Wh05ndiJmCdTa9Lsycx" + - "O/T2Z22m/J6fWLsaThR2kPVnaGbsnK2vw5snaGo94cmZtTBxZTKwxkidT" + - "ayDrycxaH3kyt1aWnpxao1VPFtZaxJOlHeg9Wdk9fk/WdlPUkzO73ebIc" + - "mKnqJ5M7Ua0JzOrLnsyp8WNSFVOSYpUZeEarSMpVS4FWlKqXNJbUqpc0l" + - "tSqlxCrihVLiFXlKqQoCpKlUvyK+ZVLsmvmFe5JL8yUknyKyOVJL8yUkn" + - "yKyOVJL8yUkn51kYqyY2aUuVSvjWlmkrya0o1FZlrSjWV5NeUairJrynV" + - "VJJfU6qpJL+mVFNJb02pppLeGaWaSnpnlGoq6Z0ZqSS9MyOVpHdmpJL0z" + - "oxUkt6ZkUrSOzNSSXpnlGomCZxRqsInEADJXEhTglMhKVVRCEmpilJISl" + - "VUQlKqohaSUhUzISlVMReSUhWNkEYqn8A0NVL5FKWmdU9WQpZ2DuDJypp" + - "oerK2xjmORMai8ovMJmMLCcpkbCnJNxlbBZIRVT75NbpNBFUJaUL26a2N" + - "VEub3gy5nE1cg8y5MDxx4mO4JWHLrqhyVs6ynAsJ4UvXrkGyVpTlRMicZ" + - "CrklGQmZEEyF7IkORWyIlkIyYjKUsgZycqRU9aKsqyFNELOhKQYbnAhyZ" + - "DdeEGSQWVeyCmLsswyIRlUlgvJBGZTIRlyVgjJBGalkExgJkKmTGAmQnK" + - "YLjMRksN0mc2FNFKJzJmRaiGkkWoppJGqFdJIJQnkMF3mEyEpVS7p5TBd" + - "5pJeDtNlLunlMF3mkl4O02Uu6eUwXeaSXg7TZS7p5TBd5pJeDtNlLunNj" + - "VSSXo6t5VSE5NhaTkVIjq3lVITk2FpORUiOreVUhGTrK6ciJOt5ORUh2d" + - "zKqUjFwbScilSFEUOkKowYUgqFEUNKoTBiSCkURgwphcKIIaXAwbQsJIE" + - "cTMtCEsjBtCwkgZURw+dkwZ6qnE+FZFBVKySDqkshGdSsFpIJnHsxClOf" + - "q5mQTFEtjk19nqVCMkXNXEgGtfRCFqYElz6fUQ+ohXrHJUuhaLyQJRNYL" + - "HyRoZ2DXE6EpONlKmRJMhOyIhn8MqjlVMgZSRGDWVcsSyFTkpWQGclayJ" + - "zkTEgjlSShMlI1QhqpFkIaqZZCGqkkvZWRymd7ySG+aCW97EWLVtLLIb5" + - "oJb0c4otW0sshvmglvRzii1bSyyG+aCW9HOKLVtLL/rloJb0c4otW0jsz" + - "Ukl60T+vmiyQBUmf/Ap97KqZBpJc6UUrdm7FaiIkxVilQlKMlU9ghQ5q1" + - "Ug3UnGYKJqpkExvE7imIpVCMqJGxOAwUTS1kIyoqYRkehsvVc1homgyIV" + - "kKTSokS6HJhaRUi+CYUi2CYyPGTEgjhq8bdW7i9XWjnpqIVkIyooWXasZ" + - "ONXN+yzRDB5WlTicHiSLLUjdBK9McXVCWujlXmRY04p9kCyGnJJdCFiRb" + - "R7LRYSh3jvO0NCOsczydcSqUUWa/kcHqqldniiRanAG57Y/rp/Vh/UPOk" + - "7jraNoPifuwMsL5Sa+XRiBU76bYnKrGR5URdK9iNp5V1MbDeF2IXTpvUl" + - "nfMwwz0PSHRyA7h61ogQ4M/517jTZE990mAhcER7ZUTNKNlSaqVP14pWk" + - "agSoxdP28PuOvybd5Fsjtevf42m/O2x9WKy5ByDoAR5Fd9+i6THxJMqld" + - "gN6sn7rT1iwGvrJpWVdx6uvWgNv1/tvalFIIJB9xRh6ngW0WM4LHYsQZe" + - "awt24olwu/WyGyR1aVtzzWYkVjZiDMK3bOfT5fjWnxxLA9w7GU10bxxRV" + - "jlmjuqECubCS8oqpDPmc3SP7hIeQqoSdHLFg2Vfdxu1/1xWe9+yDJqDu6" + - "4PXsdfdx+DlY4bg+mXm6lHrR/6Y6n9WHzAxdWAqmdTRTuV2eN22BPjyw7" + - "qFbIHD48aWBK4Hm7PjxvL+ftGhWWRlHAuHaYcVWFn/fH9cNzdza2uJgt1" + - "FeoN5lHxnEiq7jmCiN6ml3DytfUxWSiyPLMuba+QRuZuOxsrDDRgg/DGY" + - "575m2NNnG4bNbns1/Eo2J1uJy+sjTDYm0A/VpfQHS/BzRcdoACfVmj2ML" + - "684TIsTv8kPFAwPploFgv0Uo9s1Bwu0rJ/v7lBbm6qlcrfh6H9cO2OyGX" + - "qSSS/lPqTa2B4Yi+74nFwWQZnJ1ht3sT9xDyuO7UQiLbPpEAoJ8/PiAn" + - "uRJocpWdj9nbTNvZnJi50YF6RnSjQ2NpOXmNqnk8Dq/3w5n1fTa15GZ9" + - "2m6GV9oeUI/xkC1NXmQhkCtRXm8i2OWFgAt5c79zgS+ngriwl7kgLujlR" + - "BAf8jITyAS89AHbMGZ5IF0gs1mAfChUqD32uu2RGRDRuUNZb4i79ecioA" + - "zQoVlATZgOzgN8eXGYS+cWJf2t+xM1hPocES/fJJBIlUq2Q9x+TMYrWAR" + - "HB3r0qeH6gsclNQ6TFGeKjgJdKQYE//r2Q1bNWgUyKierT4zBJSqXmWfe" + - "CmSrxFQQqREuH02hzVJPbEyhFYG8PzHIeS0ISuJ+PQJ9zpUaGB5dHVhIc" + - "JL4yiMis0OMTmAKBWGdHvrebm5wr7HVQLRf5jjeTLjStHZogzj2LzRg4+" + - "zQEv5Yhmnx9gio0rxSh2mtYoxp1YLLJife8HZ65mgyF2q9456JjKRUDT3" + - "nBoY+B60yS0No0WAUgnVjUcuFIAuh0zYKo5ivrkq2pdPb/uU8mCFAdWZo" + - "IWcesEAV9/nHPuUcGYaTKfGgjwo5Bs5F6aFTkmrAI9vroeRptdPSQe0kv" + - "UNQ5y33B0OgnF5ervRRdPCXW9pihHttMQK1tgjGV2rkWz9Icdk4ugqH2f" + - "rWH9wM8o0KD4sxqCMTg4oWBlf33KPFjxoNoYDcYyT2RvKFIqOaTNxJkvF" + - "byTq3tOSA4auKWk1In51aAb3gXivCS3KPbBz0doxaBRBVZhiD78N2Zprc" + - "Rxeb5IaW8QluO+pyp/7PcwcnWyoKGGXLEoF2D+sLO4ospzO9RYhQaRriN" + - "dGaZKxLohMGNtYhZ8ajSvOM9EiXRM9qwG4/8r6YrYRzGnYY1DfCmhgZDs" + - "MQT2oWaJH3nc5HxqjtMljQ3dmur9xbU4LGQOuRFRQTdLYzCc4h0kCGiYU" + - "Bg0JvSGjZobahJt9vdb1akvY1xhC6yjgg1BkC9nh7gZLsdVaS1gklvUMu" + - "rHcPKDVzIh551B82eq4Ine6+V+YCTMEONdtXIJ6SNwBKCHVuQ6R0CAaHl" + - "6E/nKHvQEF1SjBn+YbNEcSzzW93pOfpNVd5xqzfscF5uKAYY106/d/4Wq" + - "tuvuPO69dp+r850CH55PCWO8aipEU/G3jGo2ZmlnnsHs4em7vAjNvrzGn" + - "mN9g6a13Om57cFZm5u8Ch/Q7uH9kpZKXPgeDMZd3pjG4kK9nySZrb98bp" + - "mireVbqCRyehEUeLOR270EyTLYdn9E0Zs09fU1SBHlBTswJT4/toigdfw" + - "z1XNXrXP6ZI9aCrP7J20NUftMw70Gr+CLM8RIuy7oyWgnmrIey5yUnVBP" + - "L+TH4egH2/IZIpRPfCyqsfajV2fqHnNAC6klUWtrUTYiwVbeVoFeIE0Y4" + - "iSTRDRFko0MqiES1MnehGh8Gu0YAVZ6Ihq++tNBQNipF/E3fbJlGDRCTL" + - "CLGxNBFmC2weYVE8cRA2keju3frUsk7CVRvW8iVrLeQMaUpLycKWcriKW" + - "c4OJ43RzXCBwm55JXn95imKbu6wGzHk5GECcbCj/ByyiNlYjdzWuiCchi" + - "u5UEEvuh3A40W3A9KY/p251Jm5bxM/R3au9VtoQPCYtx+pss4MdureTJf" + - "cJg/Uh/LkQVsKloDVOIY58YPc01fh2yuNxLXSaOmgNJLehWPeNcjDhoP3" + - "YaP00jrVuMv9icb8GkXkUC9TkPFysv0Lj0M+IMbh0a4lO0uwbFHZT11mC" + - "wu5KmIo9GZP3bGjEg3/DfzrpVskQe6kW+JbriLEFOlhfBXhDJDoapklwr" + - "2D5F6OO472iMRdQdiYr3AFIenQucGdRNjUnnBpgQDGE5dV+dU/cXGHeZB" + - "b+vDoK9lyZRDdvtqJgYbd5nR+49JM5YLRdRNuotM/0PAetMIza0j72mEI" + - "XT0cEOoHAZ27U9C3b1NckvPwzLkHJtxpbsjAn1YE/vfLFVeRE82xnm+YC" + - "xdkaCvpykR8+3LFBVnfv1yRWUUDa1bDbd9deEbKVA6/LpVVgWMGN2Gkwh" + - "j5KGeeEZbL5x6Kw2B12w4ImlM4M8hO5h7xQG2BPjhxnobOA0yku/EQrhn" + - "PVSpKh4/S4OBxClwoQX4HjKR36GUUKMQRXbZx3/vL7ty/7N7Q2c0qh6Fx" + - "gZo56mV34VrjrPD0AL1pZ+pWjs7dobxTnWMalw+MysMedaKYsnQo3DTRT" + - "TxblMnofJBrqkuFu74HjW3XUXkzDZk6/Xr3tcM8iOPAIrPQhnfW7whMLM" + - "Bp0tEiqUXkMBUx1Nbd5Z4TPvt1uvRnJ6yG3DIPbUoe9g/omUOXM0eTjHQ" + - "1+HJr6soRpNHHJdgdD+ZoywQjn/nc88TX+vjGbfJUIAk2dc64AqCciH5T" + - "WNqqmlTome12xXCZjnkOp1DmsjbuEdqTedxIceNLriBTkA4vEn2Ib1Uuv" + - "EM/H574wNQS99JCqodtUwtFy0LOp78NT4szjVlundyFK9ngkqS75MxCds" + - "1HhxgxXHgNsRd0XZxDUJrD0/HCdJp1c75NMFyOnLA8Hc36E1Qo82DBAIL" + - "G5o6YL3h5ETQqRzct78ChZuBoHsZmk7XkYs5rVNJA88Q7R09LLhcp2Wmg" + - "M9JZoHPSeaCnpKdCm9irldA/89JRKhCWbnnhDNQeT77nAf1JIfQHngadS" + - "HDtJ15VzKHJ0Z952XJaBZpnbUJmrHidoSlaSzLtqZA/GlLS+pOJS2T52f" + - "ide/L9nPmaimgfjWcpg0+8b20i6fzEq1cmgWvTIdn2ycop2frpi0mHRPb" + - "pN1MqUohfTGQS+j9MaMwF9/QGFYtZIE/rw4m6voZQKR+pXRBDrRtN700e" + - "jeBoaTa75utdsTRmy2ba8gYehZvfcKADNvG+DEd7vsF3aqZCBdWL5Q9Pz" + - "08BQtbJJBTFcLx863p7FyZChALQnalWcGkGnqHpvXELM6ONvqGMOk4F/H" + - "JEIA9vzGDUwrejuVOb+ZiSWrEvX9H0CMS9ZxmHj45VJNwaLafJJlLiSav" + - "FqBLkJtgIGNItTZnveImvaYmNl/igRAEd2wtMErdyZsxAomUzjzxxDWSS" + - "Tdy32bmZZClJtSJWGjosiJFW05+S3tX0x0S8CyuVFG5nl/ty+xlW9CIgr" + - "Ok5eItA7f628XxnLGVGnLDyd8U/dU88Nek46Zgz8un5AXVAf+z/EFdTBY" + - "4C8CxoB3sBZwocuXesOH2VAkfuHctu7Qtaa3Tkw/Mu9xflo9HoyIfjxTl" + - "XKnDk3rO2pso6cKLAkXvHYqfUCVgocOTesOImMJ8D00P/dGUBbQbisfP6" + - "MNpCmi4CJ8IOvApuZprn8SnIPa8sYPrFCMRM4+XQcZdFjvKYQX5aQ+r7n" + - "b8/lfWIy2/XRgrzWwy9KrQcO5DetbnJ0X5b4+LIecP10or1rvZv0XN5RG" + - "1Sc1vb54tJ05NPUymUU5RXBLSOsiCAGLnayKNBlaLd8ovJGLMxGzATzsu" + - "x33ujBJNJPmFcf8k4OiqMnpWGNWHC1c4MWtl9GBzQImShAFGpy+vR/MOq" + - "QG6J0W3kRP3l9XAedeOG9h23IXQP6oDQhRog9JGYtW3GFb2pIfpmIxP3A" + - "jm6ifYxskSxM0vpWD0SoiWid6YaQ8tiMOqbfQrm1L2szdJU2GVtrni06z" + - "FjmmOqvSrUpo6bOFwQQZPvtn1oOktDh9EDFUPfQoJS0XtHC7LROYjZTeN" + - "osbspCdg9pKn9lCsDa8Z1GPbIVsiLn8sJXcHhsrfrbiErV8j/jvdkZxjr" + - "40yuEpXHhtBZ7ICQwwTcZhE+MR6/nblD5E/rFyPMnQacJrLXwxMFjogmg" + - "Si6cOZvXifx1RNoklUS3TzhWvpUUNc8gk9pzAGK5NSFxNh1qZA+nwc3OY" + - "faven5JhtEW1Xum3P5zDL4wpLdxs0y6NGb6D7EAmE9n7ZmUayYwUO0P4H" + - "qEJYqobFtwj30aEPRHBhJPchmBgguomzWfokE3cKAmuW3MsjXCURb01sZ" + - "C9I7M82fMA/Nt55I5g6LZpLeoVquE89iCuBD1tNFOjo8UUdF9R7U3iBrd" + - "1h4zJazQLryrBLfgl2J5wEYFKISt2IkGGxOvDgtzVNP/c4rUluh7GKZq8" + - "0mQ8/OwGJRkOCavCzzoHMyK/Fvw8YqNMYSO8ZEvzOc1wMS8qyP2LaCurU" + - "CRCOqPLzoHEMSzuveLNMii8LSPOTQS/MctvTSPCU3r2kgT75ZzYCNnpQc" + - "TS5J2CXgOZ3ffmcjJUdXYzqNVj+LVcIGARE6OWo+w/eReciTJJ1abIdbv" + - "eS6SDq5ox7+7fq6X29fekCvtQt4ZchRXHG0NYfhuhbV4Hv0uAeD1UutTM" + - "3D9i2+Z6GuAMrgObVEOM0914C8+LHSqIyxM43q2zErzZAXP1KNRtde5po" + - "jb3tQelVCEFUfuwbX5zGk02eskTPuSY8q6aInPSwtR+Mhf6f3+hFOd2WH" + - "Az/63Q/0XJ1YuNf4VsUK/1H2w2u0No/y0YZX8B2dwYfckY07gnOrBnltP" + - "8MI74BQKdvWIlK0jD0AbkeLSw52jSGrZql14HKxdAF0mEj7MKpUMN+2Md" + - "oIxAa+YXufWUzlhRdH5aSPYIs+4yohXFT/th0uyJfMQzS1sdY3HFMbi2K" + - "wGpD/L9verRzkWeZSKl1+NqldGNECqcNUh+/z1SeucpFIyuqVAE59Wjkv" + - "/m6sykUu/V02qZwTbwBNcnwWgL5u3DqCzNVmeHUgI+N+1MHn4YBc1JcOG" + - "NCf/AehX4nJkbBdt7frlFArOvNkTKgrc4dIRrQekDLOHCIJp59d/8JGl9" + - "Go3FMyscky1oKgA+SekLdoKo/IWzTIAP0WTY6+db8xygiXK+23njmhgkZ" + - "6Bf2/cAA4je/gaMg5v506kwVwF1myQzY9YmA21x18vLn71vFmxG5dNEfH" + - "5g2chh86CkY5ehSH0PhOeRTOwSbHPGHZhRdy0MqGUMKIyN5OmzFp/HzYD" + - "Se7WDa3QHgzBoN+DInboo0ZXiFGBvjKMJ/g21+0hVl+F99qhUmCNbZEP+" + - "U+o2bnMNGpSkerBrMg1H/FvP3AdGclivWo8w5+dC5PIZFOXB1I7Qox671" + - "IjuK3n/xBBnLpLatzfjh9oi5JDEffQUIrtfTVoG0cegF2w/DCq9nmBKkb" + - "npWk7D2vDHArh+mWP8ai1VgGfTZG+xseX6BcSttCZtoZVsUPNRzVpKXU4" + - "Ms8VbRCXsqtL0v3LUM8cuaM2M/rxwH9jEwMOXYoPFpvCbwb0LVLP/9bIu" + - "6LVG/WAHkVqbtlB1sp2BeExrTeBPzPB7PSxwVT+637hoXD7JpqLiTNuyf" + - "cSgu03KnvwWhS4UE5P0MAUzXaDpgeEbMvO3dlf6reeFoZyla8mXGjH3ya" + - "EbAqdNrMk0dqqmXyKKsNLb7VUGBoBHDYdj1XhyYz0OetWoVrLRCtwjksW" + - "mtrkke9PlMnj0F1LJLH6MWpVfKobF7R2B4jbQjN6XFsBLvMiI1XyJc50d" + - "EKOTTVR730gNgxdlASHvt+fMRMZcLfnh8I4HHHD3gyAITpHyPVBtqIg0S" + - "zyQSRQQ8y0xq080MBnex2GMeHP63JoCVpw2jNF036nteP9iCwp8Ia+hgL" + - "y+iBE5ZVAxYWkud2sThmKC8xWxZ753ZFN8JHvhx33+3tyWRPBWcOO1wO9" + - "nSyp4ILh7109giyI4LxuIP4ikxvzyEHOrgiejydzRVMqB7diToTpvmPPe" + - "S2Vlck4kfLGLRRy/PCfAUd09JKV24MEOrCVNE3NOW6NXyvKFvfVkeF7pM" + - "WSwNo7bdxSFB+LRLrvoXDguprkVs6rhVRq7jWbTTUWkgruBYRta62pKi3" + - "C0977da6Fx3PxqqHauvAq7agTDtDu+DBMvMmEb4jlQxtKBwhxFThcXgUe" + - "xl2GsOjX/eBqvAIXXAv7CnZR3alvM474XPYLN+p+Qr5aGlVvnMDhPLNFX" + - "2rfJeG78vX+tbF6ZFQnBaJi3PqsFCcFrlVnFYiXZzWbVScFrq1BFoZji5" + - "o61YK2joIBd142he0dS8FbeXRBW0dxH3mUjDpNNMASa9ZWMzVERfQdtSa" + - "IZEomAjkuH7g3jFP9kxJHR449ucJTxFiKvukTeRI+gOFBb69tRzxcLZ5v" + - "iIZL9NjaH3iod5owGlmU6LxgNPMGLI2vasMHSzvSGs1bgFaq3Ck7UuHTW" + - "4/dwjJKRCYMDlQ3cHfTgDF7x82iZ5DTJYg/VITkifqA2RRzyEi5DBMl5Y" + - "IzyEijNFziHDvnkNMzVfggI72CuBSL2EUGWiV5ob0sOcOV3QIq2A4x45v" + - "ZjDkoAAuHC7IKnfI/vLHRu3CzpbEUVl5kpCXpq5II8A33nkeB9oGVggXR" + - "Qzt162BY0r3FBld1qT1M49VZhBXsQxb1wUHhMpgAH1/wNwCoxsEWote3S" + - "GwsvhY50F9+N5bkwVZ10+KMWE33ppE/m/D5tTcUFphJGInfiXjVE8UIkC" + - "9uQAt8UlvLsxJa12a1brfdzt7A4v5DNpPBATVx8FBiwAQbzsg0N1wxvRB" + - "Xq6QK0NbzzqdOfHK2JgDoF6/gDKnGO6s7ERjaqLG/L1mOE/pLZ5ux5EIX" + - "tRsnl7DKso5Uh3e+ITbaBRFC9d7IOhVn/QeSANautOM38G0EI3syOsl7e" + - "JPlfjlSxY1P/WyfpnojWLnwN+c6UhfjXJLhpszWwtEcjs/6jZNIh2NLjm" + - "Ut57wXQWUIo0MR25vAF82Ho+GSPE/HGUJgcms8sBwIVSVQF9VfILKAgUk" + - "kEO0mIc+hUdSwdEbFgWScuEEYD/4syDzJkDe5qux2Kk/PLlz5pN8FiC3O" + - "Uo7zye9/dEw9ON6HzaY2Mu8hf3xWcL5O6b129uPrs7IiA0qUHV1v9fQyU" + - "177jwJJ0bpSN91a+lwoy5pddhxSXJkBpIRG/d689ygYf9nRXrUB86nAPu" + - "z2mWbJ9vIgmmlaL1MUtPhDrqkXs2ncLymRKRNLRBbqWTpnTFLCSw9K7bc" + - "heXGE2vLahXr2mNjudFFKKlgz+vTcRQeqlnEvQ7Spep0eb6MWAVznja9Z" + - "qJ65MoKM/Tqyd0pM+v4MgzmEoP79fHenJtvFh62p448vqBIoSbSs7L+aj" + - "JFm5udIiTLr5DHMRJs3zR6cJcd3OJRGLTi20zUie6KI3NqU9sFSO+voKy" + - "+gvLpFRQiiOCx0BHzSuqIG4vtWN7eq0kVbS7MipBsOkbyyRgJYWt0LLDm" + - "XcmrmbG44LhHnKtEb4NN0K7iN53RItSbzuhOgvZaWSK86VwkW/2mM/jRm" + - "865oSVkuO7sbW+8UOXMfaTCfkZ2/AoTGw6I3wXNZSpUUFuIbW90sHoVrC" + - "Ipeo3xYbtG7W3VzCvNOb8O0v9h7rkdL5tZ7Dv3LTXzIuaOj4I3cyOG741" + - "HgtSaJxE2Bg2H6Iwr11OPApgplvhHNwI5OhRc6DUqBqpP4tWKjjryJRmX" + - "c3Rve14CPIjWyvw7XtQwwVHJ2rGSpSxFQXpPpf3Ur6Ch+Prucn2uqHH46" + - "PCMg8cncpYWDidyWguMTuTQmc5V9EvRCXVNRxnCaK2hK/Q+85lOFZGlmt" + - "goIrROB4zbuoOvmrnD4xYOMLrmH/kZ6X4oUH2mpcKgAR32xS0MsNlHJ5R" + - "J6+RrOko+ctPZ7VIX4Wc6U0RWKiLPFBFEd8A4+Q6+Sr7D4+QTPAzP24s3" + - "VMoomNvQ9zrzzEAPmnjhQgAUsG+xnWdqmHL4SLMysoJd/ZS0fop+ZuhvA" + - "482ObPLgpA7lclqOpxPL7x5ydxdwYIxN1fw0NRW5g3oPHVbQHHJPSjsIq" + - "NjtKT7Xl1klcN3dLC2UHRUfOgMoseFsuUyQlxmQeivXE9EOG8vW+508mp" + - "C+62tuzw/2ojxDkWpzz2gdspKh/EdrYzHXXrq07OkFxOgJb+VlrRK1KWE" + - "dZVoe42MpFucgaC9vB+FcMOAVid9bHDTJvpdlKJMem3lAmH86qExRnIB5" + - "Vm9CpzH/tgFRpOoBUea3GJW0PmFx3yluWQLZx5xkCsqUIwpmsnNY5oSlh" + - "FqjorlPC8zRs2sZ7WC6hlxuO1/vuzMoRERo4rdHLm3EuTINdfkiCypRik" + - "zzxmjwp9CypcR/8+Hbse5ogQ9i/iP3GHFbNL7xqxVczHgHh54c4j4Lm/y" + - "JfIR+yhiZVFxbddfg8BZxIH+HbIhysieBxj9syMsgKiwduiOjkHO+oon8" + - "cUsFFmILyoU9kvCiRLGYf+B9uHCnsXsc8gSdJaaNYQqkEU18bDehyyJ0u" + - "0WnHOaSWiYx+9CgqNoMPI+SI2Z5jHrBVolaoRENovZJ24hBFHicJXpFVI" + - "d5eSpe+A5JhFoFjN3jyJPlIzT8NB35zeJLxLW9nN8kjNGu6jSRfXgdB4e" + - "noWVxqzLJkQUVcjTJbTMOC72o191+1po9itXVKRAY9YwbIQTNbpv3XFgo" + - "lRtM1Um9G0q01ljAkNVGVaYkNuqxiAtAVeJMbKGoJSwFDUwjKzWFIQSKo" + - "vDVSC9bVOmMG2KyjJRlpLI7KsnmKCiRvfZshw7jo9jpdTjI6XUwWOltLJ" + - "wUEodMFJKgYp9I7JC2zeSpcwlQeqVYeR0ZNSJeq4HS7QJPdCxt5Hs5LeO" + - "yNIhJtJXhpkowSuzOmRnP35Wj+345r27E417E5II1DYkYPxOC2y0Q73+P" + - "U1uqujQ5ftgzAI/5ua5bIkc3V3ewgEL0GIgx6Hg+l3EPDH3dQ7Hm3d1Fo" + - "Y9euIKVS/Sw5EBB/RB3vwPXfbB7IHxfH+KJnXQL7WVkEIdDQrU/cBDBDz" + - "FkQbsHNP2CppCaC7Jw8EkAIo+ome0e35ZRhHPfbgVlUF89Rez8BYWkGLA" + - "vqTrr7zPqQu3OfX6ofgCIonhHJviYE2iZuZLve+4mEeIt45i9wDYbNhR+" + - "7X+xHYKAYrSjApw1JWVJX9l4pU7TNecMRaZeCHBp9N2rfd8IalsJRi+0m" + - "TRNXklQEU7U7A+UkDYvRPJjI8svtgjRzccwsFFq8CoL7eeS1slV20p15h" + - "eQAb+bdufT5H5RuFBOaymmFXyO1XzefJ7dHdKClrt4i1A+i07fusdO0uH" + - "DTvQ2tZ6kvzu9fUVv0Vfn1lCFqDQGf+OJno6df5MA3L5d3cMQ8qnWCXxB" + - "lYNutuHtdmFoUdXArYGvLoTcGXg8bo4pFQLTTNGsB2dSWuS36NdziVpn0" + - "GG0DnkgJBFBOKrWxAgWk3Oo/6/Rz0MCkYaBDJIzyKzhNeEolfByLA+bZ/" + - "7yPIyJRwkLEC6ATQnS3fjc9A3nyFsDMOmigE82mcXnpUtABpgZIbVJDcs" + - "sAw4MlBjpMogyzi5slcz6HjvdkEwvttwCUjneGHokOGkda/BcMfmwVNgu" + - "hdpFB0NQCUYLy+m15vbz/i+RlRzoG/dcDnsoQfsZbSqUmG8cNXqJaxj1d" + - "PAIif4qYVxOq2hU8TcGbjH4dirDp55cdr2mzUm/EMop4mGUcF69kz2Cun" + - "Yzag3XTHvwjVZlFPvoxST5GrrxBTH9Q76KmGwLAYMtztjjnR8jnKWYX33" + - "kiI0o2e92N0mz9EFXjPSzmqD32K1gYnvc+h2UGSxkQbZSnGEGvIcm1dOC" + - "ai9SZRiZJqh6Sg5kCK+8BM5cGWQvEJ1Ys057NaHDROaQoF7jnqXkrQeKQ" + - "oCvmEarq78Dgi13wBqH7E19Ggj0Tq62kmsDDzuIimhthmlq2AFMTOUtoI" + - "ggor7fL38WwtnpGsLY6xtzz0j6NuNh0YaN50Oz1u5uhHTWQMMcqtUYYHL" + - "2p8pmeQWeQ2epkT2Fzl1wtjsNVMzpgv647O+uYoZqcw8UDsiZR61OFJzN" + - "R3VHuRpfxzGG9WFQfddd9YHJFnEgAMNmXt0Gs/j/C5bzxhllcfH7icOl8" + - "zm6GGQUQDe4akfTsExcjMertF565VtDPrP6mQrCn18xxNSFg2IyP3rO55" + - "QrpENR05aPa8A4ZBkKdHUkKEF54qOygAVaECXE/IV2TSgw1cpqhkYk3s6" + - "85KA48Y9U466vSJnOPhDxxwqZSwv+R0SgIhOehLHruIc5CflF4yhzDzrB" + - "eMpmHp5eK7pKDXI3a8SZgPqNVBtwmMm5SLZaSuGDKSzB4SWsBPDBeJa77" + - "R0mCeRfjat4m09eJPTIuHhgKvnT1YLj3/vnZNVfe1ivPfWrqrI0Y1XT1b" + - "zaxfXwcy8o2tW41nfe/kEffmVi+tgbD7IYDkleb8x+kTjvsUwZmYQljsf" + - "uDKfQdeKgKBtOTjoVh7wV7Is7L0rAZQbchzrztyMM+arAG+6GvPJGil9L" + - "bHrYWaxMEVzpf6tiN7Q3BcLE/jzrZBMhhlptuOsX65YL8f6fjuxYHdDsG" + - "Vde+ZVRAvPuTW1WK7uEPL0zkwnnLtb46tyx5iOT2I7X7RIvd3mnyF3UFu" + - "N1RRi1UoQSK/05MhcpfSQI0pPY4n4lHG+BBqrQvBk7VWhCu60vaqjxWsV" + - "SLGsy1Eo3aO9clpf9jY38PiYO5JL67EJDwXxS8zGpoEcjt6gLcuWc4NHN" + - "mrW59hALXNo8AuV3UDaOs1CsovFWM3xIYyQvDTRXaCAGKK9QzpAtqH3tS" + - "877+Ij4CwermWxfsbjHgC+Xo+RaBe60ZyE7kcJ6NER5aacI7rd1wFKb/+" + - "gTPLTgHo7ewXdWFFo8xts7xU8axbr1jEyzC+jU4dTJDGMrEukZ3jYcqvJ" + - "7dSCPTxRgbcXimWVpw+DMeNbKFpsNDPeqetwc/VYhuox7MJlnxk6zYF7r" + - "JMUw6q/QMfsRZmrdVbttE3ie3UyT/OIEeKAE5Tc8A35YM65oD7JaAwh3Q" + - "ML6RT+/NXlPFm706tBiOMsl3Qgl/1TTBlq01XJsPLEBTMJyK1yyZLvFgt" + - "Yf4ZMzxMeuENF3Os7WtrEL3hSB7Df+p7n1GFuF3jqyGBlunRIdPVuTtAt" + - "HDBUfwkMY9N3wFg6XAFDmkq9Ots4nwoW3yNlcLUFTr/cskOn8UrjPNN/M" + - "KdXNab2Me8oB8LBnGqm1zsaDYZb550Xpq/vnuNYUHQe1eHXjYV9yLUlx2" + - "HWc+LQfrh+oPGpwv1rGyyV/rzuMQnRTmcB9rFVBsJQG4u6CnAka+tw733" + - "m6Ctpl4aBrirO6CzAUR6nDvfhzh19lbMTMt7W+0HyqwSiDRlaRUeGDEyT" + - "PYFIKQ6nN22jwXz4Q60dNQzmePKu0fO7WU+oYAwvrBSgyPUYivDC3VhLl" + - "FEYN1ENRtMRVD9tFjdNDe07bKj4e70aCZ13f7UaiXZ+Q6FoW+t3rJ1MHX" + - "qtgSzTwBo/SsKqOZojovfb63WMmt77b7HlGLJSr220qaJ1CbF22NOM9LE" + - "POqkig0ZqwKAektSjZsU0cikoFFjhkOfuEWNLwMsIj3sRz4tRhOSs0iok" + - "Rs/MkQQz0qlrgaKdgsLwzajVoI5wKe9q+SJz+GjxwsHjyfQ0iRcEWXsIv" + - "KCK62lzNfF4NMV23uMlQOgrBo0CwPRxHxnAkdYtT9NRuTLmg7mB2iQCn9" + - "pcynF9A6FxhgHcTUWVpdwV1hg8SdLoE17xfezvI0tDdh0AA40uiqP8rnu" + - "S2S6zQi0QIL5xi0QskX6Can61QDBDevUCQZ2RVgsEKAi9IsAmenNFgMPF" + - "EORZQp5hL7oPQ6FGE4SrIkRJjfYp2of5DiwMMiEEqIR7rYEgIcF0DMSFt" + - "RM19ZL6D9XRIRWXh23Qg6HLEXDHNkpk/+UxuEZnd/Fr2I0hAg+Zqtccap" + - "SKXnNoNR3lF7LkosqPArob0CcT1peLOsFK6Q7KQp1FSyBu0ARPToE09sR" + - "zDZiLBkqTUGCP6BXttd18IM1A3Pt78RgzUOU180utkKBwL2qJBFnydd89" + - "hfzFFHevnCM1rzEfwSv/y4SqGdrrQWttNUlM2cwBooNfbZlO8e1VLTrRq" + - "palg6pFWp/2mCeH6ByHpqNhtgBDnr9krDMAodDTRN/kMmlA2lYGBXOSHP" + - "zEE2PNIUw8MciHc63LpSXiiSc0skM88aSnaFgtDC0ekDPRbYkINroeUdN" + - "RCiFa9wr1/w+rTtuH0A+q0kOU6ATsjLRfWjeEXlp3QFhaJ4Aey+toLEK9" + - "TZwn5hYae4SJo8VhPJus4ITGIlcLtSuHj8YAB8fvEuSFR+MwUgvHJtN5a" + - "dEATC0wHoXK2uORBC7Q2GllwXP/3F3OAWZUutyQ29EFipqOyo0ezXqJ1p" + - "+Z/Q71GiUKntO/Cc998SucGbe0ml2tDBCOXNeKvnWJV2b4fgJmfeuj6x4" + - "JR9ctEh9dnzksHF23yK2j61YifXTduo3WPCykD6hbRA6oLywpZ8YnnvYH" + - "1K17OaBuY9UH1K2D+L6yTDA5oF4GSCKbW8ztlCAgsxoCkeLVEDjTW2B5I" + - "KPBA6ULXcDMPqgXcCkMvadeIWGPFY3+4KsRBfFEnW1O2nerhtD9qgNCx0" + - "oguEdU0WWZiCq6LFPTUWWmxwOGr/UzzcRVD8prWP0NDTlJ34+wlIdB7ai" + - "WydUDg21rwaftBUKK02au0NEZ/ZVh3TqGUt2ZsyRkX/MMfGsZdpkF1tUM" + - "pDG88XSmduiNwIrAugqsNbzrRxahmGDU57MA6/5ApWbCRJzVlWwzRfPVJ" + - "Y/4dUAWw1mpSCtFHwZZL8TkIcL90VcTWL8xj/nZAJknZ69itZ7QQZkoeX" + - "3wbtcZU7DSAEdeO2kujK2Ni9Pl3t6pVk8tidERKiSB1AJs1NYF8+5VT6k" + - "QpOiXkFEpOfCrGzvS619vXYF1ofKHTI2uD0WeRteHajqq6RUZZ72DtLCI" + - "X8J0pF7zFChsHxHa37PHejKHE3JFR4cRNEMeIlkl9mIPax3lFFrMMRVq3" + - "k0UVmFZAxf8kG/mDh5otPiQee1UkcHsxIDhch2QSh1EqEr5Q2t403pGS9" + - "rrGYbQeoYDgp7RJgN1x1Uy+BMU6DSHsOucLZPhfn082jlT4Qlt7jjz4C3" + - "j2QbMIByC1iZcZLrjF1NIEF3DmqYe0PILeGUFOrviaFNQw3WHOzJ8ix7Z" + - "WkIOd6ymGvALlMtUo0qBXM40w9+JuMw1qk1s0RcN1/emYr6iTSFzCMXr4" + - "p3KXqSGlAMmKBGfR4hHGTWvykDqMkDo2oAZ/k2w8Kyun5wn3vqSB/ftt5" + - "uc18ng7YtXyDxdHggjMmlB8vQOMgKNDIxXpI8shXlqPyWHG0srQdvcQpK" + - "rS0tH+elC9DnZMtjoqJLJPl7EjFF4uLI+hne9wz1Pbm/XI1khp5CdegkQ" + - "gos9MNTGIb4wk7kcX5hJefbeomWCb8zsaNY6s58pH+Yt7bfet08tZOxb5" + - "SrIqrLocUAfoq0vG4ufoebqmlUtHe7MYqFaDHtVnkvK09vEcJbpCHG+AK" + - "KVIriwSnKaRO+IG1KpyBXpoCFPAnnrbqc52V4/Nl5RKzpobOgbzIMqU2L" + - "2Ni9e5tWQfOx5YzbvW1+Q1Ap1ZYGgTxsgVqdTC+14UR+GqSFWrQ33lmZt" + - "UqIVa+My0qsNcutGKJMKrW8bl6JuG3a4Dqp2pFe2jWN36pEym1SL7m3kC" + - "jadk2ZGwKvPqSX6Iy+jZA0Vw2v215aQOt0uCakhg+6vTPvpz91tCsFFQ0" + - "BRAhWrcGiWNO2iAXmeoVEdN49GXzOViI6Pm/369HDZWaQhct5SIKPgpKh" + - "v+n7PNHP01WgAj/5h81XtvuUCKoYyNveeOUz3BmMsWsRFgq0xRRRsWFBb" + - "oQj0mQboQ4PoQ4X79r0E+w0DqIPybFyRWTdKzT3mwXXPVqh4t3KexE9+T" + - "AoBwn7lLGD3u9f11zeCCwE90hjk9DAcO7v3N9w6lNEo2Oe/xvQ43CQvfL" + - "Zskrys1/uXoDzWBuFZrmATlcGxnmPNQfpetcC3nz4Rf+rMzZ9ZigGBlLn" + - "yAoP7SzQPMy7VNIy0XsxOQfdva0wH/CZUxuD0+jaduLPAxkh/9DTNlOzh" + - "YRvZQS+YuNFCPMNFxOxOWNHLRKvtTN2xO7gLajD+Chkf3V/mbWCZ94XRW" + - "AWwbxgvAqD7KeUuUnxVXKL3zhSmFHwVhH0BuQmAvnjZpcbfrZPNFD1Oz0" + - "rx7IPJtULsWZVKITpJrcKjNOkIJVFzDapU6VDse8ulQnS6DM6Z5qZ/NPO" + - "/DMCpCyf2Tbmfolt1KUpYkCfl7l+p7GeaamKjiGytiLBF6YDxqXgHX52K" + - "d3h8Kp7gN+UKutmLXp9FQoPCjBLSC6rQhuzNoaj50Qk4uAuXcUynQoVJD" + - "rHuW9ilyVF/rN3b2GUORjAzZhHFhxzmib6wlOGOzlUYKceLE01RGzS0fx" + - "PO6FJB1v7ozgs6unnB25yRxMcHKOnRPVDMVm2JoHXMPRTVV3EoRkTGHRU" + - "BBNO6b612zxxmhwKqhtxZtFg0aqUO1KfxvcNIBh+LtJfMA2rPqDbYCTUF" + - "kphZrzNINY4x8G/6B75NisYxN4milcDJ2O9gYAJw4r3XGe/OflFL50ht9" + - "EZQQ9r39obQnboDQq9OwLw5XPLD6NNF4s5FXO2zzoUz2mkVxnjte5GMz1" + - "hg9HbQaEXbOPUn0qqa1OEsdhe5iSI+4mEktTbgc/P5El4qxlzdABeZnKe" + - "MYDiteX++N8eASvpiUs9fyHSV4tzho/Q6OF7/r0qPxnlQWHhkwV1lSbyF" + - "PHXAKFucbzMgjkKYKpaEosDRPkDlgjoz+8+hRDAvsvjIOROpGzxD1m2b9" + - "KhAmAOvR93YEAj3odEUG/OljQ9XBgnb2IWh7c73hCc6DGk3tUtHqFZnA5" + - "Rmn1lSjU6oMtoD5o8vymYONSy6ngX1cuAhzcNTD83sT6pI/rIkSqp5HLS" + - "Ft4h5ZuQTZhszLy/CYXQ6N0m/iAFfisTpJ6ehvAf60R6OZ+WVuQPch5VL" + - "phyasbnkz8wfUgqiHrKbWSpY/vFS6ZfjsLk8mOXaFYnfeXz1q7lFxTC5+" + - "N9t/G7BgtBLtzOWgjQkNeQxLJdmgoQF0txgmIPYY7F5pWg7aUE2nEyLrP" + - "mhpwQpgV3/nWcOUT/U6ipyJrrNBfFEd7eAVmuEqMhqjXCe/EGtO03+kKM" + - "0Nb/3ygCGgDp9l5EcGVmXxK4MjSui46N0DM1f1ea/00lErSPqQVNZFVEz" + - "TeW5pjidClRQaTwy1os8/gfPlX0H/l/9XGlUETfWq4T1PT/Xzo+Hjtc6K" + - "I1xlfyhl0xRhqKLtZPkD2eCNMdn1DHA3cBTlRjd8REUMUUGNcWA0X2AbW" + - "Vfe43woGKNuP5+O4unMT7yZbkBM6S7Gsu6mAo08moZ7rCBhWYCjdwaRpy" + - "aSqCRW8OQ+mqxOmAj15bj33y1WBOwkWvDifOnFGjk1jLc9f8Wmgg0cmsY" + - "/p1XCxUCjdyCIZ3qInG10Ru5IKN8Wiis+U5rTWWFpvJUU6H2emTcejx+1" + - "Qg8I24ERHmRj7E2xiTCU9IzpRoL74G0gronQJpVhPjnPRQs2zTBb7RwF1" + - "x6z0YeZwuE4T8T6n59Mq+wtoK4W2PThSDRQB+8mlGLw2EbQzKQ5XxJ3bP" + - "8zbMe8tHUgVQjYNpY+BbkA5op+mBNdQxgLrr16ZorjEtBWaWBKGVVwvVG" + - "qILH6Nz/ArTavZuA9NsbRSKbPjnxjdvwRKyOsCsZxt3IDK4dYcoQbkVWI" + - "JcJp2asYqtETdIcrfcNJ0l8NwdpbaI2A61N1DQdWRkgK9ZmQxBjo1nCVI" + - "u/KXjOSvSayRj3J7tTQuNOcx8ElYsy0W8spSD9rhamqcdgK4X5bnhLoUV" + - "csVUU2WpHCYPKMZrTzwzt92GKJpByJqdAfnaYQ/L5J6PQQd9qCKGwgsJU" + - "ChIUJsTdPfGBHTtPZRE6mpsALOg6IGZLYFVi0n1UKwB5asmgk08IjA4eM" + - "2BdbgvSb52x49UH5fL0btWucvxTt3fm3NwxMlVeKDoqXwplTrcZiU/b8b" + - "Bq0Xhcre3IGTNCfz1my8hR27EzZoz8OXYALe0H19qOoYKNfDuOH15rO4o" + - "KNnJtOXGyqoCNXFtOGGJrO5AGcOTesWSQre1QGsCRe8uKM6sM2Mi14/iB" + - "trbjqWAj15YjQ21tR1TBRq7JsZ2tXezPeIsdoF6pdJUFaBS7VuVlcXWoy" + - "RxeOvIFHW9o3gZSXUNfoQfTCyaYeB3DoXkSA6cfKT9sOEv7GYyhGw3ou0" + - "AKMkbXUJiAzv0Dfbi5LATDfHt3tdiQOny02ODg8bJCbuHRTawTi46Pi88" + - "1HBsNzhxL3DogNpJnf0X0yjxx4fFo1cIJN178gU5g8WjlI18oNA7dxRof" + - "Z19acLyOkbt8HZs/urQj5cd+ZIVZMiiurJuh2uyZ2bXs0THJmYOPvXfJg" + - "VCvjtSMRXeEmo46QjTXnlZ0PEvJL23ZXxjE7UVZNv06y1UTZ0C0RjeLOF" + - "r0RcQJa57ZMheO223ImjaG9Lm1WczSAWVkxbYCKQM/RydfMMs6aqPBAql" + - "x5wzYqBZChYaGHIjmaYgoOj+A0ovOC2g6ynNUI4giJwQgnOj48KOVreWC" + - "tNewUhL6Cg1y9bVEqaFH9xIxyOsTopOA+u16BekteAXf2kKc3mD7rcRbP" + - "L2lCL7edoX4Z3/KdoZoQ9bPPKH7N/iOzh8gW6PzB5qO8h+hIRij+yjNLb" + - "NonLxVTrTnq90l+2Y53InIrw93NskoTycB0TfuBfRWjubJdzP0BkvnZ55" + - "wqbLCj1bY6+QkCnvjvrXOWBYAN0GnMqSrcvS7iZWzZk5svJbUMOTNaC2p" + - "WQDU+nlt6KCfk9Z3dDBqfQmHpiOrHsYGfRn/b4cLYnzbdq9rA+3DyX4Ku" + - "u+ejZaTuu+wnBIjQfXzeNAOiGBK5Btsnlna22RMHb/f8/+dXCmC6h/wS3" + - "hmLbfw3gfnaE9ODCmBW7Lv9enM0mHeS2Fp7cRB3oUVRc592hRcuk57qT3" + - "oPVUO0I485t1YUWRfxIUh9Cw56VkPSD/rKVP3HVVFBK+mQitQ29c1LVNm" + - "9lNf3OmgG2Zzy8ay/PO6qAhhSpVZQu6Yg5Z1iuZYGcWMpEoN7YcK6DpCR" + - "s7grUP13u30SIUm0D0Mdt8sd9+jx9nmib+bccL9tFPXqaetckOPmmBmwK" + - "s2aN2OGyHK3j9iUdrPNNfEoyKyB0WEebYDxgtEDr5aH3K43j3PkhuPVtB" + - "dtBu8JKD6A5RjdK2WpqP+oAVj3z8MO7v41AQyrD4pMFosUrhsmU4N9nXo" + - "URs5TjgBZosbeDS2oMp2+m7NLEtGpjEspK/mgnU2MH6GTWUHqHF6aZFgg" + - "Fdq4NYZlYl14Ed1F4B6QLO1iB7jlx4KhnYOik3tKg8G+zoH3bKwc6JqQw" + - "/nOsp/h2lzOgeJQd3c0WJS1wrgjeqcFzGjc5HrHTjnJD7EMgmgnGKZKky" + - "OsdQOdIZ4COzxLHflQ3E7baNVs4qAGoVL0vrCtpoAbwSSa/NSh+jnkVaL" + - "MoLDnXqrBUvScPSzSPAw0bC+hK9wTyJZtr60D74yDUfRrBK538I64ikMo" + - "6TlltzZFUlef2Fo9kCXvXJvlQmTBVodcEDQBwyww1R+px4RMbHoUQRj2/" + - "Yhzkx0vduo25xaYNRvlha96jgri497ThaRvtKOgvDYoD0yaL+dmB4x6xL" + - "NxH5CVE1pIss00SkidI8OGPe6Dr7qdR0ed7EEo6xiH7rlzceSKlbd3pxv" + - "mJmvoCJpOihIGjVfwxlwtriGxU/MFC/LKzT4cLwh1INFaqCgl1lBlAhzD" + - "YSgHCzOGkUHV0StvlCj1vZP5jFRqtT8pCnKwsGmTil6dzmsz91ooYU8PZ" + - "KhhukJeaPpaCRDTvW7i3o7ZmmB6MCzAfe9tc+hijHKKcY+nK6WdKYWHq3" + - "oWHRkPdI6MF7lKZNblh/zJDb6KAwdHyilxt6zz48WZmx4o/tLl8ktcxEmk" + - "qc82Ef0f4YhyZBqwDTuwnBZBPKWvfqKbD9UGq96WHRAGBQNEA+JpYXCgG" + - "iAW8OhEUUPhsZlNBQaRA+EBpBhcGYoGQSXjvRDoHEsA6CJTg9/hh0/Mbw" + - "S6HLkfsDbBuPwHvU7NnefeWcyQuaCyPhYGciNjojL2XBnK/sZ7TQRs4c3" + - "K/epFekZ6oq+bhz1K1p4QeTcDT6pVrIwWDwec0d19O4eyi+6E5KudKvUd" + - "NQqIeWw6zcXI6uxtV6/OQW/9ixjzh7zkCdcdBKTZGQk2l+4GIt+T35WNm" + - "lIhXUhJNudC80m9lPXPAduzE6w+4yeWVOYPLM2TU6y1IQWbnRSPVlpHPb" + - "wwAswpp7a89zs0lF+08vcyw394mHL1w4x2M9nzkV4HslzfEjPTzQSXHnK" + - "hNsK9bB+6eGJUXtwd6BxVOqpgf6XmSP3JjTvFDWGzMKTJvCFp5zs3E70o" + - "YXzCddJKZ2bcIHRYLYDzWqjd1RpR3ZJ1rqiB++odo68+bHHvZymbF5RQ8" + - "zcw5Ueb7Q4HYN1GMolWtKpSHu1yhBarTIAn6TQPTqHbaLxkjPXCYjGj1X" + - "UE4uO1+0zC8c9e+mCGNkP5haNR4bSgqO+nU1IrwMiGnsqgs+RMyccFd1B" + - "hlI0ZziuG2TpODfaI0RVFmH2Wx38recOCwdz2UmHQ7YcxS4PW6rVNEwjp" + - "bsTZHH0pqymo+5kmcSvhxYUhtq9tURLkbgLLyPh0B4ZrHlKC90IqsRGHQ" + - "g2ZUsE8zZcXtfRvU6LhLbNUAr04dw5yYdneyQjc5Q1VeB7UHJqNyNH2/J" + - "aOpjyklbbvhXJ0fvcGbGr17nz5BytCa5IjzTzBUPvmaYoRcvkHC0frhQd" + - "nUmegHF+7bqdvuf8vOZBZxP0V6qXc34Y5ZRab6C2IzJoxgYM+ilIe1kn5" + - "s1nbZUPhiyDFfjG6Mu3DdBXnMPqV4mMeNDPW6IqGiBe30eVNOjYQp7F+3" + - "D1OGTDPLLw1Wl7eDEXjybnsFiWWyK+q6VKgUZWCZRVnX+CLnCOVsYaQ8s" + - "CGmTQBw6mqAjdrccG5nSoLimfkxw941ASu3Hp6zzzjPHFAZMFOVcPP1QG" + - "DQfcTcC3bjjAAOI5V0E3ZO35cO9ZvSs8U+hI/KlhxbV7VlvwRtRT4VxF3" + - "ZJ1fRtChaKJ7sUpFR01CjrcdS9bngvNeGZNSK9TmDh2PSft3WbQd7BNPO" + - "OPjksHgcGkK4XTkLeUY8MQRXdpKFEtKUpY2aFTqpZ8KO1sXx1lhp3DhXO" + - "KDBfOGTBcOGfIk66GDZpi97UPM+pZY4Fo6kUwOuJQkPa9oiF0t+iA0C8a" + - "IPQ7+cTQI/uXBUEuNT1jpBndwViPeNFFjJVm+tX+KLSrKxlRH3QvkzWGH" + - "lXTuQGv2ox1O66+jA99Qfdnfzqb+zdyCzzyMGLGd+VA2ieCavtpTnqk9n" + - "tkxE/U7KxfzWZnwhlNaIUxnr42yXiX3uSNgUYzU+P0GM+WFoLJPGgSIKm" + - "tTB60SqOvhLs2UybEHQ9Z8vPFnCYRdkaMVmOTVZtYb+r8SOUgASYWGMKB" + - "ktoi6ogJS9Ye2tF302eCnsx7cpzrhens4gY3TDENGyXDeXhuP4NXB6i5+" + - "MwiIQczDdyaj7vw/YzcBaAWr50DPUufeSjM0x0Uz9RzD4a5uoNudUhOVD" + - "1fd66jGbvDbh0SLy1LT+eda+nnnJMwpZ8L4Cf1zotb7TNHUdoY4t2aJ7N" + - "B7RjSU7o06MPkLjg/Tyeprr9E1Y3u5kKdje7m0nQ0dhgGmtFVI514xqiN" + - "enzcRLNkPDmoHDJqoHQoz7yFR7Wcoj+xkLNdyR01RORmuNzvnJPSeeARE" + - "RajXVazUDSDmFrQz+Yciozv9506PEShedIxDBulQ+LBxKAv0YtmlERd/e" + - "BOlFDm6FrxCsqtNmApQUerJJBUvwfNNhFdVYX+IrqqStNR2TIgxIPs//N" + - "Mc9qnrbUca4uIIXdGs0FaXLktPRac1R7a9xsHVQZ67M29Ms3SUGbZjxNV" + - "Enw8GB2o8WrutbDShd01hkAzRn+/8ATZwmlgj45m22GCfUSf0Jkb5GieP" + - "f0uV7YCl991ok8Uz266sqZMOR+I/i5bImq/70bHhC4CqrWMGwjZHWv3o0" + - "uTnGWRB6mn/ZA1803ZqXnSW+zOFeRNdhGC3Efo18SR5cd+/bRBsHziwRC" + - "7R16aPrXEkTtAzdwSPMRPa1jagPLZWr4013NO5D7DRCoCwlTKwWEyRSCa" + - "NBjAGHZSceNnmmlCc7J7RYRVdAeMN1gcfLXB4vB4g4XgNrrIDrmnVzPQc" + - "vUEe7Yi7W/BMIS+lccB4coOAvoE9czQ8RyQ88vrKU3DJn41u2jYEcQa7M" + - "QAXoW1lNZhPRKUWCLeOKtG5NHNYKgP0c1gmo46FlSPy/g2D47Sl/F1Hos" + - "rMDoZjSx67XZflZ7ROEQGWu8kaGm5Q2SwNH4O57ewNZw7RDSGIp9OHSYa" + - "YOUBCZkB8WauPONH0D8MqbSjmnSQOQ3kLc3IhOr1IuN1dLNO4bDvIboPm" + - "ZCjdajaAkGDMkCsP2UWCtqTAW7pTiYpWnMyLiO9ySC3tCYjtNaZjEspSM" + - "MO+tLMkV5bMo6lSI0c8m5OY7JQK0PGtVeFHNEfN0bRnCa8RhnxXeR2tXl" + - "yMes5GaK9KLM/UuqylxqkuxqtXCYXubwMIYaFFUeEy8saDchKS5VEz4Hm" + - "yWWzDt1HkYIOt41VlpSzIZDd2yFCRH3b2CKQ3jMmxIJJ9HnAJBlzhQXRV" + - "mmAnQDpUkUjdxItS4DqpjAIKTeUQUptJmnI8C4xSH3tD8LR14lBd7i4C8" + - "qaif30V860M0uraCmuvqCsbSwdhbi0mFxQtgIdX1DGHNeQzhDk3ZUdMmT" + - "UtxSVye3lYXjVt1Ogz7+EO8yQqZKZ6Ogu148YrzyoluQq43J08xOkj1RG" + - "lAVX4PytQcVK0eYS7QlTIJD2m2u3uqvJFe4vJ6Jb9xTxnJ/s7cyy9QQlJ" + - "xdaMRt8u2eRvsgLPCTQiqMtbzQonsg2158tCk/ox4ebMeh1SBO44fgLHz" + - "APc4jcn4bK8DI2xPeYO0kBEaL8ZQKsdT0v37+Mn8qGwnc1/E2L5Gr0m4+" + - "xaPBD3UAPtzZW8GrldBXgq1czG5S7f5KY/qP7rCoPSCeA6HVvh6yRboXf" + - "usVaOjRZ0le1LgN4y+45wr3FcwRqW2cwbgWSJtdhaEwHkSZf2cWXyVfZS" + - "yvwrbfSLB0MlEjrW4or0NwsWJIRtgdyRZbFCAhLkgYMS5KWNKe4oAE3Qg" + - "Wt2GDaz2pC5G0IL7uhZ/sahhkEqXo9qEHRS88YW78q3XI+JTlSLRtiV5r" + - "lguhYsVwC1JkzA23ejeDuiu8TzAg6qRYCcBKrngabLCOOPo8yizjhjaI4" + - "LAfWAKPbb9vkq5/LIE16WWMFt2iC+uEkNHcL+TrkaV1/iJ3WR31XPObpD" + - "vNNRADdTgBGHS+qoJ6rVxDImJjefGe8HTN1UjxTG602yf9isEoPOoB58l" + - "U6XVQlP/hVSGxQ+ZHjeiyeoeLogW01TV5ZyFXy6rsVJPl1re4snYHUhzd" + - "WoPXhDU1H8i7IkGBqUOM+tG49qAMkeFZ2uAWF+2ou1uMEncF+fbs9hCE1" + - "69ewU8g4R89ImtBfw0uUYTV9GjNib3WZvKpnhpbJa2i5pSXETB3d8Ksaz" + - "2uSaosN85BX1dKhO73q3axZChq+OSbwFuo0RSqixkoHIV+Rnk7dmwrJvK" + - "ZUwyFNFvTFkAaQRwox0CrAzWWAL2cOh07VHeOFmEn7HZ4qB2i/1278Cst" + - "k9T2mDmFqHaHb2huT/GJRRYi7NJzn4LjlZSqRclw7x8PrwV+kY5yEk3g8" + - "kn7lRrOXls2kfS+IRX7tRrNTz+b94ryja7SmVX6HL4tRLs2G/m46Zjcca" + - "b4LxPjzb+PxRl2H9jTYCAZcFhVnLgmnMw0Yy4mTWG0/lr48/7fFu/r7Ti" + - "StLhnQF7+X0GLsQjNRFHpBfDYBrVuNoaWZQOaoW0ce6SXXWQZa+9Z0pNQ" + - "hQwbzMMmMH5HdC1noSf1GUIY4pL9GeEbfTLmF/KrPysFV6L1RB98OZqK0" + - "Sjj3xHDzpxqB82Xypza3zpJgT4lZ1p+6F4LTqBdqkj+jEx3QCf7kBUpNm" + - "0SWjui4xawRmfynkrXNEz4EBD30bb3ehA572ib6tnRouG8yM18mcnF6Rl" + - "z1ZFkSXaNuvOmlLNJ68JiC1uOGpqOByDAkmhTUfs3h1e+6UtyroSn3oI7" + - "iCozqwgJcrdqXcB7Ko7ZEGCaq5E3P9JG8qIAsLdPgInlTCuB0TtLcCB+G" + - "sGUWwFg3ZF6Od4pXxvWtkbCMGaORcB5zxzvNqFgRf7TlDIXk7Xp7GlPwt" + - "6vdaegmb7eNKzD+vn3HuALV9e2WccXMBGa3LIezXTcJGYc6oSoi029MU5" + - "nncZsmokZbQ16dDq8ZwHG9RRN4Q9sMJhbzCI8fxjI8fXHZlBl5vLmCgwY" + - "HKDYETAUbH7VnVXasGGcFOPdhijKDDF55YIm4bYpmaj/9agumUm+91oGR" + - "C1rwgvxgdIhY+sMb+mmMFWzD8eYYhYi6G6RtMA9mm48wT1NkmJYZMEzLDB" + - "lNsTKH6PsyVk0KMaID4ag0QxC5Zji62deKjnqWkgypDSiwqzuvoe29XV16" + - "3V6BUT+C/sg8VmLPJ6AgBt1PGmFVh2ZieJNttIxJfgtv72KWJkvgLMmX4a" + - "lDIe9ZAryXaR5D+oJRlCtt4uZIpR+skDN6sIIoftrBShkGLiQhOvGNIC4q" + - "g9EJRAfAS0VHGVyQIVVpAup03z/pPrZxWD+c+8c+ejQDQxp4u/4MPUTDVY" + - "Bv+ZqRPS7GwoNa7CswKkbGrroVdowX3XuwJ9Xj5HJF2i8Yr5JvHFvnyTd9" + - "WA36xjdZRCbPO2/wrS8cIK2MOmuSI6NOBnVt1FkZNBh1Gldjo04G16szXJ" + - "mhR0e4JgC1jSdD+qN7xIRbHVhFCRs0visQvfW39fEPtSnPGN/M2adlaT9D" + - "1xABoXNwcOgeAGhtCSn1S+VVi28ZqWeWcCM1an0KwBp+8tO+sV4tzJcYVj" + - "raj9ezPPkWLeAgtpuWk2hS37pbJ6NRAaITtgg/OmFL+mh2rybmK2z/WFrt" + - "X5UG8FtSltJ7Sh4Jm0oWiXeVbLB6s8gi0W6RhfSukEXUzo8F9HkXi/jtHU" + - "uZZvT7wLfOqAusAngYDg7PJpNFwK0MwFD3ndEakhGdR0ShbDvdnOYEzKK/" + - "vko+I6oLj+HcLr3KcG4U3zL5Fh0rQwWOjpWRPgzqPnBUQW0lwoYRDYwQNT" + - "oRA/fRiRjQ0s/D79gsABOib2GDDQmK7OEReGQPP0/+7a59v0z+H+SUGTTsMAEA"; - - byte[] compressedBytes = Convert.FromBase64String(base64String); - byte[] decompressedBytes = DecompressGzip(compressedBytes); - string decompressedString = Encoding.UTF8.GetString(decompressedBytes); - //string[] lines = decompressedString.Split(Environment.NewLine); - string[] lines = decompressedString.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); - - return lines; - } - - private static byte[] DecompressGzip(byte[] compressedBytes) - { - using (MemoryStream compressedStream = new MemoryStream(compressedBytes)) - { - using (MemoryStream decompressedStream = new MemoryStream()) - { - using ( - GZipStream gzipStream = new GZipStream( - compressedStream, - CompressionMode.Decompress - ) - ) - { - gzipStream.CopyTo(decompressedStream); - } - return decompressedStream.ToArray(); - } - } - } - - public static string Unicode2GlyphName(int ch) - { - if (Utils.AdobeGlyphs.Count == 0) - { - string[] lines = Utils.GetGlyphText(); - foreach (string line in lines) - { - if (line.StartsWith("#")) - continue; - string[] items = line.Split(';'); - if (items.Length != 2) - continue; - - int c = Convert.ToInt32(items[1].Substring(0, 4), 16); - AdobeGlyphs.Add(c, items[0]); - } - } - //return AdobeGlyphs.GetValueOrDefault(ch, ".notdef"); - return AdobeGlyphs.ContainsKey(ch) ? AdobeGlyphs[ch] : ".notdef"; - } - - internal static FzMatrix ShowStringCS( - FzText text, - Font userFont, - FzMatrix trm, - string s, - int wmode, - int bidi_level, - fz_bidi_direction markupDir, - fz_text_language langauge - ) - { - int i = 0; - while (i < s.Length) - { - ll_fz_chartorune_outparams outparams = new ll_fz_chartorune_outparams(); - int l = mupdf.mupdf.ll_fz_chartorune_outparams_fn(s.Substring(i), outparams); - i += l; - float adv; - FzFont font; - int gid = mupdf.mupdf.fz_encode_character_sc(userFont.ToFzFont(), outparams.rune); - if (gid == 0) - { - using (FzFont _font = new FzFont()) - { - int _gid = userFont.ToFzFont().fz_encode_character_with_fallback(outparams.rune, 0, (int)langauge, _font); - mupdf.mupdf.fz_show_glyph( - text, - _font, - trm, - _gid, - outparams.rune, - wmode, - bidi_level, - markupDir, - langauge - ); - adv = mupdf.mupdf.fz_advance_glyph(_font, _gid, wmode); - } - /* - (gid, font) = userFont - .ToFzFont() - .fz_encode_character_with_fallback(outparams.rune, 0, (int)langauge); - mupdf.mupdf.fz_show_glyph( - text, - font, - trm, - gid, - outparams.rune, - wmode, - bidi_level, - markupDir, - langauge - ); - adv = mupdf.mupdf.fz_advance_glyph(font, gid, wmode); - */ - } - else - { - font = userFont.ToFzFont(); - mupdf.mupdf.fz_show_glyph( - text, - font, - trm, - gid, - outparams.rune, - wmode, - bidi_level, - markupDir, - langauge - ); - adv = mupdf.mupdf.fz_advance_glyph(font, gid, wmode); - } - - if (wmode == 0) - trm = trm.fz_pre_translate(adv, 0); - else - trm = trm.fz_pre_translate(0, -adv); - } - - return trm; - } - - internal static int CheckQuad(LineartDevice dev) - { - // Check whether the last 4 lines represent a quad. - // Because of how we count, the lines are a polyline already, i.e.last point - // of a line equals 1st point of next line. - // So we check for a polygon (last line's end point equals start point). - // If not true we return 0. - List items = dev.PathDict.Items; - int len = items.Count; - float[] f = new float[8] { 0, 0, 0, 0, 0, 0, 0, 0 }; // coordinates of the 4 corners - - // fill the 8 floats in f, start from items[-4:] - Point lp = new Point(); - for (int i = 0; i < 4; i++) - { - Item line = items[len - 4 + i]; - Point tmp = line.P1; - f[i * 2] = tmp.X; - f[i * 2 + 1] = tmp.Y; - lp = line.LastPoint; - } - - if (lp.X != f[0] || lp.Y != f[1]) - { - // not a polygon! - return 0; - } - - // we have detected a quad - dev.LineCount = 0; - FzQuad q = mupdf.mupdf.fz_make_quad(f[0], f[1], f[6], f[7], f[2], f[3], f[4], f[5]); - Item rect = new Item() { Type = "qu", Quad = new Quad(q) }; - - items[len - 4] = rect; - for (int i = len - 3; i < len; i++) - items.RemoveAt(len - 3); - return 1; - } - - internal static int CheckRect(LineartDevice dev) - { - dev.LineCount = 0; - int orientation = 0; - List items = dev.PathDict.Items; - int len = items.Count; - - Item line0 = items[len - 3]; - Point ll = line0.P1; - Point lr = line0.LastPoint; - - Item line2 = items[len - 1]; - Point ur = line2.P1; - Point ul = line2.LastPoint; - - if (ll.Y != lr.Y || ll.X != ul.X || ur.Y != ul.Y || ur.X != lr.X) - return 0; - - FzRect r; - if (ul.Y < lr.Y) - { - r = mupdf.mupdf.fz_make_rect(ul.X, ul.Y, lr.X, lr.Y); - orientation = 1; - } - else - { - r = mupdf.mupdf.fz_make_rect(ll.X, ll.Y, ur.X, ur.Y); - orientation = -1; - } - - Item rect = new Item() - { - Type = "re", - Rect = new Rect(r), - Orientation = orientation - }; - - items[len - 3] = rect; - for (int i = len - 2; i < len; i++) - { - items.RemoveAt(len - 2); - } - - return 1; - } - - internal static FzRect ComputerScissor(LineartDevice dev) - { - if (dev.Scissors == null) - dev.Scissors = new List(); - int numScissors = dev.Scissors.Count; - FzRect scissor; - if (numScissors > 0) - { - FzRect lastScissor = dev.Scissors[numScissors - 1]; - scissor = lastScissor; - scissor = FzRect.fz_intersect_rect(scissor, dev.PathRect); - } - else - { - scissor = dev.PathRect; - } - dev.Scissors.Add(scissor); - - return scissor; - } - - internal static List GetOutlineXrefs(PdfObj obj, List xrefs) - { - if (obj.m_internal == null) - return xrefs; - PdfObj thisobj = obj; - while (thisobj.m_internal != null) - { - int newXref = thisobj.pdf_to_num(); - if ( - xrefs.Contains(newXref) - || thisobj.pdf_dict_get(new PdfObj("Type")).m_internal != null - ) - break; - xrefs.Add(newXref); - PdfObj first = thisobj.pdf_dict_get(new PdfObj("First")); - if (first.pdf_is_dict() != 0) - { - xrefs = Utils.GetOutlineXrefs(first, xrefs); - } - - thisobj = thisobj.pdf_dict_get(new PdfObj("Next")); - PdfObj parent = thisobj.pdf_dict_get(new PdfObj("Parent")); - if (thisobj.pdf_is_dict() == 0) - thisobj = parent; - } - return xrefs; - } - - internal static void GetPageLabels(List<(int, string)> list, PdfObj nums) - { - int n = nums.pdf_array_len(); - for (int i = 0; i < n; i += 2) - { - PdfObj key = nums.pdf_array_get(i).pdf_resolve_indirect(); - int pno = key.pdf_to_int(); - PdfObj val = nums.pdf_array_get(i + 1).pdf_resolve_indirect(); - FzBuffer res = Utils.Object2Buffer(val, 1, 0); - //byte[] c = res.fz_buffer_extract(); - byte[] c = Utils.BinFromBuffer(res); - - string cStr = Encoding.UTF8.GetString(c); - list.Add((pno, cStr)); - } - } - - internal static void RemoveDestRange(PdfDocument pdf, List numbers) - { - int pageCount = pdf.pdf_count_pages(); - for (int i = 0; i < pageCount; i++) - { - int n1 = i; - if (numbers.Contains(n1)) - continue; - - PdfObj pageRef = pdf.pdf_lookup_page_obj(i); - PdfObj annots = pageRef.pdf_dict_get(new PdfObj("Annots")); - if (annots.m_internal == null) - continue; - - int len = annots.pdf_array_len(); - for (int j = len - 1; j > -1; j -= 1) - { - PdfObj o = annots.pdf_array_get(j); - if ( - mupdf.mupdf.pdf_name_eq( - o.pdf_dict_get(new PdfObj("Subtype")), - new PdfObj("Link") - ) == 0 - ) - continue; - PdfObj action = o.pdf_dict_get(new PdfObj("A")); - PdfObj dest = o.pdf_dict_get(new PdfObj("Dest")); - if (action.m_internal != null) - { - if ( - mupdf.mupdf.pdf_name_eq( - action.pdf_dict_get(new PdfObj("S")), - new PdfObj("GoTo") - ) == 0 - ) - continue; - dest = action.pdf_dict_get(new PdfObj("D")); - } - - int pno = -1; - if (dest.pdf_is_array() != 0) - { - PdfObj target = dest.pdf_array_get(0); - pno = pdf.pdf_lookup_page_number(target); - } - else if (dest.pdf_is_string() != 0) - { - FzLocation location = pdf.super() - .fz_resolve_link(dest.pdf_to_text_string(), null, null); - pno = location.page; - } - if (pno < 0) - continue; - n1 = pno; - if (numbers.Contains(n1)) - annots.pdf_array_delete(j); - } - } - } - - public static PdfPage AsPdfPage(dynamic page) - { - if (page is Page) - return (page as Page).GetPdfPage(); - if (page is PdfPage) - return page; - else if (page is FzPage) - return (page as FzPage).pdf_page_from_fz_page(); - else if (page == null) - throw new Exception("Page is none"); - return null; - } - - internal static PdfObj PdfObjFromStr(PdfDocument doc, string src) - { - byte[] bSrc = Encoding.UTF8.GetBytes(src); - - FzBuffer buffer_ = Utils.fz_new_buffer_from_data(bSrc); - FzStream stream = buffer_.fz_open_buffer(); - PdfLexbuf lexBuf = new PdfLexbuf(256); - PdfObj ret = doc.pdf_parse_stm_obj(stream, lexBuf); // issue - - return ret; - } - - public static string GetPdfNow() - { - DateTimeOffset dto = DateTimeOffset.Now; - int offsetHours = dto.Offset.Hours; - int offsetMinutes = dto.Offset.Minutes; - - string offset = string.Format( - "{0:00}'{1:00}'", - Math.Abs(offsetHours), - Math.Abs(offsetMinutes) - ); - string timestamp = dto.ToString("D:yyyyMMddHHmmss"); - - if (dto.Offset > TimeSpan.Zero) - { - timestamp += "-" + offset; - } - else if (dto.Offset < TimeSpan.Zero) - { - timestamp += "+" + offset; - } - - return timestamp; - } - - private static string MakeUtf16Be(string s) - { - byte[] bytes = Encoding.BigEndianUnicode.GetBytes(s); - byte[] resBytes = new byte[bytes.Length + 2]; - resBytes[0] = 254; - resBytes[1] = 255; - Array.Copy(bytes, 0, resBytes, 2, bytes.Length); - return "<" + BitConverter.ToString(resBytes).Replace("-", string.Empty) + ">"; - } - - internal static int Find(byte[] haystack, byte[] needle) - { - for (var i = 0; i < haystack.Length - needle.Length + 1; i++) - { - if (haystack[i] == needle[0]) - { - var fail = false; - for (var j = 1; j < needle.Length; j++) - { - if (haystack[i + j] != needle[j]) - { - fail = true; - break; - } - } - if (!fail) - return i; - } - } - return -1; // if not found - } - - internal static void EnsureOperations(PdfDocument pdf) - { - if (!HaveOperations(pdf)) - throw new Exception("No journalling operation started"); - } - - /// - /// Ensure valid journalling state - /// - /// - /// - internal static bool HaveOperations(PdfDocument pdf) - { - if (pdf.m_internal.journal != null && string.IsNullOrEmpty(pdf.pdf_undoredo_step(0))) - return false; - return true; - } - - internal static PdfObj GetXObjectFromPage( - PdfDocument pdfOut, - PdfPage pdfPage, - int xref, - GraftMap gmap - ) - { - PdfObj xobj, - resources; - if (xref > 0) - xobj = pdfOut.pdf_new_indirect(xref, 0); - else - { - PdfPage srcPage = pdfPage; - PdfObj srcPageRef = srcPage.obj(); - FzRect mediaBox = srcPageRef - .pdf_dict_get_inheritable(new PdfObj("MediaBox")) - .pdf_to_rect(); - PdfObj o = srcPageRef.pdf_dict_get_inheritable(new PdfObj("Resources")); - if (gmap.ToPdfGraftMap().m_internal != null) - { - resources = gmap.ToPdfGraftMap().pdf_graft_mapped_object(o); - } - else - { - resources = pdfOut.pdf_graft_object(o); - } - FzBuffer res = Utils.ReadContents(srcPageRef); - - xobj = pdfOut.pdf_new_xobject(mediaBox, new FzMatrix(), new PdfObj(0), res); - Utils.UpdateStream(pdfOut, xobj, res, 1); - - xobj.pdf_dict_put(new PdfObj("Resources"), resources); - } - return xobj; - } - - /// - /// Read and concatenate a PDF page's /Conents object(s) in a buffer - /// - /// - /// - internal static FzBuffer ReadContents(PdfObj pageRef) - { - PdfObj contents = pageRef.pdf_dict_get(new PdfObj("Contents")); - FzBuffer res = null; - if (contents.pdf_is_array() != 0) - { - res = new FzBuffer(1024); - for (int i = 0; i < contents.pdf_array_len(); i++) - { - if (i > 0) - res.fz_append_byte(32); - PdfObj obj = contents.pdf_array_get(i); - if (obj.pdf_is_stream() != 0) - { - FzBuffer nres = obj.pdf_load_stream(); - res.fz_append_buffer(nres); - } - } - } - else if (contents.m_internal != null) - res = contents.pdf_load_stream(); - else - res = null; - - return res; - } - - /// - /// Add OC object reference to a dictionary - /// - /// - /// - /// - /// - internal static void AddOcObject(PdfDocument pdf, PdfObj _ref, int xref) - { - PdfObj indObj = pdf.pdf_new_indirect(xref, 0); - if (indObj.pdf_is_dict() == 0) - throw new Exception(ErrorMessages["MSG_BAD_OC_REF"]); - PdfObj type = indObj.pdf_dict_get(new PdfObj("Type")); - if (type.pdf_objcmp(new PdfObj("OCG")) == 0 || type.pdf_objcmp(new PdfObj("OCMD")) == 0) - { - _ref.pdf_dict_put(new PdfObj("OC"), indObj); - } - else - throw new Exception(ErrorMessages["MSG_BAD_OC_REF"]); - } - - internal static bool IsJbig2Image(PdfObj obj) - { - return false; - } - - internal static List GetOcgArraysImp(PdfObj arr) - { - List list = new List(); - if (arr.pdf_is_array() != 0) - { - int n = arr.pdf_array_len(); - for (int i = 0; i < n; i++) - { - PdfObj obj = arr.pdf_array_get(i); - int item = obj.pdf_to_num(); - if (!list.Contains(item)) - list.Add(item); - } - } - return list; - } - - internal static void SetOcgArraysImp(PdfObj arr, List list) - { - PdfDocument pdf = mupdf.mupdf.pdf_get_bound_document(arr); - foreach (int xref in list) - { - PdfObj obj = pdf.pdf_new_indirect(xref, 0); - arr.pdf_array_push(obj); - } - } - - public static FzMatrix CalcImageMatrix( - int width, - int height, - Rect tr, - float rotate, - bool keep - ) - { - FzMatrix rot = mupdf.mupdf.fz_rotate(rotate); - float trw = tr.X1 - tr.X0; - float trh = tr.Y1 - tr.Y0; - float w = trw; - float h = trh; - float fw; - float fh; - if (keep) - { - float large = Math.Max(width, height); - fw = width / large; - fh = height / large; - } - else - fw = fh = 1; - float small = Math.Min(fw, fh); - if (rotate != 0 && rotate != 180) - { - float f = fw; - fw = fh; - fh = f; - } - if (fw < 1) - { - if (trw / fw > trh / fh) - { - w = trh * small; - h = trh; - } - else - { - w = trw; - h = trw / small; - } - } - else if (fw != fh) - { - if (trw / fw > trh / fh) - { - w = trh / small; - h = trh; - } - else - { - w = trw; - h = trw * small; - } - } - else - { - w = trw; - h = trh; - } - FzPoint tmp = mupdf.mupdf.fz_make_point((tr.X0 + tr.X1) / 2, (tr.Y0 + tr.Y1) / 2); - FzMatrix mat = mupdf.mupdf.fz_make_matrix(1, 0, 0, 1, -0.5f, -0.5f); - mat = FzMatrix.fz_concat(mat, rot); - mat = FzMatrix.fz_concat(mat, mupdf.mupdf.fz_scale(w, h)); - mat = FzMatrix.fz_concat(mat, mupdf.mupdf.fz_translate(tmp.x, tmp.y)); - return mat; - } - - public static string GetPageLabel(int pno, List<(int, string)> labels) - { - List<(int, string)> items = new List<(int, string)>(); - foreach ((int, string) label in labels) - { - if (label.Item1 <= pno) - items.Add(label); - } - - Label rule = Utils.RuleDict(items.Last()); - string prefix = rule.Prefix; - string style = rule.Style; - int delta = (style == "a" || style == "A") ? -1 : 0; - int pageNumber = pno - rule.StartPage + rule.FirstPageNum + delta; - return Utils.ConstructLabel(style, prefix, pageNumber); - } - - public static Label RuleDict((int, string) item) - { - string rule = item.Item2; - string[] rules = rule.Substring(2, rule.Length - 2 - 2).Split('/').Skip(1).ToArray(); - Label ret = new Label() - { - StartPage = item.Item1, - Prefix = "", - FirstPageNum = 1 - }; - bool skip = false; - int i = -1; - - foreach (string s in rules) - { - i++; - if (skip) - { - skip = false; - continue; - } - if (s == "S") - { - ret.Style = rules[i + 1]; - skip = true; - continue; - } - if (s.StartsWith("P")) - { - string x = s.Substring(1).Replace("(", "").Replace(")", ""); - ret.Prefix = x; - continue; - } - if (s.StartsWith("St")) - { - int x = Convert.ToInt32(s.Substring(2)); - ret.FirstPageNum = x; - } - } - - return ret; - } - - public static string ConstructLabel(string style, string prefix, int pno) - { - string nStr = ""; - if (style == "D") - nStr = Convert.ToString(pno); - else if (style == "r") - nStr = Utils.Integer2Roman(pno).ToLower(); - else if (style == "R") - nStr = Utils.Integer2Roman(pno).ToUpper(); - else if (style == "a") - nStr = Utils.Integer2Letter(pno).ToLower(); - else if (style == "A") - nStr = Utils.Integer2Letter(pno).ToUpper(); - string ret = prefix + nStr; - return ret; - } - - public static string Integer2Letter(int i) - { - string asciiUppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - int n = 1; - int a = i; - while (Math.Pow(26, n) <= a) - { - a -= Convert.ToInt32(Math.Pow(26, n)); - n += 1; - } - string ret = ""; - for (int j = n - 1; j >= 0; j--) - { - int g = a % Convert.ToInt32(Math.Pow(26, j)); - int f = a / Convert.ToInt32(Math.Pow(26, j)); - ret += asciiUppercase[f]; - } - return ret; - } - - public static string Integer2Roman(int num) - { - Dictionary roman = new Dictionary() - { - { 1000, "M" }, - { 900, "CM" }, - { 500, "D" }, - { 400, "CD" }, - { 100, "C" }, - { 90, "XC" }, - { 50, "L" }, - { 40, "XL" }, - { 10, "X" }, - { 9, "IX" }, - { 5, "V" }, - { 4, "IV" }, - { 1, "I" }, - }; - - IEnumerable RomanNum(int _num) - { - //foreach ((int r, string ltr) in roman) - foreach (var pair in roman) - { - int r = pair.Key; - string ltr = pair.Value; - int x = _num / r; - yield return string.Concat(Enumerable.Repeat(ltr, x)); - _num -= r * x; - if (_num <= 0) - break; - } - } - - return string.Concat(RomanNum(num).ToArray()); - } - - public static string GetTextbox(Page page, Rect rect, TextPage textPage = null) - { - TextPage tp = textPage; - if (tp == null) - tp = page.GetTextPage(); - else if (tp.Parent != page) - throw new Exception("not a textpage of this page"); - string ret = tp.ExtractTextBox(rect.ToFzRect()); - if (textPage == null) - tp = null; - return ret; - } - - public static string GetTextSelection( - Page page, - Point p1, - Point p2, - Rect clip = null, - TextPage textPage = null - ) - { - TextPage tp = textPage; - if (tp == null) - tp = page.GetTextPage(clip, flags: (int)TextFlags.TEXT_DEHYPHENATE); - else if (tp.Parent != page) - throw new Exception("not a textpage of this page"); - string ret = tp.ExtractSelection(p1, p2); - if (textPage == null) - tp = null; - return ret; - } - - public static void UpdateLink(Page page, LinkInfo link) - { - string annot = GetLinkText(page, link); - if (annot == "") - throw new Exception("link kind not supported"); - page.Parent.UpdateObject(link.Xref, annot, page.GetPdfPage()); - return; - } - - public static void WriteText( - Page page, - Rect rect = null, - TextWriter[] writers = null, - bool overlay = true, - float[] color = null, - float opacity = 0, - bool keepProportion = true, - int rotate = 0, - int oc = 0 - ) - { - if (writers == null) - throw new Exception("need at least one TextWriter"); - if (writers.Length == 1 && rotate == 0 && rect == null) - { - writers[0] - .WriteText(page, opacity: opacity, color: color, overlay: overlay ? 1 : 0); - return; - } - Rect clip = writers[0].TextRect; - Document textDoc = new Document(); - Page tpage = textDoc.NewPage(width: page.Rect.Width, height: page.Rect.Height); - foreach (TextWriter writer in writers) - { - clip = clip | writer.TextRect; - writer.WriteText(tpage, opacity: opacity, color: color); - } - - if (rect == null) - rect = clip; - page.ShowPdfPage( - rect, - textDoc, - 0, - overlay: overlay, - keepProportion: keepProportion, - rotate: rotate, - clip: clip, - oc: oc - ); - textDoc = null; - tpage = null; - } - - /// - /// Merge the /Resources object created by a text pdf device into the page. - /// The device may have created multiple /ExtGState/Alp? and /Font/F? objects. - /// These need to be renamed (renumbered) to not overwrite existing page - /// objects from previous executions. - /// - /// - /// the next available numbers n, m for objects /Alp, /F - internal static (int, int) MergeResources(PdfPage page, PdfObj res) - { - // page objects /Resources, /Resources/ExtGState, /Resources/Font - PdfObj resources = page.obj().pdf_dict_get(new PdfObj("Resources")); - if (resources.m_internal == null) - { - resources = mupdf.mupdf.pdf_dict_put_dict(page.obj(), new PdfObj("Resources"), 5); - } - PdfObj mainExtg = resources.pdf_dict_get(new PdfObj("ExtGState")); - PdfObj mainFonts = resources.pdf_dict_get(new PdfObj("Font")); - - // text pdf device objects /ExtGState, /Font - PdfObj tmpExtg = res.pdf_dict_get(new PdfObj("ExtGState")); - PdfObj tmpFonts = res.pdf_dict_get(new PdfObj("Font")); - - int maxAlp = -1; - int maxFonts = -1; - int n = 0; - - // Handle /Alp objects - if (tmpExtg.pdf_is_dict() != 0) // any created at all? - { - n = tmpExtg.pdf_dict_len(); // does page have /ExtGState yet? - if (mainExtg.pdf_is_dict() != 0) - { - for (int i = 0; i < mainExtg.pdf_dict_len(); i++) - { - // get highest number of objects named /Alpxxx - string alp = mainExtg.pdf_dict_get_key(i).pdf_to_name(); - if (!alp.StartsWith("Alp")) - continue; - int j = mupdf.mupdf.fz_atoi(alp.Substring(3)); - if (j > maxAlp) - maxAlp = j; - } - } - else // create a /ExtGState for the page - mainExtg = resources.pdf_dict_put_dict(new PdfObj("ExtGState"), n); - - maxAlp += 1; - for (int i = 0; i < n; i++) // copy over renumbered /Alp objects - { - string alp = tmpExtg.pdf_dict_get_key(i).pdf_to_name(); - int j = mupdf.mupdf.fz_atoi(alp.Substring(3)) + maxAlp; - string text = $"Alp{j}"; - PdfObj val = tmpExtg.pdf_dict_get_val(i); - mainExtg.pdf_dict_puts(text, val); - } - } - if (mainFonts.pdf_is_dict() != 0) // has page any fonts yet? - { - for (int i = 0; i < mainFonts.pdf_dict_len(); i++) // get max font number - { - string font = mainFonts.pdf_dict_get_key(i).pdf_to_name(); - if (!font.StartsWith("F")) - continue; - int j = mupdf.mupdf.fz_atoi(font.Substring(1)); - if (j > maxFonts) - maxFonts = j; - } - } - else // create a Resources/Font for the page - mainFonts = resources.pdf_dict_put_dict(new PdfObj("Font"), 2); - - maxFonts += 1; - for (int i = 0; i < tmpFonts.pdf_dict_len(); i++) // copy renumbered fonts - { - string font = tmpFonts.pdf_dict_get_key(i).pdf_to_name(); - int j = mupdf.mupdf.fz_atoi(font.Substring(1)) + maxFonts; - string text = $"F{j}"; - PdfObj val = tmpFonts.pdf_dict_get_val(i); - mainFonts.pdf_dict_puts(text, val); - } - return (maxAlp, maxFonts); - } - - public static void RepairMonoFont(Page page, Font font) - { - if (font.Flags["mono"] == 0) - return; - Document doc = page.Parent; - List fonts = page.GetFonts(); - List xrefs = new List(); - foreach (Entry f in fonts) - { - if ( - f.Name == font.Name - && f.RefName.StartsWith("F") - && f.Encoding.StartsWith("Identity") - ) - xrefs.Add(f.Xref); - } - - if (xrefs.Count == 0) - return; - int width = Convert.ToInt32(font.GlyphAdvance(32) * 1000); - foreach (int xref in xrefs) - { - if (Utils.SetFontWidth(doc, xref, width)) - Console.WriteLine($"Cannot set width for {font.Name} in xref {xref}"); - } - } - - public static bool SetFontWidth(Document doc, int xref, int width) - { - PdfDocument pdf = Document.AsPdfDocument(doc); - if (pdf.m_internal == null) - return false; - - PdfObj font = pdf.pdf_load_object(xref); - PdfObj dFonts = font.pdf_dict_get(new PdfObj("DescendantFonts")); - if (dFonts.pdf_is_array() != 0) - { - int n = dFonts.pdf_array_len(); - for (int i = 0; i < n; i++) - { - PdfObj dFont = dFonts.pdf_array_get(i); - PdfObj wArray = pdf.pdf_new_array(3); - wArray.pdf_array_push(mupdf.mupdf.pdf_new_int(0)); - wArray.pdf_array_push(mupdf.mupdf.pdf_new_int(65535)); - wArray.pdf_array_push(mupdf.mupdf.pdf_new_int(width)); - dFont.pdf_dict_put(new PdfObj("W"), wArray); - } - } - font.Dispose(); - pdf.Dispose(); - return true; - } - - public static int GetOC(Document doc, int xref) - { - if (doc.IsClosed || doc.IsEncrypted) - throw new Exception("document close or encrypted"); - (string t, string name) = doc.GetKeyXref(xref, "Subtype"); - if (t != "name" || !(name == "/Image" || name == "/Form")) - throw new Exception($"bad object type at xref {xref}"); - (string _t, string oc) = doc.GetKeyXref(xref, "OC"); - t = _t; - if (t != "xref") - return 0; - return Convert.ToInt32(oc.Replace("0 R", "")); - } - - public static OCMD GetOCMD(Document doc, int xref) - { - if (!INRANGE(xref, 0, doc.GetXrefLength() - 1)) - throw new Exception("bad xref"); - string text = doc.GetXrefObject(xref, compressed: 1); - if (!text.Contains("/Type/OCMD")) - throw new Exception("bad object type"); - int textLen = text.Length; - - int p0 = text.IndexOf("/OCGs["); - int p1 = text.IndexOf("]", p0); - int[] ocgs = null; - - if (p0 < 0 || p1 < 0) - ocgs = null; - else - { - ocgs = text.Substring(p0 + 6, p1 - p0 - 6) - .Replace("0 R", " ") - .Split(' ') - .Select(x => int.Parse(x)) - .ToArray(); - } - - p0 = text.IndexOf("/P/"); - string policy; - string ve; - List obj = null; - - if (p0 < 0) - policy = null; - else - { - p1 = text.IndexOf("ff", p0); - if (p1 < 0) - p1 = text.IndexOf("on", p0); - if (p1 < 0) - throw new Exception("bad object at xref"); - else - policy = text.Substring(p0 + 3, p1 + 2); - } - - p0 = text.IndexOf("/VE["); - if (p0 < 0) - ve = null; - else - { - int lp = 0; - int rp = 0; - p1 = p0; - while (lp < 1 || lp != rp) - { - p1 += 1; - if (!(p1 < textLen)) - throw new Exception("bad object ast xref"); - if (text[p1] == '[') - lp += 1; - if (text[p1] == ']') - rp += 1; - } - ve = text.Substring(p0 + 3, p1 + 1); - ve = ve.Replace("/And", "\"and\",") - .Replace("/Not", "\"not\",") - .Replace("/Or", "\"or\","); - ve = ve.Replace(" 0 R]", "]").Replace(" 0 R", ",").Replace("][", "],["); - - obj = JsonConvert.DeserializeObject>( - ve, - new JsonSerializerSettings() { Converters = { new VEConverter() } } - ); - - if (obj == null) - throw new Exception($"bad /VE key: {ve}"); - } - return new OCMD() - { - Xref = xref, - Ocgs = ocgs, - Policy = policy, - Ve = obj.ToArray() - }; - } - - internal class VEConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return (objectType == typeof(List)); - } - - public override void WriteJson( - JsonWriter writer, - dynamic value, - JsonSerializer serializer - ) - { - throw new NotImplementedException(); - } - - public override dynamic ReadJson( - JsonReader reader, - Type objectType, - object existingValue, - JsonSerializer serializer - ) - { - List result = new List(); - while (reader.Read()) - { - if (reader.TokenType == JsonToken.StartArray) - { - result.Add(ReadJson(reader, objectType, existingValue, serializer)); - } - else if (reader.TokenType == JsonToken.EndArray) - { - return result; - } - else - { - dynamic value = ( - reader.ValueType == typeof(long) - ? Convert.ToInt32(reader.Value) - : reader.Value - ); - result.Add(value); - } - } - return result; - } - } - - /// - /// Return a list of page numbers with the given label - /// - /// label - /// (bool) stop searching after first hit - /// - public static List GetPageNumbers(Document doc, string label, bool onlyOne = false) - { - List numbers = new List(); - if (string.IsNullOrEmpty(label)) - return numbers; - List<(int, string)> labels = doc._getPageLabels(); - if (labels.Count == 0) - return numbers; - for (int i = 0; i < doc.PageCount; i++) - { - string pageLabel = GetPageLabel(i, labels); - if (pageLabel == label) - { - numbers.Add(i); - if (onlyOne) - break; - } - } - return numbers; - } - - /// - /// Create pixmap of document page by page number. - /// - /// - /// page number - /// Matrix for transformation - /// - /// rgb, rgb, gray - case ignored, default csRGB - /// restrict rendering to this area - /// include alpha channel - /// also render annotations - /// - internal static Pixmap GetPagePixmap( - Document doc, - int pno, - IdentityMatrix matrix = null, - int dpi = 0, - string colorSpace = null, - Rect clip = null, - bool alpha = false, - bool annots = true - ) - { - return doc[pno] - .GetPixmap( - matrix: matrix, - dpi: dpi, - colorSpace: colorSpace, - clip: clip, - alpha: alpha, - annots: annots - ); - } - - /// - /// Return a PDF string depending on its coding - /// - /// - /// - public static string GetPdfString(string s) - { - if (string.IsNullOrEmpty(s)) - return "()"; - string MakeUtf16be(string _s) - { - byte[] _r = Annot.MergeByte( - new byte[] { 254, 255 }, - Encoding.BigEndianUnicode.GetBytes(_s) - ); - return "<" + BitConverter.ToString(_r).Replace("-", string.Empty) + ">"; - } - - string r = ""; - foreach (char c in s) - { - int oc = Convert.ToInt32(c); - if (oc > 255) - return MakeUtf16Be(s); - if (oc > 31 && oc < 127) - { - if (c == '(' || c == ')' || c == '\\') - r += '\\'; - r += c; - continue; - } - if (oc > 127) - { - r += string.Format("\\{0}", Convert.ToString(oc, 8).PadLeft(3, '0')); - continue; - } - - if (oc == 8) - r += "\\b"; - else if (oc == 9) - r += "\\t"; - else if (oc == 10) - r += "\\n"; - else if (oc == 12) - r += "\\f"; - else if (oc == 13) - r += "\\r"; - else - r += "\\267"; - } - return "(" + r + ")"; - } - - public static byte[] GetAllContents(Page page) - { - FzBuffer res = Utils.ReadContents(page.GetPdfPage().obj()); - if (res == null) - return null; - return Utils.BinFromBuffer(res); - } - - internal static PdfFilterOptions MakePdfFilterOptions( - int recurse = 0, - int instanceForms = 0, - int ascii = 0, - int noUpdate = 0, - int sanitize = 0, - PdfSanitizeFilterOptions sopts = null - ) - { - PdfFilterOptions filter = new PdfFilterOptions(); - filter.recurse = recurse; - filter.instance_forms = instanceForms; - filter.ascii = ascii; - filter.no_update = noUpdate; - if (sanitize != 0) - { - if (sopts == null) - sopts = new PdfSanitizeFilterOptions(); - Factory factory = new Factory(sopts); - filter.add_factory(factory.internal_()); - } - - return filter; - } - - /// - /// Calculate the PDF action string. - /// - /// - /// - /// - public static string GetDestString(int xref, int dDict) - { - string goto_ = "/A<>"; - - return string.Format(goto_, xref, 0, dDict, 0); - } - - /// - /// Calculate the PDF action string. - /// - /// - /// - /// - public static string GetDestString(int xref, float dDict) - { - string goto_ = "/A<>"; - - return string.Format(System.Globalization.CultureInfo.InvariantCulture, goto_, xref, 0, dDict, 0); - } - - /// - /// Calculate the PDF action string. - /// - /// - /// - /// - public static string GetDestString(int xref, LinkInfo dDict) - { - if (dDict == null) - return ""; - string goto_ = "/A<>"; - string gotor1 = "/A<>>>"; - string gotor2 = "/A<>>>"; - string launch = "/A<>>>"; - string uri = "/A<>"; - - if (dDict.Kind == LinkType.LINK_GOTO) - { - float zoom = dDict.Zoom; - Point to = dDict.To; - float left = to.X; - float top = to.Y; - return string.Format(System.Globalization.CultureInfo.InvariantCulture, goto_, xref, left, top, zoom); - } - - if (dDict.Kind == LinkType.LINK_URI) - { - return string.Format(uri, Utils.GetPdfString(dDict.Uri)); - } - - if (dDict.Kind == LinkType.LINK_LAUNCH) - { - string fSpec = Utils.GetPdfString(dDict.File); - return string.Format(launch, fSpec, fSpec); - } - - if (dDict.Kind == LinkType.LINK_GOTOR && dDict.Page < 0) - { - string fSpec = Utils.GetPdfString(dDict.File); - return string.Format(gotor2, Utils.GetPdfString(dDict.ToStr), fSpec, fSpec); - } - - if (dDict.Kind == LinkType.LINK_GOTOR && dDict.Page >= 0) - { - string fSpec = Utils.GetPdfString(dDict.File); - return string.Format( - System.Globalization.CultureInfo.InvariantCulture, - gotor1, - dDict.Page, - dDict.To.X, - dDict.To.Y, - dDict.Zoom, - fSpec, - fSpec - ); - } - - return ""; - } - - internal static void ResetWidget(PdfAnnot annot) - { - PdfAnnot thisAnnot = annot; - PdfObj thisAnnotObj = mupdf.mupdf.pdf_annot_obj(thisAnnot); - PdfDocument pdf = thisAnnotObj.pdf_get_bound_document(); - pdf.pdf_field_reset(thisAnnotObj); - } - - /// - /// Ensure that widgets with /AA/C JavaScript are in array AcroForm/CO - /// - /// - internal static void EnsureWidgetCalc(PdfAnnot annot) - { - PdfObj annotObj = annot.pdf_annot_obj(); - PdfDocument pdf = annotObj.pdf_get_bound_document(); - PdfObj coName = new PdfObj("CO"); // = PDF_NAME(CO) - PdfObj acro = Utils.pdf_dict_getl( - pdf.pdf_trailer(), - new string[] { "Root", "AcroForm" } - ); - PdfObj co = acro.pdf_dict_get(coName); // = AcroForm/CO - if (co.m_internal == null) - co = acro.pdf_dict_put_array(coName, 2); - int n = co.pdf_array_len(); - int found = 0; - int xref = annotObj.pdf_to_num(); - - for (int i = 0; i < n; i++) - { - int nxref = co.pdf_array_get(i).pdf_to_num(); - if (xref == nxref) - { - found = 1; - break; - } - } - if (found == 0) - co.pdf_array_push(pdf.pdf_new_indirect(xref, 0)); - } - - internal static void SaveWidget(PdfAnnot annot, Widget widget) - { - PdfPage page = annot.pdf_annot_page(); - PdfObj annotObj = annot.pdf_annot_obj(); - PdfDocument pageDoc = page.doc(); - - int value = widget.FieldType; - int fieldType = value; - - Rect rect = widget.Rect; - Matrix rotMat = Utils.RotatePageMatrix(page); - FzRect rect_ = mupdf.mupdf.fz_transform_rect(rect.ToFzRect(), rotMat.ToFzMatrix()); - annot.pdf_set_annot_rect(rect_); - - float[] color = widget.FillColor; - if (color != null) - { - int n = color.Length; - PdfObj fillCol = pageDoc.pdf_new_array(n); - float col = 0; - for (int i = 0; i < n; i++) - { - col = color[i]; - fillCol.pdf_array_push_real(col); - } - annotObj.pdf_field_set_fill_color(fillCol); - } - - int[] borderDashes = widget.BorderDashes; - if (borderDashes != null) - { - int n = borderDashes.Length; - PdfObj dashes = pageDoc.pdf_new_array(n); - for (int i = 0; i < n; i++) - { - dashes.pdf_array_push_int(borderDashes[i]); - } - Utils.pdf_dict_putl(annotObj, dashes, new string[] { "BS", "D" }); - } - - float[] borderColor = widget.BorderColor; - if (borderColor != null) - { - int n = borderColor.Length; - PdfObj borderCol = pageDoc.pdf_new_array(n); - float col = 0; - for (int i = 0; i < n; i++) - { - col = borderColor[i]; - borderCol.pdf_array_push_real(col); - } - Utils.pdf_dict_putl(annotObj, borderCol, new string[] { "MK", "BC" }); - } - - string fieldLabel = widget.FieldLabel; - if (!string.IsNullOrEmpty(fieldLabel)) - { - annotObj.pdf_dict_put_text_string(new PdfObj("TU"), fieldLabel); - } - - string fieldName = widget.FieldName; - if (!string.IsNullOrEmpty(fieldName)) - { - string oldName = annotObj.pdf_load_field_name(); - if (fieldName != oldName) - annotObj.pdf_dict_put_text_string(new PdfObj("T"), fieldName); - } - - if (fieldType == (int)PdfWidgetType.PDF_WIDGET_TYPE_TEXT) - { - int maxlen = widget.TextMaxLen; - if (maxlen != 0) - annotObj.pdf_dict_put_int(new PdfObj("MaxLen"), maxlen); - } - - int fieldDisplay = widget.FieldDisplay; - annotObj.pdf_field_set_display(fieldDisplay); - - if ( - fieldType == (int)PdfWidgetType.PDF_WIDGET_TYPE_LISTBOX - || fieldType == (int)PdfWidgetType.PDF_WIDGET_TYPE_COMBOBOX - ) - { - List choiceValues = widget.ChoiceValues; - SetChoiceOptions(annot, choiceValues); - } - - string borderStyle = widget.BorderStyle; - int val = Utils.GetBorderStyle(borderStyle); - Utils.pdf_dict_putl(annotObj, new PdfObj(val), new string[] { "BS", "S" }); - - float borderWidth = widget.BorderWidth; - Utils.pdf_dict_putl( - annotObj, - mupdf.mupdf.pdf_new_real(borderWidth), - new string[] { "BS", "W" } - ); - - string da = widget.TextDa; - annotObj.pdf_dict_put_text_string(new PdfObj("DA"), da); - annotObj.pdf_dict_del(new PdfObj("DS")); - annotObj.pdf_dict_del(new PdfObj("RC")); - - int fieldFlags = widget.FieldFlags; - if (fieldFlags != 0) - { - if (fieldType == (int)PdfWidgetType.PDF_WIDGET_TYPE_COMBOBOX) - fieldFlags |= (int)PdfFieldFlags.PDF_CH_FIELD_IS_COMBO; - else if (fieldType == (int)PdfWidgetType.PDF_WIDGET_TYPE_RADIOBUTTON) - fieldType |= (int)PdfFieldFlags.PDF_BTN_FIELD_IS_RADIO; - else if (fieldType == (int)PdfWidgetType.PDF_WIDGET_TYPE_BUTTON) - fieldType |= (int)PdfFieldFlags.PDF_BTN_FIELD_IS_PUSHBUTTON; - annotObj.pdf_dict_put_int(new PdfObj("Ff"), fieldFlags); - } - - string buttonCap = widget.ButtonCaption; - if (!string.IsNullOrEmpty(buttonCap)) - annotObj.pdf_field_set_button_caption(buttonCap); - - string script = widget.Script; - Utils.PutScript(annotObj, new PdfObj("A"), new PdfObj(), script); - - string scriptStroke = widget.ScriptStroke; - PutScript(annotObj, new PdfObj("AA"), new PdfObj("K"), scriptStroke); - - string scriptFormat = widget.ScriptFormat; - PutScript(annotObj, new PdfObj("AA"), new PdfObj("F"), scriptFormat); - - string scriptChange = widget.ScriptChange; - PutScript(annotObj, new PdfObj("AA"), new PdfObj("V"), scriptChange); - - string scriptCalc = widget.ScriptCalc; - PutScript(annotObj, new PdfObj("AA"), new PdfObj("C"), scriptCalc); - - string scriptBlur = widget.ScriptBlur; - PutScript(annotObj, new PdfObj("AA"), new PdfObj("B1"), scriptBlur); - - string scriptFocus = widget.ScriptFocus; - PutScript(annotObj, new PdfObj("AA"), new PdfObj("Fo"), scriptFocus); - - string fieldVal = widget.FieldValue; - if (fieldType == (int)PdfWidgetType.PDF_WIDGET_TYPE_RADIOBUTTON) - { - if (string.IsNullOrEmpty(fieldVal)) - { - pageDoc.pdf_set_field_value(annotObj, "Off", 1); - annotObj.pdf_dict_put_name(new PdfObj("AS"), "Off"); - } - else - { - PdfObj onstate = annotObj.pdf_button_field_on_state(); - if (onstate.m_internal != null) - { - string on = onstate.pdf_to_name(); - pageDoc.pdf_set_field_value(annotObj, on, 1); - annotObj.pdf_dict_put_name(new PdfObj("AS"), on); - } - else if (!string.IsNullOrEmpty(fieldVal)) - { - annotObj.pdf_dict_put_name(new PdfObj("AS"), fieldVal); - } - } - } - else if (fieldType == (int)PdfWidgetType.PDF_WIDGET_TYPE_CHECKBOX) - { - if (fieldVal == "Yes") - { - PdfObj onstate = annotObj.pdf_button_field_on_state(); - string on = onstate.pdf_to_name(); - pageDoc.pdf_set_field_value(annotObj, on, 1); - annotObj.pdf_dict_put_name(new PdfObj("AS"), "Yes"); - annotObj.pdf_dict_put_name(new PdfObj("V"), "Yes"); - } - else - { - annotObj.pdf_dict_put_name(new PdfObj("AS"), "Off"); - annotObj.pdf_dict_put_name(new PdfObj("V"), "Off"); - } - } - else if (!string.IsNullOrEmpty(fieldVal)) - { - pageDoc.pdf_set_field_value(annotObj, fieldVal, 1); - if ( - fieldType == (int)PdfWidgetType.PDF_WIDGET_TYPE_COMBOBOX - || fieldType == (int)PdfWidgetType.PDF_WIDGET_TYPE_LISTBOX - ) - annotObj.pdf_dict_del(new PdfObj("I")); - } - annot.pdf_dirty_annot(); - annot.pdf_set_annot_hot(1); - annot.pdf_set_annot_active(1); - annot.pdf_update_annot(); - - pageDoc.Dispose(); - } - - internal static void PutScript(PdfObj annotObj, PdfObj key1, PdfObj key2, string value) - { - PdfObj key1Obj = annotObj.pdf_dict_get(key1); - PdfDocument pdf = annotObj.pdf_get_bound_document(); - - if (string.IsNullOrEmpty(value)) - { - if (key2 == null || key2.m_internal == null) - annotObj.pdf_dict_del(key1); - else if (key1Obj.m_internal != null) - key1Obj.pdf_dict_del(key2); - return; - } - - string script; - if (key2.m_internal == null || key1Obj.m_internal == null) - script = Utils.GetScript(key1Obj); - else - script = Utils.GetScript(key1Obj.pdf_dict_get(key2)); - - if (value != script) - { - PdfObj newAction = NewJavaScript(pdf, value); - if (key2.m_internal == null) - annotObj.pdf_dict_put(key1, newAction); - else - Utils.pdf_dict_putl( - annotObj, - newAction, - new string[] { key1.pdf_to_name(), key2.pdf_to_name() } - ); - } - } - - internal static PdfObj NewJavaScript(PdfDocument pdf, string value) - { - if (value == null) - return null; - FzBuffer res = Utils.fz_new_buffer_from_data(Encoding.UTF8.GetBytes(value)); - PdfObj source = pdf.pdf_add_stream(res, new PdfObj(), 0); - PdfObj newAction = pdf.pdf_add_new_dict(4); - newAction.pdf_dict_put(new PdfObj("S"), mupdf.mupdf.pdf_new_name("JavaScript")); - newAction.pdf_dict_put(new PdfObj("JS"), source); - - return newAction; - } - - internal static string GetScript(PdfObj key) - { - if (key.m_internal == null) - return null; - PdfObj j = key.pdf_dict_get(new PdfObj("S")); - string jj = mupdf.mupdf.pdf_to_name(j); - PdfObj js; - if (jj == "JavaScript") - { - js = key.pdf_dict_get(new PdfObj("JS")); - if (js.m_internal == null) - return null; - } - else - return null; - - string script; - if (js.pdf_is_string() != 0) - script = Utils.UnicodeFromStr(js.pdf_to_text_string()); - else if (js.pdf_is_stream() != 0) - { - FzBuffer res = js.pdf_load_stream(); - script = Utils.UnicodeFromBuffer(res); - } - else - return null; - - if (!string.IsNullOrEmpty(script)) - return script; - return null; - } - - internal static void SetChoiceOptions(PdfAnnot annot, List list) - { - if (list == null) - return; - int n = list.Count; - if (n == 0) - return; - PdfObj annotObj = annot.pdf_annot_obj(); - PdfDocument pdf = annotObj.pdf_get_bound_document(); - PdfObj optArr = pdf.pdf_new_array(n); - for (int i = 0; i < n; i++) - { - dynamic val = list[i]; - if (val is string) - optArr.pdf_array_push_text_string(val); - else - { - PdfObj optArrSub = optArr.pdf_array_push_array(2); - optArrSub.pdf_array_push_text_string(val[0]); - optArrSub.pdf_array_push_text_string(val[1]); - } - } - annotObj.pdf_dict_put(new PdfObj("Opt"), optArr); - } - - public static int GetBorderStyle(string style) - { - int val = new PdfObj("S").pdf_to_num(); - if (string.IsNullOrEmpty(style)) - return val; - - string s = style; - if (s.StartsWith("b") || s.StartsWith("B")) - val = new PdfObj("B").pdf_to_num(); - else if (s.StartsWith("d") || s.StartsWith("D")) - val = new PdfObj("D").pdf_to_num(); - else if (s.StartsWith("i") || s.StartsWith("I")) - val = new PdfObj("I").pdf_to_num(); - else if (s.StartsWith("u") || s.StartsWith("U")) - val = new PdfObj("U").pdf_to_num(); - else if (s.StartsWith("s") || s.StartsWith("S")) - val = new PdfObj("S").pdf_to_num(); - - return val; - } - - internal static FzBuffer fz_new_buffer_from_data(byte[] data) - { - IntPtr pData = Marshal.AllocHGlobal(data.Length); - Marshal.Copy(data, 0, pData, data.Length); - - FzBuffer ret = mupdf.mupdf.fz_new_buffer_from_copied_data( - new SWIGTYPE_p_unsigned_char(pData, true), - (uint)data.Length - ); - - Marshal.FreeHGlobal(pData); - return ret; - } - - internal static void FillWidget(Annot annot, Widget widget) - { - Utils.GetWidgetProperties(annot, widget); - - widget.Rect = annot.Rect; - widget.Xref = annot.Xref; - widget.Parent = annot.Parent; - widget._annot = annot._nativeAnnotion; - - if (string.IsNullOrEmpty(widget.Script)) - widget.Script = null; - if (string.IsNullOrEmpty(widget.ScriptStroke)) - widget.ScriptStroke = null; - if (string.IsNullOrEmpty(widget.ScriptFormat)) - widget.ScriptFormat = null; - if (string.IsNullOrEmpty(widget.ScriptChange)) - widget.ScriptChange = null; - if (string.IsNullOrEmpty(widget.ScriptCalc)) - widget.ScriptCalc = null; - if (string.IsNullOrEmpty(widget.ScriptBlur)) - widget.ScriptBlur = null; - if (string.IsNullOrEmpty(widget.ScriptFocus)) - widget.ScriptFocus = null; - } - - public static string GetFieldTypeText(int wtype) - { - if (wtype == (int)PdfWidgetType.PDF_WIDGET_TYPE_BUTTON) - return "Button"; - if (wtype == (int)PdfWidgetType.PDF_WIDGET_TYPE_CHECKBOX) - return "CheckBox"; - if (wtype == (int)PdfWidgetType.PDF_WIDGET_TYPE_RADIOBUTTON) - return "RadioButton"; - if (wtype == (int)PdfWidgetType.PDF_WIDGET_TYPE_TEXT) - return "Text"; - if (wtype == (int)PdfWidgetType.PDF_WIDGET_TYPE_COMBOBOX) - return "ComboBox"; - if (wtype == (int)PdfWidgetType.PDF_WIDGET_TYPE_SIGNATURE) - return "Signature"; - return "unknown"; - } - - internal static PdfAnnot GetWidgetByXref(PdfPage page, int xref) - { - bool found = false; - PdfAnnot annot = page.pdf_first_widget(); - while (annot.m_internal != null) - { - PdfObj annotObj = annot.pdf_annot_obj(); - if (xref == annotObj.pdf_to_num()) - { - found = true; - break; - } - annot = annot.pdf_next_annot(); - } - if (!found) - throw new Exception($"xref {xref} is not a widget of this page"); - return annot; - } - - internal static Matrix GetRotateMatrix(Page page) - { - PdfPage pdfpage = page.GetPdfPage(); - if (pdfpage.m_internal == null) - return new Matrix(); - return Utils.RotatePageMatrix(pdfpage); - } - - public static Rect PaperRect(string size) - { - (int width, int height) = Utils.PaperSize(size); - return new Rect(0, 0, width, height); - } - - public static (int, int) PaperSize(string size) - { - string s = size.ToLower(); - string f = "p"; - if (s.EndsWith("-l")) - { - f = "l"; - s = s.Substring(0, s.Length - 2); - } - if (s.EndsWith("-p")) - { - s = s.Substring(0, s.Length - 2); - } - - //(int, int) ret = Utils.PaperSizes.GetValueOrDefault(s, (-1, -1)); - (int, int) ret; - if (!Utils.PaperSizes.TryGetValue(s, out ret)) - { - ret = (-1, -1); // Default value if key not found - } - if (f == "p") - return ret; - return (ret.Item2, ret.Item1); - } - - /// - /// Calculate length of a string for a built-in font. - /// - /// - /// name of the font. - /// font size points. - /// encoding to use, 0=Latin (default), 1=Greek, 2=Cyrillic. - /// length of text. - /// - public static float GetTextLength( - string text, - string fontFile, - string fontName = "helv", - float fontSize = 11, - int encoding = 0 - ) - { - fontName = fontName.ToLower(); - //string basename = Utils.Base14_fontdict.GetValueOrDefault(fontName, null); - string basename; - if (!Utils.Base14_fontdict.TryGetValue(fontName, out basename)) - { - basename = null; // Default value if key not found - } - - List<(int, double)> glyphs = new List<(int, double)>(); - if (basename == "Symbol") - glyphs = Utils.symbol_glyphs; - if (basename == "ZapfDingbats") - glyphs = Utils.zapf_glyphs; - if (glyphs.Count != 0) - { - float w = 0f; - foreach (char c in text) - { - int cInt = Convert.ToInt32(c); - w += (float)( - (Convert.ToInt32(c)) < 256 ? glyphs[cInt].Item2 : glyphs[183].Item2 - ); - } - return w * fontSize; - } - - if (Utils.Base14_fontdict.Keys.Contains(fontName)) - //if (true) - return Utils.MeasureString(text, fontFile, fontName, fontSize, encoding); - if ( - ( - new string[] - { - "china-t", - "china-s", - "china-ts", - "china-ss", - "japan", - "japan-s", - "korea", - "korea-s" - } - ).Contains(fontName) - ) - return text.Length * fontSize; - throw new Exception($"Font {fontName} is unsupported"); - } - - public static float MeasureString( - string text, - string fontFile, - string fontName, - float fontSize = 11.0f, - int encoding = 0 - ) - { - if (string.IsNullOrEmpty(fontFile)) - { - //throw new Exception("should specify font file."); - return -1f; - } - - FzFont font = new FzFont(fontName, fontFile, 0, 0); - float w = 0; - int pos = 0; - while (pos < text.Length) - { - ll_fz_chartorune_outparams o = new ll_fz_chartorune_outparams(); - int t = mupdf.mupdf.ll_fz_chartorune_outparams_fn( - text.Substring(pos, text.Length - pos), - o - ); - int c = o.rune; - pos += t; - if (encoding == (int)SimpleEncoding.PDF_SIMPLE_ENCODING_GREEK) - c = mupdf.mupdf.fz_iso8859_7_from_unicode(c); - else if (encoding == (int)SimpleEncoding.PDF_SIMPLE_ENCODING_CYRILLIC) - c = mupdf.mupdf.fz_windows_1251_from_unicode(c); - else - c = mupdf.mupdf.fz_windows_1252_from_unicode(c); - if (c < 0) - c = 0xB7; - int g = font.fz_encode_character(c); - float dw = font.fz_advance_glyph(g, 0); - w += dw; - } - float ret = w * fontSize; - return ret; - } - - /// - /// Compute the quad located inside the bbox. - /// - /// 'line["dir"]' of the owning line or None. - /// the span. May be from get_texttrace() method. - /// the bbox of the span or any of its characters. - /// The quad which is wrapped by the bbox. - public static Quad RecoverBboxQuad((float, float) lineDir, Span span, Rect bbox) - { - (float cos, float sin) = lineDir; - - float d = span.Asc - span.Desc; - float height = d * span.Size; - - float hs = height * sin; - float hc = height * cos; - Point ul, - ur, - ll, - lr; - if (hc >= 0 && hs <= 0) - { - ul = bbox.BottomLeft - new Point(0, hc); - ur = bbox.TopRight + new Point(hs, 0); - ll = bbox.BottomLeft - new Point(hs, 0); - lr = bbox.TopRight + new Point(0, hc); - } - else if (hc <= 0 && hs <= 0) - { - ul = bbox.BottomRight + new Point(hs, 0); - ur = bbox.TopLeft - new Point(0, hc); - ll = bbox.BottomRight + new Point(0, hc); - lr = bbox.TopLeft - new Point(hs, 0); - } - else if (hc <= 0 && hs >= 0) - { - ul = bbox.TopRight - new Point(0, hc); - ur = bbox.BottomLeft + new Point(hs, 0); - ll = bbox.TopRight - new Point(hs, 0); - lr = bbox.BottomLeft + new Point(0, hc); - } - else - { - ul = bbox.TopLeft + new Point(hs, 0); - ur = bbox.BottomRight - new Point(0, hc); - ll = bbox.TopLeft + new Point(0, hc); - lr = bbox.BottomRight - new Point(hs, 0); - } - - return new Quad(ul, ur, ll, lr); - } - - public static bool SetSmallGlyphHeights(bool on = false) - { - if (on) - SmallGlyphHeights = true; - return SmallGlyphHeights; - } - - /// - /// Recover the quadrilateral of a text character. - /// - /// Line Dir - /// - /// - /// - public static Quad RecoverCharQuad((float, float) lineDir, Span span, Char ch) - { - Rect bbox = new Rect(ch.Bbox); - return RecoverBboxQuad(lineDir, span, bbox); - } - - /// - /// Calculate the span quad for 'dict' / 'rawdict' text extractions. - /// - /// - /// - /// - /// - public static Quad RecoverSpanQuad((float, float) lineDir, Span span, Char[] chars) - { - if (chars == null) - return RecoverQuad(lineDir, span); - - Quad q0 = RecoverCharQuad(lineDir, span, chars[0]); - Quad q1; - if (chars.Length > 1) - q1 = RecoverCharQuad(lineDir, span, chars[chars.Length - 1]); - else - q1 = q0; - - Point spanll = q0.LowerLeft; - Point spanlr = q1.LowerRight; - Matrix mat0 = PlanishLine(spanll, spanlr); - Point xlr = spanlr * mat0; - - bool small = SetSmallGlyphHeights(); - float h = 0; - - h = span.Size * (small ? 1 : (span.Asc - span.Desc)); - Rect spanRect = new Rect(0, -h, xlr.X, 0); - Quad spanQuad = spanRect.Quad; - spanQuad = spanQuad * ~mat0; - - return spanQuad; - } - - /// - /// Recover the quadrilateral of a text span. - /// - /// 'line["dir"]' of the owning line. - /// the span. - /// The quadrilateral enveloping the span's text. - public static Quad RecoverQuad((float, float) lineDir, Span span) - { - if (span == null) - throw new Exception("bad span argument"); - return Utils.RecoverBboxQuad(lineDir, span, span.Bbox); - } - - /// - /// Calculate the line quad for 'dict' / 'rawdict' text extractions. - /// - /// - /// - /// - public static Quad RecoverLineQuad(Line line, List spans = null) - { - if (spans == null) - spans = line.Spans; - if (spans.Count == 0) - throw new Exception("bad span list"); - float cos = line.Dir.X; - float sin = line.Dir.Y; - Quad q0 = Utils.RecoverQuad((cos, sin), spans[0]); - Quad q1; - if (spans.Count > 1) - q1 = RecoverQuad((cos, sin), spans[spans.Count - 1]); - else - q1 = q0; - Point linell = q0.LowerLeft; - Point linelr = q0.LowerRight; - - Matrix mat0 = Utils.PlanishLine(linell, linelr); - Point xlr = linelr * mat0; - - float h = 0; - foreach (Span s in spans) - { - if (h < s.Size * (s.Asc - s.Desc)) - h = s.Size * (s.Asc - s.Desc); - } - Rect lineRect = new Rect(0, -h, xlr.X, 0); - Quad lineQuad = lineRect.Quad; - lineQuad *= ~mat0; - - return lineQuad; - } - - public static List GetColorList() - { - return Utils.GetColorInfoList().Select(x => x.Item1).ToList(); - } - - public static List<(string, int, int, int)> GetColorInfoList() - { - return new List<(string, int, int, int)> - { - ("ALICEBLUE", 240, 248, 255), - ("ANTIQUEWHITE", 240, 248, 255), - ("ANTIQUEWHITE2", 238, 223, 204), - ("ANTIQUEWHITE3", 205, 192, 176), - ("ANTIQUEWHITE4", 139, 131, 120), - ("AQUAMARINE", 127, 255, 212), - ("AQUAMARINE1", 127, 255, 212), - ("AQUAMARINE2", 118, 238, 198), - ("AQUAMARINE3", 102, 205, 170), - ("AQUAMARINE4", 69, 139, 116), - ("AZURE", 240, 255, 255), - ("AZURE1", 240, 255, 255), - ("AZURE2", 224, 238, 238), - ("AZURE3", 193, 205, 205), - ("AZURE4", 131, 139, 139), - ("BEIGE", 245, 245, 220), - ("BISQUE", 255, 228, 196), - ("BISQUE1", 255, 228, 196), - ("BISQUE2", 238, 213, 183), - ("BISQUE3", 205, 183, 158), - ("BISQUE4", 139, 125, 107), - ("BLACK", 0, 0, 0), - ("BLANCHEDALMOND", 255, 235, 205), - ("BLUE", 0, 0, 255), - ("BLUE1", 0, 0, 255), - ("BLUE2", 0, 0, 238), - ("BLUE3", 0, 0, 205), - ("BLUE4", 0, 0, 139), - ("BLUEVIOLET", 138, 43, 226), - ("BROWN", 165, 42, 42), - ("BROWN1", 255, 64, 64), - ("BROWN2", 238, 59, 59), - ("BROWN3", 205, 51, 51), - ("BROWN4", 139, 35, 35), - ("BURLYWOOD", 222, 184, 135), - ("BURLYWOOD1", 255, 211, 155), - ("BURLYWOOD2", 238, 197, 145), - ("BURLYWOOD3", 205, 170, 125), - ("BURLYWOOD4", 139, 115, 85), - ("CADETBLUE", 95, 158, 160), - ("CADETBLUE1", 152, 245, 255), - ("CADETBLUE2", 142, 229, 238), - ("CADETBLUE3", 122, 197, 205), - ("CADETBLUE4", 83, 134, 139), - ("CHARTREUSE", 127, 255, 0), - ("CHARTREUSE1", 127, 255, 0), - ("CHARTREUSE2", 118, 238, 0), - ("CHARTREUSE3", 102, 205, 0), - ("CHARTREUSE4", 69, 139, 0), - ("CHOCOLATE", 210, 105, 30), - ("CHOCOLATE1", 255, 127, 36), - ("CHOCOLATE2", 238, 118, 33), - ("CHOCOLATE3", 205, 102, 29), - ("CHOCOLATE4", 139, 69, 19), - ("COFFEE", 156, 79, 0), - ("CORAL", 255, 127, 80), - ("CORAL1", 255, 114, 86), - ("CORAL2", 238, 106, 80), - ("CORAL3", 205, 91, 69), - ("CORAL4", 139, 62, 47), - ("CORNFLOWERBLUE", 100, 149, 237), - ("CORNSILK", 255, 248, 220), - ("CORNSILK1", 255, 248, 220), - ("CORNSILK2", 238, 232, 205), - ("CORNSILK3", 205, 200, 177), - ("CORNSILK4", 139, 136, 120), - ("CYAN", 0, 255, 255), - ("CYAN1", 0, 255, 255), - ("CYAN2", 0, 238, 238), - ("CYAN3", 0, 205, 205), - ("CYAN4", 0, 139, 139), - ("DARKBLUE", 0, 0, 139), - ("DARKCYAN", 0, 139, 139), - ("DARKGOLDENROD", 184, 134, 11), - ("DARKGOLDENROD1", 255, 185, 15), - ("DARKGOLDENROD2", 238, 173, 14), - ("DARKGOLDENROD3", 205, 149, 12), - ("DARKGOLDENROD4", 139, 101, 8), - ("DARKGREEN", 0, 100, 0), - ("DARKGRAY", 169, 169, 169), - ("DARKKHAKI", 189, 183, 107), - ("DARKMAGENTA", 139, 0, 139), - ("DARKOLIVEGREEN", 85, 107, 47), - ("DARKOLIVEGREEN1", 202, 255, 112), - ("DARKOLIVEGREEN2", 188, 238, 104), - ("DARKOLIVEGREEN3", 162, 205, 90), - ("DARKOLIVEGREEN4", 110, 139, 61), - ("DARKORANGE", 255, 140, 0), - ("DARKORANGE1", 255, 127, 0), - ("DARKORANGE2", 238, 118, 0), - ("DARKORANGE3", 205, 102, 0), - ("DARKORANGE4", 139, 69, 0), - ("DARKORCHID", 153, 50, 204), - ("DARKORCHID1", 191, 62, 255), - ("DARKORCHID2", 178, 58, 238), - ("DARKORCHID3", 154, 50, 205), - ("DARKORCHID4", 104, 34, 139), - ("DARKRED", 139, 0, 0), - ("DARKSALMON", 233, 150, 122), - ("DARKSEAGREEN", 143, 188, 143), - ("DARKSEAGREEN1", 193, 255, 193), - ("DARKSEAGREEN2", 180, 238, 180), - ("DARKSEAGREEN3", 155, 205, 155), - ("DARKSEAGREEN4", 105, 139, 105), - ("DARKSLATEBLUE", 72, 61, 139), - ("DARKSLATEGRAY", 47, 79, 79), - ("DARKTURQUOISE", 0, 206, 209), - ("DARKVIOLET", 148, 0, 211), - ("DEEPPINK", 255, 20, 147), - ("DEEPPINK1", 255, 20, 147), - ("DEEPPINK2", 238, 18, 137), - ("DEEPPINK3", 205, 16, 118), - ("DEEPPINK4", 139, 10, 80), - ("DEEPSKYBLUE", 0, 191, 255), - ("DEEPSKYBLUE1", 0, 191, 255), - ("DEEPSKYBLUE2", 0, 178, 238), - ("DEEPSKYBLUE3", 0, 154, 205), - ("DEEPSKYBLUE4", 0, 104, 139), - ("DIMGRAY", 105, 105, 105), - ("DODGERBLUE", 30, 144, 255), - ("DODGERBLUE1", 30, 144, 255), - ("DODGERBLUE2", 28, 134, 238), - ("DODGERBLUE3", 24, 116, 205), - ("DODGERBLUE4", 16, 78, 139), - ("FIREBRICK", 178, 34, 34), - ("FIREBRICK1", 255, 48, 48), - ("FIREBRICK2", 238, 44, 44), - ("FIREBRICK3", 205, 38, 38), - ("FIREBRICK4", 139, 26, 26), - ("FLORALWHITE", 255, 250, 240), - ("FORESTGREEN", 34, 139, 34), - ("GAINSBORO", 220, 220, 220), - ("GHOSTWHITE", 248, 248, 255), - ("GOLD", 255, 215, 0), - ("GOLD1", 255, 215, 0), - ("GOLD2", 238, 201, 0), - ("GOLD3", 205, 173, 0), - ("GOLD4", 139, 117, 0), - ("GOLDENROD", 218, 165, 32), - ("GOLDENROD1", 255, 193, 37), - ("GOLDENROD2", 238, 180, 34), - ("GOLDENROD3", 205, 155, 29), - ("GOLDENROD4", 139, 105, 20), - ("GREEN YELLOW", 173, 255, 47), - ("GREEN", 0, 255, 0), - ("GREEN1", 0, 255, 0), - ("GREEN2", 0, 238, 0), - ("GREEN3", 0, 205, 0), - ("GREEN4", 0, 139, 0), - ("GREENYELLOW", 173, 255, 47), - ("GRAY", 190, 190, 190), - ("GRAY0", 0, 0, 0), - ("GRAY1", 3, 3, 3), - ("GRAY10", 26, 26, 26), - ("GRAY100", 255, 255, 255), - ("GRAY11", 28, 28, 28), - ("GRAY12", 31, 31, 31), - ("GRAY13", 33, 33, 33), - ("GRAY14", 36, 36, 36), - ("GRAY15", 38, 38, 38), - ("GRAY16", 41, 41, 41), - ("GRAY17", 43, 43, 43), - ("GRAY18", 46, 46, 46), - ("GRAY19", 48, 48, 48), - ("GRAY2", 5, 5, 5), - ("GRAY20", 51, 51, 51), - ("GRAY21", 54, 54, 54), - ("GRAY22", 56, 56, 56), - ("GRAY23", 59, 59, 59), - ("GRAY24", 61, 61, 61), - ("GRAY25", 64, 64, 64), - ("GRAY26", 66, 66, 66), - ("GRAY27", 69, 69, 69), - ("GRAY28", 71, 71, 71), - ("GRAY29", 74, 74, 74), - ("GRAY3", 8, 8, 8), - ("GRAY30", 77, 77, 77), - ("GRAY31", 79, 79, 79), - ("GRAY32", 82, 82, 82), - ("GRAY33", 84, 84, 84), - ("GRAY34", 87, 87, 87), - ("GRAY35", 89, 89, 89), - ("GRAY36", 92, 92, 92), - ("GRAY37", 94, 94, 94), - ("GRAY38", 97, 97, 97), - ("GRAY39", 99, 99, 99), - ("GRAY4", 10, 10, 10), - ("GRAY40", 102, 102, 102), - ("GRAY41", 105, 105, 105), - ("GRAY42", 107, 107, 107), - ("GRAY43", 110, 110, 110), - ("GRAY44", 112, 112, 112), - ("GRAY45", 115, 115, 115), - ("GRAY46", 117, 117, 117), - ("GRAY47", 120, 120, 120), - ("GRAY48", 122, 122, 122), - ("GRAY49", 125, 125, 125), - ("GRAY5", 13, 13, 13), - ("GRAY50", 127, 127, 127), - ("GRAY51", 130, 130, 130), - ("GRAY52", 133, 133, 133), - ("GRAY53", 135, 135, 135), - ("GRAY54", 138, 138, 138), - ("GRAY55", 140, 140, 140), - ("GRAY56", 143, 143, 143), - ("GRAY57", 145, 145, 145), - ("GRAY58", 148, 148, 148), - ("GRAY59", 150, 150, 150), - ("GRAY6", 15, 15, 15), - ("GRAY60", 153, 153, 153), - ("GRAY61", 156, 156, 156), - ("GRAY62", 158, 158, 158), - ("GRAY63", 161, 161, 161), - ("GRAY64", 163, 163, 163), - ("GRAY65", 166, 166, 166), - ("GRAY66", 168, 168, 168), - ("GRAY67", 171, 171, 171), - ("GRAY68", 173, 173, 173), - ("GRAY69", 176, 176, 176), - ("GRAY7", 18, 18, 18), - ("GRAY70", 179, 179, 179), - ("GRAY71", 181, 181, 181), - ("GRAY72", 184, 184, 184), - ("GRAY73", 186, 186, 186), - ("GRAY74", 189, 189, 189), - ("GRAY75", 191, 191, 191), - ("GRAY76", 194, 194, 194), - ("GRAY77", 196, 196, 196), - ("GRAY78", 199, 199, 199), - ("GRAY79", 201, 201, 201), - ("GRAY8", 20, 20, 20), - ("GRAY80", 204, 204, 204), - ("GRAY81", 207, 207, 207), - ("GRAY82", 209, 209, 209), - ("GRAY83", 212, 212, 212), - ("GRAY84", 214, 214, 214), - ("GRAY85", 217, 217, 217), - ("GRAY86", 219, 219, 219), - ("GRAY87", 222, 222, 222), - ("GRAY88", 224, 224, 224), - ("GRAY89", 227, 227, 227), - ("GRAY9", 23, 23, 23), - ("GRAY90", 229, 229, 229), - ("GRAY91", 232, 232, 232), - ("GRAY92", 235, 235, 235), - ("GRAY93", 237, 237, 237), - ("GRAY94", 240, 240, 240), - ("GRAY95", 242, 242, 242), - ("GRAY96", 245, 245, 245), - ("GRAY97", 247, 247, 247), - ("GRAY98", 250, 250, 250), - ("GRAY99", 252, 252, 252), - ("HONEYDEW", 240, 255, 240), - ("HONEYDEW1", 240, 255, 240), - ("HONEYDEW2", 224, 238, 224), - ("HONEYDEW3", 193, 205, 193), - ("HONEYDEW4", 131, 139, 131), - ("HOTPINK", 255, 105, 180), - ("HOTPINK1", 255, 110, 180), - ("HOTPINK2", 238, 106, 167), - ("HOTPINK3", 205, 96, 144), - ("HOTPINK4", 139, 58, 98), - ("INDIANRED", 205, 92, 92), - ("INDIANRED1", 255, 106, 106), - ("INDIANRED2", 238, 99, 99), - ("INDIANRED3", 205, 85, 85), - ("INDIANRED4", 139, 58, 58), - ("IVORY", 255, 255, 240), - ("IVORY1", 255, 255, 240), - ("IVORY2", 238, 238, 224), - ("IVORY3", 205, 205, 193), - ("IVORY4", 139, 139, 131), - ("KHAKI", 240, 230, 140), - ("KHAKI1", 255, 246, 143), - ("KHAKI2", 238, 230, 133), - ("KHAKI3", 205, 198, 115), - ("KHAKI4", 139, 134, 78), - ("LAVENDER", 230, 230, 250), - ("LAVENDERBLUSH", 255, 240, 245), - ("LAVENDERBLUSH1", 255, 240, 245), - ("LAVENDERBLUSH2", 238, 224, 229), - ("LAVENDERBLUSH3", 205, 193, 197), - ("LAVENDERBLUSH4", 139, 131, 134), - ("LAWNGREEN", 124, 252, 0), - ("LEMONCHIFFON", 255, 250, 205), - ("LEMONCHIFFON1", 255, 250, 205), - ("LEMONCHIFFON2", 238, 233, 191), - ("LEMONCHIFFON3", 205, 201, 165), - ("LEMONCHIFFON4", 139, 137, 112), - ("LIGHTBLUE", 173, 216, 230), - ("LIGHTBLUE1", 191, 239, 255), - ("LIGHTBLUE2", 178, 223, 238), - ("LIGHTBLUE3", 154, 192, 205), - ("LIGHTBLUE4", 104, 131, 139), - ("LIGHTCORAL", 240, 128, 128), - ("LIGHTCYAN", 224, 255, 255), - ("LIGHTCYAN1", 224, 255, 255), - ("LIGHTCYAN2", 209, 238, 238), - ("LIGHTCYAN3", 180, 205, 205), - ("LIGHTCYAN4", 122, 139, 139), - ("LIGHTGOLDENROD", 238, 221, 130), - ("LIGHTGOLDENROD1", 255, 236, 139), - ("LIGHTGOLDENROD2", 238, 220, 130), - ("LIGHTGOLDENROD3", 205, 190, 112), - ("LIGHTGOLDENROD4", 139, 129, 76), - ("LIGHTGOLDENRODYELLOW", 250, 250, 210), - ("LIGHTGREEN", 144, 238, 144), - ("LIGHTGRAY", 211, 211, 211), - ("LIGHTPINK", 255, 182, 193), - ("LIGHTPINK1", 255, 174, 185), - ("LIGHTPINK2", 238, 162, 173), - ("LIGHTPINK3", 205, 140, 149), - ("LIGHTPINK4", 139, 95, 101), - ("LIGHTSALMON", 255, 160, 122), - ("LIGHTSALMON1", 255, 160, 122), - ("LIGHTSALMON2", 238, 149, 114), - ("LIGHTSALMON3", 205, 129, 98), - ("LIGHTSALMON4", 139, 87, 66), - ("LIGHTSEAGREEN", 32, 178, 170), - ("LIGHTSKYBLUE", 135, 206, 250), - ("LIGHTSKYBLUE1", 176, 226, 255), - ("LIGHTSKYBLUE2", 164, 211, 238), - ("LIGHTSKYBLUE3", 141, 182, 205), - ("LIGHTSKYBLUE4", 96, 123, 139), - ("LIGHTSLATEBLUE", 132, 112, 255), - ("LIGHTSLATEGRAY", 119, 136, 153), - ("LIGHTSTEELBLUE", 176, 196, 222), - ("LIGHTSTEELBLUE1", 202, 225, 255), - ("LIGHTSTEELBLUE2", 188, 210, 238), - ("LIGHTSTEELBLUE3", 162, 181, 205), - ("LIGHTSTEELBLUE4", 110, 123, 139), - ("LIGHTYELLOW", 255, 255, 224), - ("LIGHTYELLOW1", 255, 255, 224), - ("LIGHTYELLOW2", 238, 238, 209), - ("LIGHTYELLOW3", 205, 205, 180), - ("LIGHTYELLOW4", 139, 139, 122), - ("LIMEGREEN", 50, 205, 50), - ("LINEN", 250, 240, 230), - ("MAGENTA", 255, 0, 255), - ("MAGENTA1", 255, 0, 255), - ("MAGENTA2", 238, 0, 238), - ("MAGENTA3", 205, 0, 205), - ("MAGENTA4", 139, 0, 139), - ("MAROON", 176, 48, 96), - ("MAROON1", 255, 52, 179), - ("MAROON2", 238, 48, 167), - ("MAROON3", 205, 41, 144), - ("MAROON4", 139, 28, 98), - ("MEDIUMAQUAMARINE", 102, 205, 170), - ("MEDIUMBLUE", 0, 0, 205), - ("MEDIUMORCHID", 186, 85, 211), - ("MEDIUMORCHID1", 224, 102, 255), - ("MEDIUMORCHID2", 209, 95, 238), - ("MEDIUMORCHID3", 180, 82, 205), - ("MEDIUMORCHID4", 122, 55, 139), - ("MEDIUMPURPLE", 147, 112, 219), - ("MEDIUMPURPLE1", 171, 130, 255), - ("MEDIUMPURPLE2", 159, 121, 238), - ("MEDIUMPURPLE3", 137, 104, 205), - ("MEDIUMPURPLE4", 93, 71, 139), - ("MEDIUMSEAGREEN", 60, 179, 113), - ("MEDIUMSLATEBLUE", 123, 104, 238), - ("MEDIUMSPRINGGREEN", 0, 250, 154), - ("MEDIUMTURQUOISE", 72, 209, 204), - ("MEDIUMVIOLETRED", 199, 21, 133), - ("MIDNIGHTBLUE", 25, 25, 112), - ("MINTCREAM", 245, 255, 250), - ("MISTYROSE", 255, 228, 225), - ("MISTYROSE1", 255, 228, 225), - ("MISTYROSE2", 238, 213, 210), - ("MISTYROSE3", 205, 183, 181), - ("MISTYROSE4", 139, 125, 123), - ("MOCCASIN", 255, 228, 181), - ("MUPDFBLUE", 37, 114, 172), - ("NAVAJOWHITE", 255, 222, 173), - ("NAVAJOWHITE1", 255, 222, 173), - ("NAVAJOWHITE2", 238, 207, 161), - ("NAVAJOWHITE3", 205, 179, 139), - ("NAVAJOWHITE4", 139, 121, 94), - ("NAVY", 0, 0, 128), - ("NAVYBLUE", 0, 0, 128), - ("OLDLACE", 253, 245, 230), - ("OLIVEDRAB", 107, 142, 35), - ("OLIVEDRAB1", 192, 255, 62), - ("OLIVEDRAB2", 179, 238, 58), - ("OLIVEDRAB3", 154, 205, 50), - ("OLIVEDRAB4", 105, 139, 34), - ("ORANGE", 255, 165, 0), - ("ORANGE1", 255, 165, 0), - ("ORANGE2", 238, 154, 0), - ("ORANGE3", 205, 133, 0), - ("ORANGE4", 139, 90, 0), - ("ORANGERED", 255, 69, 0), - ("ORANGERED1", 255, 69, 0), - ("ORANGERED2", 238, 64, 0), - ("ORANGERED3", 205, 55, 0), - ("ORANGERED4", 139, 37, 0), - ("ORCHID", 218, 112, 214), - ("ORCHID1", 255, 131, 250), - ("ORCHID2", 238, 122, 233), - ("ORCHID3", 205, 105, 201), - ("ORCHID4", 139, 71, 137), - ("PALEGOLDENROD", 238, 232, 170), - ("PALEGREEN", 152, 251, 152), - ("PALEGREEN1", 154, 255, 154), - ("PALEGREEN2", 144, 238, 144), - ("PALEGREEN3", 124, 205, 124), - ("PALEGREEN4", 84, 139, 84), - ("PALETURQUOISE", 175, 238, 238), - ("PALETURQUOISE1", 187, 255, 255), - ("PALETURQUOISE2", 174, 238, 238), - ("PALETURQUOISE3", 150, 205, 205), - ("PALETURQUOISE4", 102, 139, 139), - ("PALEVIOLETRED", 219, 112, 147), - ("PALEVIOLETRED1", 255, 130, 171), - ("PALEVIOLETRED2", 238, 121, 159), - ("PALEVIOLETRED3", 205, 104, 137), - ("PALEVIOLETRED4", 139, 71, 93), - ("PAPAYAWHIP", 255, 239, 213), - ("PEACHPUFF", 255, 218, 185), - ("PEACHPUFF1", 255, 218, 185), - ("PEACHPUFF2", 238, 203, 173), - ("PEACHPUFF3", 205, 175, 149), - ("PEACHPUFF4", 139, 119, 101), - ("PERU", 205, 133, 63), - ("PINK", 255, 192, 203), - ("PINK1", 255, 181, 197), - ("PINK2", 238, 169, 184), - ("PINK3", 205, 145, 158), - ("PINK4", 139, 99, 108), - ("PLUM", 221, 160, 221), - ("PLUM1", 255, 187, 255), - ("PLUM2", 238, 174, 238), - ("PLUM3", 205, 150, 205), - ("PLUM4", 139, 102, 139), - ("POWDERBLUE", 176, 224, 230), - ("PURPLE", 160, 32, 240), - ("PURPLE1", 155, 48, 255), - ("PURPLE2", 145, 44, 238), - ("PURPLE3", 125, 38, 205), - ("PURPLE4", 85, 26, 139), - ("PY_COLOR", 240, 255, 210), - ("RED", 255, 0, 0), - ("RED1", 255, 0, 0), - ("RED2", 238, 0, 0), - ("RED3", 205, 0, 0), - ("RED4", 139, 0, 0), - ("ROSYBROWN", 188, 143, 143), - ("ROSYBROWN1", 255, 193, 193), - ("ROSYBROWN2", 238, 180, 180), - ("ROSYBROWN3", 205, 155, 155), - ("ROSYBROWN4", 139, 105, 105), - ("ROYALBLUE", 65, 105, 225), - ("ROYALBLUE1", 72, 118, 255), - ("ROYALBLUE2", 67, 110, 238), - ("ROYALBLUE3", 58, 95, 205), - ("ROYALBLUE4", 39, 64, 139), - ("SADDLEBROWN", 139, 69, 19), - ("SALMON", 250, 128, 114), - ("SALMON1", 255, 140, 105), - ("SALMON2", 238, 130, 98), - ("SALMON3", 205, 112, 84), - ("SALMON4", 139, 76, 57), - ("SANDYBROWN", 244, 164, 96), - ("SEAGREEN", 46, 139, 87), - ("SEAGREEN1", 84, 255, 159), - ("SEAGREEN2", 78, 238, 148), - ("SEAGREEN3", 67, 205, 128), - ("SEAGREEN4", 46, 139, 87), - ("SEASHELL", 255, 245, 238), - ("SEASHELL1", 255, 245, 238), - ("SEASHELL2", 238, 229, 222), - ("SEASHELL3", 205, 197, 191), - ("SEASHELL4", 139, 134, 130), - ("SIENNA", 160, 82, 45), - ("SIENNA1", 255, 130, 71), - ("SIENNA2", 238, 121, 66), - ("SIENNA3", 205, 104, 57), - ("SIENNA4", 139, 71, 38), - ("SKYBLUE", 135, 206, 235), - ("SKYBLUE1", 135, 206, 255), - ("SKYBLUE2", 126, 192, 238), - ("SKYBLUE3", 108, 166, 205), - ("SKYBLUE4", 74, 112, 139), - ("SLATEBLUE", 106, 90, 205), - ("SLATEBLUE1", 131, 111, 255), - ("SLATEBLUE2", 122, 103, 238), - ("SLATEBLUE3", 105, 89, 205), - ("SLATEBLUE4", 71, 60, 139), - ("SLATEGRAY", 112, 128, 144), - ("SNOW", 255, 250, 250), - ("SNOW1", 255, 250, 250), - ("SNOW2", 238, 233, 233), - ("SNOW3", 205, 201, 201), - ("SNOW4", 139, 137, 137), - ("SPRINGGREEN", 0, 255, 127), - ("SPRINGGREEN1", 0, 255, 127), - ("SPRINGGREEN2", 0, 238, 118), - ("SPRINGGREEN3", 0, 205, 102), - ("SPRINGGREEN4", 0, 139, 69), - ("STEELBLUE", 70, 130, 180), - ("STEELBLUE1", 99, 184, 255), - ("STEELBLUE2", 92, 172, 238), - ("STEELBLUE3", 79, 148, 205), - ("STEELBLUE4", 54, 100, 139), - ("TAN", 210, 180, 140), - ("TAN1", 255, 165, 79), - ("TAN2", 238, 154, 73), - ("TAN3", 205, 133, 63), - ("TAN4", 139, 90, 43), - ("THISTLE", 216, 191, 216), - ("THISTLE1", 255, 225, 255), - ("THISTLE2", 238, 210, 238), - ("THISTLE3", 205, 181, 205), - ("THISTLE4", 139, 123, 139), - ("TOMATO", 255, 99, 71), - ("TOMATO1", 255, 99, 71), - ("TOMATO2", 238, 92, 66), - ("TOMATO3", 205, 79, 57), - ("TOMATO4", 139, 54, 38), - ("TURQUOISE", 64, 224, 208), - ("TURQUOISE1", 0, 245, 255), - ("TURQUOISE2", 0, 229, 238), - ("TURQUOISE3", 0, 197, 205), - ("TURQUOISE4", 0, 134, 139), - ("VIOLET", 238, 130, 238), - ("VIOLETRED", 208, 32, 144), - ("VIOLETRED1", 255, 62, 150), - ("VIOLETRED2", 238, 58, 140), - ("VIOLETRED3", 205, 50, 120), - ("VIOLETRED4", 139, 34, 82), - ("WHEAT", 245, 222, 179), - ("WHEAT1", 255, 231, 186), - ("WHEAT2", 238, 216, 174), - ("WHEAT3", 205, 186, 150), - ("WHEAT4", 139, 126, 102), - ("WHITE", 255, 255, 255), - ("WHITESMOKE", 245, 245, 245), - ("YELLOW", 255, 255, 0), - ("YELLOW1", 255, 255, 0), - ("YELLOW2", 238, 238, 0), - ("YELLOW3", 205, 205, 0), - ("YELLOW4", 139, 139, 0), - ("YELLOWGREEN", 154, 205, 50), - }; - } - - public static float[] GetColor(string name) - { - try - { - (string, float, float, float) c = Utils.GetColorInfoList()[ - Utils.GetColorList().IndexOf(name.ToUpper()) - ]; - return new float[] { c.Item2 / 255.0f, c.Item3 / 255.0f, c.Item4 / 255.0f }; - } - catch //(Exception e) - { - return new float[] { 1, 1, 1 }; - } - } - - public static float[] GetColorHSV(string name) - { - (string, float, float, float) x; - try - { - x = GetColorInfoList()[GetColorList().IndexOf(name.ToUpper())]; - } - catch// (Exception e) - { - return new float[] { -1, -1, -1 }; - } - float r = x.Item2 / 255.0f; - float g = x.Item3 / 255.0f; - float b = x.Item4 / 255.0f; - float cmax = Math.Max(Math.Max(r, g), b); - float V = (float)Math.Round(cmax * 100, 1); - - float cmin = Math.Min(Math.Min(r, g), b); - float delta = cmax - cmin; - float hue = 0; - - if (delta == 0) - hue = 0; - else if (cmax == r) - hue = 60.0f * (((g - b) / delta) % 6); - else if (cmax == g) - hue = 60.0f * (((b - r) / delta) + 2); - else - hue = 60.0f * (((r - g) / delta) + 4); - float H = (int)(Math.Round(hue)); - float sat = 0; - if (cmax == 0) - sat = 0; - else - sat = delta / cmax; - float S = (int)Math.Round(sat * 100); - - return new float[] { H, S, V }; - } - - internal static PdfObj EnsureOCProperties(PdfDocument pdf) - { - PdfObj ocp = pdf.pdf_trailer() - .pdf_dict_get(new PdfObj("Root")) - .pdf_dict_get(new PdfObj("OCProperties")); - if (ocp.m_internal != null) - return ocp; - - PdfObj root = pdf.pdf_trailer().pdf_dict_get(new PdfObj("Root")); - ocp = root.pdf_dict_put_dict(new PdfObj("OCProperties"), 2); - ocp.pdf_dict_put_array(new PdfObj("OCGs"), 0); - PdfObj D = ocp.pdf_dict_put_dict(new PdfObj("D"), 5); - D.pdf_dict_put_array(new PdfObj("ON"), 0); - D.pdf_dict_put_array(new PdfObj("OFF"), 0); - D.pdf_dict_put_array(new PdfObj("Order"), 0); - D.pdf_dict_put_array(new PdfObj("RBGroups"), 0); - - return ocp; - } - - internal static string GetFontName(fz_font font) - { - string name = mupdf.mupdf.fz_font_name(new FzFont(font)); - int s = name.IndexOf("+"); - return name.Substring(s + 1, name.Length - s - 1); - } - - internal static void LoadEmbeddedDllForWindows() - { - string binaryDir = AppContext.BaseDirectory; - if (!File.Exists(binaryDir + "mupdfcpp64.dll")) - { - var assembly = Assembly.GetExecutingAssembly(); - var resourceStream = assembly.GetManifestResourceStream("mupdfcpp64.dll"); - var tempFile = File.Create(binaryDir + "mupdfcpp64.dll"); - - resourceStream?.CopyTo(tempFile); - resourceStream?.Dispose(); - tempFile.Dispose(); - } - - if (!File.Exists(binaryDir + "mupdfcsharp.dll")) - { - var assembly = Assembly.GetExecutingAssembly(); - var resourceStream = assembly.GetManifestResourceStream("mupdfcsharp.dll"); - var tempFile = File.Create(binaryDir + "mupdfcsharp.dll"); - - resourceStream?.CopyTo(tempFile); - resourceStream?.Dispose(); - tempFile.Dispose(); - } - } - - internal static void LoadEmbeddedDllForLinux() - { - string binaryDir = AppContext.BaseDirectory; - if (!File.Exists(binaryDir + "libmupdf.so")) - { - var assembly = Assembly.GetExecutingAssembly(); - var resourceStream = assembly.GetManifestResourceStream("libmupdf.so"); - if (resourceStream != null) - { - var tempFile = File.Create(binaryDir + "libmupdf.so"); - resourceStream.CopyTo(tempFile); - resourceStream.Dispose(); - tempFile.Dispose(); - } - } - - if (!File.Exists(binaryDir + "libmupdf.so.26.0")) - { - var assembly = Assembly.GetExecutingAssembly(); - var resourceStream = assembly.GetManifestResourceStream("libmupdf.so.26.0"); - if (resourceStream != null) - { - var tempFile = File.Create(binaryDir + "libmupdf.so.26.0"); - resourceStream.CopyTo(tempFile); - resourceStream.Dispose(); - tempFile.Dispose(); - } - } - - if (!File.Exists(binaryDir + "libmupdfcpp.so")) - { - var assembly = Assembly.GetExecutingAssembly(); - var resourceStream = assembly.GetManifestResourceStream("libmupdfcpp.so"); - if (resourceStream != null) - { - var tempFile = File.Create(binaryDir + "libmupdfcpp.so"); - resourceStream.CopyTo(tempFile); - resourceStream.Dispose(); - tempFile.Dispose(); - } - } - - if (!File.Exists(binaryDir + "libmupdfcpp.so.26.0")) - { - var assembly = Assembly.GetExecutingAssembly(); - var resourceStream = assembly.GetManifestResourceStream("libmupdfcpp.so.26.0"); - if (resourceStream != null) - { - var tempFile = File.Create(binaryDir + "libmupdfcpp.so.26.0"); - resourceStream.CopyTo(tempFile); - resourceStream.Dispose(); - tempFile.Dispose(); - } - } - - if (!File.Exists(binaryDir + "mupdfcsharp.dll")) - { - var assembly = Assembly.GetExecutingAssembly(); - var resourceStream = assembly.GetManifestResourceStream("mupdfcsharp.so"); - if (resourceStream != null) - { - var tempFile = File.Create(binaryDir + "mupdfcsharp.dll"); - resourceStream.CopyTo(tempFile); - resourceStream.Dispose(); - tempFile.Dispose(); - } - } - } - - internal static void AddLayerConfig( - PdfDocument pdf, - string name, - string creator, - OCLayerConfig on - ) - { - try - { - PdfObj ocp = Utils.EnsureOCProperties(pdf); - PdfObj configs = ocp.pdf_dict_get(new PdfObj("Configs")); - if (configs.pdf_is_array() == 0) - configs = ocp.pdf_dict_put_array(new PdfObj("Configs"), 1); - PdfObj d = pdf.pdf_new_dict(5); - d.pdf_dict_put_text_string(new PdfObj("Name"), name); - if (!string.IsNullOrEmpty(creator)) - d.pdf_dict_put_text_string(new PdfObj("Creator"), creator); - d.pdf_dict_put(new PdfObj("BaseState"), new PdfObj("OFF")); - PdfObj onarray = d.pdf_dict_put_array(new PdfObj("ON"), 5); - if (on == null) { } - else - { - PdfObj ocgs = ocp.pdf_dict_get(new PdfObj("OCGs")); - int xref = on.Number; - PdfObj ind = pdf.pdf_new_indirect(xref, 0); - if (ocgs.pdf_array_contains(ind) != 0) - onarray.pdf_array_push(ind); - } - configs.pdf_array_push(d); - } - catch// (Exception e) - { - throw; - } - } - - internal static IntPtr Utf16_Utf8Ptr(string s) - { - byte[] utf8Bytes = Encoding.UTF8.GetBytes(s); - byte[] nullTerminator = { 0 }; - byte[] bytes = utf8Bytes.Concat(nullTerminator).ToArray(); - IntPtr utf8Ptr = Marshal.AllocHGlobal(bytes.Length); - Marshal.Copy(bytes, 0, utf8Ptr, bytes.Length); - - return utf8Ptr; - } - - internal static void SetDotCultureForNumber() - { - CultureInfo culture = new CultureInfo("en-US"); // or any specific culture you want - culture.NumberFormat.NumberDecimalSeparator = "."; - culture.NumberFormat.CurrencyDecimalSeparator = "."; - - // Set for current thread - CultureInfo.CurrentCulture = culture; - CultureInfo.CurrentUICulture = culture; - - // Set as default for all new threads (.NET Core 2.0+) - CultureInfo.DefaultThreadCurrentCulture = culture; - CultureInfo.DefaultThreadCurrentUICulture = culture; - } - - /// - /// Converts a float to string with dot as decimal separator, without scientific notation. - /// - public static string FloatToString(float value) - { - return value.ToString("0.#######", CultureInfo.InvariantCulture); - } - - /// - /// Converts a double to string with dot as decimal separator, without scientific notation. - /// - public static string DoubleToString(double value) - { - return value.ToString("0.#################", CultureInfo.InvariantCulture); - } - - /// - /// Loads embedded DLL from resources and saves to disk - /// - private static void LoadEmbeddedDll(string resourceName, string fileName) - { - var assembly = typeof(Utils).Assembly; - - // Try to extract to the same directory as the executing assembly first - string targetPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); - - // If we can't write there (permissions), use temp directory - if (!TryExtractResource(resourceName, targetPath, assembly)) - { - targetPath = Path.Combine(Path.GetTempPath(), "MuPDF.NET", - assembly.GetName().Version.ToString(), fileName); - - Directory.CreateDirectory(Path.GetDirectoryName(targetPath)); - - if (!TryExtractResource(resourceName, targetPath, assembly)) - { - throw new Exception($"Failed to extract embedded DLL: {resourceName}"); - } - } + double cos = dx / length; + double sin = dy / length; - // Load the DLL - var handle = LoadLibrary(targetPath); - if (handle == IntPtr.Zero) - { - var errorCode = Marshal.GetLastWin32Error(); - throw new Exception($"Failed to load DLL: {targetPath}. Error code: {errorCode}"); - } - } - - private static bool TryExtractResource(string resourceName, string targetPath, System.Reflection.Assembly assembly) - { - try - { - // Check if file already exists with same size - if (File.Exists(targetPath)) - { - using (var stream = assembly.GetManifestResourceStream(resourceName)) - { - if (stream != null) - { - var fileInfo = new System.IO.FileInfo(targetPath); - if (fileInfo.Length == stream.Length) - { - return true; // File already exists with correct size - } - } - } - } - - // Extract the resource - using (var stream = assembly.GetManifestResourceStream(resourceName)) - { - if (stream == null) return false; - - using (var fileStream = File.Create(targetPath)) - { - stream.CopyTo(fileStream); - } - } - - return true; - } - catch - { - return false; - } + var translate = new Matrix(1, 0, 0, 1, -p1.X, -p1.Y); + var rotate = new Matrix(cos, -sin, sin, cos, 0, 0); + return translate * rotate; } - [DllImport("kernel32.dll", SetLastError = true)] - private static extern IntPtr LoadLibrary(string lpFileName); - - [DllImport("libdl.so.2", EntryPoint = "dlopen")] - private static extern IntPtr DlOpen(string fileName, int flags); - - private const int RTLD_NOW = 2; - - /// - /// Loads Leptonica DLL from embedded resources - /// - private static void LoadEmbeddedLeptonicaDll() + private static int RecognizeImageType(mupdf.FzBuffer buffer) { - try - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - string resourceName; - string dllName = "leptonica-1.77.0.dll"; - - if (Environment.Is64BitProcess) - { - resourceName = "MuPDF.NET.Resources.leptonica-1.77.0.x64.dll"; - } - else - { - resourceName = "MuPDF.NET.Resources.leptonica-1.77.0.x86.dll"; - } - - LoadEmbeddedDll(resourceName, dllName); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - // For Linux, you would need to embed and extract .so files - // This is a placeholder for Linux support - // string resourceName = "MuPDF.NET.Resources.libleptonica.so.5"; - // LoadEmbeddedDll(resourceName, "libleptonica.so.5"); - } - } - catch (Exception ex) - { - Console.WriteLine($"Failed to load embedded Leptonica DLL: {ex.Message}"); - // Don't throw - allow fallback to system-installed DLLs - } + // #log('calling mfz_recognize_image_format with {c!r=}') + var outparams = new mupdf.ll_fz_buffer_storage_outparams(); + mupdf.mupdf.ll_fz_buffer_storage_outparams_fn(buffer.m_internal, outparams); + return mupdf.mupdf.fz_recognize_image_format(outparams.datap); } - public static void InitApp() + private static string ImageExtensionFromType(int type) { - lock (MuPDFLock) + // result[dictkey_ext] = JM_image_extension(type_) + switch (type) { - if (Utils.IsInitialized) - return; - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Utils.SetDotCultureForNumber(); - //Utils.LoadEmbeddedLeptonicaDll(); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - Utils.SetDotCultureForNumber(); - //Utils.LoadEmbeddedLeptonicaDll(); - } - else - { - return; - } - - Utils.IsInitialized = true; + // if type_ == mupdf.FZ_IMAGE_LZW: return "lzw" + case int _ when type == mupdf.mupdf.FZ_IMAGE_LZW: return "lzw"; + // if type_ == mupdf.FZ_IMAGE_RLD: return "rld" + case int _ when type == mupdf.mupdf.FZ_IMAGE_RLD: return "rld"; + // if type_ == mupdf.FZ_IMAGE_JBIG2: return "jb2" + case int _ when type == mupdf.mupdf.FZ_IMAGE_JBIG2: return "jb2"; + // if type_ == mupdf.FZ_IMAGE_PNG: return "png" + case int _ when type == mupdf.mupdf.FZ_IMAGE_PNG: return "png"; + // if type_ == mupdf.FZ_IMAGE_JPEG: return "jpeg" + case int _ when type == mupdf.mupdf.FZ_IMAGE_JPEG: return "jpeg"; + // if type_ == mupdf.FZ_IMAGE_JXR: return "jxr" + case int _ when type == mupdf.mupdf.FZ_IMAGE_JXR: return "jxr"; + // if type_ == mupdf.FZ_IMAGE_JPX: return "jpx" + case int _ when type == mupdf.mupdf.FZ_IMAGE_JPX: return "jpx"; + // if type_ == mupdf.FZ_IMAGE_BMP: return "bmp" + case int _ when type == mupdf.mupdf.FZ_IMAGE_BMP: return "bmp"; + // if type_ == mupdf.FZ_IMAGE_GIF: return "gif" + case int _ when type == mupdf.mupdf.FZ_IMAGE_GIF: return "gif"; + // if type_ == mupdf.FZ_IMAGE_TIFF: return "tiff" + case int _ when type == mupdf.mupdf.FZ_IMAGE_TIFF: return "tiff"; + // if type_ == mupdf.FZ_IMAGE_PNM: return "pnm" + case int _ when type == mupdf.mupdf.FZ_IMAGE_PNM: return "pnm"; + // if type_ == mupdf.FZ_IMAGE_PSD: return "psd" + default: return "n/a"; } } /// - /// Calculate area of rectangle. parameter is one of 'px' (default), 'in', 'cm', or 'mm'. - /// - /// - /// - /// - public static float GetArea(Rect rect, string unit = "px") - { - Dictionary u = new Dictionary() - { - { "px", (1f, 1f) }, - { "in", (1f, 72.0f) }, - { "cm", (2.54f, 72.0f) }, - { "mm", (25.4f, 72f) } - }; - float f = (float)Math.Pow(u[unit].Item1 / u[unit].Item2, 2); - - return f * rect.Width * rect.Height; - } - - /// - /// Return basic properties of an image. + /// Convert a direction value (tuple or array stored in a dictionary) + /// to a (cos, sin) tuple. /// - /// bytes array opened - /// - /// - public static ImageInfo GetImageProfile(byte[] image, int keepImage = 0) - { - if (image == null) - return null; - - int len = image.Length; - if (len < 8) - { - Console.WriteLine("bad image data"); - return null; - } - - //nint swigImage = Marshal.AllocHGlobal(len); - IntPtr swigImage = Marshal.AllocHGlobal(len); - Marshal.Copy(image, 0, swigImage, len); - SWIGTYPE_p_unsigned_char c = new SWIGTYPE_p_unsigned_char(swigImage, true); - int type = mupdf.mupdf.fz_recognize_image_format(c); - if (type == (int)ImageType.FZ_IMAGE_UNKNOWN) - return null; - - // get properties for imageinfo - FzBuffer res = null; - if (keepImage != 0) - res = mupdf.mupdf.fz_new_buffer_from_copied_data(c, (uint)len); - else - res = mupdf.mupdf.fz_new_buffer_from_shared_data(c, (uint)len); - FzImage img = mupdf.mupdf.fz_new_image_from_buffer(res); - FzMatrix ctm = mupdf.mupdf.fz_image_orientation_matrix(img); - (int xres, int yres) = img.fz_image_resolution(); - byte orientation = img.fz_image_orientation(); - string csName = img.colorspace().fz_colorspace_name(); - - // create imageinfo to return - ImageInfo ret = new ImageInfo() - { - Width = img.w(), - Height = img.h(), - Orientation = orientation, - Matrix = new Matrix(ctm), - Xres = xres, - Yres = yres, - ColorSpace = img.n(), - Bpc = img.bpc(), - Ext = GetImageExtension(type), - CsName = csName - }; - - if (keepImage != 0) - ret.Image = BinFromBuffer(res); - return ret; - } - - public static string ConversionHeader(string i, string filename = "unknown") + private static (double cos, double sin) DictToDir(object dirObj) { - string t = i.ToLower(); - string html = - @" - - - - - - - "; - - string xml = - $@" - - - "; - - string xhtml = - @" - - - - - - - - "; - - string r = ""; - string json = $"{{\"document\": \"{filename}\", \"pages\": [\n"; - if (t == "html") - r = html; - else if (t == "json") - r = json; - else if (t == "xml") - r = xml; - else if (t == "xhtml") - r = xhtml; - else - r = ""; - - return r; + if (dirObj is ValueTuple tuple) + return (tuple.Item1, tuple.Item2); + if (dirObj is IList list && list.Count >= 2) + return (list[0], list[1]); + if (dirObj is double[] arr && arr.Length >= 2) + return (arr[0], arr[1]); + if (dirObj is float[] farr && farr.Length >= 2) + return (farr[0], farr[1]); + if (dirObj is IList olist && olist.Count >= 2) + return (Convert.ToDouble(olist[0]), Convert.ToDouble(olist[1])); + throw new ArgumentException("cannot extract direction from object"); } /// - /// + /// Convert a bbox value stored in a dictionary to a . + /// Accepts Rect, double[], float[], or IList<double>. /// - /// - /// - public static string ConversionTrailer(string i) - { - string t = i.ToLower(); - string text = ""; - string json = "]\n}"; - string html = "\n\n"; - string xml = "\n"; - string xhtml = html; - string r = ""; - - if (t == "html") - r = html; - else if (t == "json") - r = json; - else if (t == "xml") - r = xml; - else if (t == "xhtml") - r = xhtml; - else - r = text; - - return r; - } - - public static Rect EMPTY_RECT() - { - return new Rect(Utils.FZ_MAX_INF_RECT, Utils.FZ_MAX_INF_RECT, Utils.FZ_MIN_INF_RECT, Utils.FZ_MIN_INF_RECT); - } - - public static Quad EMPTY_QUAD() - { - return Utils.EMPTY_RECT().Quad; - } - - public static string GetSortedText(Page page, Rect clip = null, int flags = 0, TextPage textpage = null, int tolerance = 3) - { - string LineText(Rect _clip, List<(Rect, string)> _line) - { - _line.Sort((l1, l2) => - { - return (int)((l1.Item1.X0 - l2.Item1.X0) * 10); - }); - string _ltext = ""; - float x1 = _clip.X0; - Rect _lrect = Utils.EMPTY_RECT(); - - foreach ((Rect r, string t) in _line) - { - _lrect = _lrect | r; // update _line bbox - int dist = Math.Max((int)(Math.Round((r.X0 - x1) / r.Width * t.Length)), x1 == _clip.X0 ? 0 : 1); // number of space chars - _ltext += new string(' ', dist) + t; - x1 = r.X1; - } - - return _ltext; - } - - List wordblocks = Utils.GetTextWords(page, clip, flags, textpage, sort: true, tolerance: tolerance); - - List<(Rect, string)> words = new List<(Rect, string)>(); - foreach (WordBlock block in wordblocks) - words.Add((new Rect(block.X0, block.Y0, block.X1, block.Y1), block.Text)); - - if (words.Count == 0 || words == null) - return ""; - Rect totalBox = Utils.EMPTY_RECT(); - foreach ((Rect wr, string text) in words) - totalBox = totalBox | wr; - - List<(Rect, string)> lines = new List<(Rect, string)>(); - List<(Rect, string)> line = new List<(Rect, string)>() { words[0] }; - Rect lrect = words[0].Item1; - string ltext = ""; - - foreach ((Rect wr, string text) in words.Skip(1)) - { - (Rect w0r, string _) = line[line.Count-1]; - - if (Math.Abs(lrect.Y0 - wr.Y0) < tolerance || Math.Abs(lrect.Y1 - wr.Y1) <= tolerance) - { - line.Add((lrect, text)); - lrect |= wr; - } - else - { - ltext = LineText(totalBox, line); - lines.Add((lrect, text)); - line = new List<(Rect, string)>() { (wr, text) }; - lrect = wr; - } - } - - ltext = LineText(totalBox, line); - lines.Add((lrect, ltext)); - - lines.Sort((l1, l2) => { return (int)((l1.Item1.Y1 - l2.Item1.Y1) * 10); }); - - string ret = lines[0].Item2; - float y1 = lines[0].Item1.Y1; - - foreach ((Rect lr, string lt) in lines.Skip(1)) - { - int distance = Math.Min((int)(Math.Round((lr.Y0 - y1) / lr.Height)), 5); - string breaks = new String('\n', distance + 1); - ret += breaks + lt; - y1 = lr.Y1; - } - - return ret; - } - - static bool IsRtl(string text) + private static Rect DictToRect(object bboxObj) { - foreach (char c in text) - { - if (char.IsWhiteSpace(c) || char.IsPunctuation(c)) - continue; - - var direction = CharUnicodeInfo.GetUnicodeCategory(c); - if (direction == UnicodeCategory.OtherLetter || - direction == UnicodeCategory.LetterNumber || - direction == UnicodeCategory.LowercaseLetter || - direction == UnicodeCategory.UppercaseLetter) - { - // Check if it's Hebrew, Arabic, or other RTL scripts - if ((c >= '\u0590' && c <= '\u08FF') || // Hebrew, Arabic ranges - (c >= '\uFB1D' && c <= '\uFEFC')) // Hebrew presentation forms, Arabic presentation forms - { - return true; // RTL - } - else - { - return false; // LTR - } - } - } - - // Default: assume LTR if no strong character found - return false; - } - - public static string GetTextWithLayout(Page page, Rect clip = null, int flags = 0, int tolerance = 5) + if (bboxObj is Rect r) + return r; + if (bboxObj is double[] darr && darr.Length >= 4) + return new Rect(darr[0], darr[1], darr[2], darr[3]); + if (bboxObj is float[] farr && farr.Length >= 4) + return new Rect(farr[0], farr[1], farr[2], farr[3]); + if (bboxObj is IList dlist && dlist.Count >= 4) + return new Rect(dlist[0], dlist[1], dlist[2], dlist[3]); + if (bboxObj is IList olist && olist.Count >= 4) + return new Rect( + Convert.ToDouble(olist[0]), + Convert.ToDouble(olist[1]), + Convert.ToDouble(olist[2]), + Convert.ToDouble(olist[3])); + throw new ArgumentException("cannot extract Rect from object"); + } + + public static List> MakeTable( + Rect rect, + int cols = 1, + int rows = 1) { - string LineText(Rect _clip, List<(Rect, string)> _line, int _tolerance) - { - _line.Sort((l1, l2) => - { - return (int)((l1.Item1.X0 - l2.Item1.X0) * 10); - }); - string _ltext = ""; - Rect _prevRect = Utils.EMPTY_RECT(); - - string LRM = "\u200E"; // Left-to-Right Mark - string RLM = "\u200F"; // Right-to-Left Mark - bool prevIsRLM = false; + /* + Return a list of (rows x cols) equal sized rectangles. + */ - foreach ((Rect r, string t) in _line) - { - int spaceCount = 0; - if (_prevRect.IsEmpty == true) - spaceCount = (int)Math.Max(0, (r.X0) / _tolerance); - else - { - float dist = r.X0 - _prevRect.X1; - spaceCount = (int)Math.Max(0, dist / _tolerance); - if (spaceCount > 1) - spaceCount = (int)(r.X0) / _tolerance - _ltext.Length; - else if (dist > 1) - spaceCount = 1; - } - if (spaceCount < 0) - { - spaceCount = 0; - } - _prevRect = r; - _ltext += new string(' ', spaceCount); - if (Utils.IsRtl(t)) - { - if (!prevIsRLM) - _ltext += RLM; - else - _ltext += LRM; - prevIsRLM = true; - } - else - { - if (prevIsRLM) - _ltext += LRM; - prevIsRLM = false; - } + // ensure valid rect + if (rect.IsEmpty || rect.IsInfinite) + throw new ArgumentException("rect must be finite and not empty"); + Point tl = rect.TopLeft; - // Replace all groups of 1 or more spaces with a thin space - string t1 = t.Replace(" ", " "); - _ltext += t1; - } + double height = rect.Height / rows; // height of one table cell + double width = rect.Width / cols; // width of one table cell - return _ltext; - } + // deltas + Rect deltaH = new Rect(width, 0, width, 0); + Rect deltaV = new Rect(0, height, 0, height); - // check parameters - if (flags == 0) - { - flags = (int)( - TextFlags.TEXT_PRESERVE_WHITESPACE - | TextFlags.TEXT_PRESERVE_LIGATURES - | TextFlags.TEXT_MEDIABOX_CLIP - //| TextFlags.TEXT_PRESERVE_IMAGES - | TextFlags.TEXT_INHIBIT_SPACES - //| TextFlags.TEXT_DEHYPHENATE - | TextFlags.TEXT_PRESERVE_SPANS - | TextFlags.TEXT_CID_FOR_UNKNOWN_UNICODE - | TextFlags.TEXT_ACCURATE_BBOXES - ); - } - if (clip == null) - clip = page.Rect; - if (tolerance <= 0) - tolerance = 5; + // first rectangle + Rect r = new Rect( + tl.X, + tl.Y, + tl.X + width, + tl.Y + height); - List<(Rect, string)>[] words = new List<(Rect, string)>[2]; - words[0] = new List<(Rect, string)>(); - words[1] = new List<(Rect, string)>(); + // make the first row + List row = new List { r }; - foreach (Block block in page.GetText("dict", clip:clip, flags: flags, sort:true).Blocks) + for (int i = 1; i < cols; i++) { - if (block.Lines != null) - { - foreach (Line _line in block.Lines) - { - if (_line == null) - continue; - foreach (Span span in _line.Spans) - { - // horizontal - Rect spanRect = new Rect(span.Bbox.X0, span.Bbox.Y0, span.Bbox.X1, span.Bbox.Y1); - if (page.Rotation != 0) - spanRect = spanRect.Transform(page.RotationMatrix); - if (_line.Dir.Y == 0) - words[0].Add((spanRect, span.Text)); - else - words[1].Add((spanRect, span.Text)); - } - } - } + r += deltaH; // build next rect to the right + row.Add(r); } - // sort again. - words[0].Sort((w1, w2) => - { - if (Utils.IsSameLine(w1.Item1, w2.Item1)) - { - return w1.Item1.X0.CompareTo(w2.Item1.X0); - } - else - { - float center1 = (w1.Item1.Y0 + w1.Item1.Y1) / 2; - float center2 = (w2.Item1.Y0 + w2.Item1.Y1) / 2; - - return center1.CompareTo(center2); - } - }); - words[1].Sort((w1, w2) => + // make result, starts with first row + List> rects = new List> { - if (Utils.IsSameLine(w1.Item1, w2.Item1)) - { - return w1.Item1.X0.CompareTo(w2.Item1.X0); - } - else - { - float center1 = (w1.Item1.Y0 + w1.Item1.Y1) / 2; - float center2 = (w2.Item1.Y0 + w2.Item1.Y1) / 2; - - return center1.CompareTo(center2); - } - }); + row + }; - string ret = ""; - for (int i = 0; i < 2; i++) + for (int i = 1; i < rows; i++) { - ret += "\n"; - if (words[i].Count == 0) - { - continue; - } - Rect totalBox = Utils.EMPTY_RECT(); - foreach ((Rect wr, string text) in words[i]) - totalBox = totalBox | wr; - - List<(Rect, string)> lines = new List<(Rect, string)>(); - List<(Rect, string)> line = new List<(Rect, string)>() { words[i][0] }; - Rect lrect = new Rect(words[i][0].Item1); - string ltext = ""; - - foreach ((Rect wr, string text) in words[i].Skip(1)) - { - (Rect w0r, string _) = line[line.Count - 1]; + List prevRow = rects[i - 1]; + List newRow = new List(); - if (w0r.EqualTo(wr)) - { - continue; - } - - if (w0r.X0 < wr.X0 && Utils.IsSameLine(w0r, wr)) - { - line.Add((wr, text)); - lrect |= wr; - } - else - { - ltext = LineText(totalBox, line, tolerance); - lines.Add((lrect, ltext)); - line = new List<(Rect, string)>() { (wr, text) }; - lrect = new Rect(wr); - } - } - - ltext = LineText(totalBox, line, tolerance); - lines.Add((lrect, ltext)); - - lines.Sort((l1, l2) => { return (int)((l1.Item1.Y1 - l2.Item1.Y1) * 10); }); - - ret += lines[0].Item2; - float x0 = lines[0].Item1.X0; - float y1 = lines[0].Item1.Y1; - - foreach ((Rect lr, string lt) in lines.Skip(1)) + // for each previous cell add its downward copy + foreach (Rect cell in prevRow) { - int distance = Math.Min((int)(Math.Round((lr.Y0 - y1) / lr.Height)), tolerance); - if (distance < 0) - { - distance = 0; - } - string breaks = new String('\n', distance + 1); - ret += breaks + lt; - x0 = lr.X0; - y1 = lr.Y1; + newRow.Add(cell + deltaV); } - } - - return ret; - } - - public static bool IsSameLine(Rect rect0, Rect rect1, float tolerance = 5.0f) - { - if (rect0.IsEmpty || rect1.IsEmpty) - return false; - if (rect0.Y0 <= rect1.Y0) - { - if (rect0.Y1 <= rect1.Y0) - { - return false; - } - else - { - if (rect0.Y1 >= rect1.Y1) - return true; - else - { - float overlappedHeight = rect0.Y1 - rect1.Y0; - if (overlappedHeight < rect1.Height / 2) - return false; - else - return true; - } - } - } - else - { - if (rect1.Y1 <= rect0.Y0) - { - return false; - } - else - { - if (rect1.Y1 >= rect0.Y1) - return true; - else - { - float overlappedHeight = rect1.Y1 - rect0.Y0; - if (overlappedHeight < rect1.Height / 2) - return false; - else - return true; - } - } + rects.Add(newRow); } - return false; - } - - public static int GetBlankLines(Rect rect0, Rect rect1) - { - float distanceBetweenRect = 0f; - float minLineHeight = Math.Min(rect0.Height, rect1.Height); - if (rect0.Y1 < rect1.Y0) - distanceBetweenRect = rect1.Y0 - rect0.Y1; - else if (rect1.Y1 < rect0.Y0) - distanceBetweenRect = rect0.Y0 - rect1.Y1; - - if (minLineHeight > 0f) - return (int)(distanceBetweenRect/minLineHeight); - - return 0; - } - - public static bool IsHorizontalNeighbors(Rect rect0, Rect rect1, float yTolerance = 5.0f, float xGapTolerance = 10.0f) - { - // Check if they're on the same line (vertically aligned) - bool sameLine = IsSameLine(rect0, rect1, yTolerance); - - // Check if they are next to each other (b is to the right of a, or vice versa) - bool aBeforeB = rect0.X1 <= rect1.X0 && (rect1.X0 - rect0.X1) < xGapTolerance; - bool bBeforeA = rect1.X1 <= rect0.X0 && (rect0.X0 - rect1.X1) < xGapTolerance; - - return sameLine && (aBeforeB || bBeforeA); + return rects; } } - - } diff --git a/MuPDF.NET/Widget.cs b/MuPDF.NET/Widget.cs index 72b6bcee..6db248cf 100644 --- a/MuPDF.NET/Widget.cs +++ b/MuPDF.NET/Widget.cs @@ -1,558 +1,354 @@ -using mupdf; -using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; -using System.Data; -using System.Linq; namespace MuPDF.NET { - public class Widget + /// + /// Class describing a PDF form field ('widget'). + /// + public class Widget : IDisposable { - static Widget() + private mupdf.PdfAnnot _nativeWidget; + private bool _disposed; + internal Page Parent { get; } + + internal Widget(mupdf.PdfAnnot widget, Page page) { - Utils.InitApp(); + _nativeWidget = widget; + Parent = page; } - /// - /// A list of up to 4 floats defining the field’s border color - /// - public float[] BorderColor { get; set; } - - /// - /// A string defining the line style of the field’s border - /// - public string BorderStyle { get; set; } - - /// - /// A float defining the width of the border line - /// - public float BorderWidth { get; set; } + // ─── Properties ───────────────────────────────────────────────── /// - /// A list/tuple of integers defining the dash properties of the border line + /// Field type as enum. /// - public int[] BorderDashes { get; set; } + public WidgetType FieldType + { + get + { + var t = mupdf.mupdf.pdf_widget_type(_nativeWidget); + return (WidgetType)(int)t; + } + } /// - /// A sequence of strings defining the valid choices of list boxes and combo boxes + /// Field type as string. /// - public List ChoiceValues { get; set; } - - public int RbParent { get; set; } + public string FieldTypeString + { + get + { + return FieldType switch + { + WidgetType.Button => "Button", + WidgetType.Text => "Text", + WidgetType.ComboBox => "Choice", + WidgetType.ListBox => "Choice", + WidgetType.Signature => "Signature", + _ => "Unknown" + }; + } + } /// - /// A mandatory string defining the field’s name + /// Field name. /// - public string FieldName { get; set; } + public string FieldName + { + get + { + var name = mupdf.mupdf.pdf_load_field_name(mupdf.mupdf.pdf_annot_obj(_nativeWidget)); + return name ?? ""; + } + } /// - /// An optional string containing an “alternate” field name + /// Field label (tooltip). /// - public string FieldLabel { get; set; } + public string FieldLabel + { + get + { + var label = mupdf.mupdf.pdf_dict_get_text_string(mupdf.mupdf.pdf_annot_obj(_nativeWidget), + mupdf.mupdf.pdf_new_name("TU")); + return label ?? ""; + } + } /// - /// The value of the field + /// Field value. /// - public string FieldValue { get; set; } + public string FieldValue + { + get => mupdf.mupdf.pdf_field_value(mupdf.mupdf.pdf_annot_obj(_nativeWidget)) ?? ""; + set + { + mupdf.mupdf.pdf_set_field_value(Parent.Parent.NativePdfDocument, + mupdf.mupdf.pdf_annot_obj(_nativeWidget), value, 0); + mupdf.mupdf.pdf_update_annot(_nativeWidget); + } + } /// - /// An integer defining a large amount of properties of a field + /// Field default value. /// - public int FieldFlags { get; set; } + public string FieldDefault + { + get + { + var dv = mupdf.mupdf.pdf_dict_get_text_string(mupdf.mupdf.pdf_annot_obj(_nativeWidget), + mupdf.mupdf.pdf_new_name("DV")); + return dv ?? ""; + } + } /// - /// A mandatory integer defining the field type + /// Field flags. /// - public int FieldType { get; set; } + public int FieldFlags => mupdf.mupdf.pdf_field_flags(mupdf.mupdf.pdf_annot_obj(_nativeWidget)); /// - /// + /// Widget rectangle. /// - public int FieldDisplay { get; set; } + public Rect Rect + { + get + { + var r = mupdf.mupdf.pdf_bound_annot(_nativeWidget); + return new Rect(r.x0, r.y0, r.x1, r.y1); + } + } /// - /// A string describing (and derived from) the field type + /// Widget xref number. /// - public string FieldTypeString { get; set; } + public int Xref => mupdf.mupdf.pdf_to_num(mupdf.mupdf.pdf_annot_obj(_nativeWidget)); /// - /// A list of up to 4 floats defining the field’s background color + /// Check if field is read only. /// - public float[] FillColor { get; set; } + public bool IsReadOnly + { + get + { + int flags = FieldFlags; + return (flags & 1) != 0; // PDF_FIELD_IS_READ_ONLY + } + } /// - /// The caption string of a button-type field + /// Check if field is required. /// - public string ButtonCaption { get; set; } + public bool IsRequired + { + get + { + int flags = FieldFlags; + return (flags & 2) != 0; + } + } /// - /// A bool indicating the signing status of a signature field, else false + /// Maximum text length. /// - public bool IsSigned { get; set; } + public int MaxLen + { + get + { + var ml = mupdf.mupdf.pdf_dict_get_int(mupdf.mupdf.pdf_annot_obj(_nativeWidget), + mupdf.mupdf.pdf_new_name("MaxLen")); + return ml; + } + } /// - /// A list of 1, 3 or 4 floats defining the text color + /// Check if text field is multi-line. /// - public float[] TextColor { get; set; } + public bool IsMultiline + { + get + { + int flags = FieldFlags; + return (flags & (1 << 12)) != 0; + } + } /// - /// A string defining the font to be used + /// Check if text field is a comb field. /// - public string TextFont { get; set; } + public bool IsComb + { + get + { + int flags = FieldFlags; + return (flags & (1 << 24)) != 0; + } + } /// - /// A float defining the text fontsize + /// Next form field. /// - public float TextFontSize { get; set; } + public Widget Next + { + get + { + var next = mupdf.mupdf.pdf_next_widget(_nativeWidget); + return next.m_internal != null ? new Widget(next, Parent) : null; + } + } /// - /// An integer defining the maximum number of text characters + /// Choice field option values. /// - public int TextMaxLen { get; set; } - - public int TextFormat { get; set; } - - public string TextDa { get; set; } + public List ChoiceValues + { + get + { + var result = new List(); + var obj = mupdf.mupdf.pdf_annot_obj(_nativeWidget); + var opt = mupdf.mupdf.pdf_dict_get(obj, mupdf.mupdf.pdf_new_name("Opt")); + if (opt.m_internal == null) return result; + int n = mupdf.mupdf.pdf_array_len(opt); + for (int i = 0; i < n; i++) + { + var item = mupdf.mupdf.pdf_array_get(opt, i); + if (mupdf.mupdf.pdf_is_array(item) != 0) + result.Add(mupdf.mupdf.pdf_to_text_string(mupdf.mupdf.pdf_array_get(item, 1))); + else + result.Add(mupdf.mupdf.pdf_to_text_string(item)); + } + return result; + } + } /// - /// JavaScript text (unicode) for an action associated with the widget, or null + /// Calculate script (C) from the additional-actions dictionary. /// - public string Script { get; set; } - + public string ScriptCalc => GetScript("C"); /// - /// JavaScript text (unicode) to be performed when the user types a key-stroke into a text field or combo box or modifies the selection in a scrollable list box + /// Format script (F) from the additional-actions dictionary. /// - public string ScriptStroke { get; set; } - + public string ScriptFormat => GetScript("F"); /// - /// JavaScript text (unicode) to be performed before the field is formatted to display its current value + /// Keystroke script (K) from the additional-actions dictionary. /// - public string ScriptFormat { get; set; } - + public string ScriptKeystroke => GetScript("K"); /// - /// JavaScript text (unicode) to be performed when the field’s value is changed + /// Validation script (V) from the additional-actions dictionary. /// - public string ScriptChange { get; set; } - + public string ScriptValidation => GetScript("V"); /// - /// JavaScript text (unicode) to be performed to recalculate the value of this field when that of another field changes + /// Blur script (Bl) from the additional-actions dictionary. /// - public string ScriptCalc { get; set; } - + public string ScriptBlur => GetScript("Bl"); /// - /// JavaScript text (unicode) to be performed on focusing this field + /// Focus script (Fo) from the additional-actions dictionary. /// - public string ScriptBlur { get; set; } + public string ScriptFocus => GetScript("Fo"); - /// - /// JavaScript text (unicode) to be performed on focusing this field - /// - public string ScriptFocus { get; set; } + // ─── Methods ──────────────────────────────────────────────────── /// - /// The PDF xref of the widget + /// Set the field value. /// - public int Xref { get; set; } + public void SetValue(string value) => FieldValue = value; /// - /// The rectangle containing the field + /// Set widget rectangle. /// - public Rect Rect { get; set; } - - public Page Parent { get; set; } - - public PdfAnnot _annot { get; set; } - - public Widget(Page page) - { - Parent = page; - BorderColor = null; - BorderStyle = "S"; - BorderWidth = 0; - BorderDashes = null; - ChoiceValues = null; - RbParent = 0; - - FieldName = null; - FieldLabel = null; - FieldValue = null; - FieldFlags = 0; - FieldType = 0; - FieldDisplay = 0; - FieldTypeString = null; - - FillColor = null; - ButtonCaption = null; - IsSigned = false; - TextColor = new float[] { 0, 0, 0 }; - TextFont = "Helv"; - TextFontSize = 0; - TextMaxLen = 0; - TextFormat = 0; - TextDa = ""; - - Script = null; - ScriptStroke = null; - ScriptFormat = null; - ScriptCalc = null; - ScriptChange = null; - ScriptBlur = null; - ScriptFocus = null; - - Rect = null; - Xref = 0; - } - - public override string ToString() + public void SetRect(Rect rect) { - return $"Widget:(field_type={FieldTypeString}) script={Script}"; + mupdf.mupdf.pdf_set_annot_rect(_nativeWidget, rect.ToFzRect()); + mupdf.mupdf.pdf_update_annot(_nativeWidget); } /// - /// Ensure text_font is from our list and correctly spelled. + /// Set choice field option values. /// - /// - public void AdjustFont() + public void SetChoiceValues(List values) { - if (string.IsNullOrEmpty(TextFont)) - { - TextFont = "Helv"; - return; - } - List validFonts = new List() { "Cour", "TiRo", "Helv", "ZaDb" }; - foreach (string f in validFonts) - if (TextFont.ToLower() == f.ToLower()) - { - TextFont = f; - return; - } - TextFont = "Helv"; - return; + if (values == null || values.Count == 0) return; + var obj = mupdf.mupdf.pdf_annot_obj(_nativeWidget); + var pdf = Parent.Parent.NativePdfDocument; + var optArr = mupdf.mupdf.pdf_new_array(pdf, values.Count); + foreach (var val in values) + mupdf.mupdf.pdf_array_push(optArr, mupdf.mupdf.pdf_new_text_string(val)); + mupdf.mupdf.pdf_dict_puts(obj, "Opt", optArr); + mupdf.mupdf.pdf_update_annot(_nativeWidget); } /// - /// Any widget type checks. + /// Update widget appearance. /// - /// - public void Checker() + public void Update() { - if (!(FieldType >= 1 && FieldType < 8)) - throw new Exception("bad field type"); - - if (FieldType == (int)PdfWidgetType.PDF_WIDGET_TYPE_RADIOBUTTON) - { - Document doc = Parent.Parent; - (string kidsType, string kidsValue) = doc.GetKeyXref(Xref, "Parent/Kids"); - if (kidsType == "array") - { - //List xrefs = kidsValue.Substring(1, kidsValue.Length - 2).Replace("0 R", "").Split("").Select(x => int.Parse(x)).ToList(); - List xrefs = kidsValue.Substring(1, kidsValue.Length - 2).Replace("0 R", " ").Split(' ').Select(x => int.Parse(x)).ToList(); - foreach (int xref in xrefs) - { - if (xref != Xref) - doc.SetKeyXRef(xref, "AS", "/Off"); - } - } - } + mupdf.mupdf.pdf_update_annot(_nativeWidget); } /// - /// Extract font name, size and color from default appearance string (/DA object). - /// Equivalent to 'pdf_parse_default_appearance' function in MuPDF's 'pdf-annot.c'. + /// Button widget on/checked state from the toggle flag. /// - /// - public void ParseDa() - { - if (string.IsNullOrEmpty(TextDa)) - return; - string font = "Helv"; - float fontSize = 0; - float[] col = { 0, 0, 0 }; - string[] dat = TextDa.Split(null as char[], StringSplitOptions.RemoveEmptyEntries); // split on any whitespace and remove empty entries - for (int i = 0; i < dat.Length; i++) - { - string item = dat[i]; - if (item == "Tf") - { - font = dat[i - 2].Substring(1); - fontSize = float.Parse(dat[i - 1], System.Globalization.CultureInfo.InvariantCulture); - dat[i] = dat[i - 1] = dat[i - 2] = ""; - continue; - } - if (item == "g") - { - col = new float[] { float.Parse(dat[i - 1], System.Globalization.CultureInfo.InvariantCulture) }; - dat[i] = dat[i - 1] = ""; - continue; - } - if (item == "rg") - { - col = new float[3]; - for (int j = i - 3; j < i; j++) - col[j - i + 3] = float.Parse(dat[j], System.Globalization.CultureInfo.InvariantCulture); - dat[i] = dat[i - 1] = dat[i - 2] = dat[i - 3] = ""; - continue; - } - } - TextFont = font; - TextFontSize = fontSize; - TextColor = col; - TextDa = ""; - } + public bool OnState => mupdf.mupdf.pdf_toggle_widget(_nativeWidget) != 0; /// - /// Validate the class entries. + /// Reset field to its default value. /// - /// - public void Validate() + public void Reset() { - if (Rect.IsInfinite || Rect.IsEmpty) - throw new Exception("bad rect"); - if (string.IsNullOrEmpty(FieldName)) - throw new Exception("field name missing"); - if (FieldLabel == "Unnamed") - FieldLabel = null; - Utils.CheckColor(BorderColor); - Utils.CheckColor(FillColor); - if (TextColor == null) - TextColor = new float[] { 0, 0, 0 }; - Utils.CheckColor(TextColor); - - if (BorderWidth == 0) - BorderWidth = 0; - if (TextFontSize == 0) - TextFontSize = 0; - - BorderStyle = BorderStyle.ToUpper().Substring(0, 1); - - // standardize content of JavaScript entries - bool btnType = (new List { - PdfWidgetType.PDF_WIDGET_TYPE_BUTTON, - PdfWidgetType.PDF_WIDGET_TYPE_CHECKBOX, - PdfWidgetType.PDF_WIDGET_TYPE_RADIOBUTTON}).Contains((PdfWidgetType)FieldType); - if (string.IsNullOrEmpty(Script)) - Script = null; - - // buttons cannot have the following script actions - if (btnType || string.IsNullOrEmpty(ScriptCalc)) - ScriptCalc = null; - - if (btnType || string.IsNullOrEmpty(ScriptChange)) - ScriptChange = null; - - if (btnType || string.IsNullOrEmpty(ScriptFormat)) - ScriptFormat = null; - - if (btnType || string.IsNullOrEmpty(ScriptStroke)) - ScriptStroke = null; - - if (btnType || string.IsNullOrEmpty(ScriptBlur)) - ScriptBlur = null; - - if (btnType || string.IsNullOrEmpty(ScriptFocus)) - ScriptFocus = null; - - Checker(); // any field_type specific checks + var obj = mupdf.mupdf.pdf_annot_obj(_nativeWidget); + var dv = mupdf.mupdf.pdf_dict_get(obj, mupdf.mupdf.pdf_new_name("DV")); + if (dv.m_internal != null) + mupdf.mupdf.pdf_dict_put(obj, mupdf.mupdf.pdf_new_name("V"), dv); + else + mupdf.mupdf.pdf_dict_del(obj, mupdf.mupdf.pdf_new_name("V")); + mupdf.mupdf.pdf_update_annot(_nativeWidget); } /// - /// Propagate the field flags. - /// If this widget has a "/Parent", set its field flags and that of all - /// its /Kids widgets to the value of the current widget. - /// Only possible for widgets existing in the PDF. + /// Widget Pixmap. /// - /// true/false - public bool SyncFlags() + public Pixmap GetPixmap(Matrix matrix = null, Colorspace cs = null, bool alpha = false) { - if (Xref == 0) - return false; // no xref: widget not in the PDF - Document doc = this.Parent.Parent; // the owning document - if (doc == null) - return false; - PdfDocument pdf = Document.AsPdfDocument(doc); - // load underlying PDF object - PdfObj pdf_widget = pdf.pdf_load_object(Xref); - PdfObj parent = pdf_widget.pdf_dict_get(new PdfObj("Parent")); - if (parent.pdf_is_dict() == 0) - { - pdf.Dispose(); - return false; // no /Parent: nothing to do - } - - // put the field flags value into the parent field flags: - parent.pdf_dict_put_int(new PdfObj("Ff"), this.FieldFlags); - - // also put that value into all kids of the Parent - PdfObj kids = parent.pdf_dict_get(new PdfObj("Kids")); - if (kids.pdf_is_array() == 0) - { - Console.WriteLine("warning: malformed PDF, Parent has no Kids array"); - pdf.Dispose(); - return false; // no /Kids: should never happen! - } - - for (int i = 0; i < kids.pdf_array_len(); i++) - { - // access kid widget, and do some precautionary checks - PdfObj kid = kids.pdf_array_get(i); - if (kid.pdf_is_dict() == 0) - continue; // not a dict: skip - int xref = kid.pdf_to_num(); // get xref of the kid - if (xref == this.Xref) // skip self widget - continue; - PdfObj subtype = kid.pdf_dict_get(new PdfObj("Subtype")); - if (subtype.pdf_to_name() != "Widget") - continue; - // put the field flags value into the kid field flags: - kid.pdf_dict_put_int(new PdfObj("Ff"), this.FieldFlags); - } - - pdf.Dispose(); - return true; // all done + var ctm = (matrix ?? Matrix.Identity).ToFzMatrix(); + var colorspace = (cs ?? Colorspace.CsRGB).ToFzColorspace(); + var pix = mupdf.mupdf.pdf_new_pixmap_from_annot(_nativeWidget, ctm, colorspace, new mupdf.FzSeparations(), alpha ? 1 : 0); + return new Pixmap(pix); } - /// - /// Return the names of On / Off (i.e. selected / clicked or not) states a button field may have. While the ‘Off’ state usually is also named like so, the ‘On’ state is often given a name relating to the functional context, for example ‘Yes’, ‘Female’, etc. - /// A button may have 'normal' or 'pressed down' appearances. While the 'Off' - /// state is usually called like this, the 'On' state is often given a name - /// relating to the functional context. - /// - /// - public Dictionary> ButtonStates() + private string GetScript(string trigger) { - if (!(FieldType == 2 || FieldType == 5)) - return null; // no button type - Document doc = this.Parent.Parent; // field already exists on page - if (doc == null) - return null; - - int xref = Xref; - Dictionary> states = new Dictionary>(); - states.Add("normal", null); - states.Add("down", null); - (string, string) apn = doc.GetKeyXref(xref, "AP/N"); - if (apn.Item1 == "dict") - { - List nstates = new List(); - string t = apn.Item2.Substring(2, apn.Item2.Length - 2 - 2); - string[] apnt = t.Split('/').Skip(1).ToArray(); - foreach (string x in apnt) - nstates.Add(x.Split()[0]); - states["normal"] = nstates; - } - if (apn.Item1 == "xref") - { - List nstates = new List(); - int nxref = int.Parse(apn.Item2.Split(' ')[0]); - string t = doc.GetXrefObject(nxref); - string[] apnt = t.Split('/').Skip(1).ToArray(); - foreach (string x in apnt) - nstates.Add(x.Split()[0]); - states["normal"] = nstates; - } - (string, string) apd = doc.GetKeyXref(xref, "AP/D"); - if (apd.Item1 == "dict") - { - List dstates = new List(); - string t = apd.Item2.Substring(2, apd.Item2.Length - 2 - 2); - string[] apdt = t.Split('/').Skip(1).ToArray(); - foreach (string x in apdt) - dstates.Add(x.Split()[0]); - states["down"] = dstates; - } - if (apd.Item1 == "xref") + var obj = mupdf.mupdf.pdf_annot_obj(_nativeWidget); + var aa = mupdf.mupdf.pdf_dict_get(obj, mupdf.mupdf.pdf_new_name("AA")); + if (aa.m_internal == null) return null; + var action = mupdf.mupdf.pdf_dict_gets(aa, trigger); + if (action.m_internal == null) return null; + var js = mupdf.mupdf.pdf_dict_get(action, mupdf.mupdf.pdf_new_name("JS")); + if (js.m_internal == null) return null; + if (mupdf.mupdf.pdf_is_stream(js) != 0) { - List dstates = new List(); - int dxref = int.Parse(apd.Item2.Split(' ')[0]); - string t = doc.GetXrefObject(dxref); - string[] apdt = t.Split('/').Skip(1).ToArray(); - foreach (string x in apdt) - dstates.Add(x.Split()[0]); - states["down"] = dstates; + var buf = mupdf.mupdf.pdf_load_stream(js); + return System.Text.Encoding.UTF8.GetString(buf.fz_buffer_extract()); } - return states; + return mupdf.mupdf.pdf_to_text_string(js); } - /// - /// Point to the next form field on the page. - /// - public dynamic Next - { - get - { - return (new Annot(_annot, Parent)).Next; - } - } + // ─── IDisposable ──────────────────────────────────────────────── /// - /// Return the value of the “ON” state of check boxes and radio buttons. For check boxes this is always the value “Yes”. For radio buttons, this is the value to select / activate the button. + /// Releases resources used by this widget wrapper. /// - /// - public string OnState() + public void Dispose() { - if (!(FieldType == 2 || FieldType == 5)) - return null; - if (FieldType == 2) - return "Yes"; - Dictionary> bstate = ButtonStates(); - if (bstate == null) - bstate = new Dictionary>(); - foreach (string k in bstate.Keys) - { - foreach (string v in bstate[k]) - { - if (v != "Off") - return v; - } - } - Console.WriteLine("warning: radio button has no 'On' value"); - return ""; + if (!_disposed) { _disposed = true; } + GC.SuppressFinalize(this); } - /// - /// Reset the field’s value to its default – if defined – or remove it. Do not forget to issue update() afterwards. - /// - public void Reset() - { - Utils.ResetWidget(_annot); - } + ~Widget() { Dispose(); } /// - /// After any changes to a widget, this method must be used to store them in the PDF - /// propagate field flags to parent and kids + /// Returns a string representation of this widget. /// - public void Update(bool syncFlags = false) - { - Validate(); - AdjustFont(); // ensure valid text_font name - - // now create the /DA string - TextDa = ""; - string fmt = ""; - - if (TextColor != null && TextColor.Length == 3) - fmt = $"{Utils.FloatToString(TextColor[0])} {Utils.FloatToString(TextColor[1])} {Utils.FloatToString(TextColor[2])} rg /" + "{0} {1} Tf" + TextDa; - else if (TextColor.Length == 1) - fmt = $"{Utils.FloatToString(TextColor[0])} g /" + "{0} {1} Tf" + TextDa; - else if (TextColor.Length == 4) - fmt = $"{Utils.FloatToString(TextColor[0])} {Utils.FloatToString(TextColor[1])} {Utils.FloatToString(TextColor[2])} {Utils.FloatToString(TextColor[3])} k /" + "{0} {1} Tf" + TextDa; - TextDa = string.Format(System.Globalization.CultureInfo.InvariantCulture, fmt, TextFont, Utils.FloatToString(TextFontSize)); - - // if widget has a '/AA/C' script, make sure it is in the '/CO' - // array of the '/AcroForm' dictionary. - if (!string.IsNullOrEmpty(ScriptCalc)) // there is a "calculation" script: - { - // make sure we are in the /CO array - Utils.EnsureWidgetCalc(_annot); - } - - Utils.SaveWidget(_annot, this); - TextDa = ""; - - // finally update the widget - if (syncFlags) - SyncFlags(); // propagate field flags to parent and kids - } + public override string ToString() => $"Widget('{FieldTypeString}', '{FieldName}')"; } } diff --git a/MuPDF.NET/WxColors.cs b/MuPDF.NET/WxColors.cs new file mode 100644 index 00000000..0b54035f --- /dev/null +++ b/MuPDF.NET/WxColors.cs @@ -0,0 +1,596 @@ +using System.Collections.Generic; +using System.Linq; + +namespace MuPDF.NET +{ + /// + /// Provides wx color name definitions with RGB values, PDF-range float RGB mappings, + /// and a list of color names. Ported from PyMuPDF's _wxcolors.py. + /// + public static class WxColors + { + /// + /// Complete list of wx colour definitions as (Name, R, G, B) tuples + /// where R, G, B are integers in the range 0–255. + /// + public static readonly List<(string Name, int R, int G, int B)> ColorList = new List<(string, int, int, int)> + { + ("ALICEBLUE", 240, 248, 255), + ("ANTIQUEWHITE", 250, 235, 215), + ("ANTIQUEWHITE1", 255, 239, 219), + ("ANTIQUEWHITE2", 238, 223, 204), + ("ANTIQUEWHITE3", 205, 192, 176), + ("ANTIQUEWHITE4", 139, 131, 120), + ("AQUA", 0, 255, 255), + ("AQUAMARINE", 127, 255, 212), + ("AQUAMARINE1", 127, 255, 212), + ("AQUAMARINE2", 118, 238, 198), + ("AQUAMARINE3", 102, 205, 170), + ("AQUAMARINE4", 69, 139, 116), + ("AZURE", 240, 255, 255), + ("AZURE1", 240, 255, 255), + ("AZURE2", 224, 238, 238), + ("AZURE3", 193, 205, 205), + ("AZURE4", 131, 139, 139), + ("BEIGE", 245, 245, 220), + ("BISQUE", 255, 228, 196), + ("BISQUE1", 255, 228, 196), + ("BISQUE2", 238, 213, 183), + ("BISQUE3", 205, 183, 158), + ("BISQUE4", 139, 125, 107), + ("BLACK", 0, 0, 0), + ("BLANCHEDALMOND", 255, 235, 205), + ("BLUE", 0, 0, 255), + ("BLUE1", 0, 0, 255), + ("BLUE2", 0, 0, 238), + ("BLUE3", 0, 0, 205), + ("BLUE4", 0, 0, 139), + ("BLUEVIOLET", 138, 43, 226), + ("BROWN", 165, 42, 42), + ("BROWN1", 255, 64, 64), + ("BROWN2", 238, 59, 59), + ("BROWN3", 205, 51, 51), + ("BROWN4", 139, 35, 35), + ("BURLYWOOD", 222, 184, 135), + ("BURLYWOOD1", 255, 211, 155), + ("BURLYWOOD2", 238, 197, 145), + ("BURLYWOOD3", 205, 170, 125), + ("BURLYWOOD4", 139, 115, 85), + ("CADETBLUE", 95, 158, 160), + ("CADETBLUE1", 152, 245, 255), + ("CADETBLUE2", 142, 229, 238), + ("CADETBLUE3", 122, 197, 205), + ("CADETBLUE4", 83, 134, 139), + ("CHARTREUSE", 127, 255, 0), + ("CHARTREUSE1", 127, 255, 0), + ("CHARTREUSE2", 118, 238, 0), + ("CHARTREUSE3", 102, 205, 0), + ("CHARTREUSE4", 69, 139, 0), + ("CHOCOLATE", 210, 105, 30), + ("CHOCOLATE1", 255, 127, 36), + ("CHOCOLATE2", 238, 118, 33), + ("CHOCOLATE3", 205, 102, 29), + ("CHOCOLATE4", 139, 69, 19), + ("COFFEE", 156, 79, 0), + ("CORAL", 255, 127, 80), + ("CORAL1", 255, 114, 86), + ("CORAL2", 238, 106, 80), + ("CORAL3", 205, 91, 69), + ("CORAL4", 139, 62, 47), + ("CORNFLOWERBLUE", 100, 149, 237), + ("CORNSILK", 255, 248, 220), + ("CORNSILK1", 255, 248, 220), + ("CORNSILK2", 238, 232, 205), + ("CORNSILK3", 205, 200, 177), + ("CORNSILK4", 139, 136, 120), + ("CRIMSON", 220, 20, 60), + ("CYAN", 0, 255, 255), + ("CYAN1", 0, 255, 255), + ("CYAN2", 0, 238, 238), + ("CYAN3", 0, 205, 205), + ("CYAN4", 0, 139, 139), + ("DARKBLUE", 0, 0, 139), + ("DARKCYAN", 0, 139, 139), + ("DARKGOLDENROD", 184, 134, 11), + ("DARKGOLDENROD1", 255, 185, 15), + ("DARKGOLDENROD2", 238, 173, 14), + ("DARKGOLDENROD3", 205, 149, 12), + ("DARKGOLDENROD4", 139, 101, 8), + ("DARKGRAY", 169, 169, 169), + ("DARKGREEN", 0, 100, 0), + ("DARKGREY", 169, 169, 169), + ("DARKKHAKI", 189, 183, 107), + ("DARKMAGENTA", 139, 0, 139), + ("DARKOLIVEGREEN", 85, 107, 47), + ("DARKOLIVEGREEN1", 202, 255, 112), + ("DARKOLIVEGREEN2", 188, 238, 104), + ("DARKOLIVEGREEN3", 162, 205, 90), + ("DARKOLIVEGREEN4", 110, 139, 61), + ("DARKORANGE", 255, 140, 0), + ("DARKORANGE1", 255, 127, 0), + ("DARKORANGE2", 238, 118, 0), + ("DARKORANGE3", 205, 102, 0), + ("DARKORANGE4", 139, 69, 0), + ("DARKORCHID", 153, 50, 204), + ("DARKORCHID1", 191, 62, 255), + ("DARKORCHID2", 178, 58, 238), + ("DARKORCHID3", 154, 50, 205), + ("DARKORCHID4", 104, 34, 139), + ("DARKRED", 139, 0, 0), + ("DARKSALMON", 233, 150, 122), + ("DARKSEAGREEN", 143, 188, 143), + ("DARKSEAGREEN1", 193, 255, 193), + ("DARKSEAGREEN2", 180, 238, 180), + ("DARKSEAGREEN3", 155, 205, 155), + ("DARKSEAGREEN4", 105, 139, 105), + ("DARKSLATEBLUE", 72, 61, 139), + ("DARKSLATEGRAY", 47, 79, 79), + ("DARKSLATEGREY", 47, 79, 79), + ("DARKTURQUOISE", 0, 206, 209), + ("DARKVIOLET", 148, 0, 211), + ("DEEPPINK", 255, 20, 147), + ("DEEPPINK1", 255, 20, 147), + ("DEEPPINK2", 238, 18, 137), + ("DEEPPINK3", 205, 16, 118), + ("DEEPPINK4", 139, 10, 80), + ("DEEPSKYBLUE", 0, 191, 255), + ("DEEPSKYBLUE1", 0, 191, 255), + ("DEEPSKYBLUE2", 0, 178, 238), + ("DEEPSKYBLUE3", 0, 154, 205), + ("DEEPSKYBLUE4", 0, 104, 139), + ("DIMGRAY", 105, 105, 105), + ("DIMGREY", 105, 105, 105), + ("DODGERBLUE", 30, 144, 255), + ("DODGERBLUE1", 30, 144, 255), + ("DODGERBLUE2", 28, 134, 238), + ("DODGERBLUE3", 24, 116, 205), + ("DODGERBLUE4", 16, 78, 139), + ("FIREBRICK", 178, 34, 34), + ("FIREBRICK1", 255, 48, 48), + ("FIREBRICK2", 238, 44, 44), + ("FIREBRICK3", 205, 38, 38), + ("FIREBRICK4", 139, 26, 26), + ("FLORALWHITE", 255, 250, 240), + ("FORESTGREEN", 34, 139, 34), + ("FUCHSIA", 255, 0, 255), + ("GAINSBORO", 220, 220, 220), + ("GHOSTWHITE", 248, 248, 255), + ("GOLD", 255, 215, 0), + ("GOLD1", 255, 215, 0), + ("GOLD2", 238, 201, 0), + ("GOLD3", 205, 173, 0), + ("GOLD4", 139, 117, 0), + ("GOLDENROD", 218, 165, 32), + ("GOLDENROD1", 255, 193, 37), + ("GOLDENROD2", 238, 180, 34), + ("GOLDENROD3", 205, 155, 29), + ("GOLDENROD4", 139, 105, 20), + ("GRAY", 190, 190, 190), + ("GRAY0", 0, 0, 0), + ("GRAY1", 3, 3, 3), + ("GRAY10", 26, 26, 26), + ("GRAY100", 255, 255, 255), + ("GRAY11", 28, 28, 28), + ("GRAY12", 31, 31, 31), + ("GRAY13", 33, 33, 33), + ("GRAY14", 36, 36, 36), + ("GRAY15", 38, 38, 38), + ("GRAY16", 41, 41, 41), + ("GRAY17", 43, 43, 43), + ("GRAY18", 46, 46, 46), + ("GRAY19", 48, 48, 48), + ("GRAY2", 5, 5, 5), + ("GRAY20", 51, 51, 51), + ("GRAY21", 54, 54, 54), + ("GRAY22", 56, 56, 56), + ("GRAY23", 59, 59, 59), + ("GRAY24", 61, 61, 61), + ("GRAY25", 64, 64, 64), + ("GRAY26", 66, 66, 66), + ("GRAY27", 69, 69, 69), + ("GRAY28", 71, 71, 71), + ("GRAY29", 74, 74, 74), + ("GRAY3", 8, 8, 8), + ("GRAY30", 77, 77, 77), + ("GRAY31", 79, 79, 79), + ("GRAY32", 82, 82, 82), + ("GRAY33", 84, 84, 84), + ("GRAY34", 87, 87, 87), + ("GRAY35", 89, 89, 89), + ("GRAY36", 92, 92, 92), + ("GRAY37", 94, 94, 94), + ("GRAY38", 97, 97, 97), + ("GRAY39", 99, 99, 99), + ("GRAY4", 10, 10, 10), + ("GRAY40", 102, 102, 102), + ("GRAY41", 105, 105, 105), + ("GRAY42", 107, 107, 107), + ("GRAY43", 110, 110, 110), + ("GRAY44", 112, 112, 112), + ("GRAY45", 115, 115, 115), + ("GRAY46", 117, 117, 117), + ("GRAY47", 120, 120, 120), + ("GRAY48", 122, 122, 122), + ("GRAY49", 125, 125, 125), + ("GRAY5", 13, 13, 13), + ("GRAY50", 127, 127, 127), + ("GRAY51", 130, 130, 130), + ("GRAY52", 133, 133, 133), + ("GRAY53", 135, 135, 135), + ("GRAY54", 138, 138, 138), + ("GRAY55", 140, 140, 140), + ("GRAY56", 143, 143, 143), + ("GRAY57", 145, 145, 145), + ("GRAY58", 148, 148, 148), + ("GRAY59", 150, 150, 150), + ("GRAY6", 15, 15, 15), + ("GRAY60", 153, 153, 153), + ("GRAY61", 156, 156, 156), + ("GRAY62", 158, 158, 158), + ("GRAY63", 161, 161, 161), + ("GRAY64", 163, 163, 163), + ("GRAY65", 166, 166, 166), + ("GRAY66", 168, 168, 168), + ("GRAY67", 171, 171, 171), + ("GRAY68", 173, 173, 173), + ("GRAY69", 176, 176, 176), + ("GRAY7", 18, 18, 18), + ("GRAY70", 179, 179, 179), + ("GRAY71", 181, 181, 181), + ("GRAY72", 184, 184, 184), + ("GRAY73", 186, 186, 186), + ("GRAY74", 189, 189, 189), + ("GRAY75", 191, 191, 191), + ("GRAY76", 194, 194, 194), + ("GRAY77", 196, 196, 196), + ("GRAY78", 199, 199, 199), + ("GRAY79", 201, 201, 201), + ("GRAY8", 20, 20, 20), + ("GRAY80", 204, 204, 204), + ("GRAY81", 207, 207, 207), + ("GRAY82", 209, 209, 209), + ("GRAY83", 212, 212, 212), + ("GRAY84", 214, 214, 214), + ("GRAY85", 217, 217, 217), + ("GRAY86", 219, 219, 219), + ("GRAY87", 222, 222, 222), + ("GRAY88", 224, 224, 224), + ("GRAY89", 227, 227, 227), + ("GRAY9", 23, 23, 23), + ("GRAY90", 229, 229, 229), + ("GRAY91", 232, 232, 232), + ("GRAY92", 235, 235, 235), + ("GRAY93", 237, 237, 237), + ("GRAY94", 240, 240, 240), + ("GRAY95", 242, 242, 242), + ("GRAY96", 245, 245, 245), + ("GRAY97", 247, 247, 247), + ("GRAY98", 250, 250, 250), + ("GRAY99", 252, 252, 252), + ("GREEN YELLOW", 173, 255, 47), + ("GREEN", 0, 255, 0), + ("GREEN1", 0, 255, 0), + ("GREEN2", 0, 238, 0), + ("GREEN3", 0, 205, 0), + ("GREEN4", 0, 139, 0), + ("GREENYELLOW", 173, 255, 47), + ("GREY", 128, 128, 128), + ("HONEYDEW", 240, 255, 240), + ("HONEYDEW1", 240, 255, 240), + ("HONEYDEW2", 224, 238, 224), + ("HONEYDEW3", 193, 205, 193), + ("HONEYDEW4", 131, 139, 131), + ("HOTPINK", 255, 105, 180), + ("HOTPINK1", 255, 110, 180), + ("HOTPINK2", 238, 106, 167), + ("HOTPINK3", 205, 96, 144), + ("HOTPINK4", 139, 58, 98), + ("INDIANRED", 205, 92, 92), + ("INDIANRED1", 255, 106, 106), + ("INDIANRED2", 238, 99, 99), + ("INDIANRED3", 205, 85, 85), + ("INDIANRED4", 139, 58, 58), + ("INDIGO", 75, 0, 130), + ("IVORY", 255, 255, 240), + ("IVORY1", 255, 255, 240), + ("IVORY2", 238, 238, 224), + ("IVORY3", 205, 205, 193), + ("IVORY4", 139, 139, 131), + ("KHAKI", 240, 230, 140), + ("KHAKI1", 255, 246, 143), + ("KHAKI2", 238, 230, 133), + ("KHAKI3", 205, 198, 115), + ("KHAKI4", 139, 134, 78), + ("LAVENDER", 230, 230, 250), + ("LAVENDERBLUSH", 255, 240, 245), + ("LAVENDERBLUSH1", 255, 240, 245), + ("LAVENDERBLUSH2", 238, 224, 229), + ("LAVENDERBLUSH3", 205, 193, 197), + ("LAVENDERBLUSH4", 139, 131, 134), + ("LAWNGREEN", 124, 252, 0), + ("LEMONCHIFFON", 255, 250, 205), + ("LEMONCHIFFON1", 255, 250, 205), + ("LEMONCHIFFON2", 238, 233, 191), + ("LEMONCHIFFON3", 205, 201, 165), + ("LEMONCHIFFON4", 139, 137, 112), + ("LIGHTBLUE", 173, 216, 230), + ("LIGHTBLUE1", 191, 239, 255), + ("LIGHTBLUE2", 178, 223, 238), + ("LIGHTBLUE3", 154, 192, 205), + ("LIGHTBLUE4", 104, 131, 139), + ("LIGHTCORAL", 240, 128, 128), + ("LIGHTCYAN", 224, 255, 255), + ("LIGHTCYAN1", 224, 255, 255), + ("LIGHTCYAN2", 209, 238, 238), + ("LIGHTCYAN3", 180, 205, 205), + ("LIGHTCYAN4", 122, 139, 139), + ("LIGHTGOLDENROD", 238, 221, 130), + ("LIGHTGOLDENROD1", 255, 236, 139), + ("LIGHTGOLDENROD2", 238, 220, 130), + ("LIGHTGOLDENROD3", 205, 190, 112), + ("LIGHTGOLDENROD4", 139, 129, 76), + ("LIGHTGOLDENRODYELLOW", 250, 250, 210), + ("LIGHTGRAY", 211, 211, 211), + ("LIGHTGREEN", 144, 238, 144), + ("LIGHTGREY", 211, 211, 211), + ("LIGHTPINK", 255, 182, 193), + ("LIGHTPINK1", 255, 174, 185), + ("LIGHTPINK2", 238, 162, 173), + ("LIGHTPINK3", 205, 140, 149), + ("LIGHTPINK4", 139, 95, 101), + ("LIGHTSALMON", 255, 160, 122), + ("LIGHTSALMON1", 255, 160, 122), + ("LIGHTSALMON2", 238, 149, 114), + ("LIGHTSALMON3", 205, 129, 98), + ("LIGHTSALMON4", 139, 87, 66), + ("LIGHTSEAGREEN", 32, 178, 170), + ("LIGHTSKYBLUE", 135, 206, 250), + ("LIGHTSKYBLUE1", 176, 226, 255), + ("LIGHTSKYBLUE2", 164, 211, 238), + ("LIGHTSKYBLUE3", 141, 182, 205), + ("LIGHTSKYBLUE4", 96, 123, 139), + ("LIGHTSLATEBLUE", 132, 112, 255), + ("LIGHTSLATEGRAY", 119, 136, 153), + ("LIGHTSLATEGREY", 119, 136, 153), + ("LIGHTSTEELBLUE", 176, 196, 222), + ("LIGHTSTEELBLUE1", 202, 225, 255), + ("LIGHTSTEELBLUE2", 188, 210, 238), + ("LIGHTSTEELBLUE3", 162, 181, 205), + ("LIGHTSTEELBLUE4", 110, 123, 139), + ("LIGHTYELLOW", 255, 255, 224), + ("LIGHTYELLOW1", 255, 255, 224), + ("LIGHTYELLOW2", 238, 238, 209), + ("LIGHTYELLOW3", 205, 205, 180), + ("LIGHTYELLOW4", 139, 139, 122), + ("LIME", 0, 255, 0), + ("LIMEGREEN", 50, 205, 50), + ("LINEN", 250, 240, 230), + ("MAGENTA", 255, 0, 255), + ("MAGENTA1", 255, 0, 255), + ("MAGENTA2", 238, 0, 238), + ("MAGENTA3", 205, 0, 205), + ("MAGENTA4", 139, 0, 139), + ("MAROON", 176, 48, 96), + ("MAROON1", 255, 52, 179), + ("MAROON2", 238, 48, 167), + ("MAROON3", 205, 41, 144), + ("MAROON4", 139, 28, 98), + ("MEDIUMAQUAMARINE", 102, 205, 170), + ("MEDIUMBLUE", 0, 0, 205), + ("MEDIUMORCHID", 186, 85, 211), + ("MEDIUMORCHID1", 224, 102, 255), + ("MEDIUMORCHID2", 209, 95, 238), + ("MEDIUMORCHID3", 180, 82, 205), + ("MEDIUMORCHID4", 122, 55, 139), + ("MEDIUMPURPLE", 147, 112, 219), + ("MEDIUMPURPLE1", 171, 130, 255), + ("MEDIUMPURPLE2", 159, 121, 238), + ("MEDIUMPURPLE3", 137, 104, 205), + ("MEDIUMPURPLE4", 93, 71, 139), + ("MEDIUMSEAGREEN", 60, 179, 113), + ("MEDIUMSLATEBLUE", 123, 104, 238), + ("MEDIUMSPRINGGREEN", 0, 250, 154), + ("MEDIUMTURQUOISE", 72, 209, 204), + ("MEDIUMVIOLETRED", 199, 21, 133), + ("MIDNIGHTBLUE", 25, 25, 112), + ("MINTCREAM", 245, 255, 250), + ("MISTYROSE", 255, 228, 225), + ("MISTYROSE1", 255, 228, 225), + ("MISTYROSE2", 238, 213, 210), + ("MISTYROSE3", 205, 183, 181), + ("MISTYROSE4", 139, 125, 123), + ("MOCCASIN", 255, 228, 181), + ("MUPDFBLUE", 37, 114, 172), + ("NAVAJOWHITE", 255, 222, 173), + ("NAVAJOWHITE1", 255, 222, 173), + ("NAVAJOWHITE2", 238, 207, 161), + ("NAVAJOWHITE3", 205, 179, 139), + ("NAVAJOWHITE4", 139, 121, 94), + ("NAVY", 0, 0, 128), + ("NAVYBLUE", 0, 0, 128), + ("OLDLACE", 253, 245, 230), + ("OLIVE", 128, 128, 0), + ("OLIVEDRAB", 107, 142, 35), + ("OLIVEDRAB1", 192, 255, 62), + ("OLIVEDRAB2", 179, 238, 58), + ("OLIVEDRAB3", 154, 205, 50), + ("OLIVEDRAB4", 105, 139, 34), + ("ORANGE", 255, 165, 0), + ("ORANGE1", 255, 165, 0), + ("ORANGE2", 238, 154, 0), + ("ORANGE3", 205, 133, 0), + ("ORANGE4", 139, 90, 0), + ("ORANGERED", 255, 69, 0), + ("ORANGERED1", 255, 69, 0), + ("ORANGERED2", 238, 64, 0), + ("ORANGERED3", 205, 55, 0), + ("ORANGERED4", 139, 37, 0), + ("ORCHID", 218, 112, 214), + ("ORCHID1", 255, 131, 250), + ("ORCHID2", 238, 122, 233), + ("ORCHID3", 205, 105, 201), + ("ORCHID4", 139, 71, 137), + ("PALEGOLDENROD", 238, 232, 170), + ("PALEGREEN", 152, 251, 152), + ("PALEGREEN1", 154, 255, 154), + ("PALEGREEN2", 144, 238, 144), + ("PALEGREEN3", 124, 205, 124), + ("PALEGREEN4", 84, 139, 84), + ("PALETURQUOISE", 175, 238, 238), + ("PALETURQUOISE1", 187, 255, 255), + ("PALETURQUOISE2", 174, 238, 238), + ("PALETURQUOISE3", 150, 205, 205), + ("PALETURQUOISE4", 102, 139, 139), + ("PALEVIOLETRED", 219, 112, 147), + ("PALEVIOLETRED1", 255, 130, 171), + ("PALEVIOLETRED2", 238, 121, 159), + ("PALEVIOLETRED3", 205, 104, 137), + ("PALEVIOLETRED4", 139, 71, 93), + ("PAPAYAWHIP", 255, 239, 213), + ("PEACHPUFF", 255, 218, 185), + ("PEACHPUFF1", 255, 218, 185), + ("PEACHPUFF2", 238, 203, 173), + ("PEACHPUFF3", 205, 175, 149), + ("PEACHPUFF4", 139, 119, 101), + ("PERU", 205, 133, 63), + ("PINK", 255, 192, 203), + ("PINK1", 255, 181, 197), + ("PINK2", 238, 169, 184), + ("PINK3", 205, 145, 158), + ("PINK4", 139, 99, 108), + ("PLUM", 221, 160, 221), + ("PLUM1", 255, 187, 255), + ("PLUM2", 238, 174, 238), + ("PLUM3", 205, 150, 205), + ("PLUM4", 139, 102, 139), + ("POWDERBLUE", 176, 224, 230), + ("PURPLE", 160, 32, 240), + ("PURPLE1", 155, 48, 255), + ("PURPLE2", 145, 44, 238), + ("PURPLE3", 125, 38, 205), + ("PURPLE4", 85, 26, 139), + ("PY_COLOR", 240, 255, 210), + ("RED", 255, 0, 0), + ("RED1", 255, 0, 0), + ("RED2", 238, 0, 0), + ("RED3", 205, 0, 0), + ("RED4", 139, 0, 0), + ("ROSYBROWN", 188, 143, 143), + ("ROSYBROWN1", 255, 193, 193), + ("ROSYBROWN2", 238, 180, 180), + ("ROSYBROWN3", 205, 155, 155), + ("ROSYBROWN4", 139, 105, 105), + ("ROYALBLUE", 65, 105, 225), + ("ROYALBLUE1", 72, 118, 255), + ("ROYALBLUE2", 67, 110, 238), + ("ROYALBLUE3", 58, 95, 205), + ("ROYALBLUE4", 39, 64, 139), + ("SADDLEBROWN", 139, 69, 19), + ("SALMON", 250, 128, 114), + ("SALMON1", 255, 140, 105), + ("SALMON2", 238, 130, 98), + ("SALMON3", 205, 112, 84), + ("SALMON4", 139, 76, 57), + ("SANDYBROWN", 244, 164, 96), + ("SEAGREEN", 46, 139, 87), + ("SEAGREEN1", 84, 255, 159), + ("SEAGREEN2", 78, 238, 148), + ("SEAGREEN3", 67, 205, 128), + ("SEAGREEN4", 46, 139, 87), + ("SEASHELL", 255, 245, 238), + ("SEASHELL1", 255, 245, 238), + ("SEASHELL2", 238, 229, 222), + ("SEASHELL3", 205, 197, 191), + ("SEASHELL4", 139, 134, 130), + ("SIENNA", 160, 82, 45), + ("SIENNA1", 255, 130, 71), + ("SIENNA2", 238, 121, 66), + ("SIENNA3", 205, 104, 57), + ("SIENNA4", 139, 71, 38), + ("SILVER", 192, 192, 192), + ("SKYBLUE", 135, 206, 235), + ("SKYBLUE1", 135, 206, 255), + ("SKYBLUE2", 126, 192, 238), + ("SKYBLUE3", 108, 166, 205), + ("SKYBLUE4", 74, 112, 139), + ("SLATEBLUE", 106, 90, 205), + ("SLATEBLUE1", 131, 111, 255), + ("SLATEBLUE2", 122, 103, 238), + ("SLATEBLUE3", 105, 89, 205), + ("SLATEBLUE4", 71, 60, 139), + ("SLATEGRAY", 112, 128, 144), + ("SLATEGREY", 112, 128, 144), + ("SNOW", 255, 250, 250), + ("SNOW1", 255, 250, 250), + ("SNOW2", 238, 233, 233), + ("SNOW3", 205, 201, 201), + ("SNOW4", 139, 137, 137), + ("SPRINGGREEN", 0, 255, 127), + ("SPRINGGREEN1", 0, 255, 127), + ("SPRINGGREEN2", 0, 238, 118), + ("SPRINGGREEN3", 0, 205, 102), + ("SPRINGGREEN4", 0, 139, 69), + ("STEELBLUE", 70, 130, 180), + ("STEELBLUE1", 99, 184, 255), + ("STEELBLUE2", 92, 172, 238), + ("STEELBLUE3", 79, 148, 205), + ("STEELBLUE4", 54, 100, 139), + ("TAN", 210, 180, 140), + ("TAN1", 255, 165, 79), + ("TAN2", 238, 154, 73), + ("TAN3", 205, 133, 63), + ("TAN4", 139, 90, 43), + ("TEAL", 0, 128, 128), + ("THISTLE", 216, 191, 216), + ("THISTLE1", 255, 225, 255), + ("THISTLE2", 238, 210, 238), + ("THISTLE3", 205, 181, 205), + ("THISTLE4", 139, 123, 139), + ("TOMATO", 255, 99, 71), + ("TOMATO1", 255, 99, 71), + ("TOMATO2", 238, 92, 66), + ("TOMATO3", 205, 79, 57), + ("TOMATO4", 139, 54, 38), + ("TURQUOISE", 64, 224, 208), + ("TURQUOISE1", 0, 245, 255), + ("TURQUOISE2", 0, 229, 238), + ("TURQUOISE3", 0, 197, 205), + ("TURQUOISE4", 0, 134, 139), + ("VIOLET", 238, 130, 238), + ("VIOLETRED", 208, 32, 144), + ("VIOLETRED1", 255, 62, 150), + ("VIOLETRED2", 238, 58, 140), + ("VIOLETRED3", 205, 50, 120), + ("VIOLETRED4", 139, 34, 82), + ("WHEAT", 245, 222, 179), + ("WHEAT1", 255, 231, 186), + ("WHEAT2", 238, 216, 174), + ("WHEAT3", 205, 186, 150), + ("WHEAT4", 139, 126, 102), + ("WHITE", 255, 255, 255), + ("WHITESMOKE", 245, 245, 245), + ("YELLOW", 255, 255, 0), + ("YELLOW1", 255, 255, 0), + ("YELLOW2", 238, 238, 0), + ("YELLOW3", 205, 205, 0), + ("YELLOW4", 139, 139, 0), + ("YELLOWGREEN", 154, 205, 50), + }; + + /// + /// Dictionary mapping lower-case colour names to PDF-range float RGB + /// triples (each component in 0.0–1.0). + /// + public static readonly Dictionary PdfColorDict = + ColorList.ToDictionary( + c => c.Name.ToLower(), + c => (c.R / 255.0f, c.G / 255.0f, c.B / 255.0f) + ); + + /// + /// List of all upper-case colour names. + /// + public static readonly List ColorNames = + ColorList.Select(c => c.Name).ToList(); + } +} diff --git a/MuPDF.NET/Xml.cs b/MuPDF.NET/Xml.cs deleted file mode 100644 index bee38e43..00000000 --- a/MuPDF.NET/Xml.cs +++ /dev/null @@ -1,1169 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; -using mupdf; - -namespace MuPDF.NET -{ - public class Xml - { - static Xml() - { - Utils.InitApp(); - } - - private FzXml _nativeXml; - - public FzXml ToFzXml() - { - return _nativeXml; - } - - public Xml(FzXml rhs) - { - _nativeXml = rhs; - } - - /// - /// Either the node’s text or Null if a tag node. - /// - public string Text - { - get { return _nativeXml.fz_xml_text(); } - } - - /// - /// Check if a text node. - /// - public bool IsText - { - get { return Text != null; } - } - - /// - /// Either the HTML tag name like p or Null if a text node. - /// - public string TagName - { - get { return _nativeXml.fz_xml_tag(); } - } - - /// - /// The top node of the DOM, which hence has the tagname - /// - public Xml Root - { - get { return new Xml(_nativeXml.fz_xml_root()); } - } - - /// - /// The previous node at the same level. - /// - public Xml Previous - { - get - { - FzXml ret = _nativeXml.fz_dom_previous(); - if (ret != null) - return new Xml(ret); - else - return null; - } - } - - /// - /// The next node at the same level (or None). - /// - public Xml Next - { - get - { - FzXml ret = _nativeXml.fz_dom_next(); - if (ret != null) - return new Xml(ret); - else - return null; - } - } - - public Xml Parent - { - get - { - FzXml ret = _nativeXml.fz_dom_parent(); - if (ret != null) - return new Xml(ret); - else - return null; - } - } - - /// - /// Contains the first node one level below this one (or null) - /// - public Xml FirstChild - { - get - { - if (_nativeXml.fz_xml_text() == null) - return null; - FzXml ret = _nativeXml.fz_dom_first_child(); - if (ret.m_internal != null) - return new Xml(ret); - else - return null; - } - } - - /// - /// Contains the last node one level below this one (or null) - /// - public Xml LastChild - { - get - { - Xml child = FirstChild; - if (child == null) - return null; - while (true) - { - Xml next = child.Next; - if (next == null) - return child; - child = next; - } - } - } - - public Xml(string rhs) - { - byte[] bytes = Encoding.UTF8.GetBytes(rhs); - FzBuffer buf = Utils.fz_new_buffer_from_data(bytes); - _nativeXml = mupdf.mupdf.fz_parse_xml_from_html5(buf); - } - - private List<(int, string)> ShowNode(Xml node, List<(int, string)> items, int shift) - { - while (node != null) - { - if (node.IsText) - { - items.Add((shift, $"\"{node.Text}\"")); - node = node.Next; - continue; - } - items.Add((shift, $"\"{node.TagName}\"")); - foreach (var attr in node.GetAttributes()) - items.Add((shift, $"={attr.Key} \"{attr.Value}\"")); - Xml child = node.FirstChild; - if (child != null) - { - items = ShowNode(child, items, shift + 1); - } - - items.Add((shift, $"){node.TagName}")); - node = node.Next; - } - return items; - } - - private List<(int, string)> GetNodeTree() - { - int shift = 0; - List<(int, string)> items = new List<(int, string)>(); - items = ShowNode(this, items, shift); - return items; - } - - /// - /// Create a new node with a given tag. This a low-level method used by other methods like - /// - /// the element tag. - /// the created element. To actually bind it to the DOM - public Xml CreateElement(string tag) - { - return new Xml(_nativeXml.fz_dom_create_element(tag)); - } - - /// - /// Create direct text for the current node. - /// - /// the text to append. - /// the created element. - public Xml CreateTextNode(string text) - { - return new Xml(_nativeXml.fz_dom_create_text_node(text)); - } - - /// - /// Append a child node. - /// - /// the Xml node to append. - public void AppendChild(Xml child) - { - _nativeXml.fz_dom_append_child(child.ToFzXml()); - } - - /// - /// Add an ul tag - bulleted list, context manager. - /// - /// - public Xml AddBulletList() - { - Xml child = CreateElement("ul"); - AppendChild(child); - return child; - } - - /// - /// Set (add) some "class" attribute. - /// - /// the name of the class. Must have been defined in either the HTML or the CSS source of the DOM. - /// - public Xml AddClass(string text) - { - string cls = GetAttributeValue("class"); - if (cls != null && cls.Contains(text)) - return this; - RemoveAttribute("class"); - if (cls == null) - cls = text; - else - cls += " " + text; - SetAttribute("class", cls); - return this; - } - - /// - /// Add "class" text (code tag) - inline element, treated like text. - /// - /// - /// - public Xml AddCode(string text = null) - { - Xml child = CreateElement("code"); - AppendChild(CreateTextNode(text)); - Xml prev = SpanBottom(); - if (prev != null) - prev = this; - prev.AppendChild(child); - return this; - } - - /// - /// Add "variable" text (var tag) - inline element, treated like text - /// - /// - /// - public Xml AddVar(string text = null) - { - return AddCode(text); - } - - /// - /// Add “sample output” text (samp tag) - inline element, treated like text - /// - /// - /// - public Xml AddSamp(string text = null) - { - return AddCode(text); - } - - /// - /// Add “keyboard input” text (kbd tag) - inline element, treated like text - /// - /// - /// - public Xml AddKbd(string text = null) - { - return AddCode(text); - } - - /// - /// Add a pre tag, context manager. - /// - /// - public Xml AddCodeBlock() - { - Xml child = CreateElement("pre"); - AppendChild(child); - return child; - } - - /// - /// Add a dl tag, context manager. - /// - /// - public Xml AddDescriptionList() - { - Xml child = CreateElement("dl"); - AppendChild(child); - return child; - } - - /// - /// Add a div tag, context manager. - /// - /// - public Xml AddDivision() - { - Xml child = CreateElement("div"); - AppendChild(child); - return child; - } - - /// - /// Add a header tag (one of h1 to h6), context manager. - /// - /// - /// - /// - public Xml AddHeader(int level = 1) - { - if (!Utils.INRANGE(level, 1, 6)) - throw new Exception("Header level must be in [1, 6]"); - string tagName = TagName; - string newTag = $"h{level}"; - Xml child = CreateElement(newTag); - //if (!(new[] { "h1", "h2", "h3", "h4", "h5", "h6" }).Contains(tagName)) - if (!(new List { "h1", "h2", "h3", "h4", "h5", "h6" }).Contains(tagName)) - { - AppendChild(child); - return child; - } - Parent.AppendChild(child); - return child; - } - - /// - /// Add a hr tag. - /// - /// - public Xml AddHorizontalLine() - { - Xml child = CreateElement("hr"); - AppendChild(child); - return child; - } - - /// - /// Add an img tag. This causes the inclusion of the named image in the DOM. - /// - /// the filename of the image. This must be the member name of some entry of the MuPDFArchive parameter of the MuPDFStory constructor. - /// if provided, either an absolute (int) value, or a percentage string like “30%”. - /// if provided, either an absolute (int) value, or a percentage string like “30%”. - /// - /// - /// - public Xml AddImage( - string name, - string width = null, - string height = null, - string imgFloat = null, - string align = null - ) - { - Xml child = CreateElement("img"); - if (width != null) - child.SetAttribute("width", $"{width}"); - if (height != null) - child.SetAttribute("height", $"{height}"); - if (imgFloat != null) - child.SetAttribute("style", $"float: {imgFloat}"); - if (align != null) - child.SetAttribute("align", $"{align}"); - child.SetAttribute("src", $"{name}"); - AppendChild(child); - return child; - } - - /// - /// Add an a tag - inline element, treated like text. - /// - /// the URL target. - /// the text to display. If omitted, the href text is shown instead. - /// - public Xml AddLink(string href, string text = null) - { - Xml child = CreateElement("a"); - if (text is string) - text = href; - child.SetAttribute("href", href); - child.AppendChild(CreateTextNode(text)); - Xml prev = SpanBottom(); - if (prev == null) - prev = this; - prev.AppendChild(child); - return this; - } - - /// - /// Add an ol tag, context manager. - /// - /// - /// - public Xml AddListItem() - { - if (TagName != "ol" || TagName != "ul") - throw new Exception("cannot add list item"); - Xml child = CreateElement("li"); - AppendChild(child); - return child; - } - - /// - /// Add an ol tag, context manager. - /// - /// - /// - /// - public Xml AddNumberList(int start = 1, string numType = null) - { - Xml child = CreateElement("ol"); - if (start > 1) - child.SetAttribute("start", Convert.ToString(start)); - if (numType != null) - child.SetAttribute("type", numType); - AppendChild(child); - return child; - } - - /// - /// Add a p tag, context manager. - /// - /// - public Xml AddParagraph() - { - Xml child = CreateElement("p"); - if (TagName != "p") - AppendChild(child); - else - Parent.AppendChild(child); - return child; - } - - /// - /// Add a span tag, context manager. - /// - /// - public Xml AddSpan() - { - Xml child = CreateElement("span"); - AppendChild(child); - return child; - } - - /// - /// Set (add) some style attribute not supported by its own set_ method. - /// - /// any valid CSS style value. - /// - public Xml AddStyle(string text) - { - string style = GetAttributeValue("style"); - if (style != null && style.Contains(text)) - return this; - RemoveAttribute("style"); - if (style == null) - style = Text; - else - style += ";" + Text; - SetAttribute("style", style); - return this; - } - - /// - /// Add “subscript” text(sub tag) - inline element, treated like text. - /// - /// - /// - public Xml AddSubscript(string text) - { - Xml child = CreateElement("sub"); - child.AppendChild(CreateTextNode(text)); - Xml prev = SpanBottom(); - if (prev == null) - prev = this; - prev.AppendChild(child); - return this; - } - - /// - /// Add “superscript” text (sup tag) - inline element, treated like text. - /// - /// - /// - public Xml AddSuperscript(string text = null) - { - Xml child = CreateElement("sup"); - child.AppendChild(CreateTextNode(text)); - Xml prev = SpanBottom(); - if (prev == null) - prev = this; - prev.AppendChild(child); - return this; - } - - /// - /// Add a text string. Line breaks n are honored as br tags. - /// - /// - /// - public Xml AddText(string text) - { - string[] lines = text.Split('\n'); - int lineCount = lines.Length; - Xml prev = SpanBottom(); - if (prev == null) - prev = this; - - for (int i = 0; i < lineCount; i++) - { - prev.AppendChild(CreateTextNode(lines[i])); - if (i < lineCount - 1) - { - Xml br = CreateElement("br"); - prev.AppendChild(br); - } - } - return this; - } - - /// - /// - /// - /// - /// - public Xml AppendStyledSpan(string style) - { - Xml span = CreateElement("span"); - span.AddStyle(style); - Xml prev = SpanBottom(); - if (prev == null) - prev = this; - prev.AppendChild(span); - return prev; - } - - /// - /// Make a copy if this node - /// - /// - public Xml Clone() - { - return new Xml(_nativeXml.fz_dom_clone()); - } - - /// - /// - /// - /// - /// - public static string ColorText(dynamic color) - { - if (color is string) - return color; - if (color is int) - { - (int, int, int) rgb = Utils.sRGB2Rgb(color); - return $"rgb({rgb.Item1},{rgb.Item2},{rgb.Item3})"; - } - if ((color is float[] && color.Length == 3)) - { - return $"rbg({color[0]},{color[1]},{color[2]}"; - } - return color; - } - - /// - /// For debugging purposes, print this node’s structure in a simplified form. - /// - public void Debug() - { - List<(int, string)> items = GetNodeTree(); - foreach ((int k, string v) in items) - { - Console.WriteLine($"{k}: " + v.Replace("\n", "\\n")); - } - } - - /// - /// Under the current node, find the first node with the given tag, attribute att and value match. - /// - /// restrict search to this tag. May be null for unrestricted searches. - /// check this attribute. May be None. - /// the desired attribute value to match. May be null. - /// - public Xml Find(string tag, string att, string match) - { - FzXml ret = _nativeXml.fz_dom_find(tag, att, match); - if (ret != null) - return new Xml(ret); - return null; - } - - /// - /// Continue a previous Xml.find() (or find_next()) with the same values. - /// - /// - /// - /// - /// Null if null more found, otherwise the next matching node. - public Xml FindNext(string tag, string att, string match) - { - FzXml ret = _nativeXml.fz_dom_find_next(tag, att, match); - if (ret != null) - return new Xml(ret); - return null; - } - - /// - /// Insert the given element elem after this node. - /// - /// - public void InsertAfter(Xml node) - { - _nativeXml.fz_dom_insert_after(node.ToFzXml()); - } - - /// - /// Insert the given element elem before this node. - /// - /// - public void InsertBefore(Xml node) - { - _nativeXml.fz_dom_insert_before(node.ToFzXml()); - } - - public Xml InsertText(string text) - { - string[] lines = text.Split('\n'); - int lineCount = lines.Length; - - for (int i = 0; i < lineCount; i++) - { - AppendChild(CreateTextNode(lines[i])); - if (i < lineCount - 1) - AppendChild(CreateElement("br")); - } - return this; - } - - /// - /// Remove this node - /// - public void Remove() - { - _nativeXml.fz_dom_remove(); - } - - /// - /// Set the text alignment. Only works for block-level tags. - /// - /// either one of the Text Alignment or the text-align values. - /// - /// - public Xml SetAlign(dynamic align) - { - string text = "text-align: "; - if (align is string) - text += align; - else if (align == TextAlign.TEXT_ALIGN_LEFT) - text += "left"; - else if (align == TextAlign.TEXT_ALIGN_CENTER) - text += "center"; - else if (align == TextAlign.TEXT_ALIGN_RIGHT) - text += "right"; - else if (align == TextAlign.TEXT_ALIGN_JUSTIFY) - text += "justify"; - else - throw new Exception($"Unrecognised {align}"); - - AddStyle(text); - return this; - } - - /// - /// Set the background color. Only works for block-level tags. - /// - /// either an RGB value like (255, 0, 0) (for “red”) or a valid background-color value. - /// - public Xml SetBgColor(int color) - { - string text = $"backgroud-color: {Xml.ColorText(color)}"; - AddStyle(text); - return this; - } - - /// - /// Set the background color. Only works for block-level tags. - /// - /// either an RGB value like (255, 0, 0) (for “red”) or a valid background-color value. - /// - public Xml SetBgColor(float[] color) - { - string text = $"backgroud-color: {Xml.ColorText(color)}"; - AddStyle(text); - return this; - } - - /// - /// Set the background color. Only works for block-level tags. - /// - /// either an RGB value like (255, 0, 0) (for “red”) or a valid background-color value. - /// - public Xml SetBgColor(string color) - { - string text = $"backgroud-color: {Xml.ColorText(color)}"; - AddStyle(text); - return this; - } - - /// - /// Set bold on or off or to some string value. - /// - /// True, False or a valid font-weight value. - /// - public Xml SetBold(bool isBold) - { - string text = "font-weight: "; - if (isBold) - text += "bold"; - else - text += "normal"; - AppendStyledSpan(text); - return this; - } - - /// - /// Set the color of the text following. - /// - /// either an RGB value like (255, 0, 0) (for “red”) or a valid color value. - /// - public Xml SetColor(dynamic color) - { - string text = $"color: {Xml.ColorText(color)}"; - AppendStyledSpan(text); - return this; - } - - /// - /// Set the number of columns. - /// - /// a valid columns value. - /// - public Xml SetColumns(int cols) - { - string text = $"columns: {cols}"; - AppendStyledSpan(text); - return this; - } - - /// - /// Set the font-family. - /// - /// - /// - public Xml SetFont(string font) - { - string text = $"font-family: {font}"; - AppendStyledSpan(text); - return this; - } - - /// - /// Set the font size for text following. - /// - /// a float or a valid font-size value. - /// - public Xml SetFontSize(int fontSize) - { - string text = $"font-size: {fontSize}px"; - AppendStyledSpan(text); - return this; - } - - /// - /// Set a id. This serves as a unique identification of the node within the DOM. - /// - /// id string of the node. - /// - /// - public Xml SetId(string unique) - { - Xml root = Root; - if (root.Find(null, "id", unique) == null) - throw new Exception($"id \'{unique}\' already exists"); - SetAttribute("id", unique); - return this; - } - - /// - /// Set italic on or off or to some string value for the text following it. - /// - /// True, False or some valid font-style value. - /// - public Xml SetItalic(bool isItalic) - { - string text = "font-style: "; - if (isItalic) - text += "italic"; - else - text += "normal"; - AppendStyledSpan(text); - return this; - } - - /// - /// Set inter-block text distance (-mupdf-leading), only works on block-level nodes. - /// - /// the distance in points to the previous block. - /// - public Xml SetLeading(string leading) - { - string text = $"-mupdf-leading: {leading}"; - AddStyle(text); - return this; - } - - /// - /// - /// - /// - /// - public Xml SetLetterSpacing(string spacing) - { - string text = $"leter-spacing: {spacing}"; - AppendStyledSpan(text); - return this; - } - - /// - /// Set height of a line. - /// - /// a float like 1.5 (which sets to 1.5 * fontsize), or some valid line-height value. - /// - public Xml SetLineHeight(string lineHeight) - { - string text = $"line-height: {lineHeight}"; - AddStyle(text); - return this; - } - - /// - /// Set the margin(s). - /// - /// float or string with up to 4 values. - /// - public Xml SetMargins(string val) - { - string text = $"margin: {val}"; - AppendStyledSpan(text); - return this; - } - - /// - /// Set opacity - /// - /// - /// - public Xml SetOpacity(string opacity) - { - string text = $"opacity: {opacity}"; - AppendStyledSpan(text); - return this; - } - - /// - /// Insert a page break after this node. - /// - /// - public Xml SetPageBreakAfter() - { - string text = "page-break-after: always"; - AddStyle(text); - return this; - } - - /// - /// Insert a page break before this node. - /// - /// - public Xml SetPageBreakBefore() - { - string text = "page-break-before: always"; - AddStyle(text); - return this; - } - - /// - /// Set any or all desired properties in one call. The meaning of argument values equal the values of the corresponding set_ methods. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Xml SetProperties( - string align = null, - string bgcolor = null, - bool bold = false, - string color = null, - int columns = 0, - string font = null, - int fontSize = 10, - bool italic = false, - string indent = null, - string leading = null, - string letterSpacing = null, - string lineHeight = null, - string margins = null, - string pageBreakAfter = null, - string pageBreakBefore = null, - string wordSpacing = null, - string unqid = null, - string cls = null - ) - { - Xml root = Root; - Xml temp = root.AddDivision(); - if (align != null) - { - temp.SetAlign(align); - } - if (bgcolor != null) - { - temp.SetBgColor(bgcolor); - } - if (bold) - { - temp.SetBold(bold); - } - if (color != null) - { - temp.SetColor(color); - } - if (columns != 0) - { - temp.SetColumns(columns); - } - if (font != null) - { - temp.SetFont(font); - } - if (fontSize != 10) - { - temp.SetFontSize(fontSize); - } - if (indent != null) - { - temp.SetTextIndent(indent); - } - if (italic) - { - temp.SetItalic(italic); - } - if (leading != null) - { - temp.SetLeading(leading); - } - if (letterSpacing != null) - { - temp.SetLetterSpacing(letterSpacing); - } - if (lineHeight != null) - { - temp.SetLineHeight(lineHeight); - } - if (margins != null) - { - temp.SetMargins(margins); - } - if (pageBreakAfter != null) - { - temp.SetPageBreakAfter(); - } - if (pageBreakBefore != null) - { - temp.SetPageBreakBefore(); - } - if (wordSpacing != null) - { - temp.SetWordSpacing(wordSpacing); - } - if (unqid != null) - { - this.SetId(unqid); - } - if (cls != null) - { - this.AddClass(cls); - } - - List styles = new List(); - string topStyle = GetAttributeValue("style"); - if (topStyle != null) - styles.Add(topStyle); - Xml child = temp.FirstChild; - while (child != null) - { - styles.Add(child.GetAttributeValue("style")); - child = child.FirstChild; - } - SetAttribute("style", string.Join(";", styles.ToArray())); - temp.Remove(); - return this; - } - - /// - /// Set indentation for the first textblock line. Only works for block-level nodes. - /// - /// - /// - public Xml SetTextIndent(string indent) - { - string text = $"text-indent: {indent}"; - AddStyle(text); - return this; - } - - /// - /// Set underline for the text - /// - /// string "underline" - /// - public Xml SetUnderline(string val = "underline") - { - string text = $"text-decoration: {val}"; - AppendStyledSpan(text); - return this; - } - - /// - /// Set word-spacing for the text - /// - /// - /// - public Xml SetWordSpacing(string spacing) - { - string text = $"text-spacing: {spacing}"; - AppendStyledSpan(text); - return this; - } - - /// - /// Retrieve all attributes of the current nodes as a dictionary - /// - /// a dictionary with the attributes and their values of the node - public Dictionary GetAttributes() - { - if (!IsText) - return null; - Dictionary ret = new Dictionary(); - int i = 0; - while (true) - { - (string val, string attr) = _nativeXml.fz_dom_get_attribute(i); - if (val == null || attr == null) - break; - ret.Add(attr, val); - i += 1; - } - return ret; - } - - /// - /// Returns body tag from xml - /// - /// Xml object - public Xml GetBodyTag() - { - return new Xml(_nativeXml.fz_dom_body()); - } - - /// - /// Get the attribute value of key - /// - /// the name of the attribute - /// a string with the value of key - public string GetAttributeValue(string attr) - { - return _nativeXml.fz_dom_attribute(attr); - } - - /// - /// Remove the attribute key from the node - /// - /// the name of attribute - public void RemoveAttribute(string attr) - { - _nativeXml.fz_dom_remove_attribute(attr); - } - - /// - /// Set an arbitrary key to some value - /// - /// the name of the attribute - /// the value of the attribute - public void SetAttribute(string attr, string value) - { - _nativeXml.fz_dom_add_attribute(attr, value); - } - - /// - /// Find deepest level in stacked spans - /// - /// spans in the bottom - public Xml SpanBottom() - { - Xml parent = this; - Xml child = LastChild; - if (child == null) - return null; - while (child.IsText) - { - child = child.Previous; - if (child == null) - break; - } - - if (child == null || child.TagName != "span") - return null; - - while (true) - { - if (child == null) - return parent; - if ((new List() { "a", "sub", "sup", "body" }).Contains(child.TagName)) - { - child = child.Next; - continue; - } - - if (child.TagName == "span") - { - parent = child; - child = child.FirstChild; - } - else - return parent; - } - } - } -} diff --git a/MuPDF.NET/build/net4/MuPDF.NET.targets b/MuPDF.NET/build/net4/MuPDF.NET.targets deleted file mode 100644 index 1d0d15e9..00000000 --- a/MuPDF.NET/build/net4/MuPDF.NET.targets +++ /dev/null @@ -1,44 +0,0 @@ - - - - false - false - false - false - - - - - true - true - true - true - - - - - - - - - - - - - - - - - - - - diff --git a/MuPDF.NET/enums/BarcodeFlags.cs b/MuPDF.NET/enums/BarcodeFlags.cs deleted file mode 100644 index 513abfeb..00000000 --- a/MuPDF.NET/enums/BarcodeFlags.cs +++ /dev/null @@ -1,63 +0,0 @@ -namespace MuPDF.NET -{ - public enum BarcodeFormat - { - ALL = 0, - AZTEC, - BOXES, - CODABAR, - CODABLOCKF, - CODE128, - CODE16K, - CODE39, - CODE39_LINEARREADER, - CODE39_EX, - CODE39_NOISE1, - CODE93, - DM, - DM_DPM, - EAN13, - EAN2, - EAN5, - EAN8, - EAN_UPC_OLD, - GS1DATABAREXP, - GS1DATABAREXPSTACKED, - GS1DATABAROMNI, - GS1DATABARSTACKED, - GS1DATABARSTACKEDOMNI, - GS1DATABARLIMITED, - HORIZONTALLINES, - I2OF5, - IM, - KIX, - LINETABLES, - MAXICODE, - MICR, - MICROPDF, - MSI, - OMRCIRCLE, - OMRCIRCLE_EXT, - OMROVAL, - OMROVAL_EXT, - OMRSQUARE, - OMRSQUARE_EXT, - OMRSQUARELPATTERN, - OMRRECTANGLE, - OMRRECTANGLE_EXT, - OMRRECTANGLELPATTERNVERT, - OMRRECTANGLELPATTERNHORIZ, - PATCH, - PHARMA, - PDF417, - POSTCODE, - POSTNET, - QR, - RAWOMR, - RM, - VERTICALLINES, - UPC_A, - UPC_E, - TRIOPTIC - } -} diff --git a/MuPDF.NET/enums/ExtractFormat.cs b/MuPDF.NET/enums/ExtractFormat.cs deleted file mode 100644 index ce036e09..00000000 --- a/MuPDF.NET/enums/ExtractFormat.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace MuPDF.NET -{ - public enum ExtractFormat - { - HTML = 1, - - XML = 3, - - XHTML = 4, - - TEXT = 5 - } -} diff --git a/MuPDF.NET/enums/FontStyle.cs b/MuPDF.NET/enums/FontStyle.cs deleted file mode 100644 index d392d636..00000000 --- a/MuPDF.NET/enums/FontStyle.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace MuPDF.NET -{ - public enum FontStyle - { - TEXT_FONT_SUPERSCRIPT = 1, - - TEXT_FONT_ITALIC = 2, - - TEXT_FONT_SERIFED = 4, - - TEXT_FONT_MONOSPACED = 8, - - TEXT_FONT_BOLD = 16 - } -} diff --git a/MuPDF.NET/enums/FormFlags.cs b/MuPDF.NET/enums/FormFlags.cs deleted file mode 100644 index ce2a60e6..00000000 --- a/MuPDF.NET/enums/FormFlags.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MuPDF.NET -{ - public enum FormFlags - { - /* All fields */ - PDF_FIELD_IS_READ_ONLY = 1, - PDF_FIELD_IS_REQUIRED = 1 << 1, - PDF_FIELD_IS_NO_EXPORT = 1 << 2, - - /* Text fields */ - PDF_TX_FIELD_IS_MULTILINE = 1 << 12, - PDF_TX_FIELD_IS_PASSWORD = 1 << 13, - PDF_TX_FIELD_IS_FILE_SELECT = 1 << 20, - PDF_TX_FIELD_IS_DO_NOT_SPELL_CHECK = 1 << 22, - PDF_TX_FIELD_IS_DO_NOT_SCROLL = 1 << 23, - PDF_TX_FIELD_IS_COMB = 1 << 24, - PDF_TX_FIELD_IS_RICH_TEXT = 1 << 25, - - /* Button fields */ - PDF_BTN_FIELD_IS_NO_TOGGLE_TO_OFF = 1 << 14, - PDF_BTN_FIELD_IS_RADIO = 1 << 15, - PDF_BTN_FIELD_IS_PUSHBUTTON = 1 << 16, - PDF_BTN_FIELD_IS_RADIOS_IN_UNISON = 1 << 25, - - /* Choice fields */ - PDF_CH_FIELD_IS_COMBO = 1 << 17, - PDF_CH_FIELD_IS_EDIT = 1 << 18, - PDF_CH_FIELD_IS_SORT = 1 << 19, - PDF_CH_FIELD_IS_MULTI_SELECT = 1 << 21, - PDF_CH_FIELD_IS_DO_NOT_SPELL_CHECK = 1 << 22, - PDF_CH_FIELD_IS_COMMIT_ON_SEL_CHANGE = 1 << 25, - } -} diff --git a/MuPDF.NET/enums/ImageType.cs b/MuPDF.NET/enums/ImageType.cs deleted file mode 100644 index 2e3e1606..00000000 --- a/MuPDF.NET/enums/ImageType.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace MuPDF.NET -{ - public enum ImageType - { - FZ_IMAGE_UNKNOWN = 0, - - /* Uncompressed samples */ - FZ_IMAGE_RAW, - - /* Compressed samples */ - FZ_IMAGE_FAX, - FZ_IMAGE_FLATE, - FZ_IMAGE_LZW, - FZ_IMAGE_RLD, - - /* Full image formats */ - FZ_IMAGE_BMP, - FZ_IMAGE_GIF, - FZ_IMAGE_JBIG2, - FZ_IMAGE_JPEG, - FZ_IMAGE_JPX, - FZ_IMAGE_JXR, - FZ_IMAGE_PNG, - FZ_IMAGE_PNM, - FZ_IMAGE_TIFF, - FZ_IMAGE_PSD, - } -} diff --git a/MuPDF.NET/enums/LanguageType.cs b/MuPDF.NET/enums/LanguageType.cs deleted file mode 100644 index 8d1deda9..00000000 --- a/MuPDF.NET/enums/LanguageType.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace MuPDF.NET -{ - public enum LanguageType - { - FZ_LANG_UNSET = 0, - FZ_LANG_ur = mupdf.fz_text_language.FZ_LANG_ur, - FZ_LANG_urd = mupdf.fz_text_language.FZ_LANG_urd, - FZ_LANG_ko = mupdf.fz_text_language.FZ_LANG_ko, - FZ_LANG_ja = mupdf.fz_text_language.FZ_LANG_ja, - FZ_LANG_zh = mupdf.fz_text_language.FZ_LANG_zh, - FZ_LANG_zh_Hans = mupdf.fz_text_language.FZ_LANG_zh_Hans, - FZ_LANG_zh_Hant = mupdf.fz_text_language.FZ_LANG_zh_Hant - } -} diff --git a/MuPDF.NET/enums/LineCapType.cs b/MuPDF.NET/enums/LineCapType.cs deleted file mode 100644 index c0bf257f..00000000 --- a/MuPDF.NET/enums/LineCapType.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MuPDF.NET -{ - public enum LineCapType - { - FZ_LINECAP_BUTT = 0, - - FZ_LINECAP_ROUND = 1, - - FZ_LINECAP_SQUARE = 2, - - FZ_LINECAP_TRIANGLE = 3 - } -} diff --git a/MuPDF.NET/enums/LinkFlags.cs b/MuPDF.NET/enums/LinkFlags.cs deleted file mode 100644 index e0face59..00000000 --- a/MuPDF.NET/enums/LinkFlags.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace MuPDF.NET -{ - public enum LinkFlags - { - LINK_FLAG_L_VALID = 1, - - LINK_FLAG_T_VALID = 2, - - LINK_FLAG_R_VALID = 4, - - LINK_FLAG_B_VALID = 8, - - LINK_FLAG_FIT_H = 16, - - LINK_FLAG_FIT_V = 32, - - LINK_FLAG_R_IS_ZOOM = 64, - } -} diff --git a/MuPDF.NET/enums/LinkType.cs b/MuPDF.NET/enums/LinkType.cs deleted file mode 100644 index 44e05d0d..00000000 --- a/MuPDF.NET/enums/LinkType.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace MuPDF.NET -{ - public enum LinkType - { - LINK_NONE = 0, - - LINK_GOTO = 1, - - LINK_URI = 2, - - LINK_LAUNCH = 3, - - LINK_NAMED = 4, - - LINK_GOTOR = 5 - } -} diff --git a/MuPDF.NET/enums/OcFlags.cs b/MuPDF.NET/enums/OcFlags.cs deleted file mode 100644 index d9ca2a18..00000000 --- a/MuPDF.NET/enums/OcFlags.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MuPDF.NET -{ - public enum OcFlags - { - PDF_OC_ON = 0, - - PDF_OC_TOGGLE = 1, - - PDF_OC_OFF = 2 - } -} diff --git a/MuPDF.NET/enums/PdfAccess.cs b/MuPDF.NET/enums/PdfAccess.cs deleted file mode 100644 index 2204ab8c..00000000 --- a/MuPDF.NET/enums/PdfAccess.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace MuPDF.NET.enums -{ - public enum PdfAccess - { - PDF_PERM_PRINT = 1 << 2, - PDF_PERM_MODIFY = 1 << 3, - PDF_PERM_COPY = 1 << 4, - PDF_PERM_ANNOTATE = 1 << 5, - PDF_PERM_FORM = 1 << 8, - PDF_PERM_ACCESSIBILITY = 1 << 9, /* deprecated in pdf 2.0 (this permission is always granted) */ - PDF_PERM_ASSEMBLE = 1 << 10, - PDF_PERM_PRINT_HQ = 1 << 11, - } -} diff --git a/MuPDF.NET/enums/PdfAnnotStatus.cs b/MuPDF.NET/enums/PdfAnnotStatus.cs deleted file mode 100644 index e055490d..00000000 --- a/MuPDF.NET/enums/PdfAnnotStatus.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace MuPDF.NET -{ - public enum PdfAnnotStatus - { - PDF_ANNOT_IS_INVISIBLE = 1 << (1 - 1), - PDF_ANNOT_IS_HIDDEN = 1 << (2 - 1), - PDF_ANNOT_IS_PRINT = 1 << (3 - 1), - PDF_ANNOT_IS_NO_ZOOM = 1 << (4 - 1), - PDF_ANNOT_IS_NO_ROTATE = 1 << (5 - 1), - PDF_ANNOT_IS_NO_VIEW = 1 << (6 - 1), - PDF_ANNOT_IS_READ_ONLY = 1 << (7 - 1), - PDF_ANNOT_IS_LOCKED = 1 << (8 - 1), - PDF_ANNOT_IS_TOGGLE_NO_VIEW = 1 << (9 - 1), - PDF_ANNOT_IS_LOCKED_CONTENTS = 1 << (10 - 1) - } -} diff --git a/MuPDF.NET/enums/PdfAnnotType.cs b/MuPDF.NET/enums/PdfAnnotType.cs deleted file mode 100644 index fa290099..00000000 --- a/MuPDF.NET/enums/PdfAnnotType.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace MuPDF.NET -{ - public enum PdfAnnotType - { - PDF_ANNOT_TEXT, - PDF_ANNOT_LINK, - PDF_ANNOT_FREE_TEXT, - PDF_ANNOT_LINE, - PDF_ANNOT_SQUARE, - PDF_ANNOT_CIRCLE, - PDF_ANNOT_POLYGON, - PDF_ANNOT_POLY_LINE, - PDF_ANNOT_HIGHLIGHT, - PDF_ANNOT_UNDERLINE, - PDF_ANNOT_SQUIGGLY, - PDF_ANNOT_STRIKE_OUT, - PDF_ANNOT_REDACT, - PDF_ANNOT_STAMP, - PDF_ANNOT_CARET, - PDF_ANNOT_INK, - PDF_ANNOT_POPUP, - PDF_ANNOT_FILE_ATTACHMENT, - PDF_ANNOT_SOUND, - PDF_ANNOT_MOVIE, - PDF_ANNOT_RICH_MEDIA, - PDF_ANNOT_WIDGET, - PDF_ANNOT_SCREEN, - PDF_ANNOT_PRINTER_MARK, - PDF_ANNOT_TRAP_NET, - PDF_ANNOT_WATERMARK, - PDF_ANNOT_3D, - PDF_ANNOT_PROJECTION, - PDF_ANNOT_UNKNOWN = -1 - } -} diff --git a/MuPDF.NET/enums/PdfCrypt.cs b/MuPDF.NET/enums/PdfCrypt.cs deleted file mode 100644 index a54d03c4..00000000 --- a/MuPDF.NET/enums/PdfCrypt.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace MuPDF.NET -{ - public enum PdfCrypt - { - PDF_ENCRYPT_KEEP, - PDF_ENCRYPT_NONE, - PDF_ENCRYPT_RC4_40, - PDF_ENCRYPT_RC4_128, - PDF_ENCRYPT_AES_128, - PDF_ENCRYPT_AES_256, - PDF_ENCRYPT_UNKNOWN - } -} diff --git a/MuPDF.NET/enums/PdfFieldFlags.cs b/MuPDF.NET/enums/PdfFieldFlags.cs deleted file mode 100644 index 1cf2b440..00000000 --- a/MuPDF.NET/enums/PdfFieldFlags.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace MuPDF.NET -{ - public enum PdfFieldFlags - { - PDF_FIELD_IS_READ_ONLY = 1, - PDF_FIELD_IS_REQUIRED = 1 << 1, - PDF_FIELD_IS_NO_EXPORT = 1 << 2, - - /* Text fields */ - PDF_TX_FIELD_IS_MULTILINE = 1 << 12, - PDF_TX_FIELD_IS_PASSWORD = 1 << 13, - PDF_TX_FIELD_IS_FILE_SELECT = 1 << 20, - PDF_TX_FIELD_IS_DO_NOT_SPELL_CHECK = 1 << 22, - PDF_TX_FIELD_IS_DO_NOT_SCROLL = 1 << 23, - PDF_TX_FIELD_IS_COMB = 1 << 24, - PDF_TX_FIELD_IS_RICH_TEXT = 1 << 25, - - /* Button fields */ - PDF_BTN_FIELD_IS_NO_TOGGLE_TO_OFF = 1 << 14, - PDF_BTN_FIELD_IS_RADIO = 1 << 15, - PDF_BTN_FIELD_IS_PUSHBUTTON = 1 << 16, - PDF_BTN_FIELD_IS_RADIOS_IN_UNISON = 1 << 25, - - /* Choice fields */ - PDF_CH_FIELD_IS_COMBO = 1 << 17, - PDF_CH_FIELD_IS_EDIT = 1 << 18, - PDF_CH_FIELD_IS_SORT = 1 << 19, - PDF_CH_FIELD_IS_MULTI_SELECT = 1 << 21, - PDF_CH_FIELD_IS_DO_NOT_SPELL_CHECK = 1 << 22, - PDF_CH_FIELD_IS_COMMIT_ON_SEL_CHANGE = 1 << 25, - } -} diff --git a/MuPDF.NET/enums/PdfLineEnding.cs b/MuPDF.NET/enums/PdfLineEnding.cs deleted file mode 100644 index 16027e92..00000000 --- a/MuPDF.NET/enums/PdfLineEnding.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace MuPDF.NET -{ - public enum PdfLineEnding - { - PDF_ANNOT_LE_NONE = 0, - PDF_ANNOT_LE_SQUARE, - PDF_ANNOT_LE_CIRCLE, - PDF_ANNOT_LE_DIAMOND, - PDF_ANNOT_LE_OPEN_ARROW, - PDF_ANNOT_LE_CLOSED_ARROW, - PDF_ANNOT_LE_BUTT, - PDF_ANNOT_LE_R_OPEN_ARROW, - PDF_ANNOT_LE_R_CLOSED_ARROW, - PDF_ANNOT_LE_SLASH - } -} diff --git a/MuPDF.NET/enums/PdfWidgetType.cs b/MuPDF.NET/enums/PdfWidgetType.cs deleted file mode 100644 index 1289fe89..00000000 --- a/MuPDF.NET/enums/PdfWidgetType.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace MuPDF.NET -{ - public enum PdfWidgetType - { - PDF_WIDGET_TYPE_UNKNOWN, - PDF_WIDGET_TYPE_BUTTON, - PDF_WIDGET_TYPE_CHECKBOX, - PDF_WIDGET_TYPE_COMBOBOX, - PDF_WIDGET_TYPE_LISTBOX, - PDF_WIDGET_TYPE_RADIOBUTTON, - PDF_WIDGET_TYPE_SIGNATURE, - PDF_WIDGET_TYPE_TEXT, - } -} diff --git a/MuPDF.NET/enums/PermissionCodes.cs b/MuPDF.NET/enums/PermissionCodes.cs deleted file mode 100644 index 7fb4b727..00000000 --- a/MuPDF.NET/enums/PermissionCodes.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace MuPDF.NET -{ - public enum PermissionCodes - { - PDF_PERM_PRINT = 1 << 2, - PDF_PERM_MODIFY = 1 << 3, - PDF_PERM_COPY = 1 << 4, - PDF_PERM_ANNOTATE = 1 << 5, - PDF_PERM_FORM = 1 << 8, - PDF_PERM_ACCESSIBILITY = 1 << 9, /* deprecated in pdf 2.0 (this permission is always granted) */ - PDF_PERM_ASSEMBLE = 1 << 10, - PDF_PERM_PRINT_HQ = 1 << 11, - } -} diff --git a/MuPDF.NET/enums/STextBlockType.cs b/MuPDF.NET/enums/STextBlockType.cs deleted file mode 100644 index bd05989f..00000000 --- a/MuPDF.NET/enums/STextBlockType.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace MuPDF.NET -{ - public enum STextBlockType - { - FZ_STEXT_BLOCK_TEXT = 0, - - FZ_STEXT_BLOCK_IMAGE = 1, - - FZ_STEXT_BLOCK_STRUCT = 2, - - FZ_STEXT_BLOCK_VECTOR = 3, - - FZ_STEXT_BLOCK_GRID = 4 - } -} diff --git a/MuPDF.NET/enums/SimpleEncoding.cs b/MuPDF.NET/enums/SimpleEncoding.cs deleted file mode 100644 index 852e00ac..00000000 --- a/MuPDF.NET/enums/SimpleEncoding.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MuPDF.NET -{ - public enum SimpleEncoding - { - PDF_SIMPLE_ENCODING_LATIN, - - PDF_SIMPLE_ENCODING_GREEK, - - PDF_SIMPLE_ENCODING_CYRILLIC - } -} diff --git a/MuPDF.NET/enums/SvgText.cs b/MuPDF.NET/enums/SvgText.cs deleted file mode 100644 index b5b2e292..00000000 --- a/MuPDF.NET/enums/SvgText.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MuPDF.NET -{ - public enum SvgText - { - FZ_SVG_TEXT_AS_PATH = 0, - - FZ_SVG_TEXT_AS_TEXT = 1 - } -} diff --git a/MuPDF.NET/enums/TableFlags.cs b/MuPDF.NET/enums/TableFlags.cs deleted file mode 100644 index ea35f144..00000000 --- a/MuPDF.NET/enums/TableFlags.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MuPDF.NET -{ - public class TableFlags - { - public const float TABLE_UNSET = 0.0f; - public const float TABLE_DEFAULT_SNAP_TOLERANCE = 3.0f; - public const float TABLE_DEFAULT_JOIN_TOLERANCE = 3.0f; - public const float TABLE_DEFAULT_MIN_WORDS_VERTICAL = 3.0f; - public const float TABLE_DEFAULT_MIN_WORDS_HORIZONTAL = 1.0f; - public const float TABLE_DEFAULT_X_TOLERANCE = 3.0f; - public const float TABLE_DEFAULT_Y_TOLERANCE = 3.0f; - public const float TABLE_DEFAULT_X_DENSITY = 7.25f; - public const float TABLE_DEFAULT_Y_DENSITY = 13.0f; - - public static readonly string[] TABLE_STRATEGIES = { "lines", "lines_strict", "text", "explicit" }; - public static readonly Dictionary TABLE_LIGATURES = new Dictionary - { - { "ff", "ff" }, - { "ffi", "ffi" }, - { "ffl", "ffl" }, - { "fi", "fi" }, - { "fl", "fl" }, - { "st", "st" }, - { "ſt", "st" } - }; - } -} diff --git a/MuPDF.NET/enums/TextAlign.cs b/MuPDF.NET/enums/TextAlign.cs deleted file mode 100644 index d5d38991..00000000 --- a/MuPDF.NET/enums/TextAlign.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MuPDF.NET -{ - public enum TextAlign - { - TEXT_ALIGN_LEFT = 0, - - TEXT_ALIGN_CENTER = 1, - - TEXT_ALIGN_RIGHT = 2, - - TEXT_ALIGN_JUSTIFY = 3 - } -} diff --git a/MuPDF.NET/enums/TextFlags.cs b/MuPDF.NET/enums/TextFlags.cs deleted file mode 100644 index 19571786..00000000 --- a/MuPDF.NET/enums/TextFlags.cs +++ /dev/null @@ -1,90 +0,0 @@ -namespace MuPDF.NET -{ - public enum TextFlags - { - TEXT_PRESERVE_LIGATURES = 1, - - TEXT_PRESERVE_WHITESPACE = 2, - - TEXT_PRESERVE_IMAGES = 4, - - TEXT_INHIBIT_SPACES = 8, - - TEXT_DEHYPHENATE = 16, - - TEXT_PRESERVE_SPANS = 32, - - TEXT_MEDIABOX_CLIP = 64, - - TEXT_CID_FOR_UNKNOWN_UNICODE = 128, - - TEXT_COLLECT_STRUCTURE = 256, - - TEXT_ACCURATE_BBOXES = 512 - } - - public enum TextFlagsExtension - { - TEXTFLAGS_WORDS = (0 - | TextFlags.TEXT_PRESERVE_LIGATURES - | TextFlags.TEXT_PRESERVE_WHITESPACE - | TextFlags.TEXT_MEDIABOX_CLIP - | TextFlags.TEXT_CID_FOR_UNKNOWN_UNICODE - ), - - TEXTFLAGS_BLOCKS = (0 - | TextFlags.TEXT_PRESERVE_LIGATURES - | TextFlags.TEXT_PRESERVE_WHITESPACE - | TextFlags.TEXT_MEDIABOX_CLIP - | TextFlags.TEXT_CID_FOR_UNKNOWN_UNICODE - ), - - TEXTFLAGS_DICT = (0 - | TextFlags.TEXT_PRESERVE_LIGATURES - | TextFlags.TEXT_PRESERVE_WHITESPACE - | TextFlags.TEXT_MEDIABOX_CLIP - | TextFlags.TEXT_PRESERVE_IMAGES - | TextFlags.TEXT_CID_FOR_UNKNOWN_UNICODE - ), - - TEXTFLAGS_RAWDICT = TextFlagsExtension.TEXTFLAGS_DICT, - - TEXTFLAGS_SEARCH = (0 - | TextFlags.TEXT_PRESERVE_LIGATURES - | TextFlags.TEXT_PRESERVE_WHITESPACE - | TextFlags.TEXT_MEDIABOX_CLIP - | TextFlags.TEXT_DEHYPHENATE - | TextFlags.TEXT_CID_FOR_UNKNOWN_UNICODE - ), - - TEXTFLAGS_HTML = (0 - | TextFlags.TEXT_PRESERVE_LIGATURES - | TextFlags.TEXT_PRESERVE_WHITESPACE - | TextFlags.TEXT_MEDIABOX_CLIP - | TextFlags.TEXT_PRESERVE_IMAGES - | TextFlags.TEXT_CID_FOR_UNKNOWN_UNICODE - ), - - TEXTFLAGS_XHTML = (0 - | TextFlags.TEXT_PRESERVE_LIGATURES - | TextFlags.TEXT_PRESERVE_WHITESPACE - | TextFlags.TEXT_MEDIABOX_CLIP - | TextFlags.TEXT_PRESERVE_IMAGES - | TextFlags.TEXT_CID_FOR_UNKNOWN_UNICODE - ), - - TEXTFLAGS_XML = (0 - | TextFlags.TEXT_PRESERVE_LIGATURES - | TextFlags.TEXT_PRESERVE_WHITESPACE - | TextFlags.TEXT_MEDIABOX_CLIP - | TextFlags.TEXT_CID_FOR_UNKNOWN_UNICODE - ), - - TEXTFLAGS_TEXT = (0 - | TextFlags.TEXT_PRESERVE_LIGATURES - | TextFlags.TEXT_PRESERVE_WHITESPACE - | TextFlags.TEXT_MEDIABOX_CLIP - | TextFlags.TEXT_CID_FOR_UNKNOWN_UNICODE - ) - } -} diff --git a/MuPDF.NET/enums/TextLanguage.cs b/MuPDF.NET/enums/TextLanguage.cs deleted file mode 100644 index 8842e491..00000000 --- a/MuPDF.NET/enums/TextLanguage.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace MuPDF.NET -{ - public enum TextLanguage - { - FZ_LANG_UNSET = 0, - FZ_LANG_ur = (('u' - 'a' + 1) + (('r' - 'a' + 1) * 27)), - FZ_LANG_urd = (('u' - 'a' + 1) + (('r' - 'a' + 1) * 27) + (('d' - 'a' + 1) * 27 * 27)), - FZ_LANG_ko = (('k' - 'a' + 1) + (('o' - 'a' + 1) * 27)), - FZ_LANG_ja = (('j' - 'a' + 1) + (('a' - 'a' + 1) * 27)), - FZ_LANG_zh = (('z' - 'a' + 1) + (('h' - 'a' + 1) * 27)), - FZ_LANG_zh_Hans = (('z' - 'a' + 1) + (('h' - 'a' + 1) * 27) + (('s' - 'a' + 1) * 27 * 27)), - FZ_LANG_zh_Hant = (('z' - 'a' + 1) + (('h' - 'a' + 1) * 27) + (('t' - 'a' + 1) * 27 * 27)) - } -} diff --git a/MuPDF.NET/enums/TextType.cs b/MuPDF.NET/enums/TextType.cs deleted file mode 100644 index 96cd308b..00000000 --- a/MuPDF.NET/enums/TextType.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MuPDF.NET -{ - internal enum TextType - { - TEXT_FONT_SUPERSCRIPT = 1, - - TEXT_FONT_ITALIC = 2, - - TEXT_FONT_SERIFED = 4, - - TEXT_FONT_MONOSPACED = 8, - - TEXT_FONT_BOLD = 16 - } -} diff --git a/MuPDF.NET/global.json b/MuPDF.NET/global.json deleted file mode 100644 index d8b6de67..00000000 --- a/MuPDF.NET/global.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "sdk": { - "version": "5.0.408", - "rollForward": "latestPatch" - } -} diff --git a/MuPDF.NET/structures/AnnotInfo.cs b/MuPDF.NET/structures/AnnotInfo.cs deleted file mode 100644 index 2775b5b6..00000000 --- a/MuPDF.NET/structures/AnnotInfo.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace MuPDF.NET -{ - public class AnnotInfo - { - /// - /// a string containing the text for type Text and FreeText annotations - /// - public string Content { get; set; } - - /// - /// name of annot's icon *string* - /// - public string Name { get; set; } - - /// - /// a string containing the title of the annotation pop-up window. - /// - public string Title { get; set; } - - /// - /// creation timestamp - /// - public string CreationDate { get; set; } - - /// - /// last modified timestamp - /// - public string ModDate { get; set; } - - /// - /// subject string - /// - public string Subject { get; set; } - - /// - /// a unique identification of the annotation - /// - public string Id { get; set; } - } -} diff --git a/MuPDF.NET/structures/AnnotValues.cs b/MuPDF.NET/structures/AnnotValues.cs deleted file mode 100644 index 055e6649..00000000 --- a/MuPDF.NET/structures/AnnotValues.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace MuPDF.NET -{ - public class AnnotValues - { - public int Xref { get; set; } - - public string Text { get; set; } = null; - - public int Align { get; set; } = 0; - - public Rect Rect { get; set; } - - public float[] TextColor { get; set; } - - public string FontName { get; set; } - - public float FontSize { get; set; } - - public float[] Fill { get; set; } - } -} diff --git a/MuPDF.NET/structures/AnnotXref.cs b/MuPDF.NET/structures/AnnotXref.cs deleted file mode 100644 index 058003df..00000000 --- a/MuPDF.NET/structures/AnnotXref.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace MuPDF.NET -{ - public class AnnotXref - { - /// - /// a unique identification of the annotation - /// - public string Id { get; set; } - - /// - /// annot's xref - /// - public int Xref { get; set; } - - /// - /// annotation type - /// - public PdfAnnotType AnnotType { get; set; } - } -} diff --git a/MuPDF.NET/structures/BarcodeInfo.cs b/MuPDF.NET/structures/BarcodeInfo.cs deleted file mode 100644 index f2a26620..00000000 --- a/MuPDF.NET/structures/BarcodeInfo.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text; - -namespace MuPDF.NET -{ - public class BarcodePoint - { - private readonly float x; - - private readonly float y; - - private readonly byte[] bytesX; - - private readonly byte[] bytesY; - - private string toString; - - public virtual float X => x; - - public virtual float Y => y; - - public BarcodePoint() - { - } - - public BarcodePoint(float x, float y) - { - this.x = x; - this.y = y; - bytesX = BitConverter.GetBytes(x); - bytesY = BitConverter.GetBytes(y); - } - - public override bool Equals(object other) - { - if (!(other is BarcodePoint resultPoint)) - { - return false; - } - - if (x == resultPoint.x) - { - return y == resultPoint.y; - } - - return false; - } - - public override int GetHashCode() - { - return 31 * ((bytesX[0] << 24) + (bytesX[1] << 16) + (bytesX[2] << 8) + bytesX[3]) + (bytesY[0] << 24) + (bytesY[1] << 16) + (bytesY[2] << 8) + bytesY[3]; - } - - public override string ToString() - { - if (toString == null) - { - StringBuilder stringBuilder = new StringBuilder(25); - stringBuilder.AppendFormat(CultureInfo.CurrentUICulture, "({0}, {1})", x, y); - toString = stringBuilder.ToString(); - } - - return toString; - } - } - - public class Barcode - { - public string Text { get; private set; } - public byte[] RawBytes { get; private set; } - public BarcodePoint[] ResultPoints { get; private set; } - public BarcodeFormat BarcodeFormat { get; private set; } - public long Timestamp { get; private set; } - public int NumBits { get; private set; } - public Barcode(string text, byte[] rawBytes, BarcodePoint[] resultPoints, BarcodeFormat type) - : this(text, rawBytes, (rawBytes != null) ? (8 * rawBytes.Length) : 0, resultPoints, type, DateTime.Now.Ticks) - { - } - - public Barcode(string text, byte[] rawBytes, int numBits, BarcodePoint[] resultPoints, BarcodeFormat type) - : this(text, rawBytes, numBits, resultPoints, type, DateTime.Now.Ticks) - { - } - - public Barcode(string text, byte[] rawBytes, BarcodePoint[] resultPoints, BarcodeFormat type, long timestamp) - : this(text, rawBytes, (rawBytes != null) ? (8 * rawBytes.Length) : 0, resultPoints, type, timestamp) - { - } - - public Barcode(string text, byte[] rawBytes, int numBits, BarcodePoint[] resultPoints, BarcodeFormat type, long timestamp) - { - if (text == null && rawBytes == null) - { - throw new ArgumentException("Text and bytes are null"); - } - - Text = text; - RawBytes = rawBytes; - NumBits = numBits; - ResultPoints = resultPoints; - BarcodeFormat = type; - Timestamp = timestamp; - } - - public void addResultPoints(BarcodePoint[] newPoints) - { - BarcodePoint[] resultPoints = ResultPoints; - if (resultPoints == null) - { - ResultPoints = newPoints; - } - else if (newPoints != null && newPoints.Length != 0) - { - BarcodePoint[] array = new BarcodePoint[resultPoints.Length + newPoints.Length]; - Array.Copy(resultPoints, 0, array, 0, resultPoints.Length); - Array.Copy(newPoints, 0, array, resultPoints.Length, newPoints.Length); - ResultPoints = array; - } - } - - public override string ToString() - { - if (Text == null) - { - return "[" + RawBytes.Length + " bytes]"; - } - - return Text; - } - } -} diff --git a/MuPDF.NET/structures/Block.cs b/MuPDF.NET/structures/Block.cs deleted file mode 100644 index 8a929266..00000000 --- a/MuPDF.NET/structures/Block.cs +++ /dev/null @@ -1,86 +0,0 @@ -using mupdf; -using System.Collections.Generic; - -namespace MuPDF.NET -{ - public class Block - { - public int Xref { get; set; } - - /// - /// block count - /// - public int Number { get; set; } - - /// - /// block type, 0 = text, 1 = image - /// - public int Type { get; set; } - - /// - /// block rectangle - /// - public Rect Bbox { get; set; } - - /// - /// original image width - /// - public int Width { get; set; } - - /// - /// original image height - /// - public int Height { get; set; } - - /// - /// image type, as file extension - /// - public string Ext { get; set; } - - /// - /// colorspace component count - /// - public int ColorSpace { get; set; } - - /// - /// resolution in x-direction - /// - public int Xres { get; set; } - - /// - /// resolution in y-direction - /// - public int Yres { get; set; } - - /// - /// bits per component - /// - public byte Bpc { get; set; } - - /// - /// matrix transforming image rect to bbox - /// - public Matrix Transform { get; set; } - - /// - /// size of the image in bytes - /// - public uint Size { get; set; } - - /// - /// image content - /// - public byte[] Image { get; set; } - - /// - /// optional image mask as PNG bytes (same idea as MuPdf mask) - /// - public byte[] Mask { get; set; } - - public string CsName { get; set; } - - public vectoruc Digest { get; set; } - - public List Lines { get; set; } - } -} diff --git a/MuPDF.NET/structures/Border.cs b/MuPDF.NET/structures/Border.cs deleted file mode 100644 index 47286bc1..00000000 --- a/MuPDF.NET/structures/Border.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace MuPDF.NET -{ - public class Border - { - /// - /// border thickness in points - /// - public float Width { get; set; } - - /// - /// 1-byte border style: `S`, others include `B`, `U`, `I` and `D`. - /// - public string Style { get; set; } - - /// - /// a sequence of *int* specifying a line dashing pattern - /// - public int[] Dashes { get; set; } - - /// - /// an integer indicating a "cloudy" border - /// - public float Clouds { get; set; } = -1; - - } -} diff --git a/MuPDF.NET/structures/Box.cs b/MuPDF.NET/structures/Box.cs deleted file mode 100644 index f26be5a1..00000000 --- a/MuPDF.NET/structures/Box.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MuPDF.NET -{ - public class Box - { - /// - /// boundary box - /// - public Rect Rect { get; set; } - - /// - /// respective transformation matricx - /// - public Matrix Matrix { get; set; } - - public dynamic this[int index] - { - get - { - switch (index) - { - case 0: return Rect; - case 1: return Matrix; - default: throw new IndexOutOfRangeException(); - } - } - } - } -} diff --git a/MuPDF.NET/structures/BoxLog.cs b/MuPDF.NET/structures/BoxLog.cs deleted file mode 100644 index 31c0a8f4..00000000 --- a/MuPDF.NET/structures/BoxLog.cs +++ /dev/null @@ -1,43 +0,0 @@ -using mupdf; - -namespace MuPDF.NET -{ - public class BoxLog - { - /// - /// a type of rectangle - /// - public string Type { get; set; } - - /// - /// rectangle coordinates - /// - public Rect Box { get; set; } - - /// - /// optional. layer name - /// - public string LayerName { get; set; } - - public BoxLog(string type = null, Rect box = null, string layername = null) - { - Type = type; - Box = new Rect(box); - LayerName = layername; - } - - public BoxLog(string type = null, Rect box = null) - { - Type = type; - Box = new Rect(box); - LayerName = null; - } - - public BoxLog(string type = null, fz_rect box = null, string layername = null) - { - Type = type; - Box = new Rect(box); - LayerName = layername; - } - } -} diff --git a/MuPDF.NET/structures/Char.cs b/MuPDF.NET/structures/Char.cs deleted file mode 100644 index 0257e07a..00000000 --- a/MuPDF.NET/structures/Char.cs +++ /dev/null @@ -1,31 +0,0 @@ -using mupdf; - -namespace MuPDF.NET -{ - public class Char - { - /// - /// character's left baseline point - /// - public FzPoint Origin { get; set; } - - /// - /// character rectangle - /// - public FzRect Bbox { get; set; } - - /// - /// the character - /// - public char C { get; set; } - - /// - /// true if MuPDF set FZ_STEXT_SYNTHETIC on this glyph (raw dict mode). - /// - public bool Synthetic { get; set; } - - public int UCS { get; set; } - - public int GID { get; set; } - } -} diff --git a/MuPDF.NET/structures/Color.cs b/MuPDF.NET/structures/Color.cs deleted file mode 100644 index 630a657a..00000000 --- a/MuPDF.NET/structures/Color.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace MuPDF.NET -{ - public class Color - { - /// - /// fill color, each item is between 0 and 1 - /// - public float[] Fill { get; set; } - - /// - /// stroke color - /// - public float[] Stroke { get; set; } - } -} diff --git a/MuPDF.NET/structures/ColorSpace.cs b/MuPDF.NET/structures/ColorSpace.cs deleted file mode 100644 index 3b2162f3..00000000 --- a/MuPDF.NET/structures/ColorSpace.cs +++ /dev/null @@ -1,80 +0,0 @@ -using mupdf; -using System.Collections.Generic; -using System.Runtime.InteropServices; - -namespace MuPDF.NET -{ - public class ColorSpace - { - static ColorSpace() - { - /* - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Utils.LoadEmbeddedDllForWindows(); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - Utils.LoadEmbeddedDllForLinux(); - } - */ - } - - private readonly FzColorspace _nativeColorSpace; - - /// - /// The number of bytes required to define the color of one pixel. - /// - public int N - { - get - { - return _nativeColorSpace.fz_colorspace_n(); - } - } - - /// - /// The name identifying the colorspace. - /// - public string Name - { - get - { - return _nativeColorSpace.fz_colorspace_name(); - } - } - - public ColorSpace(int type) - { - if (type == Utils.CS_GRAY) - _nativeColorSpace = new FzColorspace(FzColorspace.Fixed.Fixed_GRAY); - else if (type == Utils.CS_CMYK) - _nativeColorSpace = new FzColorspace(FzColorspace.Fixed.Fixed_CMYK); - else if (type == Utils.CS_RGB) - _nativeColorSpace = new FzColorspace(FzColorspace.Fixed.Fixed_RGB); - else - _nativeColorSpace = new FzColorspace(FzColorspace.Fixed.Fixed_RGB); - } - - public ColorSpace(ColorSpace cs) : this(cs.N) - { - - } - - public ColorSpace(FzColorspace nativeColorSpace) - { - _nativeColorSpace = nativeColorSpace; - } - - public FzColorspace ToFzColorspace() - { - return _nativeColorSpace; - } - - public override string ToString() - { - string x = (new List() { "", "GRAY", "", "RGB", "CMYK" })[N]; - return $"ColorSpace(CS_{x}) - {Name}"; - } - } -} diff --git a/MuPDF.NET/structures/DestName.cs b/MuPDF.NET/structures/DestName.cs deleted file mode 100644 index 69161b67..00000000 --- a/MuPDF.NET/structures/DestName.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace MuPDF.NET -{ - public class DestName - { - public int Page { get; set; } - - public Point To { get; set; } - - public float Zoom { get; set; } - - public string Dest { get; set; } - - public Rect Rect { get; set; } - } -} diff --git a/MuPDF.NET/structures/EmbfileInfo.cs b/MuPDF.NET/structures/EmbfileInfo.cs deleted file mode 100644 index 21e3f5df..00000000 --- a/MuPDF.NET/structures/EmbfileInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace MuPDF.NET -{ - public class EmbfileInfo - { - /// - /// name under which this entry is stored - /// - public string Name { get; set; } - - /// - /// date-time of item creation in PDF format - /// - public string CreationDate { get; set; } - - /// - /// date-time of last change in PDF format - /// - public string ModDate { get; set; } - - /// - /// a hashcode of the stored file content as a hexadecimal string - /// - public string CheckSum { get; set; } - - /// - /// xref of the associated PDF portfolio item if any, else zero - /// - public int Collection { get; set; } - - /// - /// filename - /// - public string FileName { get; set; } - - /// - /// filename - /// - public string UFileName { get; set; } - - /// - /// description - /// - public string Desc { get; set; } - - /// - /// original file size - /// - public int Size { get; set; } - - /// - /// compressed file length - /// - public int Length { get; set; } - } -} diff --git a/MuPDF.NET/structures/Entry.cs b/MuPDF.NET/structures/Entry.cs deleted file mode 100644 index 3316ea8e..00000000 --- a/MuPDF.NET/structures/Entry.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace MuPDF.NET -{ - public class Entry - { - // image info struct - - public string Ext { get; set; } - - public int Smask { get; set; } - - public float Width { get; set; } - - public float Height { get; set; } - - public int Bpc { get; set; } - - public string CsName { get; set; } - - public string AltCsName { get; set; } - - public string Filter { get; set; } - - // font struct - /// - /// the image, font and form object number - /// - public int Xref { get; set; } - - public string Type { get; set; } - - /// - /// name under which this entry is stored or basefont name of font entry - /// - public string Name { get; set; } - - public string RefName { get; set; } - - public string Encoding { get; set; } - - public int StreamXref { get; set; } - - // form info struct - - public Rect Bbox { get; set; } = null; - } -} diff --git a/MuPDF.NET/structures/FileInfo.cs b/MuPDF.NET/structures/FileInfo.cs deleted file mode 100644 index e8eeed37..00000000 --- a/MuPDF.NET/structures/FileInfo.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace MuPDF.NET -{ - public class FileInfo - { - /// - /// file name - /// - public string FileName { get; set; } - - /// - /// description of the file - /// - public string Desc { get; set; } - - /// - /// compressed length - /// - public int Length { get; set; } - - /// - /// uncompressed file size - /// - public int Size { get; set; } - } -} diff --git a/MuPDF.NET/structures/FontInfo.cs b/MuPDF.NET/structures/FontInfo.cs deleted file mode 100644 index a839607c..00000000 --- a/MuPDF.NET/structures/FontInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; - -namespace MuPDF.NET -{ - public class FontInfo - { - - public int Xref { get; set; } - - public string Ext { get; set; } - - public string Type { get; set; } - - public string Name { get; set; } - - public string RefName { get; set; } - - public string Encoding { get; set; } - - public int StreamXref { get; set; } - - public int Ordering { get; set; } - - public bool Simple { get; set; } - - public List<(int, double)> Glyphs { get; set; } - - public float Ascender { get; set; } - - public float Descender { get; set; } - - public byte[] Content { get; set; } - - } -} diff --git a/MuPDF.NET/structures/Hits.cs b/MuPDF.NET/structures/Hits.cs deleted file mode 100644 index dbcc30a6..00000000 --- a/MuPDF.NET/structures/Hits.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; - -namespace MuPDF.NET -{ - public class Hits - { - public int Len { get; set; } - - public List Quads { get; set; } - - public float HFuzz { get; set; } - - public float VFuzz { get; set; } - - public override string ToString() - { - return $"Hits(len={Len} quads={Quads} hfuzz={HFuzz} vfuzz={VFuzz})"; - } - } -} diff --git a/MuPDF.NET/structures/IRect.cs b/MuPDF.NET/structures/IRect.cs deleted file mode 100644 index 91f69bf8..00000000 --- a/MuPDF.NET/structures/IRect.cs +++ /dev/null @@ -1,417 +0,0 @@ -using mupdf; -using System; - -namespace MuPDF.NET -{ - public class IRect - { - static IRect() - { - Utils.InitApp(); - } - - /// - /// X-coordinate of the left corners. - /// - public float X0 { get; set; } - - /// - /// Y-coordinate of the top corners. - /// - public float Y0 { get; set; } - - /// - /// X-coordinate of the right corners. - /// - public float X1 { get; set; } - - /// - /// Y-coordinate of the bottom corners. - /// - public float Y1 { get; set; } - - /// - /// Contains the height of the bounding box. Equals abs(y1 - y0). - /// - public float Height - { - get - { - return Math.Abs(Y1 - Y0); - } - } - - /// - /// Contains the width of the bounding box. Equals abs(x1 - x0). - /// - public float Width - { - get - { - return Math.Abs(X1 - X0); - } - } - - /// - /// Equals Point(x0, y1). - /// - public Point BottomLeft - { - get - { - return new Point(X0, Y1); - } - } - - /// - /// Equals Point(x1, y1). - /// - public Point BottomRight - { - get - { - return new Point(X1, Y1); - } - } - - /// - /// Equals Point(x0, y0). - /// - public Point TopLeft - { - get - { - return new Point(X0, Y0); - } - } - - /// - /// Equals Point(x1, y0). - /// - public Point TopRight - { - get - { - return new Point(X1, Y0); - } - } - - - public int Length - { - get { return 4; } - } - - public IRect() - { - X0 = Y0 = X1 = Y1 = 0; - } - - public IRect(Point ul, float x1, float y1) - { - X0 = ul.X; - Y0 = ul.Y; - X1 = x1; - Y1 = y1; - } - - public IRect(float x0, float y0, Point br) - { - X0 = x0; - Y0 = y0; - X1 = br.X; - Y1 = br.Y; - } - - /// - /// True if rectangle is infinite. - /// - public bool IsInfinite - { - get - { - return (X0 == Utils.FZ_MIN_INF_RECT) && (Y0 == Utils.FZ_MIN_INF_RECT) && (X1 == Utils.FZ_MAX_INF_RECT) && (Y1 == Utils.FZ_MAX_INF_RECT); - } - } - - /// - /// True if rectangle is valid. - /// - public bool IsValid - { - get - { - return X0 <= X1 && Y0 <= Y1; - } - } - - /// - /// True if rectangle area is empty. - /// - public bool IsEmpty - { - get - { - return X0 >= X1 && Y0 >= Y1; - } - } - - public Quad Quad - { - get - { - return new Quad(TopLeft, TopRight, BottomLeft, BottomRight); - } - } - - public Rect Rect - { - get - { - return new Rect(this); - } - } - - public FzIrect ToFzIrect() - { - return new FzIrect(Rect.ToFzRect()); - } - - public IRect(Point ul, Point br) - { - X0 = ul.X; - Y0 = ul.Y; - X1 = br.X; - Y1 = br.Y; - } - - public IRect(float x0, float y0, float x1, float y1) - { - X0 = x0; - Y0 = y0; - X1 = x1; - Y1 = y1; - } - - public IRect(Rect r) - { - X0 = r.X0; - Y0 = r.Y0; - X1 = r.X1; - Y1 = r.Y1; - } - - public IRect(FzIrect ir) - { - X0 = ir.x0; - Y0 = ir.y0; - X1 = ir.x1; - Y1 = ir.y1; - } - - public Rect ToRect() - { - return new Rect(X0, Y0, X1, Y1); - } - - public IRect Round() - { - return new IRect((new Rect(this)).ToFzRect().fz_round_rect()); - } - - public static IRect operator +(IRect left, IRect right) - { - Rect r = new Rect(left) + new Rect(right); - return (new IRect(r)).Round(); - } - - public static IRect operator &(IRect left, IRect right) - { - Rect left_rect = new Rect(left); - Rect right_rect = new Rect(right); - - return new IRect(left_rect & right_rect); - } - - /// - /// Checks whether x is contained in the rectangle. - /// - /// the object to check. - /// the object to check. - /// - public static bool Contains(IRect left, IRect right) - { - Rect left_rect = new Rect(left); - Rect right_rect = new Rect(right); - - return left_rect.Contains(right_rect); - } - - public float this[int i] - { - get - { - switch (i) - { - case 0: return this.X0; - case 1: return this.Y0; - case 2: return this.X1; - case 3: return this.Y1; - default: throw new IndexOutOfRangeException(); - } - } - set - { - switch (i) - { - case 0: this.X0 = value; break; - case 1: this.Y0 = value; break; - case 2: this.X1 = value; break; - case 3: this.Y1 = value; break; - default: throw new IndexOutOfRangeException(); - } - } - } - - public static IRect operator *(IRect left, float right) - { - return new IRect(left.ToRect() * right).Round(); - } - - public static IRect operator -(IRect op) - { - return new IRect(-op.X0, -op.Y0, -op.X1, -op.Y1); - } - - public static IRect operator |(IRect left, IRect right) - { - return new IRect(left.ToRect() | right.ToRect()); - } - - public static IRect operator +(IRect op) - { - return op; - } - - public override string ToString() - { - return $"IRect({X0}, {Y0}, {X1}, {Y1})"; - } - - public static IRect operator -(IRect left, IRect right) - { - return new IRect(left.ToRect() - right.ToRect()); - } - - public IRect IncludePoint(Point p) - { - return new IRect(new Rect(this).IncludePoint(p)); - } - - public IRect IncludeRect(Rect r) - { - return new IRect(new Rect(this).IncludeRect(r)); - } - - /// - /// The intersection (common rectangular area) of the current rectangle and ir is calculated and replaces the current rectangle. - /// - /// - /// - public Rect Intersect(Rect r) - { - return ToRect().Intersect(r); - } - - /// - /// Checks whether the rectangle and the rect_like “r” contain a common non-empty IRect. - /// - /// - /// - public bool Intersects(Rect r) - { - return ToRect().Intersects(r); - } - - /// - /// Return a new quad after applying a matrix to it using a fixed point. - /// - /// - /// - /// - public Quad Morph(Point p, Matrix m) - { - if (IsInfinite) - return Utils.INFINITE_RECT().Quad; - return Quad.Morph(p, m); - } - - /// - /// Return the Euclidean norm of the rectangle treated as a vector of four numbers. - /// - /// - public float Norm() - { - float ret = 0.0f; - for (int i = 0; i < this.Length; i++) - { - ret += this[i] * this[i]; - } - - return (float)Math.Sqrt(ret); - } - - /// - /// Make the rectangle finite. - /// - public void Normalize() - { - if (X1 < X0) - { - float tmp = X0; - X0 = X1; - X1 = tmp; - } - if (Y1 < Y0) - { - float tmp = Y0; - Y0 = Y1; - Y1 = tmp; - } - } - - /// - /// Compute the matrix which transforms this rectangle to a given one. - /// - /// - /// - /// - public Matrix ToRect(Rect r) - { - if (IsInfinite || IsEmpty || r.IsInfinite || r.IsEmpty) - throw new Exception("rectangles must be finite and not empty"); - return new Matrix(1, 0, 0, 1, -X0, Y0) * new Matrix(r.Width / Width, r.Height / Height) * new Matrix(1, 0, 0, 1, r.X0, r.Y0); - } - - /// - /// - /// - /// - /// - public Rect Trnasform(Matrix m) - { - return new Rect(this).Transform(m); - } - - /// - /// Calculates the area of the rectangle and, with no parameter, equals abs(IRect). - /// - /// the area to be calculated - /// Specify required unit - /// - public float GetArea(Rect rect, string unit = "px") - { - return Utils.GetArea(rect, unit); - } - } -} diff --git a/MuPDF.NET/structures/IdentityMatrix.cs b/MuPDF.NET/structures/IdentityMatrix.cs deleted file mode 100644 index 00145fe6..00000000 --- a/MuPDF.NET/structures/IdentityMatrix.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Reflection; - -namespace MuPDF.NET -{ - public class IdentityMatrix : Matrix - { - public IdentityMatrix() : base(1.0f, 1.0f) - { - - } - - public IdentityMatrix(float a, float b) : base(a, b) - { - - } - - public float this[string k] - { - set - { - switch (k) - { - case "A": - base.A = 1.0f; - break; - case "D": - base.A = 1.0f; - break; - case "E": - base.E = 0.0f; - break; - case "B": - base.E = 0.0f; - break; - case "C": - base.E = 0.0f; - break; - case "F": - base.E = 0.0f; - break; - default: - PropertyInfo kInfo = base.GetType().GetProperty(k); - kInfo.SetValue(this, value, null); - break; - } - } - } - } -} diff --git a/MuPDF.NET/structures/ImageInfo.cs b/MuPDF.NET/structures/ImageInfo.cs deleted file mode 100644 index 05806d96..00000000 --- a/MuPDF.NET/structures/ImageInfo.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace MuPDF.NET -{ - public class ImageInfo - { - public string Ext { get; set; } - - public int Smask { get; set; } - - public float Width { get; set; } - - public float Height { get; set; } - - public int ColorSpace { get; set; } - - public int Bpc { get; set; } - - public float Xres { get; set; } - - public float Yres { get; set; } - - public string CsName { get; set; } - - public byte[] Image { get; set; } - - public byte Orientation { get; set; } - - public Matrix Matrix { get; set; } - } -} diff --git a/MuPDF.NET/structures/Label.cs b/MuPDF.NET/structures/Label.cs deleted file mode 100644 index 7426dd92..00000000 --- a/MuPDF.NET/structures/Label.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace MuPDF.NET -{ - public class Label - { - /// - /// the first page number (0-based) to apply the label rule - /// - public int StartPage { get; set; } - - /// - /// an arbitrary string to start the label with, e.g. "A-". Default is "". - /// - public string Prefix { get; set; } - - /// - /// start numbering with this value. - /// - public int FirstPageNum { get; set; } - - /// - /// the numbering style - /// - public string Style { get; set; } - } -} diff --git a/MuPDF.NET/structures/LayerConfigUI.cs b/MuPDF.NET/structures/LayerConfigUI.cs deleted file mode 100644 index 892d8ba5..00000000 --- a/MuPDF.NET/structures/LayerConfigUI.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace MuPDF.NET -{ - public class LayerConfigUI - { - /// - /// running sequence number - /// - public int Number { get; set; } - - /// - /// text string or name field of the originating OCG - /// - public string Text { get; set; } - - /// - /// one of "label" (set by a text string), "checkbox" (set by a single OCG) or "radiobox" (set by a set of connected OCGs) - /// - public string Type { get; set; } - - /// - /// item's nesting level in the `/Order` array - /// - public int Depth { get; set; } - - /// - /// item state - /// - public bool On { get; set; } - - /// - /// true if cannot be changed via user interfaces - /// - public bool IsLocked { get; set; } - } -} diff --git a/MuPDF.NET/structures/Line.cs b/MuPDF.NET/structures/Line.cs deleted file mode 100644 index 60ca9968..00000000 --- a/MuPDF.NET/structures/Line.cs +++ /dev/null @@ -1,28 +0,0 @@ -using mupdf; -using System.Collections.Generic; - -namespace MuPDF.NET -{ - public class Line - { - /// - /// list of span dictionaries - /// - public List Spans { get; set; } - - /// - /// writing mode *(int)*: 0 = horizontal, 1 = vertical - /// - public int WMode { get; set; } - - /// - /// writing direction - /// - public Point Dir { get; set; } - - /// - /// line rectangle - /// - public Rect Bbox { get; set; } - } -} diff --git a/MuPDF.NET/structures/LinkInfo.cs b/MuPDF.NET/structures/LinkInfo.cs deleted file mode 100644 index 30005635..00000000 --- a/MuPDF.NET/structures/LinkInfo.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace MuPDF.NET -{ - public class LinkInfo - { - /// - /// describing the "hot spot" location on the page's visible representation - /// - public Rect From { get; set; } - - /// - /// an integer indicating the kind of link - /// - public LinkType Kind { get; set; } - - public Point To { get; set; } = null; - - public string ToStr { get; set; } //used page number is less than 0 - - /// - /// page number - /// - public int Page { get; set; } - - public string Name { get; set; } - - /// - /// a string specifying the destination internet resource - /// - public string Uri { get; set; } - - /// - /// zoom value - /// - public float Zoom { get; set; } = 0; - - /// - /// a string specifying the destination file - /// - public string File { get; set; } - - /// - /// - /// - public string Id { get; set; } - - /// - /// xref of the item - /// - public int Xref { get; set; } - - /// - /// true if italic item text, or omitted. PDF only - /// - public bool Italic { get; set; } = false; - - /// - /// true if bold item text or omitted. PDF only - /// - public bool Bold { get; set; } = false; - - /// - /// true if sub-items are folded, or omitted in toc. PDF only - /// - public bool Collapse { get; set; } - - /// - /// item color in PDF RGB format - /// - public float[] Color { get; set; } - - public override string ToString() - { - return $"Kind = {(int)Kind}, Xref = {Xref}, Page = {Page}, To = {To.ToString()}, Zoom = {Zoom}, Collapse = {Collapse}"; - } - } -} diff --git a/MuPDF.NET/structures/Location.cs b/MuPDF.NET/structures/Location.cs deleted file mode 100644 index b604449d..00000000 --- a/MuPDF.NET/structures/Location.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace MuPDF.NET -{ - public class Location - { - /// - /// number of pages in chapter - /// - public int Chapter { get; set; } - - /// - /// number of page - /// - public int Page { get; set; } - } -} diff --git a/MuPDF.NET/structures/Matrix.cs b/MuPDF.NET/structures/Matrix.cs deleted file mode 100644 index d448e42f..00000000 --- a/MuPDF.NET/structures/Matrix.cs +++ /dev/null @@ -1,341 +0,0 @@ -using mupdf; -using System; -using System.Collections.Generic; -using static System.Net.Mime.MediaTypeNames; - -namespace MuPDF.NET -{ - public class Matrix - { - static Matrix() - { - Utils.InitApp(); - } - public float A { get; set; } - public float B { get; set; } - public float C { get; set; } - public float D { get; set; } - public float E { get; set; } - public float F { get; set; } - public int Length { get; set; } = 6; - - /// - /// Rectilinear means that no shearing is present and that any rotations are integer multiples of 90 degrees. Usually this is used to confirm that (axis-aligned) rectangles before the transformation are still axis-aligned rectangles afterwards. - /// - public bool IsRectilinear - { - get - { - return (Math.Abs(B) < float.Epsilon && Math.Abs(C) < float.Epsilon) || - (Math.Abs(A) < float.Epsilon && Math.Abs(D) < float.Epsilon); - } - } - public Matrix(float a, float b, float c, float d, float e, float f) - { - A = a; - B = b; - C = c; - D = d; - E = e; - F = f; - } - - public Matrix(Rect rect) - { - double theta = Math.PI * rect.X0 / 180; - double c = Math.Round(Math.Cos(theta), 8); - double s = Math.Round(Math.Sin(theta), 8); - A = D = (float)c; - B = (float)s; - C = (float)-s; - E = F = 0; - } - - public Matrix(int degree) - { - double theta = Math.PI * degree / 180.0f; - double c = Math.Round(Math.Cos(theta), 8); - double s = Math.Round(Math.Sin(theta), 8); - A = D = (float)c; - B = (float)s; - C = (float)-s; - E = F = 0; - } - - public Matrix(float shear_x, float shear_y, float arg2) - { - if (arg2 == 0) - (A, B, C, D, E, F) = (shear_x, 0, 0, shear_y, 0, 0); - else if (arg2 == 1) - (A, B, C, D, E, F) = (1, 0, 0, shear_y, 0, 0); - else - throw new ArgumentException("bad args"); - } - - public Matrix(float zoom_x, float zoom_y) - { - (A, B, C, D, E, F) = (zoom_x, 0, 0, zoom_y, 0, 0); - } - - public float Abs() - { - float sum = 0; - for (int i = 0; i < Length; i++) - sum += this[i] * this[i]; - return (float)Math.Sqrt((double)sum); - } - - /// - /// the Euclidean norm - /// - /// - public float Norm() - { - return Abs(); - } - - public Matrix(FzMatrix m) - { - A = m.a; - B = m.b; - C = m.c; - D = m.d; - E = m.e; - F = m.f; - } - - public Matrix() - { - A = 0.0f; - B = 0.0f; - C = 0.0f; - D = 0.0f; - E = 0.0f; - F = 0.0f; - } - - public Matrix(Matrix m) - { - A = m.A; - B = m.B; - C = m.C; - D = m.D; - E = m.E; - F = m.F; - } - - public static Matrix operator +(Matrix op1, Matrix op2) - { - return new Matrix(op1.A + op2.A, op1.B + op2.B, op1.C + op2.C, op1.D + op2.D, op1.E + op2.E, op1.F + op2.F); - } - - public FzMatrix ToFzMatrix() - { - return new FzMatrix(A, B, C, D, E, F); - } - - public static Matrix operator +(Matrix op1, float op2) - { - return new Matrix(op1.A + op2, op1.B + op2, op1.C + op2, op1.D + op2, op1.E + op2, op1.F + op2); - } - - public float this[int i] - { - get - { - return (new List { A, B, C, D, E, F })[i]; - } - set - { - switch (i) - { - case 0: A = value; break; - case 1: B = value; break; - case 2: C = value; break; - case 3: D = value; break; - case 4: E = value; break; - case 5: F = value; break; - default: throw new IndexOutOfRangeException(); - } - } - } - - /// - /// Multiplication operator - /// - /// first operand / matrix - /// second operand / matrix - /// - public static Matrix operator *(Matrix op1, float op2) - { - return new Matrix(op1.A * op2, op1.B * op2, op1.C * op2, op1.D * op2, op1.E * op2, op1.F * op2); - } - - public static Matrix operator *(Matrix op1, Matrix op2) - { - Matrix t = new Matrix(1, 1); - return Matrix.Concat(op1, op2); - } - - public static Matrix operator -(Matrix op1, Matrix op2) - { - return new Matrix(op1.A - op2.A, op1.B - op2.B, op1.C - op2.C, op1.D - op2.D, op1.E - op2.E, op1.F - op2.F); - } - - public static Matrix operator -(Matrix op1, float m) - { - return new Matrix(op1.A - m, op1.B - m, op1.C - m, op1.D - m, op1.E - m, op1.F - m); - } - - /// - /// Calculate the matrix inverse of *m* and store the result in the current matrix. - /// - /// - /// - public int Invert(Matrix src) - { - (int, Matrix) dst; - if (src == null) - { - dst = Utils.InvertMatrix(this); - } - else - { - dst = Utils.InvertMatrix(src); - } - - if (dst.Item1 == 1) - return 1; - - (A, B, C, D, E, F) = (dst.Item2.A, dst.Item2.B, dst.Item2.C, dst.Item2.D, dst.Item2.E, dst.Item2.F); - return 0; - } - - /// - /// Calculate the matrix product *m1 * m2* and store the result in the current matrix. - /// - /// - /// - /// - public static Matrix Concat(Matrix op1, Matrix op2) - { - Matrix ret = new Matrix(mupdf.mupdf.fz_concat(op1.ToFzMatrix(), op2.ToFzMatrix())); - return ret; - } - - /// - /// Modify the matrix to perform a counter-clockwise rotation for positive *deg* degrees, else clockwise. - /// - /// - /// - public Matrix Prerotate(float theta) - { - while (theta < 0) - { - theta += 360; - } - while (theta >= 360) - { - theta -= 360; - } - if (Math.Abs(0 - theta) < float.Epsilon) - { - // do nothing - } - else if (Math.Abs(90.0 - theta) < float.Epsilon) - { - float a = this.A; - float b = this.B; - this.A = this.C; - this.B = this.D; - this.C = -a; - this.D = -b; - } - else if (Math.Abs(180.0 - theta) < float.Epsilon) - { - this.A = -this.A; - this.B = -this.B; - this.C = -this.C; - this.D = -this.D; - } - else if (Math.Abs(270.0 - theta) < float.Epsilon) - { - float a = this.A; - float b = this.B; - this.A = -this.C; - this.B = -this.D; - this.C = a; - this.D = b; - } - else - { - double rad = Math.PI * theta / 180.0; - double s = Math.Sin(rad); - double c = Math.Cos(rad); - float a = this.A; - float b = this.B; - this.A = (float)(c * a + s * this.C); - this.B = (float)(c * b + s * this.D); - this.C = (float)(-s * a + c * this.C); - this.D = (float)(-s * b + c * this.D); - } - return this; - } - - /// - /// Modify the matrix to scale by the zoom factors sx and sy. - /// - /// Zoom factor in X direction. - /// Zoom factor in Y direction. - public void Prescale(float sx, float sy) - { - this.A *= sx; - this.B *= sx; - this.C *= sy; - this.D *= sy; - } - - /// - /// Modify the matrix to perform a shearing, i.e. transformation of rectangles into parallelograms (rhomboids). - /// - /// Shearing effect in X direction. - /// Shearing effect in Y direction. - /// - public Matrix Preshear(float h, float v) - { - float a = A; - float b = B; - A += v * C; - B += v * D; - C += h * a; - D += h * b; - return this; - } - - /// - /// Modify the matrix to perform a shifting / translation operation along the x and / or y axis. - /// - /// Translation effect in X direction. - /// Translation effect in Y direction. - /// - public Matrix Pretranslate(float tx, float ty) - { - E += tx * A + ty * C; - F += tx * B + ty * D; - return this; - } - - public static Matrix operator ~(Matrix m) - { - Matrix m1 = new Matrix(); - int _ = m1.Invert(m); - return m1; - } - - public override string ToString() - { - return $"{A} {B} {C} {D} {E} {F}"; - } - - } -} diff --git a/MuPDF.NET/structures/Morph.cs b/MuPDF.NET/structures/Morph.cs deleted file mode 100644 index fd7c54f3..00000000 --- a/MuPDF.NET/structures/Morph.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; - -namespace MuPDF.NET -{ - public class Morph - { - public Point P { get; set; } - - public Matrix M { get; set; } - - public Morph() - { - P = new Point(); - M = new Matrix(); - } - - public Morph(Point p, Matrix m) - { - P = p; - M = m; - } - - public object this[int i] - { - get - { - switch (i) - { - case 0: - return P; - case 1: - return M; - } - throw new NotImplementedException(); - } - } - - public int Length - { - get - { - return 2; - } - } - } -} diff --git a/MuPDF.NET/structures/OCGroup.cs b/MuPDF.NET/structures/OCGroup.cs deleted file mode 100644 index 25e8e2e4..00000000 --- a/MuPDF.NET/structures/OCGroup.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; - -namespace MuPDF.NET -{ - public class OCGroup - { - /// - /// the name of the group - /// - public string Name { get; set; } - - /// - /// the intended use of the group - /// - public List Intents { get; set; } - - /// - /// the state of the group - /// - public int On { get; set; } - - /// - /// the usage of the group - /// - public string Usage { get; set; } - } -} diff --git a/MuPDF.NET/structures/OCLayer.cs b/MuPDF.NET/structures/OCLayer.cs deleted file mode 100644 index e873b94c..00000000 --- a/MuPDF.NET/structures/OCLayer.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Collections.Generic; - -namespace MuPDF.NET -{ - public class OCLayer - { - /// - /// list of xref of OCGs to set ON. - /// - public int[] On { get; set; } - - /// - /// list of xref of OCGs to set OFF - /// - public int[] Off { get; set; } - - /// - /// a list of OCG xref number that cannot be changed by the user interface - /// - public int[] Locked { get; set; } - - /// - /// a list of lists. Replaces previous values - /// - public List RBGroups { get; set; } - - /// - /// state of OCGs that are not mentioned in on or off - /// - public string BaseState { get; set; } - } -} diff --git a/MuPDF.NET/structures/OCLayerConfig.cs b/MuPDF.NET/structures/OCLayerConfig.cs deleted file mode 100644 index 11fdba4e..00000000 --- a/MuPDF.NET/structures/OCLayerConfig.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace MuPDF.NET -{ - public class OCLayerConfig - { - /// - /// numbers to set ON in the layer - /// - public int Number { get; set; } - - /// - /// the layer name - /// - public string Name { get; set; } - - /// - /// creation date - /// - public string Creator { get; set; } - - public OCLayerConfig(int number, string name, string creator) - { - Number = number; - Name = name; - Creator = creator; - } - } -} diff --git a/MuPDF.NET/structures/OCMD.cs b/MuPDF.NET/structures/OCMD.cs deleted file mode 100644 index 02d66c6b..00000000 --- a/MuPDF.NET/structures/OCMD.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace MuPDF.NET -{ - public class OCMD - { - /// - /// xref of the OCMD to be updated, or 0 for a new OCMD - /// - public int Xref { get; set; } - - /// - /// a sequence of xref numbers of existing OCG PDF objects - /// - public int[] Ocgs { get; set; } - - /// - /// one of "AnyOn" (default), "AnyOff", "AllOn", "AllOff" - /// - public string Policy { get; set; } - - /// - /// visibility expression - /// - public dynamic[] Ve { get; set; } - } -} diff --git a/MuPDF.NET/structures/Outline.cs b/MuPDF.NET/structures/Outline.cs deleted file mode 100644 index 2a71ec56..00000000 --- a/MuPDF.NET/structures/Outline.cs +++ /dev/null @@ -1,160 +0,0 @@ -using mupdf; -using System; -using static System.Net.Mime.MediaTypeNames; - -namespace MuPDF.NET -{ - public class Outline : IDisposable - { - static Outline() - { - Utils.InitApp(); - } - - private FzOutline _nativeOutline; - - /// - /// The link destination details object. - /// - public LinkDest Dest - { - get - { - return new LinkDest(this, (null, 0, 0), null); - } - } - - /// - /// The next outline item on the next level down. - /// - public Outline Down - { - get - { - FzOutline downOL = _nativeOutline.down(); - if (downOL == null) - return null; - return new Outline(downOL); - } - } - - /// - /// A bool specifying whether the target is outside of the current document. - /// - public bool IsExternal - { - get - { - if (_nativeOutline == null) - return false; - - string uri = _nativeOutline.m_internal.uri; - if (uri is null) - return false; - return mupdf.mupdf.fz_is_external_link(uri) != 0; - } - } - - /// - /// Indicator showing whether any sub-outlines should be expanded (true) or be collapsed (false). This information is interpreted by PDF reader software. - /// - public bool IsOpen - { - get - { - return _nativeOutline.m_internal.is_open != 0; - } - } - - /// - /// The next outline item at the same level as this item. - /// - public Outline Next - { - get - { - FzOutline nextOL = _nativeOutline.next(); - if (nextOL.m_internal == null) - return null; - return new Outline(nextOL); - } - } - - /// - /// The page number (0-based) this bookmark points to. - /// - public int Page - { - get - { - return _nativeOutline.page().page; - } - } - - /// - /// The item's title as a string or *null*. - /// - public string Title - { - get - { - return _nativeOutline.title(); - } - } - - /// - /// A string specifying the link target. - /// - public string Uri - { - get - { - return _nativeOutline.m_internal.uri; - } - } - - public float X - { - get - { - return _nativeOutline.m_internal.x; - } - } - - public float Y - { - get - { - return _nativeOutline.m_internal.y; - } - } - public Outline(FzOutline ol) - { - _nativeOutline = ol; - } - - /// - /// Like `dest` property but uses `document` to resolve destinations for kind=LINK_NAMED. - /// - /// - /// - public LinkDest Destination(PdfDocument doc) - { - return new LinkDest(this, (null, 0, 0), new Document(doc)); - } - - public FzOutline ToFzOutline() - { - return _nativeOutline; - } - - public void Dispose() - { - if (_nativeOutline != null) - { - _nativeOutline.Dispose(); - _nativeOutline = null; - } - } - } -} diff --git a/MuPDF.NET/structures/PageInfo.cs b/MuPDF.NET/structures/PageInfo.cs deleted file mode 100644 index 9e8539b6..00000000 --- a/MuPDF.NET/structures/PageInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace MuPDF.NET -{ - public class PageInfo - { - /// - /// width of the clip rectangle - /// - public float Width { get; set; } - - /// - /// height of the clip rectangle - /// - public float Height { get; set; } - - /// - /// list of Block - /// - public List Blocks { get; set; } - } -} diff --git a/MuPDF.NET/structures/PathInfo.cs b/MuPDF.NET/structures/PathInfo.cs deleted file mode 100644 index 19d8a91b..00000000 --- a/MuPDF.NET/structures/PathInfo.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace MuPDF.NET -{ - public class PathInfo - { - /// - /// List of draw commands: lines, rectangles, quads or curves - /// - public List Items { get; set; } - - /// - /// Type of this path - /// - public string Type { get; set; } - - /// - /// Fill colors of area overlaps - /// - public bool EvenOdd { get; set; } - - /// - /// Fill color transparency - /// - public float FillOpacity { get; set; } - - /// - /// Fill color - /// - public float[] Fill { get; set; } - - /// - /// Page area covered by this path - /// - public Rect Rect { get; set; } - - /// - /// Command number when building page appearance - /// - public int SeqNo { get; set; } - - /// - /// Name of applicable Optional Content Group - /// - public string Layer { get; set; } - - /// - /// Stroke line width - /// - public float Width { get; set; } - - /// - /// Stroke color transparency - /// - public float StrokeOpacity { get; set; } - - public float LineJoin { get; set; } - - /// - /// Same as the parameter - /// - public bool ClosePath { get; set; } - - /// - /// Dashed line specification - /// - public string Dashes { get; set; } - - /// - /// Stroke color - /// - public float[] Color { get; set; } - - /// - /// Number 3-tuple, use its max value on output with Shape - /// - public List LineCap { get; set; } - - public Rect Scissor { get; set; } - - /// - /// the hierarchy level - /// - public int Level { get; set; } - - public bool Isolated { get; set; } - - public bool Knockout { get; set; } - - public string BlendMode { get; set; } - - public float Opacity { get; set; } - - public PathInfo() - { - - } - - public PathInfo(PathInfo path) - { - Items = new List(path.Items); - Type = path.Type; - EvenOdd = path.EvenOdd; - FillOpacity = path.FillOpacity; - - if (path.Fill != null) - { - Fill = new float[path.Fill.Length]; - Array.Copy(path.Fill, Fill, Fill.Length); - } - - Rect = new Rect(path.Rect); - SeqNo = path.SeqNo; - Layer = path.Layer; - Width = path.Width; - StrokeOpacity = path.StrokeOpacity; - LineJoin = path.LineJoin; - ClosePath = path.ClosePath; - Dashes = path.Dashes; - - if (path.Color != null) - { - Color = new float[path.Color.Length]; - Array.Copy(path.Color, Color, Color.Length); - } - - LineCap = new List(path.LineCap); - Scissor = path.Scissor; - Level = path.Level; - Isolated = path.Isolated; - Knockout = path.Knockout; - BlendMode = path.BlendMode; - Opacity = path.Opacity; - } - } - - public class Item - { - public string Type { get; set; } - - public Rect Rect { get; set; } - - public int Orientation { get; set; } = -1; - - public Point LastPoint { get; set; } - - public Point P1 { get; set; } - - public Point P2 { get; set; } - - public Point P3 { get; set; } - - public Quad Quad { get; set; } - - public bool Equal(Item other) - { - if (Type != other.Type) return false; - - bool ret = true; - ret = other != null && (Rect == null ? other.Rect == null : Rect.EqualTo(other.Rect)); - ret = other != null && (LastPoint == null ? other.LastPoint == null : LastPoint.EqualTo(other.LastPoint)); - ret = other != null && (P1 == null ? other.P1 == null : P1.EqualTo(other.P1)); - ret = other != null && (P2 == null ? other.P2 == null : P2.EqualTo(other.P2)); - ret = other != null && (P3 == null ? other.P3 == null : P3.EqualTo(other.P3)); - ret = other != null && (Quad == null ? other.Quad == null : Quad.EqualTo(other.Quad)); - - return true; - } - } -} diff --git a/MuPDF.NET/structures/Point.cs b/MuPDF.NET/structures/Point.cs deleted file mode 100644 index e2c05af0..00000000 --- a/MuPDF.NET/structures/Point.cs +++ /dev/null @@ -1,274 +0,0 @@ -using mupdf; -using System; -using System.Collections.Generic; -using static System.Net.Mime.MediaTypeNames; - -namespace MuPDF.NET -{ - - public class Point - { - - static Point() - { - Utils.InitApp(); - } - - public float X { get; set; } - public float Y { get; set; } - public int Length { get; } = 2; - - public Point() - { - X = 0.0f; - Y = 0.0f; - } - public Point(float x, float y) - { - this.X = x; - this.Y = y; - } - - public Point(FzPoint p) - { - this.X = p.x; - this.Y = p.y; - } - - public Point(Point p) - { - X = p.X; - Y = p.Y; - } - - public FzPoint ToFzPoint() - { - return new FzPoint(X, Y); - } - - public float Abs() - { - return (float)Math.Sqrt(X * X + Y * Y); - } - - public static Point operator +(Point p, float t) - { - return new Point(p.X + t, p.Y + t); - } - - public static Point operator +(Point p, double t) - { - return new Point((float)(p.X + t), (float)(p.Y + (float)t)); - } - - public static Point operator +(Point p1, Point p2) - { - return new Point(p1.X + p2.X, p1.Y + p2.Y); - } - - public float this[int i] - { - get - { - switch (i) - { - case 0: return this.X; - case 1: return this.Y; - default: throw new IndexOutOfRangeException(); - } - } - set - { - switch (i) - { - case 0: this.X = value; break; - case 1: this.Y = value; break; - default: throw new IndexOutOfRangeException(); - } - } - } - - public static Point operator *(Point p, float m) - { - return new Point(p.X * m, p.Y * m); - } - - public static Point operator *(Point p, double m) - { - return new Point((float)(p.X * m), (float)(p.Y * m)); - } - - public static Point operator *(Point op1, Matrix m) - { - Point op = new Point(op1); - return op.Transform(m); - } - - public static Point operator /(Point p, float m) - { - return new Point(p.X / m, p.Y / m); - } - - public static Point operator -(Point p) - { - return new Point(-p.X, -p.Y); - } - - public bool IsZero() - { - return X == 0 && Y == 0; - } - - // set time - - public static Point operator -(Point p, float t) - { - return new Point(p.X - t, p.Y - t); - } - - public static Point operator -(Point p1, Point p2) - { - return new Point(p1.X - p2.X, p1.Y - p2.Y); - } - - public Point Position() - { - return new Point(X, Y); - } - - public static FzPoint toFzPoint(Point p) - { - return new FzPoint(p.X, p.Y); - } - - public Point TrueDivide(float m) - { - return new Point((float)(this.X * 1.0 / m), (float)(this.Y * 1.0 / m)); - } - - public Point TrueDivide(Matrix m) - { - (int, Matrix) result = Utils.InvertMatrix(m); // util - - if (result.Item1 == 0) - { - throw new DivideByZeroException("Matrix not invertible"); - } - - Point p = new Point(this.X, this.Y); - return p.Transform(result.Item2); - } - - /// - /// Transform point with a matrix - /// - /// - /// - public Point Transform(Matrix m) - { - FzPoint p = mupdf.mupdf.fz_transform_point(ToFzPoint(), m.ToFzMatrix()); - return new Point(p); - } - - public float DistanceTo(Point p, string unit = "px") - { - Dictionary u = new Dictionary - { - {"px", (1.0f, 1.0f) }, - {"in", (1.0f, 72.0f) }, - {"cm", (2.54f, 72.0f) }, - {"mm", (25.4f, 72.0f) } - }; - float f = u[unit].Item1 / u[unit].Item2; - return (this - p).Abs() * f; - } - - public float DistanceTo(Rect rect, string unit = "px") - { - Dictionary u = new Dictionary - { - {"px", (1.0f, 1.0f) }, - {"in", (1.0f, 72.0f) }, - {"cm", (2.54f, 72.0f) }, - {"mm", (25.4f, 72.0f) } - }; - float f = u[unit].Item1 / u[unit].Item2; - - Rect r = new Rect(rect.TopLeft, rect.TopLeft); - r = r | rect.BottomRight; - if ((r.X0 < X && r.X1 > X) && (r.Y0 < Y && r.Y1 > Y)) - return 0.0f; - if (X > r.X1) - { - if (Y >= r.Y1) - return DistanceTo(r.BottomRight, unit); - else if (Y <= r.Y0) - return DistanceTo(r.TopRight, unit); - else - return (X - r.X1) * f; - } - else if (r.X0 <= X && X <= r.X1) - { - if (Y >= r.Y1) - return (Y - r.Y1) * f; - else - return (r.Y0 - Y) * f; - } - else - { - if (Y >= r.Y1) - return DistanceTo(r.BottomLeft, unit); - else if (Y <= r.Y0) - return DistanceTo(r.TopLeft, unit); - else - return (r.X0- X) * f; - } - } - - /// - /// Unit vector of the point. - /// - public Point Unit - { - get - { - float s = X * X + Y * Y; - if (s < Utils.FLT_EPSILON) - return new Point(0, 0); - s = (float)Math.Sqrt(s); - return new Point(X / s, Y / s); - } - } - - public override string ToString() - { - return $"Point({X}, {Y})"; - } - - /// - /// - /// - /// - /// - public bool EqualTo(Point obj) - { - if (obj == null) - throw new NullReferenceException("is null object."); - return X == obj.X && Y == obj.Y; - } - - public static int CombineHashes(int hash1, int hash2) - { - int hash = 17; // A prime number as seed - hash = hash * 31 + hash1; // Multiply by prime and add first hash - hash = hash * 31 + hash2; // Multiply by prime and add second hash - return hash; - } - - public override int GetHashCode() - { - //return HashCode.Combine(X, Y); - return CombineHashes(X.GetHashCode(), Y.GetHashCode()); - } - } -} diff --git a/MuPDF.NET/structures/Position.cs b/MuPDF.NET/structures/Position.cs deleted file mode 100644 index 414641db..00000000 --- a/MuPDF.NET/structures/Position.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace MuPDF.NET -{ - public class Position - { - /// - /// depth of this element in the box - /// - public int Depth { get; set; } - - /// - /// the header level, 0 if no header, 1-6 for h1 - h6 - /// - public int Heading { get; set; } - - /// - /// value of the `href` attribute, or null if not defined - /// - public string Href { get; set; } - - /// - /// value of the `id` attribute, or null if not defined - /// - public string Id { get; set; } - - /// - /// element position on page - /// - public Rect Rect { get; set; } - - /// - /// immediate text of the element - /// - public string Text { get; set; } - - /// - /// bit 0 set: opens element, bit 1 set: closes element - /// - public bool OpenClose { get; set; } - - /// - /// count of rectangles filled by the story so far - /// - public int RectNum { get; set; } - - /// - /// page number - /// - public int PageNum { get; set; } - - public Position() { } - - public Position(Position arg) - { - Depth = arg.Depth; - Heading = arg.Heading; - Href = arg.Href; - Id = arg.Id; - Rect = arg.Rect; - Text = arg.Text; - OpenClose = arg.OpenClose; - RectNum = arg.RectNum; - PageNum = arg.PageNum; - } - } -} diff --git a/MuPDF.NET/structures/Quad.cs b/MuPDF.NET/structures/Quad.cs deleted file mode 100644 index b18b8d86..00000000 --- a/MuPDF.NET/structures/Quad.cs +++ /dev/null @@ -1,344 +0,0 @@ -using mupdf; -using System; - -namespace MuPDF.NET -{ - public class Quad - { - static Quad() - { - Utils.InitApp(); - } - - /// - /// Upper left point - /// - public Point UpperLeft { get; set; } - - /// - /// Upper right point - /// - public Point UpperRight { get; set; } - - /// - /// Lower left point - /// - public Point LowerLeft { get; set; } - - /// - /// Lower right point - /// - public Point LowerRight { get; set; } - - /// - /// The longest width value - /// - public float Width - { - get { return Math.Max((UpperLeft - UpperRight).Abs(), (LowerLeft - LowerRight).Abs()); } - } - - /// - /// The longest height value - /// - public float Height - { - get { return Math.Max((UpperLeft - LowerLeft).Abs(), (UpperRight - LowerRight).Abs()); } - } - - /// - /// Smallest containing Rect - /// - public Rect Rect - { - get - { - Rect r = new Rect(); - r.X0 = Math.Min( - Math.Min(UpperLeft.X, UpperRight.X), - Math.Min(LowerLeft.X, LowerRight.X) - ); - r.Y0 = Math.Min( - Math.Min(UpperLeft.Y, UpperRight.Y), - Math.Min(LowerLeft.Y, LowerRight.Y) - ); - r.X1 = Math.Max( - Math.Max(UpperLeft.X, UpperRight.X), - Math.Max(LowerLeft.X, LowerRight.X) - ); - r.Y1 = Math.Max( - Math.Max(UpperLeft.Y, UpperRight.Y), - Math.Max(LowerLeft.Y, LowerRight.Y) - ); - return r; - } - } - public int Length { get; set; } = 4; - - /// - /// True if quad is a convex set - /// - public bool IsConvex - { - get - { - Matrix m = Utils.PlanishLine(this.UpperLeft, this.LowerRight); - Point p1 = this.LowerLeft * m; - Point p2 = this.UpperRight * m; - - if (p1.Y * p2.Y > 0) - return false; - m = Utils.PlanishLine(this.LowerLeft, this.UpperRight); - p1 = this.LowerRight * m; - p2 = this.UpperLeft * m; - if (p1.Y * p2.Y > 0) - return false; - return true; - } - } - - /// - /// True if quad is congruent to a rectangle - /// - public bool IsRectangular - { - get - { - float sine = Utils.SineBetween(UpperLeft, UpperRight, LowerRight); - if (Math.Abs(sine - 1) > float.Epsilon) - return false; - - sine = Utils.SineBetween(UpperRight, LowerRight, LowerLeft); - if (Math.Abs(sine - 1) > float.Epsilon) - return false; - - sine = Utils.SineBetween(LowerRight, LowerLeft, UpperLeft); - if (Math.Abs(sine - 1) > float.Epsilon) - return false; - - return true; - } - } - - public Quad() - { - UpperLeft = UpperRight = LowerLeft = LowerRight = new Point(0.0f, 0.0f); - } - - /// - /// True if quad is an empty set - /// - public bool IsEmpty - { - get { return (Width < float.Epsilon) || (Height < float.Epsilon); } - } - - public bool IsInfinite - { - get { return this.Rect.IsInfinite; } - } - - public Quad(Point ul, Point ur, Point ll, Point lr) - { - UpperLeft = ul; - UpperRight = ur; - LowerLeft = ll; - LowerRight = lr; - } - - public Quad(Rect rect) - { - UpperLeft = new Point(new FzPoint(rect.X0, rect.Y0)); - UpperRight = new Point(new FzPoint(rect.X1, rect.Y0)); - LowerLeft = new Point(new FzPoint(rect.X0, rect.Y1)); - LowerRight = new Point(new FzPoint(rect.X1, rect.Y1)); - } - - public Quad(Quad quad) - { - UpperLeft = quad.UpperLeft; - UpperRight = quad.UpperRight; - LowerLeft = quad.LowerLeft; - LowerRight = quad.LowerRight; - } - - public Quad(FzQuad quad) - { - UpperLeft = new Point(new FzPoint(quad.ul)); - UpperRight = new Point(new FzPoint(quad.ur)); - LowerLeft = new Point(new FzPoint(quad.ll)); - LowerRight = new Point(new FzPoint(quad.lr)); - } - - public Point this[int i] - { - get - { - switch (i) - { - case 0: - return this.UpperLeft; - case 1: - return this.UpperRight; - case 2: - return this.LowerLeft; - case 3: - return this.LowerRight; - default: - throw new IndexOutOfRangeException(); - } - } - set - { - switch (i) - { - case 0: - this.UpperLeft = value; - break; - case 1: - this.UpperRight = value; - break; - case 2: - this.LowerLeft = value; - break; - case 3: - this.LowerRight = value; - break; - default: - throw new IndexOutOfRangeException(); - } - } - } - - public FzQuad ToFzQuad() - { - return mupdf.mupdf.fz_make_quad( - UpperLeft.X, - UpperLeft.Y, - UpperRight.X, - UpperRight.Y, - LowerLeft.X, - LowerLeft.Y, - LowerRight.X, - LowerRight.Y - ); - } - - public float Abs() - { - if (IsEmpty) - { - return 0.0f; - } - return (UpperLeft - UpperRight).Abs() * (UpperLeft - LowerLeft).Abs(); - } - - public static Quad operator +(Quad op1, float op2) - { - return new Quad( - op1.UpperLeft + op2, - op1.UpperRight + op2, - op1.LowerLeft + op2, - op1.LowerRight + op2 - ); - } - - public static Quad operator +(Quad op1, Quad op2) - { - return new Quad( - op1.UpperLeft + op2.UpperLeft, - op1.UpperRight + op2.UpperRight, - op1.LowerLeft + op2.LowerLeft, - op1.LowerRight + op2.LowerRight - ); - } - - public static Quad operator -(Quad op1, float op2) - { - return new Quad( - op1.UpperLeft - op2, - op1.UpperRight - op2, - op1.LowerLeft - op2, - op1.LowerRight - op2 - ); - } - - public static Quad operator -(Quad op1, Quad op2) - { - return new Quad( - op1.UpperLeft - op2.UpperLeft, - op1.UpperRight - op2.UpperRight, - op1.LowerLeft - op2.LowerLeft, - op1.LowerRight - op2.LowerRight - ); - } - - public static Quad operator -(Quad op) - { - return new Quad(-op.UpperLeft, -op.UpperRight, -op.LowerLeft, -op.LowerRight); - } - - public override string ToString() => - $"Quad ({UpperLeft}, {UpperRight}, {LowerLeft}, {LowerRight})"; - - public bool Contains(Point p) - { - return mupdf.mupdf.fz_is_point_inside_quad(p.ToFzPoint(), ToFzQuad()) == 0 - ? false - : true; - } - - public bool Contains(Rect r) - { - if (r.IsEmpty) - { - return true; - } - return Contains(r.TopLeft) && Contains(r.BottomRight); - } - - public bool Contains(Quad q) - { - for (int i = 0; i < 4; i++) - { - if (Contains(q[i]) == false) - { - return false; - } - } - return true; - } - - public Quad Transform(Matrix m) - { - UpperLeft = UpperLeft * m; - UpperRight = UpperRight * m; - LowerLeft = LowerLeft * m; - LowerRight = LowerRight * m; - - return this; - } - - public static Quad operator *(Quad op1, Matrix op2) - { - Quad op = new Quad(op1); - op = op.Transform(op2); - return op; - } - - public Quad Morph(Point p, Matrix m) - { - if (IsInfinite) - { - return Utils.INFINITE_RECT().Quad; - } - Matrix delta = (new Matrix(1f, 1f)).Pretranslate(p.X, p.Y); - return this * ~delta * m * delta; - } - - public bool EqualTo(Quad obj) - { - return Rect.EqualTo(((Quad)obj).Rect); - } - } -} diff --git a/MuPDF.NET/structures/Rect.cs b/MuPDF.NET/structures/Rect.cs deleted file mode 100644 index 22a96c72..00000000 --- a/MuPDF.NET/structures/Rect.cs +++ /dev/null @@ -1,486 +0,0 @@ -using FzRect = mupdf.FzRect; -using mupdf; -using System; -using System.Collections.Generic; - -namespace MuPDF.NET -{ - public class Rect - { - static Rect() - { - Utils.InitApp(); - } - - /// - /// Left corners' x coordinate - /// - public float X0 { get; set; } - - /// - /// Top corners' y coordinate - /// - public float Y0 { get; set; } - - /// - /// Right corners' x -coordinate - /// - public float X1 { get; set; } - - /// - /// Bottom corners' y coordinate - /// - public float Y1 { get; set; } - public int Length { get; set; } = 4; - - public Point BottomLeft - { - get - { - return new Point(X0, Y1); - } - } - - public Point BottomRight - { - get - { - return new Point(X1, Y1); - } - } - - public Point TopLeft - { - get - { - return new Point(X0, Y0); - } - } - - public Point TopRight - { - get - { - return new Point(X1, Y0); - } - } - - public Rect(float X0, float Y0, float X1, float Y1) - { - this.X0 = X0; this.Y0 = Y0; - this.X1 = X1; this.Y1 = Y1; - } - - public Rect(Point tl, float x1, float y1) - { - this.X0 = tl.X; - this.Y0 = tl.Y; - X1 = x1; - Y1 = y1; - } - - public Rect(Rect r) - { - X0 = r[0]; Y0 = r[1]; - X1 = r[2]; Y1 = r[3]; - } - - public Rect(fz_rect r) - { - X0 = r.x0; Y0 = r.y0; - X1 = r.x1; Y1 = r.y1; - } - - public Rect(Point p1, Point p2) - { - X0 = p1.X; - Y0 = p1.Y; - X1 = p2.X; - Y1 = p2.Y; - } - - public bool IsInfinite - { - get - { - // Assuming that FZ_MIN_INF_RECT and FZ_MAX_INF_RECT are constants - return this.X0 == this.Y0 && this.X0 == Utils.FZ_MIN_INF_RECT && this.X1 == this.Y1 && this.X1 == Utils.FZ_MAX_INF_RECT; - } - } - - /// - /// Whether rectangle is empty - /// - public bool IsEmpty - { - get - { - return X0 >= X1 || Y0 >= Y1; - } - } - - /// - /// Quad made from rectangle corners - /// - public Quad Quad - { - get - { - return new Quad(TopLeft, TopRight, BottomLeft, BottomRight); - } - } - - /// - /// Rectangle height - /// - public float Height - { - get - { - return Math.Abs(Y1 - Y0); - } - } - - /// - /// Rectangle width - /// - public float Width - { - get - { - return Math.Abs(X1 - X0); - } - } - - public float Abs() - { - if (IsEmpty || IsInfinite) - return 0.0f; - return (X1 - X0) * (Y1 - Y0); - } - - public Rect(FzRect rect) - { - X0 = rect.x0; - Y0 = rect.y0; - X1 = rect.x1; - Y1 = rect.y1; - } - - public Rect(IRect rect) : this(rect.X0, rect.Y0, rect.X1, rect.Y1) - { - - } - - public static Rect operator +(Rect p, float op) - { - return new Rect(p.X0 + op, p.Y0 + op, p.X1 + op, p.Y1 + op); - } - - public float this[int i] - { - get - { - switch (i) - { - case 0: return this.X0; - case 1: return this.Y0; - case 2: return this.X1; - case 3: return this.Y1; - default: throw new IndexOutOfRangeException(); - } - } - set - { - switch (i) - { - case 0: this.X0 = value; break; - case 1: this.Y0 = value; break; - case 2: this.X1 = value; break; - case 3: this.Y1 = value; break; - default: throw new IndexOutOfRangeException(); - } - } - } - - public static Rect operator +(Rect op1, Rect op2) - { - return new Rect(op1.X0 + op2.X0, op1.Y0 + op2.Y0, op1.X1 + op2.X1, op1.Y1 + op2.Y1); - } - - public FzRect ToFzRect() - { - return new FzRect(X0, Y0, X1, Y1); - } - - public Rect() - { - X0 = X1 = Y0 = Y1 = 0.0f; - } - - public Rect Intersect(Rect op) - { - if (this.IsInfinite) - { - return this; - } - else if (op.IsInfinite) - { - this.X0 = op.X0; - this.Y0 = op.Y0; - this.X1 = op.X1; - this.Y1 = op.Y1; - } - else if (op.IsEmpty) - { - this.X0 = op.X0; - this.Y0 = op.Y0; - this.X1 = op.X1; - this.Y1 = op.Y1; - } - else if (this.IsEmpty) - { - return this; - } - else - { - FzRect result = FzRect.fz_intersect_rect(ToFzRect(), op.ToFzRect()); // Replace this with actual method call - - this.X0 = result.x0; - this.Y0 = result.y0; - this.X1 = result.x1; - this.Y1 = result.y1; - } - return this; - } - - public static Rect operator &(Rect op1, Rect op2) - { - Rect t1 = new Rect(op1); - Rect t2 = new Rect(op2); - - return t1.Intersect(t2); // Assuming you have implemented the Intersect method - } - - public bool Contains(float op) - { - // Assuming tuple equivalent is a list in this C# class - return (new List { X0, Y0, X1, Y1 }).Contains(op); - } - - public bool Contains(Rect op) - { - Rect r = null; - try - { - r = new Rect(op); - } - catch// (Exception ex) - { - r = (new Quad(op)).Rect; - } - return (X0 <= r.X0 && r.X1 <= X1) && (Y0 <= r.Y0 && r.Y1 <= Y1); - } - - public static Rect operator *(Rect op1, float op2) - { - return new Rect(op1.X0 * op2, op1.Y0 * op2, op1.X1 * op2, op1.Y1 * op2); - } - - public static Rect operator *(Rect op, Matrix m) - { - return op.Transform(m); - } - - public static Rect operator -(Rect op) // negative - { - return new Rect(-op.X0, -op.Y0, -op.X1, -op.Y1); - } - - public static Rect operator |(Rect op1, Rect op2) // | - { - return op1.IncludeRect(op2); - } - - public static Rect operator |(Rect op1, Point op2) - { - return op1.IncludePoint(op2); - } - - public static Rect operator +(Rect op) // positive - { - return new Rect(+op.X0, +op.Y0, +op.X1, +op.Y1); - } - - override - public string ToString() => $"Rect({Utils.FloatToString(X0)}, {Utils.FloatToString(Y0)}, {Utils.FloatToString(X1)}, {Utils.FloatToString(Y1)})"; - - public static Rect operator -(Rect p, float op) - { - return new Rect(p.X0 - op, p.Y0 - op, p.X1 - op, p.Y1 - op); - } - - public static Rect operator -(Rect op1, Rect op2) - { - return new Rect(op1.X0 - op2.X0, op1.Y0 - op2.Y0, op1.X1 - op2.Y1, op1.Y1 - op2.Y1); - } - - /// - /// Enlarge rectangle to also contain another one - /// - /// - /// - public Rect IncludeRect(Rect r) - { - if (r.IsInfinite || this.IsInfinite) - { - this.X0 = Utils.FZ_MIN_INF_RECT; - this.Y0 = Utils.FZ_MIN_INF_RECT; - this.X1 = Utils.FZ_MAX_INF_RECT; - this.Y1 = Utils.FZ_MAX_INF_RECT; - } - else if (r.IsEmpty) - { - return this; - } - else if (this.IsEmpty) - { - this.X0 = r.X0; - this.Y0 = r.Y0; - this.X1 = r.X1; - this.Y1 = r.Y1; - } - else - { - FzRect ret = FzRect.fz_union_rect(ToFzRect(), r.ToFzRect()); - this.X0 = ret.x0; - this.Y0 = ret.y0; - this.X1 = ret.x1; - this.Y1 = ret.y1; - } - return this; - } - - /// - /// Whether rectangle is valid - /// - /// - public bool IsValid() - { - return X0 <= X1 && Y0 <= Y1; - } - - public Quad Morph(Point p, Matrix m) - { - if (this.IsInfinite) - { - return Utils.INFINITE_RECT().Quad; - } - return this.Quad.Morph(p, m); - } - - public float Norm() - { - float ret = 0.0f; - for (int i = 0; i < this.Length; i++) - { - ret += this[i] * this[i]; - } - - return (float)Math.Sqrt(ret); - } - - public void Normalize() - { - if (X1 < X0) - { - float tmp = X0; - X0 = X1; - X1 = tmp; - } - if (Y1 < Y0) - { - float tmp = Y0; - Y0 = Y1; - Y1 = tmp; - } - } - - /// - /// Enlarge rectangle to also contain a point - /// - /// - /// - public Rect IncludePoint(Point p) - { - return new Rect(ToFzRect().fz_include_point_in_rect(p.ToFzPoint())); - } - - /// - /// Returns true if this rectangle and overlap (without mutating either). - /// - public bool Intersects(Rect r) - { - if (IsEmpty || IsInfinite || r.IsEmpty || r.IsInfinite) - return false; - return (X0 < r.X1 && r.X0 < X1) && (Y0 < r.Y1 && r.Y0 < Y1); - } - - /// - /// Create smallest IRect containing rectangle - /// - /// - public IRect Round() - { - return new IRect(ToFzRect().fz_round_rect()); - } - - /// - /// Return matrix that converts to target rect. - /// - /// - /// - /// - public Matrix ToRect(Rect rect) - { - if (IsInfinite || IsEmpty || rect.IsInfinite || rect.IsEmpty) - throw new Exception("rectangles must be finite and not empty."); - return new Matrix(1, 0, 0, 1, -X0, -Y0) * new Matrix(rect.Width / Width, rect.Height / Height) * new Matrix(1, 0, 0, 1, rect.X0, rect.Y0); - } - - public Rect Transform(Matrix matrix) - { - FzRect r = mupdf.mupdf.fz_transform_rect(ToFzRect(), matrix.ToFzMatrix()); - - return new Rect(r); - } - - /// - /// Calculate area of rectangle.\nparameter is one of 'px' (default), 'in', 'cm', or 'mm'. - /// - /// - /// - public float GetArea(string unit = null) - { - string unit_ = "px"; - if (!string.IsNullOrEmpty(unit)) - unit_ = unit; - Dictionary u = new Dictionary - { - { "px", (1, 1) }, - { "in", (1.0f, 72.0f) }, - { "cm", (2.54f, 72.0f) }, - { "mm", (25.4f, 72.0f) } - }; - - float f = (float)Math.Pow(u[unit].Item1 / u[unit].Item2, 2); - return f * Width * Height; - } - - public bool EqualTo(Rect obj) - { - return (X0 == ((Rect)obj).X0 && Y0 == ((Rect)obj).Y0 && X1 == ((Rect)obj).X1 && Y1 == ((Rect)obj).Y1); - } - } - -} diff --git a/MuPDF.NET/structures/Sound.cs b/MuPDF.NET/structures/Sound.cs deleted file mode 100644 index d003674e..00000000 --- a/MuPDF.NET/structures/Sound.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace MuPDF.NET -{ - public class Sound - { - /// - /// samples per second - /// - public float Rate { get; set; } - - /// - /// number of sound channels - /// - public int Channels { get; set; } - - /// - /// bits per sample value per channel - /// - public int Bps { get; set; } - - /// - /// encoding format: Raw, Signed, muLaw, ALaw - /// - public string Encoding { get; set; } - - /// - /// name of compression filter - /// - public string Compression { get; set; } - - /// - /// the sound file content - /// - public byte[] Stream { get; set; } - } -} diff --git a/MuPDF.NET/structures/Span.cs b/MuPDF.NET/structures/Span.cs deleted file mode 100644 index 581f0f50..00000000 --- a/MuPDF.NET/structures/Span.cs +++ /dev/null @@ -1,62 +0,0 @@ -using mupdf; -using System.Collections.Generic; - -namespace MuPDF.NET -{ - public class Span - { - - public List Chars { get; set; } - - /// - /// the first character's origin - /// - public Point Origin { get; set; } - - /// - /// span rectangle - /// - public Rect Bbox { get; set; } - - /// - /// text - /// - public string Text { get; set; } - - /// - /// font size - /// - public float Size { get; set; } - - public float Flags { get; set; } - - /// - /// per-character flags from MuPDF with FZ_STEXT_SYNTHETIC cleared (span boundary key in MuPdf). - /// - public uint CharFlags { get; set; } - - /// - /// Unicode bidi level from fz_stext_char. - /// - public ushort Bidi { get; set; } - - /// - /// font name - /// - public string Font { get; set; } - - /// - /// font characteristics - /// - public int Color { get; set; } - - /// - /// alpha from ARGB (argb >> 24), matching MuPdf span alpha. - /// - public int Alpha { get; set; } - - public float Asc { get; set; } - - public float Desc { get; set; } - } -} diff --git a/MuPDF.NET/structures/SpanInfo.cs b/MuPDF.NET/structures/SpanInfo.cs deleted file mode 100644 index f5d36e68..00000000 --- a/MuPDF.NET/structures/SpanInfo.cs +++ /dev/null @@ -1,100 +0,0 @@ -using mupdf; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MuPDF.NET -{ - public class SpanInfo - { - /// - /// direction of the span - /// - public Point Dir { get; set; } - - /// - /// font of the span - /// - public string Font { get; set; } - - /// - /// write mode - /// - public uint WMode { get; set; } - - /// - /// A dictionary with various font properties, each represented as bools - /// - public float Flags { get; set; } - - /// - /// the bidirectional level - /// - public uint BidiLevel { get; set; } - - /// - /// the bidirectional direction - /// - public uint BidiDir { get; set; } - - /// - /// ascender of the font - /// - public float Ascender { get; set; } - - /// - /// descender of the font - /// - public float Descender { get; set; } - - /// - /// - /// - public int ColorSpace { get; set; } - - public float[] Color { get; set; } - - /// - /// the font size - /// - public float Size { get; set; } - - /// - /// span opacity - /// - public float Opacity { get; set; } - - /// - /// line width - /// - public float LineWidth { get; set; } - - public float SpaceWidth { get; set; } - /// - /// the span bbox - /// - public int Type { get; set; } - - /// - /// the span bbox - /// - public Rect Bbox { get; set; } - - /// - /// the layer name - /// - public string Layer { get; set; } - - /// - /// no - /// - public int SeqNo { get; set; } - - /// - /// a list of char in span - /// - public List Chars { get; set; } - } -} diff --git a/MuPDF.NET/structures/SubArchive.cs b/MuPDF.NET/structures/SubArchive.cs deleted file mode 100644 index d2d30a0e..00000000 --- a/MuPDF.NET/structures/SubArchive.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace MuPDF.NET -{ - public class SubArchive - { - /// - /// Format of Archive - /// - public string Fmt { get; set; } - - /// - /// entities in Archive - /// - public List Entries { get; set; } - - /// - /// File path - /// - public string Path { get; set; } - } -} diff --git a/MuPDF.NET/structures/TextBlock.cs b/MuPDF.NET/structures/TextBlock.cs deleted file mode 100644 index b4449292..00000000 --- a/MuPDF.NET/structures/TextBlock.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace MuPDF.NET -{ - public class TextBlock - { - public float X0 { get; set; } - - public float Y0 { get; set; } - - public float X1 { get; set; } - - public float Y1 { get; set; } - - public string Text { get; set; } - - public int BlockNum { get; set; } - - public int Type { get; set; } - } -} diff --git a/MuPDF.NET/structures/Toc.cs b/MuPDF.NET/structures/Toc.cs deleted file mode 100644 index 95724eab..00000000 --- a/MuPDF.NET/structures/Toc.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace MuPDF.NET -{ - public class Toc - { - /// - /// hierarchy level - /// - public int Level { get; set; } - - /// - /// title - /// - public string Title { get; set; } - - /// - /// 1-based source page number - /// - public int Page { get; set; } - - /// - /// included only if simpleis False. Contains details of the TOC item - /// - public dynamic Link { get; set; } = null; - - public override string ToString() - { - return $"Level={Level}, Title={Title}, Page={Page}, Link={Link != null}"; - } - } -} diff --git a/MuPDF.NET/structures/WordBlock.cs b/MuPDF.NET/structures/WordBlock.cs deleted file mode 100644 index 7101dc88..00000000 --- a/MuPDF.NET/structures/WordBlock.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace MuPDF.NET -{ - public class WordBlock - { - public float X0 { get; set; } - - public float Y0 { get; set; } - - public float X1 { get; set; } - - public float Y1 { get; set; } - - public string Text { get; set; } - - public int BlockNum { get; set; } - - public int LineNum { get; set; } - - public int WordNum { get; set; } - - } -}