/// <summary> /// Ejecuta el proceso de extracción de metadatos /// a partir de los patrones almacenados. /// En este caso los metadatos deben coincidir en 4 propiedades: /// FontType, FontSize, ColorFill, ColorStroke. /// En caso de acierto, se compara la RegEx. /// </summary> /// <param name="pdfDocTextStrings">Los textStrings obtenidos con el texto y las 4 propiedades.</param> /// <param name="metadataType">Implementa IMetadata. Sirve para averiguar el tipo de dato. Ejemplos: int, string, etc.</param> /// <param name="hierarchySet">Catálogo de jerarquías.</param> /// <param name="result">Guarda los resultados obtenidos con este método de extracción.</param> private void ExtractFromTextStrings(List <PdfClownTextString> pdfDocTextStrings, Type metadataType, IHierarchySet hierarchySet, PdfTagExtractionResult result) { foreach (var textString in pdfDocTextStrings) { foreach (var pattern in PdfPatterns) { if (pattern.SourceTypeName == "TextStringInfos") { if (textString.ColorFill.BaseDataObject.ToString().Equals(pattern.ColorFill) && textString.ColorStroke.BaseDataObject.ToString().Equals(pattern.ColorStroke) && textString.FontSize.ToString().Equals(pattern.FontSize) && textString.FontType.Name.Equals(pattern.FontType) && textString.Type.Equals(pattern.TsType)) { if (pattern.TsType.Equals("NA") || (pattern.TsType.Equals("X") && textString.Rectangle != null && pattern.TsCoordinate.Equals(textString.Rectangle.Value.X.ToString())) || (pattern.TsType.Equals("Y") && textString.Rectangle != null && pattern.TsCoordinate.Equals(textString.Rectangle.Value.Y.ToString()))) { // Cumple los 4 parámetros del textString // por lo que debemos comprobar el contenido // (convirtiendo el dato primero a un tipo comparable) string textInput = textString.Text; PropertyInfo pInf = metadataType.GetProperty(pattern.MetadataItemName); ITextParserHierarchy parserHierarchy = hierarchySet.GetParserHierarchy(pInf); if (pInf.PropertyType == typeof(string)) { parserHierarchy.SetParserRegexPattern(0, pattern.RegexPattern); } dynamic converter = parserHierarchy.GetConverter(pattern.RegexPattern); MatchCollection matches = Regex.Matches(textString.Text, pattern.RegexPattern); string val = (pattern.Position < matches.Count) ? matches[pattern.Position].Value : null; object pValue = null; if (val != null && converter != null) { pValue = converter.Convert(val); } if (pValue != null && !PdfCompare.IsZeroNumeric(pValue)) { result.AddResult(pattern, pValue); if (!_Converters.ContainsKey(pInf.PropertyType)) { _Converters.Add(pInf.PropertyType, converter); } } } } } } } }
/// <summary> /// Ejecuta el proceso de extracción de metadatos /// en base a los patrones almacenados. /// </summary> /// <param name="pdfDocRectangles">rectángulos del pdf doc.</param> /// <param name="metadataType">Implementa IMetadata.</param> /// <param name="hierarchySet">Catálogo de jerarquías.</param> /// <param name="result">Resultados.</param> /// <param name="pageNumber">Número de la página sobre la que se realiza la extracción.</param> /// <param name="sourceTypeName">Nombre de la fuente.</param> private void ExtractFromRectangles(List <PdfTextRectangle> pdfDocRectangles, Type metadataType, IHierarchySet hierarchySet, PdfTagExtractionResult result, int pageNumber, string sourceTypeName = "WordGroupsInfos") { foreach (var pdfDocRectangle in pdfDocRectangles) { foreach (var pattern in PdfPatterns) { if (pattern.PdfPageN == pageNumber || pattern.IsLastPage) // Comprobamos que los patrones realicen la extracción sobre la página que les corresponde. // Se comprueba la última página porque en algunos documentos viene primero los albaranes y al final la factura. { if (pattern.SourceTypeName == sourceTypeName) { if (IsAlmostSameArea(pdfDocRectangle, pattern.PdfRectangle)) { string textInput = pdfDocRectangle.Text; PropertyInfo pInf = metadataType.GetProperty(pattern.MetadataItemName); ITextParserHierarchy parserHierarchy = hierarchySet.GetParserHierarchy(pInf); if (pInf.PropertyType == typeof(string)) { parserHierarchy.SetParserRegexPattern(0, pattern.RegexPattern); } dynamic converter = parserHierarchy.GetConverter(pattern.RegexPattern); MatchCollection matches = Regex.Matches(pdfDocRectangle.Text, pattern.RegexPattern); int p = pattern.Position; int m = matches.Count; string val = (pattern.Position < matches.Count) ? matches[pattern.Position].Value : null; object pValue = null; if (val != null && converter != null) { pValue = converter.Convert(val); } if (pValue != null && !PdfCompare.IsZeroNumeric(pValue)) { result.AddResult(pattern, pValue); if (!_Converters.ContainsKey(pInf.PropertyType)) { _Converters.Add(pInf.PropertyType, converter); } } } } } } } }
/// <summary> /// Ejecuta el proceso de extracción de metadatos /// en base a los patrones almacenados. /// </summary> /// <param name="pdfDocRectangles">rectángulos del pdf doc.</param> /// <param name="metadataType">Implementa IMetadata.</param> /// <param name="hierarchySet">Catálogo de jerarquías.</param> /// <param name="result">Resultados.</param> /// <param name="sourceTypeName">Nombre de la fuente.</param> private void ExtractFromRectangles(List <PdfTextRectangle> pdfDocRectangles, Type metadataType, IHierarchySet hierarchySet, PdfTagExtractionResult result, string sourceTypeName = "WordGroupsInfos") { foreach (var pdfDocRectangle in pdfDocRectangles) { foreach (var pattern in PdfPatterns) { if (pattern.SourceTypeName == sourceTypeName) { if (IsAlmostSameArea(pdfDocRectangle, pattern.PdfRectangle)) { string textInput = pdfDocRectangle.Text; PropertyInfo pInf = metadataType.GetProperty(pattern.MetadataItemName); ITextParserHierarchy parserHierarchy = hierarchySet.GetParserHierarchy(pInf); if (pInf.PropertyType == typeof(string)) { parserHierarchy.SetParserRegexPattern(0, pattern.RegexPattern); } dynamic converter = parserHierarchy.GetConverter(pattern.RegexPattern); MatchCollection matches = Regex.Matches(pdfDocRectangle.Text, pattern.RegexPattern); string val = (pattern.Position < matches.Count) ? matches[pattern.Position].Value : null; object pValue = null; if (val != null && converter != null) { pValue = converter.Convert(val); } if (pValue != null && !PdfCompare.IsZeroNumeric(pValue)) { result.AddResult(pattern, pValue); if (!_Converters.ContainsKey(pInf.PropertyType)) { _Converters.Add(pInf.PropertyType, converter); } } } } } } }
/// <summary> /// Ejecuata la extracción basada en limites /// textuales. /// </summary> /// <param name="metadataType">Tipo de la clase que implementa IMetadata.</param> /// <param name="result">Resultado de extracción.</param> /// <param name="page">PdfUnstructuredPage del doc. pdf.</param> /// <param name="hierarchySet">Catálogo de jerarquías.</param> private void ExtractFromText(Type metadataType, PdfTagExtractionResult result, PdfUnstructuredPage page, IHierarchySet hierarchySet) { foreach (var pattern in PdfPatterns) { if (pattern.PdfPageN == page.PdfPageN || pattern.IsLastPage) // Comprobamos que los patrones realicen la extracción sobre la página que les corresponde. // Se comprueba la última página porque en algunos documentos viene primero los albaranes y al final la factura. { if (pattern.SourceTypeName == "PdfTextInfos") { foreach (Match match in Regex.Matches(page.PdfText, pattern.RegexPattern)) { PropertyInfo pInf = metadataType.GetProperty(pattern.MetadataItemName); if (pInf.PropertyType == typeof(string)) { result.AddResult(pattern, match.Value); } else { dynamic converter = null; if (_Converters.ContainsKey(pInf.PropertyType)) { converter = _Converters[pInf.PropertyType]; } else { ITextParserHierarchy parserHierarchy = hierarchySet.GetParserHierarchy(pInf); converter = parserHierarchy.GetConverter(pInf.PropertyType); if (converter == null) { Type converterGenType = typeof(Converter <>).MakeGenericType(pInf.PropertyType); converter = Activator.CreateInstance(converterGenType); } } object pValue = converter.Convert(match.Value); result.AddResult(pattern, pValue); } } } } } }
/// <summary> /// Ejecuata la extracción basada en limites /// textuales. /// </summary> /// <param name="metadataType">Tipo de la clase que implementa IMetadata.</param> /// <param name="result">Resultado de extracción.</param> /// <param name="page">PdfUnstructuredPage del doc. pdf.</param> private void ExtractFromText(Type metadataType, PdfTagExtractionResult result, PdfUnstructuredPage page, IHierarchySet hierarchySet) { foreach (var pattern in PdfPatterns) { if (pattern.SourceTypeName == "PdfTextInfos") { foreach (Match match in Regex.Matches(page.PdfText, pattern.RegexPattern)) { PropertyInfo pInf = metadataType.GetProperty(pattern.MetadataItemName); if (pInf.PropertyType == typeof(string)) { result.AddResult(pattern, match.Value); } else { dynamic converter = null; if (_Converters.ContainsKey(pInf.PropertyType)) { converter = _Converters[pInf.PropertyType]; } else { ITextParserHierarchy parserHierarchy = hierarchySet.GetParserHierarchy(pInf); converter = parserHierarchy.GetConverter(pInf.PropertyType); if (converter == null) { Type converterGenType = typeof(Converter <>).MakeGenericType(pInf.PropertyType); converter = Activator.CreateInstance(converterGenType); } } object pValue = converter.Convert(match.Value); result.AddResult(pattern, pValue); } } } } }
/// <summary> /// Devuelve las coincidencias entre los datos /// del pdf, y los metadatos facilitados. /// <code lang="C#"> /// // Partiendo de una entrada de datos no estructurados de pdf /// PdfUnstructuredDoc pdf = new PdfUnstructuredDoc(@"C:\ProgramData\PdfTagger\Inbox\0000021101.pdf"); /// /// // y de un conjunto de datos estructurados /// InvoiceMetadata metadata = new InvoiceMetadata(); /// /// metadata.InvoiceNumber = "1 / 33050"; /// metadata.BuyerPartyID = "ES - A12070330"; /// metadata.IssueDate = new DateTime(2017, 11, 30); /// metadata.GrossAmount = 3646.50m; /// metadata.TaxesOutputsBase01 = 3013.64m; /// metadata.TaxesOutputsRate01 = 21m; /// metadata.TaxesOutputsAmount01 = 632.86m; /// /// PdfCompareResult compareResult = PdfCompare.Compare(new BusinessHierarchySet(), pdf, metadata); /// </code> /// <code lang="VB"> /// ' Partiendo de una entrada de datos no estructurados de pdf /// Dim pdf As PdfUnstructuredDoc = New PdfUnstructuredDoc(@"C:\ProgramData\PdfTagger\Inbox\0000021101.pdf") /// /// ' y de un conjunto de datos estructurados /// Dim metadata As InvoiceMetadata = New InvoiceMetadata() /// /// metadata.InvoiceNumber = "1 / 33050" /// metadata.BuyerPartyID = "ES - A12070330" /// metadata.IssueDate = New Date(2017, 11, 30) /// metadata.GrossAmount = CDec(3646.5) /// metadata.TaxesOutputsBase01 = CDec(3013.64) /// metadata.TaxesOutputsRate01 = 21 /// metadata.TaxesOutputsAmount01 = CDec(632.86) /// /// Dim compareResult As PdfCompareResult = PdfCompare.Compare(New BusinessHierarchySet(), pdf, metadata) /// </code> /// </summary> /// <param name="hierarchySet">Catalogo de jerarquías de analizadores /// por tipo. La operación utilizara para comparar cada tipo de variable /// el parser obtenido del catálogo. La comparación se irá ejecutando /// por cada uno de los parsers según su orden en la jerarquía, hasta /// que se encuentre un valor coincidente o se llegue al final /// de la jerarquía.</param> /// <param name="pdf">Instancia de la clase PdfUnstructuredDoc fruto /// del análisis y obtención de los datos no estructurados de un pdf.</param> /// <param name="metadata">Datos estructurados a comparar con los /// datos no estructurados obtenidos del pdf.</param> /// <returns>Instancia de la clase PdfCompareResult con /// los resultados obtenidos de la comparación.</returns> public static PdfCompareResult Compare(IHierarchySet hierarchySet, PdfUnstructuredDoc pdf, IMetadata metadata) { PdfCompareResult compareResult = new PdfCompareResult(pdf, metadata, hierarchySet); foreach (PropertyInfo pInf in metadata.GetType().GetProperties()) { object pValue = pInf.GetValue(metadata); // Obtengo la jerarquía de analizadores ITextParserHierarchy parserHierarchy = hierarchySet.GetParserHierarchy(pInf); if (pInf.PropertyType == typeof(string)) { parserHierarchy.SetParserRegexPattern(0, TxtRegex.Replace($"{pValue}")); } // Recorro todos los datos del pdf que quiero comparar if (parserHierarchy != null && pValue != null && !IsZeroNumeric(pValue)) { foreach (var page in pdf.PdfUnstructuredPages) { // Grupos de palabras foreach (var wordGroup in page.WordGroups) { foreach (var match in parserHierarchy.GetMatches(pValue, wordGroup.Text)) { compareResult.WordGroupsInfos.Add(new PdfCompareInfo(pdf, page, wordGroup, match, pInf, null)); } } // Grupos de líneas foreach (var line in page.Lines) { foreach (var match in parserHierarchy.GetMatches(pValue, line.Text)) { compareResult.LinesInfos.Add(new PdfCompareInfo(pdf, page, line, match, pInf, null)); } } // Grupos de texto con porpiedades como el color de la fuente foreach (var textString in page.TextStringGroups) { foreach (var match in parserHierarchy.GetMatches(pValue, textString.Text)) { PdfClownTextString tsNA = new PdfClownTextString(textString.Text, textString.ColorFill, textString.ColorStroke, textString.FontType, textString.FontSize) { Rectangle = textString.Rectangle, Type = "NA" }; PdfClownTextString tsX = new PdfClownTextString(textString.Text, textString.ColorFill, textString.ColorStroke, textString.FontType, textString.FontSize) { Type = "X", Rectangle = textString.Rectangle }; PdfClownTextString tsY = new PdfClownTextString(textString.Text, textString.ColorFill, textString.ColorStroke, textString.FontType, textString.FontSize) { Type = "Y", Rectangle = textString.Rectangle }; compareResult.TextStringInfos.Add(new PdfCompareInfo(pdf, page, null, match, pInf, tsNA)); compareResult.TextStringInfos.Add(new PdfCompareInfo(pdf, page, null, match, pInf, tsX)); compareResult.TextStringInfos.Add(new PdfCompareInfo(pdf, page, null, match, pInf, tsY)); } } foreach (var match in parserHierarchy.GetMatches(pValue, page.PdfText)) { Type txtBoundMatchGenType = typeof(TextBoundMatch <>).MakeGenericType(pInf.PropertyType); ITextMatch txtBoundMatch = (ITextMatch)Activator.CreateInstance(txtBoundMatchGenType, match); ITextMatch txtBoundMatchSoft = (ITextMatch)Activator.CreateInstance(txtBoundMatchGenType, match); (txtBoundMatchSoft as ITextBoundMatch).UseLengthOnPatternDigitReplacement = false; if (txtBoundMatch.Pattern != null) { dynamic converter = parserHierarchy.GetConverter(match.Pattern); // Límites contextuales if (IsAllMatchesOK(txtBoundMatch, page, pValue, converter)) { compareResult.PdfTextInfos.Add( new PdfCompareInfo(pdf, page, null, txtBoundMatch, pInf, null)); } // Límites contextuales menos estrictos if (IsAllMatchesOK(txtBoundMatchSoft, page, pValue, converter)) { compareResult.PdfTextInfos.Add( new PdfCompareInfo(pdf, page, null, txtBoundMatchSoft, pInf, null)); } } } } } } return(compareResult); }
/// <summary> /// Ejecuta el proceso de extracción de metadatos /// en base a los patrones almacenados. /// </summary> /// <param name="pdfDocColorFontText">Rectángulos del pdf doc con color, tamaño y nombre de fuente.</param> /// <param name="metadataType">Implementa IMetadata.</param> /// <param name="hierarchySet">Catálogo de jerarquías.</param> /// <param name="result">Resultados.</param> /// <param name="pageNumber">Número de la página sobre la que se realiza la extracción.</param> private void ExtractFromColorFontText(List <PdfColorFontTextRectangle> pdfDocColorFontText, Type metadataType, IHierarchySet hierarchySet, PdfTagExtractionResult result, int pageNumber) { string sourceTypeName = "ColorFontWordGroupsInfos"; foreach (var pdfDocColorFontWord in pdfDocColorFontText) { foreach (var pattern in PdfPatterns) { if (pattern.PdfPageN == pageNumber || pattern.IsLastPage) // Comprobamos que los patrones realicen la extracción sobre la página que les corresponde. // Se comprueba la última página porque en algunos documentos viene primero los albaranes y al final la factura. { if (pattern.SourceTypeName == sourceTypeName) { if (pdfDocColorFontWord.FillColor == pattern.FillColor && pdfDocColorFontWord.StrokeColor == pattern.StrokeColor && pdfDocColorFontWord.FontName == pattern.FontName && pdfDocColorFontWord.FontSize.ToString() == pattern.FontSize ) // Comprobamos que tienen el mismo color, tamaño y nombre de fuente. // No comprobamos el CFType porque cuando llega aquí, pdfDocColorFontWord no tiene un CFType asignado aún. { if (pattern.CFType.Equals("NA") || (pattern.CFType.Equals("X") && (pattern.PdfRectangle.Llx.Equals(pdfDocColorFontWord.Llx) || pattern.PdfRectangle.Urx.Equals(pdfDocColorFontWord.Urx))) || (pattern.CFType.Equals("Y") && (pattern.PdfRectangle.Lly.Equals(pdfDocColorFontWord.Lly) || pattern.PdfRectangle.Ury.Equals(pdfDocColorFontWord.Ury)))) { string textInput = pdfDocColorFontWord.Text; PropertyInfo pInf = metadataType.GetProperty(pattern.MetadataItemName); ITextParserHierarchy parserHierarchy = hierarchySet.GetParserHierarchy(pInf); if (pInf.PropertyType == typeof(string)) { parserHierarchy.SetParserRegexPattern(0, pattern.RegexPattern); } dynamic converter = parserHierarchy.GetConverter(pattern.RegexPattern); MatchCollection matches = Regex.Matches(pdfDocColorFontWord.Text, pattern.RegexPattern); string val = (pattern.Position < matches.Count) ? matches[pattern.Position].Value : null; object pValue = null; if (val != null && converter != null) { pValue = converter.Convert(val); } if (pValue != null && !PdfCompare.IsZeroNumeric(pValue)) { result.AddResult(pattern, pValue); if (!_Converters.ContainsKey(pInf.PropertyType)) { _Converters.Add(pInf.PropertyType, converter); } } } } } } } } }