I'm trying to use OpenHardwareMonitor to get a reading on the diagnostics of my hardware. If you've ever tried using OHM you know that there is very little documentation on it yet to my knowledge there is no other open source library that is as accurate and robust.
I've been able to get load and clock speed from the CPU but not the temperature.
I also don't receive any information about the hard drive (temp concerns me most).
Here's my implementation, it should return everything it has but so far I only get limited information about the RAM (no temp), CPU (no temp) and the GPU.
CPU Temperature is my most important stat to track.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using OpenHardwareMonitor.Hardware;
namespace OHMWrapper
{
public class MySettings : ISettings
{
private IDictionary<string, string> settings = new Dictionary<string, string>();
public MySettings(IDictionary<string, string> settings)
{
this.settings = settings;
}
public bool Contains(string name)
{
return settings.ContainsKey(name);
}
public string GetValue(string name, string value)
{
string result;
if (settings.TryGetValue(name, out result))
return result;
else
return value;
}
public void Remove(string name)
{
settings.Remove(name);
}
public void SetValue(string name, string value)
{
settings[name] = value;
}
}
public class OHW
{
private static OHW m_Instance;
public static OHW Instance
{
get
{
if (m_Instance == null)
m_Instance = new OHW();
return m_Instance;
}
}
private OHW()
{
m_Instance = this;
}
public void GetCPUTemp()
{
MySettings settings = new MySettings(new Dictionary<string, string>
{
{ "/intelcpu/0/temperature/0/values", "H4sIAAAAAAAEAOy9B2AcSZYlJi9tynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmVdZhZAzO2dvPfee++999577733ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9+fB8/Iu6//MH37x79i9/+NX6N3/TJm9/5f/01fw1+fosnv+A/+OlfS37/jZ/s/Lpv9fff6Ml/NTef/yZPnozc5679b+i193//TQZ+/w2Dd+P9/sZeX/67v/GTf/b3iP3u4/ObBL//73+i+f039+D8Zk/+xz/e/P6beu2TQZju8yH8f6OgzcvPv/U3/Rb8+z/0f/9b/+yfaOn8079X6fr6Cws7ln/iHzNwflPv99/wyS/+xY4+v/evcJ+733+jJ5//Cw7/4ndy9Im3+U2e/Fbnrk31C93vrt/fyPvdb+N//hsF7/4/AQAA//9NLZZ8WAIAAA==" },
{ "/intelcpu/0/load/0/values", "H4sIAAAAAAAEAOy9B2AcSZYlJi9tynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmVdZhZAzO2dvPfee++999577733ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9+fB8/Iu6//MH37x79i9++mpwcv/md/9df89egZ/xX/ym/5y/4D37618Lv7ya//u+58+u+5d9/z7/5t/w9/6u5fP5bH/6av+eTkXyefXxp26ONaf/v/dG/sf39D/rvnv4e5vc/0IP56/waK/vuHzf5I38P8/tv+mv8Rbb9f0pwTF9/zr/1X9vP/8I//+/6Pf7Z30N+/zdf/HX29zd/859q4aCNP5b//U+U3/+7f+zXOjZwfqvDX/V7/o9/vPz+a1G/pv0f+fGlhfk7eZ//N3/0v28//5X0u/n8Cxq7+f1X/tHft20A5x8a/W5/02+BP36Nf+j/nv8XfzrT+c2//Ob4p3+vktvUhNs/+xcWikP6e/4T/5jS5M8/sL8vP/5ff49f/Ivl9//sHzv6PX/vXyG//9R/94/9HuZ34P/5vyC//3W/5e/1exa/k+Bw4bUBnU2bP4Xg/1bn0uafeTH6PatfKL//N3/0t2y/gG9+/8+IzqYNxmU+/+jwX7afY67/nwAAAP//GYSA31gCAAA=" },
});
Computer myComputer = new Computer(settings)
{
MainboardEnabled = true,
CPUEnabled = true,
RAMEnabled = true,
GPUEnabled = true,
FanControllerEnabled = true,
HDDEnabled = true
};
myComputer.Open();
foreach (var hardwareItem in myComputer.Hardware)
{
hardwareItem.Update();
if (hardwareItem.SubHardware.Length > 0)
{
foreach (IHardware subHardware in hardwareItem.SubHardware)
{
subHardware.Update();
foreach (var sensor in subHardware.Sensors)
{
Console.WriteLine(String.Format("{0} {1} = {2}", sensor.Name, sensor.Hardware, sensor.Value.HasValue ? sensor.Value.Value.ToString() : "no value"));
}
}
}
else
{
foreach (var sensor in hardwareItem.Sensors)
{
Console.WriteLine(String.Format("{0} {1} = {2}", sensor.Identifier, sensor.Hardware, sensor.Value.HasValue ? sensor.Value.Value.ToString() : "no value"));
}
}
}
}
}
}
I've been looking at the source code since it's the only way to get any info on it. It's been a slow process but I'd really appreciate any help.
OpenHardwareMonitor Source Code: http://code.google.com/p/open-hardware-monitor/source/browse/
I had to run this code with administrative priviledges to get all the information I wanted. I added a manifest file that requires the code to run with those. Thank you for all your helpful posts.
I also got stuck with this and I just wanted to share my experiences...
So running this with administrative priviledges is mandantory. Nevertheless it still may not work when running from a networkshare.
The Settings stuff (MySettings) and the cpu strings are not necessary...
I just used
myComputer = new Computer() { CPUEnabled = true };
That's all ;)
Cheers, Stephan
Related
I am trying to make a source generator for mapping columns from the google bigquery api client to class properties. I'm having trouble getting custom column names from a ColumnAttribute on the properties. ConstructorArguments is always empty and columnAttribute.AttributeClass in this sample is always an ErrorTypeSymbol. If I try to load that type using compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.Schema.ColumnAttribute") the result is always null.
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
namespace BigQueryMapping;
[Generator]
public class BigQueryMapperGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// add marker attribute
context.RegisterPostInitializationOutput(ctx =>
ctx.AddSource("BigQueryMappedAttribute.g.cs", SourceText.From(Attribute, Encoding.UTF8)));
// add static interface
context.RegisterPostInitializationOutput(ctx =>
ctx.AddSource("BigQueryMappedInterface.g.cs", SourceText.From(Interface, Encoding.UTF8)));
// get classes
IncrementalValuesProvider<ClassDeclarationSyntax> classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (s, _) => s is ClassDeclarationSyntax c && c.AttributeLists.Any(),
transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx)
)
.Where(static m => m is not null)!;
IncrementalValueProvider<(Compilation Compilation, ImmutableArray<ClassDeclarationSyntax>Syntaxes)>
compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
context.RegisterSourceOutput(compilationAndClasses,
static (spc, source) => Execute(source.Compilation, source.Syntaxes, spc));
static ClassDeclarationSyntax? GetSemanticTargetForGeneration(GeneratorSyntaxContext context)
{
var classDeclarationSyntax = (ClassDeclarationSyntax)context.Node;
foreach (var attributeListSyntax in classDeclarationSyntax.AttributeLists)
{
foreach (var attributeSyntax in attributeListSyntax.Attributes)
{
var fullName = context.SemanticModel.GetTypeInfo(attributeSyntax).Type?.ToDisplayString();
if (fullName == "BigQueryMapping.BigQueryMappedAttribute")
return classDeclarationSyntax;
}
}
return null;
}
static void Execute(Compilation compilation, ImmutableArray<ClassDeclarationSyntax> classes,
SourceProductionContext context)
{
try
{
if (classes.IsDefaultOrEmpty)
return;
var distinctClasses = classes.Distinct();
var classesToGenerate = GetTypesToGenerate(compilation, distinctClasses, context.CancellationToken);
foreach (var classToGenerate in classesToGenerate)
{
var result = GeneratePartialClass(classToGenerate);
context.AddSource($"{classToGenerate.RowClass.Name}.g.cs", SourceText.From(result, Encoding.UTF8));
}
}
catch (Exception e)
{
var descriptor = new DiagnosticDescriptor(id: "BQD001",
title: "Error creating bigquery mapper",
messageFormat: "{0} {1}",
category: "BigQueryMapperGenerator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);
context.ReportDiagnostic(Diagnostic.Create(descriptor, null, e.Message, e.StackTrace));
}
}
}
static IEnumerable<ClassToGenerate> GetTypesToGenerate(Compilation compilation,
IEnumerable<ClassDeclarationSyntax> classes,
CancellationToken ct)
{
var columnAttributeSymbol =
compilation.GetTypeByMetadataName("System.ComponentModel.DataAnnotations.Schema.ColumnAttribute");
foreach (var #class in classes)
{
Debug.WriteLine($"Checking class {#class}");
ct.ThrowIfCancellationRequested();
var semanticModel = compilation.GetSemanticModel(#class.SyntaxTree);
if (semanticModel.GetDeclaredSymbol(#class) is not INamedTypeSymbol classSymbol)
continue;
var info = new ClassToGenerate(classSymbol, new());
foreach (var member in classSymbol.GetMembers())
{
if (member is IPropertySymbol propertySymbol)
{
if (propertySymbol.DeclaredAccessibility == Accessibility.Public)
{
if (propertySymbol.SetMethod is not null)
{
var columnName = propertySymbol.Name;
var columnAttribute = propertySymbol.GetAttributes().FirstOrDefault(a =>
a.AttributeClass!.ToDisplayString() == "Column");
if (columnAttribute is not null)
{
if (!columnAttribute.ConstructorArguments.IsDefaultOrEmpty)
{
var nameArg = columnAttribute.ConstructorArguments.First();
if (nameArg.Value is string name)
{
columnName = name;
}
}
}
info.Properties.Add((columnName, propertySymbol));
}
}
}
}
yield return info;
}
}
static string GeneratePartialClass(ClassToGenerate c)
{
var sb = new StringBuilder();
sb.Append($#"// <auto-generated/>
namespace {c.RowClass.ContainingNamespace.ToDisplayString()}
{{
public partial class {c.RowClass.Name} : BigQueryMapping.IBigQueryGenerated<{c.RowClass.Name}>
{{
public static {c.RowClass.Name} FromBigQueryRow(Google.Cloud.BigQuery.V2.BigQueryRow row)
{{
return new {c.RowClass.Name}
{{");
foreach (var (columnName, property) in c.Properties)
{
// would like to check if key exists but don't see any sort of ContainsKey implemented on BigQueryRow
var tempName = $"___{property.Name}";
var basePropertyType = property.Type.WithNullableAnnotation(NullableAnnotation.None).ToDisplayString();
if (basePropertyType.EndsWith("?"))
{
basePropertyType = basePropertyType.Substring(default, basePropertyType.Length - 1);
}
sb.Append($#"
{property.Name} = row[""{columnName}""] is {basePropertyType} {tempName} ? {tempName} : default,");
}
sb.Append($#"
}};
}}
}}
}}");
return sb.ToString();
}
private record struct ClassToGenerate(INamedTypeSymbol RowClass,
List<(string ColumnName, IPropertySymbol Property)> Properties);
public const string Attribute = /* lang=csharp */ #"// <auto-generated/>
namespace BigQueryMapping {
[System.AttributeUsage(System.AttributeTargets.Class)]
public class BigQueryMappedAttribute : System.Attribute
{
}
}";
public const string Interface = /* lang=csharp */ #"// <auto-generated/>
namespace BigQueryMapping {
public interface IBigQueryGenerated<TRow> {
static TRow FromBigQueryRow(Google.Cloud.BigQuery.V2.BigQueryRow row) => throw new System.NotImplementedException();
}
}";
}
I have tried this with both System.ComponentModel.DataAnnotations.Schema.ColumnAttribute and a custom attribute injected via context.RegisterPostInitializationOutput to similar results. I have also tried rewriting this to use ISourceGenerator instead of IIncrementalGenerator and gotten the same behavior. Am wondering what I need to do to get columnAttribute loading correctly.
Thanks for any help in advance
It's hard to psychic debug the code but this does jump out:
var columnAttribute = propertySymbol.GetAttributes().FirstOrDefault(a =>
a.AttributeClass!.ToDisplayString() == "Column");
I'd guess the class here would be the fully qualified name. You already fetched the ColumnAttribute type earlier, so what'd be even better is to compare the AttributeClass to that type rather than doing string checks like this.
As a semi-related comment, if you're looking for types/members that are annotated with a specific attribute, rather than doing it yourself we have SyntaxValueProvider.ForAttributeWithMetadataName which is pretty heavily optimized to reduce the performance impact on your Visual Studio. It requires 17.3 or higher, but as long as you're OK with that it'll generally help performance.
`using Mono.Cecil; using Mono.Cecil.Cil;
using System; using System.Linq; using System.Collections.Generic;
namespace StormKittyBuilder {
internal sealed class build
{
private static Random random = new Random();
private static string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
public static Dictionary<string, string> ConfigValues = new Dictionary<string, string>
{
{ "Discord url", "" },
{ "Mutex", RandomString(20) },
};
// Read stub
private static AssemblyDefinition ReadStub()
{
return AssemblyDefinition.ReadAssembly("stub\\stub.exe");
}
// Write stub
private static void WriteStub(AssemblyDefinition definition, string filename)
{
definition.Write(filename);
}
// Replace values in config
private static string ReplaceConfigParams(string value)
{
foreach (KeyValuePair<string, string> config in ConfigValues)
if (value.Equals($"--- {config.Key} ---"))
return config.Value;
return value;
}
// Проходим по всем классам, строкам и заменяем значения.
public static AssemblyDefinition IterValues(AssemblyDefinition definition)
{
foreach (ModuleDefinition definition2 in definition.Modules)
foreach (TypeDefinition definition3 in definition2.Types)
if (definition3.Name.Equals("Config"))
foreach (MethodDefinition definition4 in definition3.Methods)
if (definition4.IsConstructor && definition4.HasBody)
{
IEnumerator<Instruction> enumerator;
enumerator = definition4.Body.Instructions.GetEnumerator();
while (enumerator.MoveNext())
{
var current = enumerator.Current;
if (current.OpCode.Code == Code.Ldstr & current.Operand is object)
{
string str = current.Operand.ToString();
if (str.StartsWith("---") && str.EndsWith("---"))
current.Operand = ReplaceConfigParams(str);
}
}
}
return definition;
}
public static string BuildStub()
{
var definition = ReadStub();
definition = IterValues(definition);
WriteStub(definition, "bot\\build.exe");
return "bot\\build.exe";
}
} }`
Working on my college project (a discord bot), I want an .exe file
assume named as builder.exe, which can overwrite another .exe - assume named as base.exe - and we get a new .exe named output.exe.
I tried this BUILDER.EXE Github repo to overwrite my base.exe with help of builder.exe created from this repo no doubt it worked but the help I need from the master reading my problem is that I want to embed the base.exe into my builder.exe (to get a single exe file) which can read and over write base.exe and produce output.exe.
In short a single .exe file (embedded with base.exe) which read and overwrites a reference (base.exe) and produces output.exe as final result.
All I need is to update a page with alerts when someone else creates one. Wouldn't have to be real time, but every 10 seconds or so minimum. It also needs to flash the tab when something is new is displayed for the user to know something was updated if they are currently on a different page. Thank you.
I've never tried to use knockout with signalR, this is how we do it with near-0 latency.
In our masterpage we include the jquery signalR library
Underneath, we tell it to include the signalR auto-generated script
our Hub class looks like so.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
namespace CHSI.Shared.APIs
{
[HubName("applicationMessageHub")]
public class ApplicationMessageHub : Hub
{
private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(5000);
private IList< DSIDGroup> DSIDGroups = new List<DSIDGroup>();
public ApplicationMessageHub()
{
}
public void CheckMessages(object state)
{
GetMessages();
}
public Models.ApplicationMessage GetCurrentMessage(int DSID)
{
Models.ApplicationMessage currentMessage = null;
return currentMessage;
}
public override Task OnConnected()
{
string DSID = Context.QueryString["DSID"];
if (!string.IsNullOrEmpty(DSID))
{
Groups.Add(Context.ConnectionId, DSID);
DSIDGroup currentGroup = (from g in this.DSIDGroups where g.DSID == DSID select g).FirstOrDefault();
if (currentGroup != null)
{
currentGroup.ConnecedIDs.Add(Context.ConnectionId);
}
else
{
currentGroup = new DSIDGroup();
currentGroup.DSID = DSID;
currentGroup.ConnecedIDs.Add(Context.ConnectionId);
this.DSIDGroups.Add(currentGroup);
}
}
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
foreach (var DSIDgroup in DSIDGroups)
{
if (DSIDgroup.ConnecedIDs.Contains(Context.ConnectionId))
{
DSIDgroup.ConnecedIDs.Remove(Context.ConnectionId);
if (DSIDgroup.ConnecedIDs.Count == 0)
{
DSIDGroups.Remove(DSIDgroup);
}
break;
}
}
return base.OnDisconnected(stopCalled);
}
public void BroadcastMessage(Models.ApplicationMessage message)
{
Clients.All.SendMessage(message);
}
public void clearCache(int DSID)
{
Clients.Group(DSID.ToString()).clearCache();
}
public Models.ApplicationMessage GetMessages()
{
foreach (var group in this.DSIDGroups)
{
Models.ApplicationMessage currentMessage = GetCurrentMessage(Convert.ToInt32(group.DSID));
if (currentMessage != null)
{
Clients.Group(group.DSID).SendMessage(currentMessage);
}
}
return null;
//return _applicationMessage.GetCurrentMessage();
}
}
public class DSIDGroup
{
public string DSID {get;set;}
public IList<string> ConnecedIDs { get;set; }
public DSIDGroup()
{
this.ConnecedIDs = new List<string>();
}
}
}
This class handles grouping my users into groups based on their account (DSID), but you could group users by chat room, not at all, or some other methodology.
We also call javascript functions elsewhere in the codebase like so.
var context = Microsoft.AspNet.SignalR.GlobalHost.ConnectionManager.GetHubContext<CHSI.Shared.APIs.ApplicationMessageHub>();
List<string> dsids = new List<string>();
dsids.Add(CHSI.Shared.Models.ConnectionManager.Current().CurrentDSID.Value.ToString());
context.Clients.Groups(dsids).clearCache();
There is a javascript function called SendMessage and another called clearCache which handles those calls.
They're defined like so.
applicationMessageHub.client.sendMessage = function (message) {
alert(message.SummaryMessage);
};
applicationMessageHub.client.clearCache = function () {
localStorage.removeItem("companiesCache");
Class.loadPage(Class);
}
I hope this helps!
I am attempting to use a managed code action in an InstallShield suite project.
I followed their example in hopes it wouldn't be a big deal.
http://helpnet.flexerasoftware.com/installshield21helplib/helplibrary/SteActionManagedCd.htm
Ideally, I want to have my .dll method to change an install shield property when the action is executed. When I test out the installer to see if the property changed, I get the default value I set.
Maybe I am doing it wrong, any suggestions would be greatly appreciated.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Web.Administration;
namespace IISHelp
{
[Guid("BAFAEAED-08C6-4679-B94E-487A4D89DE63")]
[TypeLibType(4288)]
public interface ISuiteExtension
{
[DispId(1)]
string get_Attribute(string bstrName);
[DispId(2)]
void LogInfo(string bstr);
[DispId(3)]
string get_Property(string bstrName);
[DispId(3)]
void set_Property(string bstrName, string bstrValue);
[DispId(4)]
string FormatProperty(string bstrValue);
[DispId(5)]
string ResolveString(string bstrStringId);
}
public class Help
{
const UInt32 ERROR_INSTALL_FAILURE = 1603;
const UInt32 ERROR_SUCCESS = 0;
ISuiteExtension GetExtensionFromDispatch(object pDispatch)
{
if (Marshal.IsComObject(pDispatch) == false)
throw new ContextMarshalException("Invalid dispatch object passed to CLR method");
return (ISuiteExtension)pDispatch;
}
public UInt32 IsUniqueName(object pDispatch)
{
try
{
List<string> currentVirtualDirectories = GetApplicationNames();
ISuiteExtension suiteExtension = GetExtensionFromDispatch(pDispatch);
var name = suiteExtension.get_Property("APPLICATION_NAME");
suiteExtension.set_Property("IS_UNIQUE_APPLICATION_NAME", (!currentVirtualDirectories.Contains(name)).ToString());
}
catch (System.ContextMarshalException)
{
return ERROR_INSTALL_FAILURE;
}
return ERROR_SUCCESS;
}
public List<string> GetApplicationNames()
{
List<string> currentVirtualDirectories = new List<string>();
ServerManager mgr = new ServerManager();
foreach (Site s in mgr.Sites)
{
foreach (Application app in s.Applications)
{
currentVirtualDirectories.Add(app.Path.Replace('/', ' ').Trim());
}
}
return currentVirtualDirectories.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList();
}
}
}
I am evaluating Winnovative's PdfToText library and have run into something that concerns me.
Everything runs fine and I am able to extract the text content from a small 20k or less pdf immediately if I am running a console application. However, if I call the same code from the NUnit gui running it takes 15-25 seconds (I've verified it's PdfToText by putting a breakpoint on the line that extracts the text and hitting F10 to see how long it takes to advance to the next line).
This concerns me because I'm not sure where to lay blame since I don't know the cause. Is there a problem with NUnit or PdfToText? All I want to do is extract the text from a pdf, but 20 seconds is completely unreasonable if I'm going to see this behavior under certain conditions. If it's just when running NUnit, that's acceptable, but otherwise I'll have to look elsewhere.
It's easier to demonstrate the problem using a complete VS Solution (2010), so here's the link to make it easier to setup and run (no need to download NUnit or PdfToText or even a sample pdf):
http://dl.dropbox.com/u/273037/PdfToTextProblem.zip (You may have to change the reference to PdfToText to use the x86 dll if you're running on a 32-bit machine).
Just hit F5 and the NUnit Gui runner will load.
I'm not tied to this library, if you have suggestions, I've tried iTextSharp (way too expensive for 2 lines of code), and looked at Aspose (I didn't try it, but the SaaS license is $11k). But they either lack the required functionality or are way too expensive.
(comment turned into answer)
How complex are your PDFs? The 4.1.6 version of iText allows for a closed sourced solution. Although 4.1.6 doesn't directly have a text extractor it isn't too terribly hard to write one using the PdfReader and GetPageContent().
Below is the code I used to extract the text from the PDF using iTextSharp v4.1.6. If it seems overly verbose, it's related to how I'm using it and the flexibility required.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using iTextSharp.text.pdf;
namespace ClassLibrary1
{
public class PdfToken
{
private PdfToken(int type, string value)
{
Type = type;
Value = value;
}
public static PdfToken Create(PRTokeniser tokenizer)
{
return new PdfToken(tokenizer.TokenType, tokenizer.StringValue);
}
public int Type { get; private set; }
public string Value { get; private set; }
public bool IsOperand
{
get
{
return Type == PRTokeniser.TK_OTHER;
}
}
}
public class PdfOperation
{
public PdfOperation(PdfToken operationToken, IEnumerable<PdfToken> arguments)
{
Name = operationToken.Value;
Arguments = arguments;
}
public string Name { get; private set; }
public IEnumerable<PdfToken> Arguments { get; private set; }
}
public interface IPdfParsingStrategy
{
void Execute(PdfOperation op);
}
public class PlainTextParsingStrategy : IPdfParsingStrategy
{
StringBuilder text = new StringBuilder();
public PlainTextParsingStrategy()
{
}
public String GetText()
{
return text.ToString();
}
#region IPdfParsingStrategy Members
public void Execute(PdfOperation op)
{
// see Adobe PDF specs for additional operations
switch (op.Name)
{
case "TJ":
PrintText(op);
break;
case "Tm":
SetMatrix(op);
break;
case "Tf":
SetFont(op);
break;
case "S":
PrintSection(op);
break;
case "G":
case "g":
case "rg":
SetColor(op);
break;
}
}
#endregion
bool newSection = false;
private void PrintSection(PdfOperation op)
{
text.AppendLine("------------------------------------------------------------");
newSection = true;
}
private void PrintNewline(PdfOperation op)
{
text.AppendLine();
}
private void PrintText(PdfOperation op)
{
if (newSection)
{
newSection = false;
StringBuilder header = new StringBuilder();
PrintText(op, header);
}
PrintText(op, text);
}
private static void PrintText(PdfOperation op, StringBuilder text)
{
foreach (PdfToken t in op.Arguments)
{
switch (t.Type)
{
case PRTokeniser.TK_STRING:
text.Append(t.Value);
break;
case PRTokeniser.TK_NUMBER:
text.Append(" ");
break;
}
}
}
String lastFont = String.Empty;
String lastFontSize = String.Empty;
private void SetFont(PdfOperation op)
{
var args = op.Arguments.ToList();
string font = args[0].Value;
string size = args[1].Value;
//if (font != lastFont || size != lastFontSize)
// text.AppendLine();
lastFont = font;
lastFontSize = size;
}
String lastX = String.Empty;
String lastY = String.Empty;
private void SetMatrix(PdfOperation op)
{
var args = op.Arguments.ToList();
string x = args[4].Value;
string y = args[5].Value;
if (lastY != y)
text.AppendLine();
else if (lastX != x)
text.Append(" ");
lastX = x;
lastY = y;
}
String lastColor = String.Empty;
private void SetColor(PdfOperation op)
{
lastColor = PrintCommand(op).Replace(" ", "_");
}
private static string PrintCommand(PdfOperation op)
{
StringBuilder text = new StringBuilder();
foreach (PdfToken t in op.Arguments)
text.AppendFormat("{0} ", t.Value);
text.Append(op.Name);
return text.ToString();
}
}
}
And here's how I call it:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using iTextSharp.text.pdf;
namespace ClassLibrary1
{
public class PdfExtractor
{
public static string GetText(byte[] pdfBuffer)
{
PlainTextParsingStrategy strategy = new PlainTextParsingStrategy();
ParsePdf(pdfBuffer, strategy);
return strategy.GetText();
}
private static void ParsePdf(byte[] pdf, IPdfParsingStrategy strategy)
{
PdfReader reader = new PdfReader(pdf);
for (int i = 1; i <= reader.NumberOfPages; i++)
{
byte[] page = reader.GetPageContent(i);
if (page != null)
{
PRTokeniser tokenizer = new PRTokeniser(page);
List<PdfToken> parameters = new List<PdfToken>();
while (tokenizer.NextToken())
{
var token = PdfToken.Create(tokenizer);
if (token.IsOperand)
{
strategy.Execute(new PdfOperation(token, parameters));
parameters.Clear();
}
else
{
parameters.Add(token);
}
}
}
}
}
}
}