项目结构调整

This commit is contained in:
艾竹
2023-04-16 20:11:40 +08:00
parent cbfbf96033
commit 81f91f3f35
2124 changed files with 218 additions and 5516 deletions

View File

@@ -0,0 +1,9 @@
<Application x:Class="WPFDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFDemo"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace WPFDemo
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@@ -0,0 +1,64 @@
<Window x:Class="WPFDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="380" Width="531" ResizeMode="CanResize">
<Grid>
<TabControl Height="Auto" Margin="0,1,0,0" Name="tabControl1" Width="Auto">
<TabItem Header="Decode" Name="tabItemDecode">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Grid.Column="0" Grid.Row="0" Height="23" HorizontalAlignment="Stretch" Margin="12,46,30,0" Name="txtBarcodeImageFile" VerticalAlignment="Top" Width="Auto" IsReadOnly="True" TextChanged="txtBarcodeImageFile_TextChanged" />
<Button Grid.Column="0" Grid.Row="0" Content="..." Height="23" HorizontalAlignment="Right" Margin="465,46,0,0" Name="btnSelectFile" VerticalAlignment="Top" Width="26" Click="btnSelectFile_Click" />
<Label Grid.Column="0" Grid.Row="0" Content="Barcode Image File" Height="28" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" Width="124" />
<Button Grid.Column="0" Grid.Row="0" Content="Decode" Height="23" HorizontalAlignment="Right" Margin="416,75,0,0" Name="btnDecode" VerticalAlignment="Top" Width="75" Click="btnDecode_Click" />
<Grid Grid.Column="0" Grid.Row="1" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="350" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Image Grid.Column="0" Grid.Row="0" Height="Auto" HorizontalAlignment="Left" Margin="0,0,0,0" Name="imageBarcode" Stretch="None" VerticalAlignment="Top" Width="Auto" />
<TextBox Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Left" Margin="0,64,0,0" Name="txtBarcodeType" VerticalAlignment="Top" Width="265" IsReadOnly="True" />
<Label Grid.Column="1" Grid.Row="0" Content="Barcode Type" Height="28" HorizontalAlignment="Left" Margin="0,30,0,0" Name="label2" VerticalAlignment="Top" Width="95" />
<Label Grid.Column="1" Grid.Row="0" Content="Content" Height="28" HorizontalAlignment="Left" Margin="0,93,0,0" Name="label3" VerticalAlignment="Top" Width="95" />
<TextBox Grid.Column="1" Grid.Row="0" Height="97" HorizontalAlignment="Left" Margin="0,127,0,0" Name="txtBarcodeContent" VerticalAlignment="Top" Width="265" IsReadOnly="True" />
<Label Grid.Column="1" Grid.Row="0" Height="28" HorizontalAlignment="Left" Margin="0,0,0,0" Name="labDuration" VerticalAlignment="Top" Width="184" />
</Grid>
</Grid>
</TabItem>
<TabItem Header="Encode" Name="tabItemEncode">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="290" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border BorderBrush="#202020" BorderThickness="1" CornerRadius="4">
<Image Grid.Column="0" Grid.Row="0" Height="Auto" HorizontalAlignment="Stretch" Name="imageBarcodeEncoder" Stretch="Uniform" VerticalAlignment="Stretch" Width="Auto" />
</Border>
<Border BorderBrush="#202020" BorderThickness="1" CornerRadius="4">
<Path Grid.Column="0" Grid.Row="0" Name="imageBarcodeEncoderGeometry" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="Black" Width="Auto" Height="Auto" />
</Border>
<Button Grid.Column="1" Grid.Row="0" Content="Encode" Height="23" HorizontalAlignment="Right" Margin="190,269,10,0" Name="btnEncode" VerticalAlignment="Top" Width="75" Click="btnEncode_Click" />
<Label Grid.Column="1" Grid.Row="0" Content="Barcode Type" Height="28" HorizontalAlignment="Right" Margin="0,6,180,0" Name="label4" VerticalAlignment="Top" Width="95" />
<Label Grid.Column="1" Grid.Row="0" Content="Content" Height="28" HorizontalAlignment="Right" Margin="0,132,180,0" Name="label5" VerticalAlignment="Top" Width="95" />
<TextBox Grid.Column="1" Grid.Row="0" Height="97" HorizontalAlignment="Right" Margin="0,166,10,0" Name="txtBarcodeContentEncode" VerticalAlignment="Top" Width="265" />
<ComboBox Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Right" Margin="0,40,10,0" Name="cmbEncoderType" VerticalAlignment="Top" Width="265" />
<ComboBox Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Right" Margin="0,103,10,0" Name="cmbRendererType" VerticalAlignment="Top" Width="265" />
<Label Grid.Column="1" Grid.Row="0" Content="Renderer Type" Height="28" HorizontalAlignment="Right" Margin="0,69,180,0" Name="label6" VerticalAlignment="Top" Width="95" />
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>

View File

@@ -0,0 +1,134 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
using ZXing;
using BarcodeReader = ZXing.Presentation.BarcodeReader;
using BarcodeWriter = ZXing.Presentation.BarcodeWriter;
using BarcodeWriterGeometry = ZXing.Presentation.BarcodeWriterGeometry;
namespace WPFDemo
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly BarcodeReader reader = new BarcodeReader();
public MainWindow()
{
InitializeComponent();
foreach (var format in MultiFormatWriter.SupportedWriters)
cmbEncoderType.Items.Add(format);
cmbEncoderType.SelectedItem = BarcodeFormat.QR_CODE;
cmbRendererType.Items.Add("WriteableBitmap");
cmbRendererType.Items.Add("XAML Geometry");
cmbRendererType.SelectedItem = "WriteableBitmap";
}
private void btnSelectFile_Click(object sender, RoutedEventArgs e)
{
var dlg = new OpenFileDialog
{
Filter = "All documents (*.*)|*.*",
FileName = txtBarcodeImageFile.Text
};
if (dlg.ShowDialog(this).GetValueOrDefault(false))
{
txtBarcodeImageFile.Text = dlg.FileName;
}
}
private void btnDecode_Click(object sender, RoutedEventArgs e)
{
var start = DateTime.Now;
var result = reader.Decode((BitmapSource)imageBarcode.Source);
labDuration.Content = (DateTime.Now - start).Milliseconds + " ms";
if (result != null)
{
txtBarcodeType.Text = result.BarcodeFormat.ToString();
txtBarcodeContent.Text = result.Text;
}
else
{
txtBarcodeType.Text = "";
txtBarcodeContent.Text = "No barcode found.";
}
}
private void txtBarcodeImageFile_TextChanged(object sender, TextChangedEventArgs e)
{
if (File.Exists(txtBarcodeImageFile.Text))
{
imageBarcode.Source = new BitmapImage(new Uri(txtBarcodeImageFile.Text));
}
}
private void btnEncode_Click(object sender, RoutedEventArgs e)
{
imageBarcodeEncoder.Visibility = Visibility.Hidden;
imageBarcodeEncoderGeometry.Visibility = Visibility.Hidden;
switch (cmbRendererType.SelectedItem.ToString())
{
case "WriteableBitmap":
{
var writer = new BarcodeWriter
{
Format = (BarcodeFormat)cmbEncoderType.SelectedItem,
Options = new ZXing.Common.EncodingOptions
{
Height = (int)((FrameworkElement)imageBarcodeEncoder.Parent).ActualHeight,
Width = (int)((FrameworkElement)imageBarcodeEncoder.Parent).ActualWidth,
Margin = 0
}
};
var image = writer.Write(txtBarcodeContentEncode.Text);
imageBarcodeEncoder.Source = image;
imageBarcodeEncoder.Visibility = Visibility.Visible;
}
break;
case "XAML Geometry":
{
var writer = new BarcodeWriterGeometry
{
Format = (BarcodeFormat)cmbEncoderType.SelectedItem,
Options = new ZXing.Common.EncodingOptions
{
Height = (int)((FrameworkElement)imageBarcodeEncoder.Parent).ActualHeight,
Width = (int)((FrameworkElement)imageBarcodeEncoder.Parent).ActualWidth,
Margin = 0
}
};
var image = writer.Write(txtBarcodeContentEncode.Text);
imageBarcodeEncoderGeometry.Data = image;
imageBarcodeEncoderGeometry.Visibility = Visibility.Visible;
}
break;
}
}
}
}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\zxing.core\zxing.core.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29905.134
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WPFDemo", "WPFDemo\WPFDemo.csproj", "{85681BF4-62DA-42A6-A921-BED5865C1A8C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "zxing.core", "zxing.core\zxing.core.csproj", "{B1E14F51-DEE6-4D33-B3EF-B1B553F9CDC2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{85681BF4-62DA-42A6-A921-BED5865C1A8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85681BF4-62DA-42A6-A921-BED5865C1A8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85681BF4-62DA-42A6-A921-BED5865C1A8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85681BF4-62DA-42A6-A921-BED5865C1A8C}.Release|Any CPU.Build.0 = Release|Any CPU
{B1E14F51-DEE6-4D33-B3EF-B1B553F9CDC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B1E14F51-DEE6-4D33-B3EF-B1B553F9CDC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B1E14F51-DEE6-4D33-B3EF-B1B553F9CDC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B1E14F51-DEE6-4D33-B3EF-B1B553F9CDC2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B855CA10-D6A4-4ED5-9E49-628767623C7E}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace ZXing
{
/// <summary>
/// Enumerates barcode formats known to this package.
/// </summary>
/// <author>Sean Owen</author>
[System.Flags]
public enum BarcodeFormat
{
/// <summary>Aztec 2D barcode format.</summary>
AZTEC = 1,
/// <summary>CODABAR 1D format.</summary>
CODABAR = 2,
/// <summary>Code 39 1D format.</summary>
CODE_39 = 4,
/// <summary>Code 93 1D format.</summary>
CODE_93 = 8,
/// <summary>Code 128 1D format.</summary>
CODE_128 = 16,
/// <summary>Data Matrix 2D barcode format.</summary>
DATA_MATRIX = 32,
/// <summary>EAN-8 1D format.</summary>
EAN_8 = 64,
/// <summary>EAN-13 1D format.</summary>
EAN_13 = 128,
/// <summary>ITF (Interleaved Two of Five) 1D format.</summary>
ITF = 256,
/// <summary>MaxiCode 2D barcode format.</summary>
MAXICODE = 512,
/// <summary>PDF417 format.</summary>
PDF_417 = 1024,
/// <summary>QR Code 2D barcode format.</summary>
QR_CODE = 2048,
/// <summary>RSS 14</summary>
RSS_14 = 4096,
/// <summary>RSS EXPANDED</summary>
RSS_EXPANDED = 8192,
/// <summary>UPC-A 1D format.</summary>
UPC_A = 16384,
/// <summary>UPC-E 1D format.</summary>
UPC_E = 32768,
/// <summary>UPC/EAN extension format. Not a stand-alone format.</summary>
UPC_EAN_EXTENSION = 65536,
/// <summary>MSI</summary>
MSI = 131072,
/// <summary>Plessey</summary>
PLESSEY = 262144,
/// <summary>Intelligent Mail barcode</summary>
IMB = 524288,
/// <summary>Pharmacode format.</summary>
PHARMA_CODE = 1048576,
/// <summary>
/// UPC_A | UPC_E | EAN_13 | EAN_8 | CODABAR | CODE_39 | CODE_93 | CODE_128 | ITF | RSS_14 | RSS_EXPANDED
/// without MSI (to many false-positives) and IMB (not enough tested, and it looks more like a 2D)
/// </summary>
All_1D = UPC_A | UPC_E | EAN_13 | EAN_8 | CODABAR | CODE_39 | CODE_93 | CODE_128 | ITF | RSS_14 | RSS_EXPANDED
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Drawing;
namespace ZXing
{
/// <summary>
/// A smart class to decode the barcode inside a bitmap object
/// </summary>
public class BarcodeReader : BarcodeReader<Bitmap>, IBarcodeReader
{
private static readonly Func<Bitmap, LuminanceSource> defaultCreateLuminanceSource =
(bitmap) => new BitmapLuminanceSource(bitmap);
/// <summary>
/// Initializes a new instance of the <see cref="BarcodeReader"/> class.
/// </summary>
public BarcodeReader()
: this(null, defaultCreateLuminanceSource, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BarcodeReader"/> class.
/// </summary>
/// <param name="reader">Sets the reader which should be used to find and decode the barcode.
/// If null then MultiFormatReader is used</param>
/// <param name="createLuminanceSource">Sets the function to create a luminance source object for a bitmap.
/// If null, an exception is thrown when Decode is called</param>
/// <param name="createBinarizer">Sets the function to create a binarizer object for a luminance source.
/// If null then HybridBinarizer is used</param>
public BarcodeReader(Reader reader,
Func<Bitmap, LuminanceSource> createLuminanceSource,
Func<LuminanceSource, Binarizer> createBinarizer
)
: base(reader, createLuminanceSource ?? defaultCreateLuminanceSource, createBinarizer)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BarcodeReader"/> class.
/// </summary>
/// <param name="reader">Sets the reader which should be used to find and decode the barcode.
/// If null then MultiFormatReader is used</param>
/// <param name="createLuminanceSource">Sets the function to create a luminance source object for a bitmap.
/// If null, an exception is thrown when Decode is called</param>
/// <param name="createBinarizer">Sets the function to create a binarizer object for a luminance source.
/// If null then HybridBinarizer is used</param>
/// <param name="createRGBLuminanceSource">Sets the function to create a luminance source object for a rgb raw byte array.</param>
public BarcodeReader(Reader reader,
Func<Bitmap, LuminanceSource> createLuminanceSource,
Func<LuminanceSource, Binarizer> createBinarizer,
Func<byte[], int, int, RGBLuminanceSource.BitmapFormat, LuminanceSource> createRGBLuminanceSource
)
: base(reader, createLuminanceSource ?? defaultCreateLuminanceSource, createBinarizer, createRGBLuminanceSource)
{
}
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing
{
/// <summary>
/// A smart class to decode the barcode inside a bitmap object
/// </summary>
public class BarcodeReader<T> : BarcodeReaderGeneric, IBarcodeReader<T>
{
private readonly Func<T, LuminanceSource> createLuminanceSource;
/// <summary>
/// Initializes a new instance of the <see cref="BarcodeReader"/> class.
/// </summary>
public BarcodeReader(Func<T, LuminanceSource> createLuminanceSource)
: this(null, createLuminanceSource, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BarcodeReader"/> class.
/// </summary>
/// <param name="reader">Sets the reader which should be used to find and decode the barcode.
/// If null then MultiFormatReader is used</param>
/// <param name="createLuminanceSource">Sets the function to create a luminance source object for a bitmap.
/// If null, an exception is thrown when Decode is called</param>
/// <param name="createBinarizer">Sets the function to create a binarizer object for a luminance source.
/// If null then HybridBinarizer is used</param>
public BarcodeReader(Reader reader,
Func<T, LuminanceSource> createLuminanceSource,
Func<LuminanceSource, Binarizer> createBinarizer
)
: this(reader, createLuminanceSource, createBinarizer, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BarcodeReader"/> class.
/// </summary>
/// <param name="reader">Sets the reader which should be used to find and decode the barcode.
/// If null then MultiFormatReader is used</param>
/// <param name="createLuminanceSource">Sets the function to create a luminance source object for a bitmap.
/// If null, an exception is thrown when Decode is called</param>
/// <param name="createBinarizer">Sets the function to create a binarizer object for a luminance source.
/// If null then HybridBinarizer is used</param>
/// <param name="createRGBLuminanceSource">Sets the function to create a luminance source object for a rgb raw byte array.</param>
public BarcodeReader(Reader reader,
Func<T, LuminanceSource> createLuminanceSource,
Func<LuminanceSource, Binarizer> createBinarizer,
Func<byte[], int, int, RGBLuminanceSource.BitmapFormat, LuminanceSource> createRGBLuminanceSource
)
: base(reader, createBinarizer, createRGBLuminanceSource)
{
this.createLuminanceSource = createLuminanceSource;
}
/// <summary>
/// Constructor for additional image formats for one BarcodeReader class
/// </summary>
/// <param name="reader">Sets the reader which should be used to find and decode the barcode.
/// If null then MultiFormatReader is used</param>
/// <param name="createBinarizer">Sets the function to create a binarizer object for a luminance source.
/// If null then HybridBinarizer is used</param>
/// <param name="createRGBLuminanceSource">Sets the function to create a luminance source object for a rgb raw byte array.</param>
protected BarcodeReader(Reader reader,
Func<LuminanceSource, Binarizer> createBinarizer,
Func<byte[], int, int, RGBLuminanceSource.BitmapFormat, LuminanceSource> createRGBLuminanceSource
)
: base(reader, createBinarizer, createRGBLuminanceSource)
{
}
/// <summary>
/// Optional: Gets or sets the function to create a luminance source object for a bitmap.
/// If null a platform specific default LuminanceSource is used
/// </summary>
/// <value>
/// The function to create a luminance source object.
/// </value>
protected Func<T, LuminanceSource> CreateLuminanceSource
{
get
{
return createLuminanceSource;
}
}
/// <summary>
/// Decodes the specified barcode bitmap.
/// </summary>
/// <param name="barcodeBitmap">The barcode bitmap.</param>
/// <returns>the result data or null</returns>
public Result Decode(T barcodeBitmap)
{
if (CreateLuminanceSource == null)
{
throw new InvalidOperationException("You have to declare a luminance source delegate.");
}
if (barcodeBitmap == null)
throw new ArgumentNullException("barcodeBitmap");
var luminanceSource = CreateLuminanceSource(barcodeBitmap);
return Decode(luminanceSource);
}
/// <summary>
/// Decodes the specified barcode bitmap.
/// </summary>
/// <param name="barcodeBitmap">The barcode bitmap.</param>
/// <returns>the result data or null</returns>
public Result[] DecodeMultiple(T barcodeBitmap)
{
if (CreateLuminanceSource == null)
{
throw new InvalidOperationException("You have to declare a luminance source delegate.");
}
if (barcodeBitmap == null)
throw new ArgumentNullException("barcodeBitmap");
var luminanceSource = CreateLuminanceSource(barcodeBitmap);
return DecodeMultiple(luminanceSource);
}
}
}

View File

@@ -0,0 +1,442 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using ZXing.Common;
using ZXing.Multi;
using ZXing.Multi.QrCode;
namespace ZXing
{
/// <summary>
/// A smart class to decode the barcode inside a bitmap object
/// </summary>
public class BarcodeReaderGeneric : IBarcodeReaderGeneric
{
private static readonly Func<LuminanceSource, Binarizer> defaultCreateBinarizer =
(luminanceSource) => new HybridBinarizer(luminanceSource);
/// <summary>
/// represents the default function which is called to get a <see cref="RGBLuminanceSource"/> instance from a raw byte array
/// </summary>
protected static readonly Func<byte[], int, int, RGBLuminanceSource.BitmapFormat, LuminanceSource> defaultCreateRGBLuminanceSource =
(rawBytes, width, height, format) => new RGBLuminanceSource(rawBytes, width, height, format);
private Reader reader;
private readonly Func<byte[], int, int, RGBLuminanceSource.BitmapFormat, LuminanceSource> createRGBLuminanceSource;
private readonly Func<LuminanceSource, Binarizer> createBinarizer;
private bool usePreviousState;
private DecodingOptions options;
/// <summary>
/// Gets or sets the options.
/// </summary>
/// <value>
/// The options.
/// </value>
public DecodingOptions Options
{
get
{
if (options == null)
{
options = new DecodingOptions();
options.ValueChanged += (o, args) => usePreviousState = false;
}
return options;
}
set
{
if (value != null)
{
options = value;
options.ValueChanged += (o, args) => usePreviousState = false;
}
else
{
options = null;
}
usePreviousState = false;
}
}
/// <summary>
/// Gets the reader which should be used to find and decode the barcode.
/// </summary>
/// <value>
/// The reader.
/// </value>
protected Reader Reader
{
get
{
return reader ?? (reader = new MultiFormatReader());
}
}
/// <summary>
/// Gets or sets a method which is called if an important point is found
/// </summary>
/// <value>
/// The result point callback.
/// </value>
public event Action<ResultPoint> ResultPointFound
{
add
{
if (!Options.Hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK))
{
var callback = new ResultPointCallback(OnResultPointFound);
Options.Hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK] = callback;
}
explicitResultPointFound += value;
usePreviousState = false;
}
remove
{
explicitResultPointFound -= value;
if (explicitResultPointFound == null)
Options.Hints.Remove(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
usePreviousState = false;
}
}
private event Action<ResultPoint> explicitResultPointFound;
/// <summary>
/// event is executed if a result was found via decode
/// </summary>
public event Action<Result> ResultFound;
/// <summary>
/// Gets or sets a value indicating whether the image should be automatically rotated.
/// Rotation is supported for 90, 180 and 270 degrees
/// </summary>
/// <value>
/// <c>true</c> if image should be rotated; otherwise, <c>false</c>.
/// </value>
public bool AutoRotate { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the image should be automatically inverted
/// if no result is found in the original image.
/// ATTENTION: Please be carefully because it slows down the decoding process if it is used
/// </summary>
/// <value>
/// <c>true</c> if image should be inverted; otherwise, <c>false</c>.
/// </value>
public bool TryInverted { get; set; }
/// <summary>
/// Optional: Gets or sets the function to create a binarizer object for a luminance source.
/// If null then HybridBinarizer is used
/// </summary>
/// <value>
/// The function to create a binarizer object.
/// </value>
protected Func<LuminanceSource, Binarizer> CreateBinarizer
{
get
{
return createBinarizer ?? defaultCreateBinarizer;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="BarcodeReaderGeneric"/> class.
/// </summary>
public BarcodeReaderGeneric()
: this(new MultiFormatReader(), defaultCreateBinarizer, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BarcodeReaderGeneric"/> class.
/// </summary>
/// <param name="reader">Sets the reader which should be used to find and decode the barcode.
/// If null then MultiFormatReader is used</param>
/// <param name="createBinarizer">Sets the function to create a binarizer object for a luminance source.
/// If null then HybridBinarizer is used</param>
/// <param name="createRGBLuminanceSource">Sets the function to create a luminance source object for a rgb array.
/// If null the RGBLuminanceSource is used. The handler is only called when Decode with a byte[] array is called.</param>
public BarcodeReaderGeneric(Reader reader,
Func<LuminanceSource, Binarizer> createBinarizer,
Func<byte[], int, int, RGBLuminanceSource.BitmapFormat, LuminanceSource> createRGBLuminanceSource
)
{
this.reader = reader ?? new MultiFormatReader();
this.createBinarizer = createBinarizer ?? defaultCreateBinarizer;
this.createRGBLuminanceSource = createRGBLuminanceSource ?? defaultCreateRGBLuminanceSource;
usePreviousState = false;
}
/// <summary>
/// Tries to decode a barcode within an image which is given by a luminance source.
/// That method gives a chance to prepare a luminance source completely before calling
/// the time consuming decoding method. On the other hand there is a chance to create
/// a luminance source which is independent from external resources (like Bitmap objects)
/// and the decoding call can be made in a background thread.
/// </summary>
/// <param name="luminanceSource">The luminance source.</param>
/// <returns></returns>
public virtual Result Decode(LuminanceSource luminanceSource)
{
var result = default(Result);
var binarizer = CreateBinarizer(luminanceSource);
var binaryBitmap = new BinaryBitmap(binarizer);
var multiformatReader = Reader as MultiFormatReader;
var rotationCount = 0;
var rotationMaxCount = 1;
if (AutoRotate)
{
Options.Hints[DecodeHintType.TRY_HARDER_WITHOUT_ROTATION] = true;
rotationMaxCount = 4;
}
else
{
if (Options.Hints.ContainsKey(DecodeHintType.TRY_HARDER_WITHOUT_ROTATION))
Options.Hints.Remove(DecodeHintType.TRY_HARDER_WITHOUT_ROTATION);
}
for (; rotationCount < rotationMaxCount; rotationCount++)
{
if (usePreviousState && multiformatReader != null)
{
result = multiformatReader.decodeWithState(binaryBitmap);
}
else
{
result = Reader.decode(binaryBitmap, Options.Hints);
usePreviousState = true;
}
if (result == null)
{
if (TryInverted && luminanceSource.InversionSupported)
{
binaryBitmap = new BinaryBitmap(CreateBinarizer(luminanceSource.invert()));
if (usePreviousState && multiformatReader != null)
{
result = multiformatReader.decodeWithState(binaryBitmap);
}
else
{
result = Reader.decode(binaryBitmap, Options.Hints);
usePreviousState = true;
}
}
}
if (result != null ||
!luminanceSource.RotateSupported ||
!AutoRotate)
break;
luminanceSource = luminanceSource.rotateCounterClockwise();
binarizer = CreateBinarizer(luminanceSource);
binaryBitmap = new BinaryBitmap(binarizer);
}
if (result != null)
{
if (result.ResultMetadata == null)
{
result.putMetadata(ResultMetadataType.ORIENTATION, rotationCount * 90);
}
else if (!result.ResultMetadata.ContainsKey(ResultMetadataType.ORIENTATION))
{
result.ResultMetadata[ResultMetadataType.ORIENTATION] = rotationCount * 90;
}
else
{
// perhaps the core decoder rotates the image already (can happen if TryHarder is specified)
result.ResultMetadata[ResultMetadataType.ORIENTATION] = ((int)(result.ResultMetadata[ResultMetadataType.ORIENTATION]) + rotationCount * 90) % 360;
}
OnResultFound(result);
}
return result;
}
/// <summary>
/// Tries to decode barcodes within an image which is given by a luminance source.
/// That method gives a chance to prepare a luminance source completely before calling
/// the time consuming decoding method. On the other hand there is a chance to create
/// a luminance source which is independent from external resources (like Bitmap objects)
/// and the decoding call can be made in a background thread.
/// </summary>
/// <param name="luminanceSource">The luminance source.</param>
/// <returns></returns>
public virtual Result[] DecodeMultiple(LuminanceSource luminanceSource)
{
var results = default(Result[]);
var binarizer = CreateBinarizer(luminanceSource);
var binaryBitmap = new BinaryBitmap(binarizer);
var rotationCount = 0;
var rotationMaxCount = 1;
MultipleBarcodeReader multiReader = null;
if (AutoRotate)
{
Options.Hints[DecodeHintType.TRY_HARDER_WITHOUT_ROTATION] = true;
rotationMaxCount = 4;
}
var formats = Options.PossibleFormats;
if (formats != null &&
formats.Count == 1 &&
formats.Contains(BarcodeFormat.QR_CODE))
{
multiReader = new QRCodeMultiReader();
}
else
{
multiReader = new GenericMultipleBarcodeReader(Reader);
}
for (; rotationCount < rotationMaxCount; rotationCount++)
{
results = multiReader.decodeMultiple(binaryBitmap, Options.Hints);
if (results == null)
{
if (TryInverted && luminanceSource.InversionSupported)
{
binaryBitmap = new BinaryBitmap(CreateBinarizer(luminanceSource.invert()));
results = multiReader.decodeMultiple(binaryBitmap, Options.Hints);
}
}
if (results != null ||
!luminanceSource.RotateSupported ||
!AutoRotate)
break;
binaryBitmap = new BinaryBitmap(CreateBinarizer(luminanceSource.rotateCounterClockwise()));
}
if (results != null)
{
foreach (var result in results)
{
if (result.ResultMetadata == null)
{
result.putMetadata(ResultMetadataType.ORIENTATION, rotationCount * 90);
}
else if (!result.ResultMetadata.ContainsKey(ResultMetadataType.ORIENTATION))
{
result.ResultMetadata[ResultMetadataType.ORIENTATION] = rotationCount * 90;
}
else
{
// perhaps the core decoder rotates the image already (can happen if TryHarder is specified)
result.ResultMetadata[ResultMetadataType.ORIENTATION] =
((int)(result.ResultMetadata[ResultMetadataType.ORIENTATION]) + rotationCount * 90) % 360;
}
}
OnResultsFound(results);
}
return results;
}
/// <summary>
/// raises the ResultFound event
/// </summary>
/// <param name="results"></param>
protected void OnResultsFound(IEnumerable<Result> results)
{
if (ResultFound != null)
{
foreach (var result in results)
{
ResultFound(result);
}
}
}
/// <summary>
/// raises the ResultFound event
/// </summary>
/// <param name="result"></param>
protected void OnResultFound(Result result)
{
if (ResultFound != null)
{
ResultFound(result);
}
}
/// <summary>
/// calls the explicitResultPointFound action
/// </summary>
/// <param name="resultPoint"></param>
protected void OnResultPointFound(ResultPoint resultPoint)
{
if (explicitResultPointFound != null)
{
explicitResultPointFound(resultPoint);
}
}
/// <summary>
/// Decodes the specified barcode bitmap.
/// </summary>
/// <param name="rawRGB">The image as byte[] array.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="format">The format.</param>
/// <returns>
/// the result data or null
/// </returns>
public Result Decode(byte[] rawRGB, int width, int height, RGBLuminanceSource.BitmapFormat format)
{
if (rawRGB == null)
throw new ArgumentNullException("rawRGB");
var luminanceSource = createRGBLuminanceSource(rawRGB, width, height, format);
return Decode(luminanceSource);
}
/// <summary>
/// Decodes the specified barcode bitmap.
/// </summary>
/// <param name="rawRGB">The image as byte[] array.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="format">The format.</param>
/// <returns>
/// the result data or null
/// </returns>
public Result[] DecodeMultiple(byte[] rawRGB, int width, int height, RGBLuminanceSource.BitmapFormat format)
{
if (rawRGB == null)
throw new ArgumentNullException("rawRGB");
var luminanceSource = createRGBLuminanceSource(rawRGB, width, height, format);
return DecodeMultiple(luminanceSource);
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using ZXing.Rendering;
namespace ZXing
{
/// <summary>
/// A smart class to encode some content to a barcode image
/// </summary>
public class BarcodeWriter : BarcodeWriter<System.Drawing.Bitmap>, IBarcodeWriter
{
/// <summary>
/// Initializes a new instance of the <see cref="BarcodeWriter"/> class.
/// </summary>
public BarcodeWriter()
{
Renderer = new BitmapRenderer();
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using ZXing.Rendering;
namespace ZXing
{
/// <summary>
/// A smart class to encode some content to raw pixel data
/// </summary>
public class BarcodeWriterPixelData : BarcodeWriter<PixelData>, IBarcodeWriterPixelData
{
/// <summary>
/// Initializes a new instance of the <see cref="BarcodeWriterPixelData"/> class.
/// </summary>
public BarcodeWriterPixelData()
{
Renderer = new PixelDataRenderer();
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using ZXing.Rendering;
namespace ZXing
{
/// <summary>
/// A smart class to encode some content to a svg barcode image
/// </summary>
public class BarcodeWriterSvg : BarcodeWriter<SvgRenderer.SvgImage>, IBarcodeWriterSvg
{
/// <summary>
/// Initializes a new instance of the <see cref="BarcodeWriterSvg"/> class.
/// </summary>
public BarcodeWriterSvg()
{
Renderer = new SvgRenderer();
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using ZXing.Common;
using ZXing.Rendering;
namespace ZXing
{
/// <summary>
/// A base class for specific barcode writers with specific formats of barcode images.
/// </summary>
/// <typeparam name="TOutput">The type of the output.</typeparam>
public class BarcodeWriter<TOutput> : BarcodeWriterGeneric, IBarcodeWriter<TOutput>
{
/// <summary>
/// Gets or sets the renderer which should be used to render the encoded BitMatrix.
/// </summary>
public IBarcodeRenderer<TOutput> Renderer { get; set; }
/// <summary>
/// Encodes the specified contents and returns a rendered instance of the barcode.
/// For rendering the instance of the property Renderer is used and has to be set before
/// calling that method.
/// </summary>
/// <param name="contents">The contents.</param>
/// <returns></returns>
public TOutput Write(string contents)
{
if (Renderer == null)
{
throw new InvalidOperationException("You have to set a renderer instance.");
}
var matrix = Encode(contents);
return Renderer.Render(matrix, Format, contents, Options);
}
/// <summary>
/// Returns a rendered instance of the barcode which is given by a BitMatrix.
/// For rendering the instance of the property Renderer is used and has to be set before
/// calling that method.
/// </summary>
/// <param name="matrix">The matrix.</param>
/// <returns></returns>
public TOutput Write(BitMatrix matrix)
{
if (Renderer == null)
{
throw new InvalidOperationException("You have to set a renderer instance.");
}
return Renderer.Render(matrix, Format, null, Options);
}
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using ZXing.Common;
namespace ZXing
{
/// <summary>
/// A base class for specific barcode writers with specific formats of barcode images.
/// </summary>
public class BarcodeWriterGeneric : IBarcodeWriterGeneric
{
private EncodingOptions options;
/// <summary>
/// Gets or sets the barcode format.
/// The value is only suitable if the MultiFormatWriter is used.
/// </summary>
public BarcodeFormat Format { get; set; }
/// <summary>
/// Gets or sets the options container for the encoding and renderer process.
/// </summary>
public EncodingOptions Options
{
get
{
return (options ?? (options = new EncodingOptions { Height = 100, Width = 100 }));
}
set
{
options = value;
}
}
/// <summary>
/// Gets or sets the writer which encodes the content to a BitMatrix.
/// If no value is set the MultiFormatWriter is used.
/// </summary>
public Writer Encoder { get; set; }
/// <summary>
///
/// </summary>
public BarcodeWriterGeneric()
{
}
/// <summary>
///
/// </summary>
/// <param name="encoder"></param>
public BarcodeWriterGeneric(Writer encoder)
{
Encoder = encoder;
}
/// <summary>
/// Encodes the specified contents and returns a BitMatrix array.
/// That array has to be rendered manually or with a IBarcodeRenderer.
/// </summary>
/// <param name="contents">The contents.</param>
/// <returns></returns>
public BitMatrix Encode(string contents)
{
var encoder = Encoder ?? new MultiFormatWriter();
var currentOptions = Options;
return encoder.encode(contents, Format, currentOptions.Width, currentOptions.Height, currentOptions.Hints);
}
}
}

View File

@@ -0,0 +1,228 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing
{
/// <summary>
/// The base class for luminance sources which supports
/// cropping and rotating based upon the luminance values.
/// </summary>
public abstract class BaseLuminanceSource : LuminanceSource
{
// the following channel weights give nearly the same
// gray scale picture as the java version with BufferedImage.TYPE_BYTE_GRAY
// they are used in sub classes for luminance / gray scale calculation
/// <summary>
/// weight of the red channel for calculating a gray scale image
/// </summary>
protected const int RChannelWeight = 19562;
/// <summary>
/// weight of the green channel for calculating a gray scale image
/// </summary>
protected const int GChannelWeight = 38550;
/// <summary>
/// weight of the blue channel for calculating a gray scale image
/// </summary>
protected const int BChannelWeight = 7424;
/// <summary>
/// numbers of bits which for right shifting
/// </summary>
protected const int ChannelWeight = 16;
/// <summary>
///
/// </summary>
protected byte[] luminances;
/// <summary>
/// Initializes a new instance of the <see cref="BaseLuminanceSource"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected BaseLuminanceSource(int width, int height)
: base(width, height)
{
luminances = new byte[width * height];
}
/// <summary>
/// Initializes a new instance of the <see cref="BaseLuminanceSource"/> class.
/// </summary>
/// <param name="luminanceArray">The luminance array.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected BaseLuminanceSource(byte[] luminanceArray, int width, int height)
: base(width, height)
{
luminances = new byte[width * height];
Buffer.BlockCopy(luminanceArray, 0, luminances, 0, width * height);
}
/// <summary>
/// Fetches one row of luminance data from the underlying platform's bitmap. Values range from
/// 0 (black) to 255 (white). It is preferable for implementations of this method
/// to only fetch this row rather than the whole image, since no 2D Readers may be installed and
/// getMatrix() may never be called.
/// </summary>
/// <param name="y">The row to fetch, 0 &lt;= y &lt; Height.</param>
/// <param name="row">An optional preallocated array. If null or too small, it will be ignored.
/// Always use the returned object, and ignore the .length of the array.</param>
/// <returns>
/// An array containing the luminance data.
/// </returns>
override public byte[] getRow(int y, byte[] row)
{
int width = Width;
if (row == null || row.Length < width)
{
row = new byte[width];
}
for (int i = 0; i < width; i++)
row[i] = luminances[y * width + i];
return row;
}
/// <summary>
/// gets the luminance matrix
/// </summary>
public override byte[] Matrix
{
get { return luminances; }
}
/// <summary>
/// Returns a new object with rotated image data by 90 degrees counterclockwise.
/// Only callable if {@link #isRotateSupported()} is true.
/// </summary>
/// <returns>
/// A rotated version of this object.
/// </returns>
public override LuminanceSource rotateCounterClockwise()
{
var rotatedLuminances = new byte[Width * Height];
var newWidth = Height;
var newHeight = Width;
var localLuminances = Matrix;
for (var yold = 0; yold < Height; yold++)
{
for (var xold = 0; xold < Width; xold++)
{
var ynew = newHeight - xold - 1;
var xnew = yold;
rotatedLuminances[ynew * newWidth + xnew] = localLuminances[yold * Width + xold];
}
}
return CreateLuminanceSource(rotatedLuminances, newWidth, newHeight);
}
/// <summary>
/// TODO: not implemented yet
/// </summary>
/// <returns>
/// A rotated version of this object.
/// </returns>
public override LuminanceSource rotateCounterClockwise45()
{
// TODO: implement a good 45 degrees rotation without lost of information
return base.rotateCounterClockwise45();
}
/// <summary>
/// </summary>
/// <returns> Whether this subclass supports counter-clockwise rotation.</returns>
public override bool RotateSupported
{
get
{
return true;
}
}
/// <summary>
/// Returns a new object with cropped image data. Implementations may keep a reference to the
/// original data rather than a copy. Only callable if CropSupported is true.
/// </summary>
/// <param name="left">The left coordinate, 0 &lt;= left &lt; Width.</param>
/// <param name="top">The top coordinate, 0 &lt;= top &lt;= Height.</param>
/// <param name="width">The width of the rectangle to crop.</param>
/// <param name="height">The height of the rectangle to crop.</param>
/// <returns>
/// A cropped version of this object.
/// </returns>
public override LuminanceSource crop(int left, int top, int width, int height)
{
if (left + width > Width || top + height > Height)
{
throw new ArgumentException("Crop rectangle does not fit within image data.");
}
var croppedLuminances = new byte[width * height];
var oldLuminances = Matrix;
var oldWidth = Width;
var oldRightBound = left + width;
var oldBottomBound = top + height;
for (int yold = top, ynew = 0; yold < oldBottomBound; yold++, ynew++)
{
for (int xold = left, xnew = 0; xold < oldRightBound; xold++, xnew++)
{
croppedLuminances[ynew * width + xnew] = oldLuminances[yold * oldWidth + xold];
}
}
return CreateLuminanceSource(croppedLuminances, width, height);
}
/// <summary>
/// </summary>
/// <returns> Whether this subclass supports cropping.</returns>
public override bool CropSupported
{
get
{
return true;
}
}
/// <summary>
/// </summary>
/// <returns>Whether this subclass supports invertion.</returns>
public override bool InversionSupported
{
get
{
return true;
}
}
/// <summary>
/// Inverts the luminance values (newValue = 255 - oldValue)
/// </summary>
public override LuminanceSource invert()
{
return new InvertedLuminanceSource(this);
}
/// <summary>
/// Should create a new luminance source with the right class type.
/// The method is used in methods crop and rotate.
/// </summary>
/// <param name="newLuminances">The new luminances.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns></returns>
protected abstract LuminanceSource CreateLuminanceSource(byte[] newLuminances, int width, int height);
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using ZXing.Common;
namespace ZXing
{
/// <summary> This class hierarchy provides a set of methods to convert luminance data to 1 bit data.
/// It allows the algorithm to vary polymorphically, for example allowing a very expensive
/// thresholding technique for servers and a fast one for mobile. It also permits the implementation
/// to vary, e.g. a JNI version for Android and a Java fallback version for other platforms.
///
/// <author>dswitkin@google.com (Daniel Switkin)</author>
/// </summary>
public abstract class Binarizer
{
private readonly LuminanceSource source;
/// <summary>
/// Initializes a new instance of the <see cref="Binarizer"/> class.
/// </summary>
/// <param name="source">The source.</param>
protected internal Binarizer(LuminanceSource source)
{
if (source == null)
{
throw new ArgumentException("Source must be non-null.");
}
this.source = source;
}
/// <summary>
/// Gets the luminance source object.
/// </summary>
virtual public LuminanceSource LuminanceSource
{
get
{
return source;
}
}
/// <summary> Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
/// cached data. Callers should assume this method is expensive and call it as seldom as possible.
/// This method is intended for decoding 1D barcodes and may choose to apply sharpening.
/// For callers which only examine one row of pixels at a time, the same BitArray should be reused
/// and passed in with each call for performance. However it is legal to keep more than one row
/// at a time if needed.
/// </summary>
/// <param name="y">The row to fetch, which must be in [0, bitmap height)</param>
/// <param name="row">An optional preallocated array. If null or too small, it will be ignored.
/// If used, the Binarizer will call BitArray.clear(). Always use the returned object.
/// </param>
/// <returns> The array of bits for this row (true means black).</returns>
public abstract BitArray getBlackRow(int y, BitArray row);
/// <summary> Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive
/// and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
/// may not apply sharpening. Therefore, a row from this matrix may not be identical to one
/// fetched using getBlackRow(), so don't mix and match between them.
/// </summary>
/// <returns> The 2D array of bits for the image (true means black).</returns>
public abstract BitMatrix BlackMatrix { get; }
/// <summary> Creates a new object with the same type as this Binarizer implementation, but with pristine
/// state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache
/// of 1 bit data. See Effective Java for why we can't use Java's clone() method.
/// </summary>
/// <param name="source">The LuminanceSource this Binarizer will operate on.</param>
/// <returns> A new concrete Binarizer implementation object.</returns>
public abstract Binarizer createBinarizer(LuminanceSource source);
/// <summary>
/// Gets the width of the luminance source object.
/// </summary>
public int Width
{
get { return source.Width; }
}
/// <summary>
/// Gets the height of the luminance source object.
/// </summary>
public int Height
{
get { return source.Height; }
}
}
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using ZXing.Common;
namespace ZXing
{
/// <summary>
/// This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects
/// accept a BinaryBitmap and attempt to decode it.
/// </summary>
/// <author>dswitkin@google.com (Daniel Switkin)</author>
public sealed class BinaryBitmap
{
private readonly Binarizer binarizer;
private BitMatrix matrix;
/// <summary>
///
/// </summary>
/// <param name="binarizer"></param>
public BinaryBitmap(Binarizer binarizer)
{
if (binarizer == null)
{
throw new ArgumentException("Binarizer must be non-null.");
}
this.binarizer = binarizer;
}
internal BinaryBitmap(BitMatrix matrix)
{
if (matrix == null)
{
throw new ArgumentException("matrix must be non-null.");
}
this.matrix = matrix;
}
/// <returns>
/// The width of the bitmap.
/// </returns>
public int Width
{
get
{
return binarizer.Width;
}
}
/// <returns>
/// The height of the bitmap.
/// </returns>
public int Height
{
get
{
return binarizer.Height;
}
}
/// <summary>
/// Converts one row of luminance data to 1 bit data. May actually do the conversion, or return
/// cached data. Callers should assume this method is expensive and call it as seldom as possible.
/// This method is intended for decoding 1D barcodes and may choose to apply sharpening.
/// </summary>
/// <param name="y">The row to fetch, which must be in [0, bitmap height).</param>
/// <param name="row">An optional preallocated array. If null or too small, it will be ignored.
/// If used, the Binarizer will call BitArray.clear(). Always use the returned object.
/// </param>
/// <returns> The array of bits for this row (true means black).</returns>
public BitArray getBlackRow(int y, BitArray row)
{
return binarizer.getBlackRow(y, row);
}
/// <summary>
/// Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive
/// and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or
/// may not apply sharpening. Therefore, a row from this matrix may not be identical to one
/// fetched using getBlackRow(), so don't mix and match between them.
/// </summary>
/// <returns> The 2D array of bits for the image (true means black).</returns>
public BitMatrix BlackMatrix
{
get
{
// The matrix is created on demand the first time it is requested, then cached. There are two
// reasons for this:
// 1. This work will never be done if the caller only installs 1D Reader objects, or if a
// 1D Reader finds a barcode before the 2D Readers run.
// 2. This work will only be done once even if the caller installs multiple 2D Readers.
return matrix ?? (matrix = binarizer.BlackMatrix);
}
}
/// <returns>
/// Whether this bitmap can be cropped.
/// </returns>
public bool CropSupported
{
get
{
return binarizer.LuminanceSource.CropSupported;
}
}
/// <summary>
/// Returns a new object with cropped image data. Implementations may keep a reference to the
/// original data rather than a copy. Only callable if isCropSupported() is true.
/// </summary>
/// <param name="left">The left coordinate, which must be in [0, Width)</param>
/// <param name="top">The top coordinate, which must be in [0, Height)</param>
/// <param name="width">The width of the rectangle to crop.</param>
/// <param name="height">The height of the rectangle to crop.</param>
/// <returns> A cropped version of this object.</returns>
public BinaryBitmap crop(int left, int top, int width, int height)
{
var newSource = binarizer.LuminanceSource.crop(left, top, width, height);
return new BinaryBitmap(binarizer.createBinarizer(newSource));
}
/// <returns>
/// Whether this bitmap supports counter-clockwise rotation.
/// </returns>
public bool RotateSupported
{
get
{
return binarizer.LuminanceSource.RotateSupported;
}
}
/// <summary>
/// Returns a new object with rotated image data by 90 degrees counterclockwise.
/// Only callable if <see cref="RotateSupported"/> is true.
/// </summary>
/// <returns>A rotated version of this object.</returns>
public BinaryBitmap rotateCounterClockwise()
{
var newSource = binarizer.LuminanceSource.rotateCounterClockwise();
return new BinaryBitmap(binarizer.createBinarizer(newSource));
}
/// <summary>
/// Returns a new object with rotated image data by 45 degrees counterclockwise.
/// Only callable if <see cref="RotateSupported"/> is true.
/// </summary>
/// <returns>A rotated version of this object.</returns>
public BinaryBitmap rotateCounterClockwise45()
{
LuminanceSource newSource = binarizer.LuminanceSource.rotateCounterClockwise45();
return new BinaryBitmap(binarizer.createBinarizer(newSource));
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString()
{
var blackMatrix = BlackMatrix;
return blackMatrix != null ? blackMatrix.ToString() : String.Empty;
}
}
}

View File

@@ -0,0 +1,565 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Drawing.Imaging;
using System.Drawing;
using System.Runtime.InteropServices;
namespace ZXing
{
/// <summary>
/// class which represents the luminance values for a bitmap object
/// </summary>
public partial class BitmapLuminanceSource : BaseLuminanceSource
{
/// <summary>
/// Initializes a new instance of the <see cref="BitmapLuminanceSource"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected BitmapLuminanceSource(int width, int height)
: base(width, height)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BitmapLuminanceSource"/> class
/// with the image of a Bitmap instance
/// </summary>
/// <param name="bitmap">The bitmap.</param>
public BitmapLuminanceSource(Bitmap bitmap)
: base(bitmap.Width, bitmap.Height)
{
CalculateLuminanceValues(bitmap, luminances);
}
/// <summary>
/// calculates the luminance values for bitmaps
/// </summary>
/// <param name="bitmap"></param>
/// <param name="luminances"></param>
protected static void CalculateLuminanceValues(Bitmap bitmap, byte[] luminances)
{
var height = bitmap.Height;
var width = bitmap.Width;
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
// The underlying raster of image consists of bytes with the luminance values
#if WindowsCE
var data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
#else
var data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
#endif
try
{
var stride = Math.Abs(data.Stride);
var pixelWidth = stride / width;
if (pixelWidth > 4)
{
// old slow way for unsupported bit depth
CalculateLuminanceValuesSlow(bitmap, luminances);
}
else
{
#if !WindowsCE
if (bitmap.PixelFormat == PixelFormat.Format32bppArgb ||
bitmap.PixelFormat == PixelFormat.Format32bppPArgb)
{
pixelWidth = 40;
}
if ((int)bitmap.PixelFormat == 8207 ||
(bitmap.Flags & (int)ImageFlags.ColorSpaceCmyk) == (int)ImageFlags.ColorSpaceCmyk)
{
pixelWidth = 41;
}
#endif
switch (pixelWidth)
{
#if !WindowsCE
case 0:
if (bitmap.PixelFormat == PixelFormat.Format4bppIndexed)
CalculateLuminanceValuesForIndexed4Bit(bitmap, data, luminances);
else
CalculateLuminanceValuesForIndexed1Bit(bitmap, data, luminances);
break;
case 1:
CalculateLuminanceValuesForIndexed8Bit(bitmap, data, luminances);
break;
#endif
case 2:
// should be RGB565 or RGB555, assume RGB565
CalculateLuminanceValues565(bitmap, data, luminances);
break;
case 3:
CalculateLuminanceValues24Bit(bitmap, data, luminances);
break;
case 4:
CalculateLuminanceValues32BitWithoutAlpha(bitmap, data, luminances);
break;
case 40:
CalculateLuminanceValues32BitWithAlpha(bitmap, data, luminances);
break;
case 41:
CalculateLuminanceValues32BitCMYK(bitmap, data, luminances);
break;
default:
throw new NotSupportedException();
}
}
}
finally
{
bitmap.UnlockBits(data);
}
}
/// <summary>
/// old slow way for unsupported bit depth
/// </summary>
/// <param name="bitmap"></param>
/// <param name="luminances"></param>
protected static void CalculateLuminanceValuesSlow(Bitmap bitmap, byte[] luminances)
{
var height = bitmap.Height;
var width = bitmap.Width;
for (int y = 0; y < height; y++)
{
int offset = y * width;
for (int x = 0; x < width; x++)
{
var c = bitmap.GetPixel(x, y);
luminances[offset + x] = (byte)((RChannelWeight * c.R + GChannelWeight * c.G + BChannelWeight * c.B) >> ChannelWeight);
}
}
}
#if !WindowsCE
/// <summary>
/// calculates the luminance values for 1-bit indexed bitmaps
/// </summary>
/// <param name="bitmap"></param>
/// <param name="data"></param>
/// <param name="luminances"></param>
protected static void CalculateLuminanceValuesForIndexed1Bit(Bitmap bitmap, BitmapData data, byte[] luminances)
{
var height = data.Height;
var width = data.Width;
var stride = Math.Abs(data.Stride);
var pixelWidth = stride / width;
var strideStep = data.Stride;
var buffer = new byte[stride];
var ptrInBitmap = data.Scan0;
if (pixelWidth != 0)
throw new InvalidOperationException("Unsupported pixel format: " + bitmap.PixelFormat);
// prepare palette for 1, 4 and 8 bit indexed bitmaps
var luminancePalette = new byte[256];
var luminancePaletteLength = Math.Min(bitmap.Palette.Entries.Length, luminancePalette.Length);
for (var index = 0; index < luminancePaletteLength; index++)
{
var color = bitmap.Palette.Entries[index];
luminancePalette[index] = (byte)((RChannelWeight * color.R +
GChannelWeight * color.G +
BChannelWeight * color.B) >> ChannelWeight);
}
for (int y = 0; y < height; y++)
{
// copy a scanline not the whole bitmap because of memory usage
Marshal.Copy(ptrInBitmap, buffer, 0, stride);
#if NET40 || NET45 || NET46 || NET47 || NET48
ptrInBitmap = IntPtr.Add(ptrInBitmap, strideStep);
#else
ptrInBitmap = new IntPtr(ptrInBitmap.ToInt64() + strideStep);
#endif
var offset = y * width;
for (int x = 0; x * 8 < width; x++)
{
var x8 = 8 * x;
var offset8 = offset + x8;
for (int subX = 0; subX < 8 && x8 + subX < width; subX++)
{
var index = (buffer[x] >> (7 - subX)) & 1;
luminances[offset8 + subX] = luminancePalette[index];
}
}
}
}
/// <summary>
/// calculates the luminance values for 4-bit indexed bitmaps
/// </summary>
/// <param name="bitmap"></param>
/// <param name="data"></param>
/// <param name="luminances"></param>
protected static void CalculateLuminanceValuesForIndexed4Bit(Bitmap bitmap, BitmapData data, byte[] luminances)
{
if (bitmap.PixelFormat != PixelFormat.Format4bppIndexed)
throw new InvalidOperationException("Unsupported pixel format: " + bitmap.PixelFormat);
var height = data.Height;
var width = data.Width;
var stride = Math.Abs(data.Stride);
var pixelWidth = stride / width;
var strideStep = data.Stride;
var buffer = new byte[stride];
var ptrInBitmap = data.Scan0;
var evenWidth = (width / 2) * 2;
if (pixelWidth != 0)
throw new InvalidOperationException("Unsupported pixel format: " + bitmap.PixelFormat);
// prepare palette for 1, 4 and 8 bit indexed bitmaps
var luminancePalette = new byte[256];
var luminancePaletteLength = Math.Min(bitmap.Palette.Entries.Length, luminancePalette.Length);
for (var index = 0; index < luminancePaletteLength; index++)
{
var color = bitmap.Palette.Entries[index];
luminancePalette[index] = (byte)((RChannelWeight * color.R +
GChannelWeight * color.G +
BChannelWeight * color.B) >> ChannelWeight);
}
for (int y = 0; y < height; y++)
{
// copy a scanline not the whole bitmap because of memory usage
Marshal.Copy(ptrInBitmap, buffer, 0, stride);
#if NET40 || NET45 || NET46 || NET47 || NET48
ptrInBitmap = IntPtr.Add(ptrInBitmap, strideStep);
#else
ptrInBitmap = new IntPtr(ptrInBitmap.ToInt64() + strideStep);
#endif
var offset = y * width;
int sourceX = 0;
int destX = 0;
byte sourceValue = 0;
for (; destX < evenWidth; sourceX++, destX += 2)
{
sourceValue = buffer[sourceX];
var index = sourceValue & 15;
luminances[offset + destX + 1] = luminancePalette[index];
index = (sourceValue >> 4) & 15;
luminances[offset + destX] = luminancePalette[index];
}
if (width > evenWidth)
{
var index = (sourceValue >> 4) & 15;
luminances[offset + destX] = luminancePalette[index];
}
}
}
/// <summary>
/// calculates the luminance values for 8-bit indexed bitmaps
/// </summary>
/// <param name="bitmap"></param>
/// <param name="data"></param>
/// <param name="luminances"></param>
protected static void CalculateLuminanceValuesForIndexed8Bit(Bitmap bitmap, BitmapData data, byte[] luminances)
{
var height = data.Height;
var width = data.Width;
var stride = Math.Abs(data.Stride);
var pixelWidth = stride / width;
var strideStep = data.Stride;
var buffer = new byte[stride];
var ptrInBitmap = data.Scan0;
if (pixelWidth != 1)
throw new InvalidOperationException("Unsupported pixel format: " + bitmap.PixelFormat);
// prepare palette for 1, 4 and 8 bit indexed bitmaps
var luminancePalette = new byte[256];
var luminancePaletteLength = Math.Min(bitmap.Palette.Entries.Length, luminancePalette.Length);
for (var index = 0; index < luminancePaletteLength; index++)
{
var color = bitmap.Palette.Entries[index];
luminancePalette[index] = (byte)((RChannelWeight * color.R +
GChannelWeight * color.G +
BChannelWeight * color.B) >> ChannelWeight);
}
for (int y = 0; y < height; y++)
{
// copy a scanline not the whole bitmap because of memory usage
Marshal.Copy(ptrInBitmap, buffer, 0, stride);
#if NET40 || NET45 || NET46 || NET47 || NET48
ptrInBitmap = IntPtr.Add(ptrInBitmap, strideStep);
#else
ptrInBitmap = new IntPtr(ptrInBitmap.ToInt64() + strideStep);
#endif
var offset = y * width;
for (int x = 0; x < width; x++)
{
luminances[offset + x] = luminancePalette[buffer[x]];
}
}
}
#endif
/// <summary>
/// calculates the luminance values for 565 encoded bitmaps
/// </summary>
/// <param name="bitmap"></param>
/// <param name="data"></param>
/// <param name="luminances"></param>
private static void CalculateLuminanceValues565(Bitmap bitmap, BitmapData data, byte[] luminances)
{
var height = data.Height;
var width = data.Width;
var stride = Math.Abs(data.Stride);
var pixelWidth = stride / width;
var strideStep = data.Stride;
var buffer = new byte[stride];
var ptrInBitmap = data.Scan0;
if (pixelWidth != 2)
#if !WindowsCE
throw new InvalidOperationException("Unsupported pixel format: " + bitmap.PixelFormat);
#else
throw new InvalidOperationException("Unsupported pixel format");
#endif
for (int y = 0; y < height; y++)
{
// copy a scanline not the whole bitmap because of memory usage
Marshal.Copy(ptrInBitmap, buffer, 0, stride);
#if NET40 || NET45 || NET46 || NET47 || NET48
ptrInBitmap = IntPtr.Add(ptrInBitmap, strideStep);
#else
ptrInBitmap = new IntPtr(ptrInBitmap.ToInt64() + strideStep);
#endif
var offset = y * width;
var maxIndex = 2 * width;
for (int index = 0; index < maxIndex; index += 2)
{
var byte1 = buffer[index];
var byte2 = buffer[index + 1];
var b5 = byte1 & 0x1F;
var g5 = (((byte1 & 0xE0) >> 5) | ((byte2 & 0x03) << 3)) & 0x1F;
var r5 = (byte2 >> 2) & 0x1F;
var r8 = (r5 * 527 + 23) >> 6;
var g8 = (g5 * 527 + 23) >> 6;
var b8 = (b5 * 527 + 23) >> 6;
luminances[offset] = (byte)((RChannelWeight * r8 + GChannelWeight * g8 + BChannelWeight * b8) >> ChannelWeight);
offset++;
}
}
}
/// <summary>
/// calculates the luminance values for 24-bit encoded bitmaps
/// </summary>
/// <param name="bitmap"></param>
/// <param name="data"></param>
/// <param name="luminances"></param>
private static void CalculateLuminanceValues24Bit(Bitmap bitmap, BitmapData data, byte[] luminances)
{
var height = data.Height;
var width = data.Width;
var stride = Math.Abs(data.Stride);
var pixelWidth = stride / width;
var strideStep = data.Stride;
var buffer = new byte[stride];
var ptrInBitmap = data.Scan0;
if (pixelWidth != 3)
#if !WindowsCE
throw new InvalidOperationException("Unsupported pixel format: " + bitmap.PixelFormat);
#else
throw new InvalidOperationException("Unsupported pixel format");
#endif
for (int y = 0; y < height; y++)
{
// copy a scanline not the whole bitmap because of memory usage
Marshal.Copy(ptrInBitmap, buffer, 0, stride);
#if NET40 || NET45 || NET46 || NET47 || NET48
ptrInBitmap = IntPtr.Add(ptrInBitmap, strideStep);
#else
ptrInBitmap = new IntPtr(ptrInBitmap.ToInt64() + strideStep);
#endif
var offset = y * width;
var maxIndex = width * 3;
for (int x = 0; x < maxIndex; x += 3)
{
var luminance = (byte)((BChannelWeight * buffer[x] +
GChannelWeight * buffer[x + 1] +
RChannelWeight * buffer[x + 2]) >> ChannelWeight);
luminances[offset] = luminance;
offset++;
}
}
}
/// <summary>
/// calculates the luminance values for 32-bit encoded bitmaps without respecting the alpha channel
/// </summary>
/// <param name="bitmap"></param>
/// <param name="data"></param>
/// <param name="luminances"></param>
private static void CalculateLuminanceValues32BitWithoutAlpha(Bitmap bitmap, BitmapData data, byte[] luminances)
{
var height = data.Height;
var width = data.Width;
var stride = Math.Abs(data.Stride);
var pixelWidth = stride / width;
var strideStep = data.Stride;
var buffer = new byte[stride];
var ptrInBitmap = data.Scan0;
var maxIndex = 4 * width;
if (pixelWidth != 4)
#if !WindowsCE
throw new InvalidOperationException("Unsupported pixel format: " + bitmap.PixelFormat);
#else
throw new InvalidOperationException("Unsupported pixel format");
#endif
for (int y = 0; y < height; y++)
{
// copy a scanline not the whole bitmap because of memory usage
Marshal.Copy(ptrInBitmap, buffer, 0, stride);
#if NET40 || NET45 || NET46 || NET47 || NET48
ptrInBitmap = IntPtr.Add(ptrInBitmap, strideStep);
#else
ptrInBitmap = new IntPtr(ptrInBitmap.ToInt64() + strideStep);
#endif
var offset = y * width;
// 4 bytes without alpha channel value
for (int x = 0; x < maxIndex; x += 4)
{
var luminance = (byte)((BChannelWeight * buffer[x] +
GChannelWeight * buffer[x + 1] +
RChannelWeight * buffer[x + 2]) >> ChannelWeight);
luminances[offset] = luminance;
offset++;
}
}
}
/// calculates the luminance values for 32-bit encoded bitmaps with alpha channel
private static void CalculateLuminanceValues32BitWithAlpha(Bitmap bitmap, BitmapData data, byte[] luminances)
{
var height = data.Height;
var width = data.Width;
var stride = Math.Abs(data.Stride);
var pixelWidth = stride / width;
var strideStep = data.Stride;
var buffer = new byte[stride];
var ptrInBitmap = data.Scan0;
var maxIndex = 4 * width;
if (pixelWidth != 4)
#if !WindowsCE
throw new InvalidOperationException("Unsupported pixel format: " + bitmap.PixelFormat);
#else
throw new InvalidOperationException("Unsupported pixel format");
#endif
for (int y = 0; y < height; y++)
{
// copy a scanline not the whole bitmap because of memory usage
Marshal.Copy(ptrInBitmap, buffer, 0, stride);
#if NET40 || NET45 || NET46 || NET47 || NET48
ptrInBitmap = IntPtr.Add(ptrInBitmap, strideStep);
#else
ptrInBitmap = new IntPtr(ptrInBitmap.ToInt64() + strideStep);
#endif
var offset = y * width;
// with alpha channel; some barcodes are completely black if you
// only look at the r, g and b channel but the alpha channel controls
// the view
for (int x = 0; x < maxIndex; x += 4)
{
var luminance = (byte)((BChannelWeight * buffer[x] +
GChannelWeight * buffer[x + 1] +
RChannelWeight * buffer[x + 2]) >> ChannelWeight);
// calculating the resulting luminance based upon a white background
// var alpha = buffer[x * pixelWidth + 3] / 255.0;
// luminance = (byte)(luminance * alpha + 255 * (1 - alpha));
var alpha = buffer[x + 3];
luminance = (byte)(((luminance * alpha) >> 8) + (255 * (255 - alpha) >> 8) + 1);
luminances[offset] = luminance;
offset++;
}
}
}
/// calculates the luminance values for 32-bit CMYK encoded bitmaps (k is ignored at the momen)
private static void CalculateLuminanceValues32BitCMYK(Bitmap bitmap, BitmapData data, byte[] luminances)
{
var height = data.Height;
var width = data.Width;
var stride = Math.Abs(data.Stride);
var pixelWidth = stride / width;
var strideStep = data.Stride;
var buffer = new byte[stride];
var ptrInBitmap = data.Scan0;
var maxIndex = 4 * width;
if (pixelWidth != 4)
#if !WindowsCE
throw new InvalidOperationException("Unsupported pixel format: " + bitmap.PixelFormat);
#else
throw new InvalidOperationException("Unsupported pixel format");
#endif
for (int y = 0; y < height; y++)
{
// copy a scanline not the whole bitmap because of memory usage
Marshal.Copy(ptrInBitmap, buffer, 0, stride);
#if NET40 || NET45 || NET46 || NET47 || NET48
ptrInBitmap = IntPtr.Add(ptrInBitmap, strideStep);
#else
ptrInBitmap = new IntPtr(ptrInBitmap.ToInt64() + strideStep);
#endif
var offset = y * width;
for (int x = 0; x < maxIndex; x += 4)
{
var luminance = (byte)(255 - ((BChannelWeight * buffer[x] +
GChannelWeight * buffer[x + 1] +
RChannelWeight * buffer[x + 2]) >> ChannelWeight));
// Ignore value of k at the moment
luminances[offset] = luminance;
offset++;
}
}
}
/// <summary>
/// Should create a new luminance source with the right class type.
/// The method is used in methods crop and rotate.
/// </summary>
/// <param name="newLuminances">The new luminances.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns></returns>
protected override LuminanceSource CreateLuminanceSource(byte[] newLuminances, int width, int height)
{
return new BitmapLuminanceSource(width, height) { luminances = newLuminances };
}
}
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Windows;
#if NETFX_CORE
using Windows.UI.Xaml.Media.Imaging;
#else
using System.Windows.Media;
using System.Windows.Media.Imaging;
#endif
namespace ZXing
{
/// <summary>
/// class which represents the luminance source values for bitmap source objects
/// </summary>
public partial class BitmapSourceLuminanceSource : BaseLuminanceSource
{
/// <summary>
/// Initializes a new instance of the <see cref="BitmapSourceLuminanceSource"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected BitmapSourceLuminanceSource(int width, int height)
: base(width, height)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BitmapSourceLuminanceSource"/> class.
/// </summary>
/// <param name="bitmap">The bitmap.</param>
public BitmapSourceLuminanceSource(BitmapSource bitmap)
: base(bitmap.PixelWidth, bitmap.PixelHeight)
{
switch (bitmap.Format.ToString())
{
case "Bgr24":
case "Bgr32":
CalculateLuminanceBGR(bitmap);
break;
case "Bgra32":
CalculateLuminanceBGRA(bitmap);
break;
case "Rgb24":
CalculateLuminanceRGB(bitmap);
break;
case "Bgr565":
CalculateLuminanceBGR565(bitmap);
break;
default:
// there is no special conversion routine to luminance values
// we have to convert the image to a supported format
bitmap = new FormatConvertedBitmap(bitmap, PixelFormats.Bgra32, null, 0);
CalculateLuminanceBGR(bitmap);
break;
}
}
private void CalculateLuminanceRGB(BitmapSource bitmap)
{
var width = bitmap.PixelWidth;
var height = bitmap.PixelHeight;
var stepX = (bitmap.Format.BitsPerPixel + 7) / 8;
var bufferSize = width * stepX;
var buffer = new byte[bufferSize];
var rect = new Int32Rect(0, 0, width, 1);
var luminanceIndex = 0;
for (var curY = 0; curY < height; curY++)
{
bitmap.CopyPixels(rect, buffer, bufferSize, 0);
for (var curX = 0; curX < bufferSize; curX += stepX)
{
var r = buffer[curX];
var g = buffer[curX + 1];
var b = buffer[curX + 2];
luminances[luminanceIndex] = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
luminanceIndex++;
}
rect.Y++;
}
}
private void CalculateLuminanceBGR(BitmapSource bitmap)
{
var width = bitmap.PixelWidth;
var height = bitmap.PixelHeight;
var stepX = (bitmap.Format.BitsPerPixel + 7) / 8;
var bufferSize = width * stepX;
var buffer = new byte[bufferSize];
var rect = new Int32Rect(0, 0, width, 1);
var luminanceIndex = 0;
for (var curY = 0; curY < height; curY++)
{
bitmap.CopyPixels(rect, buffer, bufferSize, 0);
for (var curX = 0; curX < bufferSize; curX += stepX)
{
var b = buffer[curX];
var g = buffer[curX + 1];
var r = buffer[curX + 2];
luminances[luminanceIndex] = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
luminanceIndex++;
}
rect.Y++;
}
}
private void CalculateLuminanceBGRA(BitmapSource bitmap)
{
var width = bitmap.PixelWidth;
var height = bitmap.PixelHeight;
var stepX = (bitmap.Format.BitsPerPixel + 7) / 8;
var bufferSize = width * stepX;
var buffer = new byte[bufferSize];
var rect = new Int32Rect(0, 0, width, 1);
var luminanceIndex = 0;
for (var curY = 0; curY < height; curY++)
{
bitmap.CopyPixels(rect, buffer, bufferSize, 0);
for (var curX = 0; curX < bufferSize; curX += stepX)
{
var b = buffer[curX];
var g = buffer[curX + 1];
var r = buffer[curX + 2];
var luminance = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
var alpha = buffer[curX + 3];
luminance = (byte)(((luminance * alpha) >> 8) + (255 * (255 - alpha) >> 8));
luminances[luminanceIndex] = luminance;
luminanceIndex++;
}
rect.Y++;
}
}
private void CalculateLuminanceBGR565(BitmapSource bitmap)
{
var width = bitmap.PixelWidth;
var height = bitmap.PixelHeight;
var stepX = (bitmap.Format.BitsPerPixel + 7) / 8;
var bufferSize = width * stepX;
var buffer = new byte[bufferSize];
var rect = new Int32Rect(0, 0, width, 1);
var luminanceIndex = 0;
for (var curY = 0; curY < height; curY++)
{
bitmap.CopyPixels(rect, buffer, bufferSize, 0);
for (var curX = 0; curX < bufferSize; curX += stepX)
{
var byte1 = buffer[curX];
var byte2 = buffer[curX + 1];
var b5 = byte1 & 0x1F;
var g5 = (((byte1 & 0xE0) >> 5) | ((byte2 & 0x03) << 3)) & 0x1F;
var r5 = (byte2 >> 2) & 0x1F;
var r8 = (r5 * 527 + 23) >> 6;
var g8 = (g5 * 527 + 23) >> 6;
var b8 = (b5 * 527 + 23) >> 6;
// cheap, not fully accurate conversion
//var pixel = (byte2 << 8) | byte1;
//b8 = (((pixel) & 0x001F) << 3);
//g8 = (((pixel) & 0x07E0) >> 2) & 0xFF;
//r8 = (((pixel) & 0xF800) >> 8);
luminances[luminanceIndex] = (byte)((RChannelWeight * r8 + GChannelWeight * g8 + BChannelWeight * b8) >> ChannelWeight);
luminanceIndex++;
}
rect.Y++;
}
}
/// <summary>
/// Should create a new luminance source with the right class type.
/// The method is used in methods crop and rotate.
/// </summary>
/// <param name="newLuminances">The new luminances.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns></returns>
protected override LuminanceSource CreateLuminanceSource(byte[] newLuminances, int width, int height)
{
return new BitmapSourceLuminanceSource(width, height) { luminances = newLuminances };
}
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
namespace ZXing
{
/// <summary>
/// Encapsulates a type of hint that a caller may pass to a barcode reader to help it
/// more quickly or accurately decode it. It is up to implementations to decide what,
/// if anything, to do with the information that is supplied.
/// <seealso cref="Reader.decode(BinaryBitmap, IDictionary{DecodeHintType, object})" />
/// </summary>
/// <author>Sean Owen</author>
/// <author>dswitkin@google.com (Daniel Switkin)</author>
public enum DecodeHintType
{
/// <summary>
/// Unspecified, application-specific hint. Maps to an unspecified <see cref="System.Object" />.
/// </summary>
OTHER,
/// <summary>
/// Image is a pure monochrome image of a barcode. Doesn't matter what it maps to;
/// use <see cref="bool" /> = true.
/// </summary>
PURE_BARCODE,
/// <summary>
/// Image is known to be of one of a few possible formats.
/// Maps to a <see cref="System.Collections.ICollection" /> of <see cref="BarcodeFormat" />s.
/// </summary>
POSSIBLE_FORMATS,
/// <summary>
/// Spend more time to try to find a barcode; optimize for accuracy, not speed.
/// Doesn't matter what it maps to; use <see cref="bool" /> = true.
/// </summary>
TRY_HARDER,
/// <summary>
/// Specifies what character encoding to use when decoding, where applicable (type String)
/// </summary>
CHARACTER_SET,
/// <summary>
/// Allowed lengths of encoded data -- reject anything else. Maps to an int[].
/// </summary>
ALLOWED_LENGTHS,
/// <summary>
/// Assume Code 39 codes employ a check digit. Maps to <see cref="bool" />.
/// </summary>
ASSUME_CODE_39_CHECK_DIGIT,
/// <summary>
/// The caller needs to be notified via callback when a possible <see cref="ResultPoint" />
/// is found. Maps to a <see cref="ResultPointCallback" />.
/// </summary>
NEED_RESULT_POINT_CALLBACK,
/// <summary>
/// Assume MSI codes employ a check digit. Maps to <see cref="bool" />.
/// </summary>
ASSUME_MSI_CHECK_DIGIT,
/// <summary>
/// if Code39 could be detected try to use extended mode for full ASCII character set
/// Maps to <see cref="bool" />.
/// </summary>
USE_CODE_39_EXTENDED_MODE,
/// <summary>
/// Don't fail if a Code39 is detected but can't be decoded in extended mode.
/// Return the raw Code39 result instead. Maps to <see cref="bool" />.
/// </summary>
RELAXED_CODE_39_EXTENDED_MODE,
/// <summary>
/// 1D readers supporting rotation with TRY_HARDER enabled.
/// But BarcodeReader class can do auto-rotating for 1D and 2D codes.
/// Enabling that option prevents 1D readers doing double rotation.
/// BarcodeReader enables that option automatically if "global" auto-rotation is enabled.
/// Maps to <see cref="bool" />.
/// </summary>
TRY_HARDER_WITHOUT_ROTATION,
/// <summary>
/// Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed.
/// For example this affects FNC1 handling for Code 128 (aka GS1-128). Doesn't matter what it maps to;
/// use <see cref="bool" />.
/// </summary>
ASSUME_GS1,
/// <summary>
/// If true, return the start and end digits in a Codabar barcode instead of stripping them. They
/// are alpha, whereas the rest are numeric. By default, they are stripped, but this causes them
/// to not be. Doesn't matter what it maps to; use <see cref="bool" />.
/// </summary>
RETURN_CODABAR_START_END,
/// <summary>
/// Allowed extension lengths for EAN or UPC barcodes. Other formats will ignore this.
/// Maps to an int[] of the allowed extension lengths, for example [2], [5], or [2, 5].
/// If it is optional to have an extension, do not set this hint. If this is set,
/// and a UPC or EAN barcode is found but an extension is not, then no result will be returned
/// at all.
/// </summary>
ALLOWED_EAN_EXTENSIONS
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2012 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing
{
/// <summary>
/// Simply encapsulates a width and height.
/// </summary>
public sealed class Dimension
{
private readonly int width;
private readonly int height;
/// <summary>
/// initializing constructor
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
public Dimension(int width, int height)
{
if (width < 0 || height < 0)
{
throw new ArgumentException();
}
this.width = width;
this.height = height;
}
/// <summary>
/// the width
/// </summary>
public int Width
{
get { return width; }
}
/// <summary>
/// the height
/// </summary>
public int Height
{
get { return height; }
}
/// <summary>
///
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public override bool Equals(Object other)
{
if (other is Dimension)
{
var d = (Dimension)other;
return width == d.width && height == d.height;
}
return false;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return width * 32713 + height;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override String ToString()
{
return width + "x" + height;
}
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace ZXing
{
/// <summary>
/// These are a set of hints that you may pass to Writers to specify their behavior.
/// </summary>
/// <author>dswitkin@google.com (Daniel Switkin)</author>
public enum EncodeHintType
{
/// <summary>
/// Specifies the width of the barcode image
/// type: <see cref="System.Int32" />
/// </summary>
WIDTH,
/// <summary>
/// Specifies the height of the barcode image
/// type: <see cref="System.Int32" />
/// </summary>
HEIGHT,
/// <summary>
/// Don't put the content string into the output image.
/// type: <see cref="System.Boolean" />
/// </summary>
PURE_BARCODE,
/// <summary>
/// Specifies what degree of error correction to use, for example in QR Codes.
/// Type depends on the encoder. For example for QR codes it's type
/// <see cref="ZXing.QrCode.Internal.ErrorCorrectionLevel" />
/// For Aztec it is of type <see cref="System.Int32" />, representing the minimal percentage of error correction words.
/// In all cases, it can also be a <see cref="System.String" /> representation of the desired value as well.
/// Note: an Aztec symbol should have a minimum of 25% EC words.
/// For PDF417 it is of type <see cref="ZXing.PDF417.Internal.PDF417ErrorCorrectionLevel"/> or <see cref="System.Int32" /> (between 0 and 8),
/// </summary>
ERROR_CORRECTION,
/// <summary>
/// Specifies what character encoding to use where applicable.
/// type: <see cref="System.String" />
/// </summary>
CHARACTER_SET,
/// <summary>
/// Specifies margin, in pixels, to use when generating the barcode. The meaning can vary
/// by format; for example it controls margin before and after the barcode horizontally for
/// most 1D formats.
/// type: <see cref="System.Int32" />, or <see cref="System.String" /> representation of the integer value
/// </summary>
MARGIN,
/// <summary>
/// Specifies the aspect ratio to use. Default is 4.
/// type: <see cref="ZXing.PDF417.Internal.PDF417AspectRatio" />, or 1-4.
/// </summary>
PDF417_ASPECT_RATIO,
/// <summary>
/// Specifies the desired aspect ratio (number of columns / number of rows) of the output image. Default is 3.
/// type: <see cref="System.Single" />.
/// </summary>
PDF417_IMAGE_ASPECT_RATIO,
/// <summary>
/// Specifies whether to use compact mode for PDF417
/// type: <see cref="System.Boolean" />, or "true" or "false"
/// <see cref="System.String" /> value
/// </summary>
PDF417_COMPACT,
/// <summary>
/// Specifies what compaction mode to use for PDF417.
/// type: <see cref="ZXing.PDF417.Internal.Compaction" /> or <see cref="System.String" /> value of one of its
/// enum values
/// </summary>
PDF417_COMPACTION,
/// <summary>
/// Specifies the minimum and maximum number of rows and columns for PDF417.
/// type: <see cref="ZXing.PDF417.Internal.Dimensions" />
/// </summary>
PDF417_DIMENSIONS,
/// <summary>
/// The Specifies that the PDF417 will contain macro metadata.
/// type: <see cref="ZXing.PDF417.PDF417MacroMetadata"/>
/// </summary>
PDF417_MACRO_META_DATA,
/// <summary>
/// Don't append ECI segment.
/// That is against the specification of QR Code but some
/// readers have problems if the charset is switched from
/// ISO-8859-1 (default) to UTF-8 with the necessary ECI segment.
/// If you set the property to true you can use UTF-8 encoding
/// and the ECI segment is omitted.
/// type: <see cref="System.Boolean" />
/// </summary>
DISABLE_ECI,
/// <summary>
/// Specifies the matrix shape for Data Matrix (type <see cref="ZXing.Datamatrix.Encoder.SymbolShapeHint"/>)
/// </summary>
DATA_MATRIX_SHAPE,
/// <summary>
/// Specifies a minimum barcode size (type <see cref="ZXing.Dimension"/>). Only applicable to Data Matrix now.
/// </summary>
MIN_SIZE,
/// <summary>
/// Specifies a maximum barcode size (type <see cref="ZXing.Dimension"/>). Only applicable to Data Matrix now.
/// </summary>
MAX_SIZE,
/// <summary>
/// if true, don't switch to codeset C for numbers
/// </summary>
CODE128_FORCE_CODESET_B,
/// <summary>
/// Specifies the default encodation for Data Matrix (type <see cref="ZXing.Datamatrix.Encoder.Encodation"/>)
/// Make sure that the content fits into the encodation value, otherwise there will be an exception thrown.
/// standard value: Encodation.ASCII
/// </summary>
DATA_MATRIX_DEFAULT_ENCODATION,
/// <summary>
/// Specifies the required number of layers for an Aztec code.
/// A negative number (-1, -2, -3, -4) specifies a compact Aztec code
/// 0 indicates to use the minimum number of layers (the default)
/// A positive number (1, 2, .. 32) specifies a normal (non-compact) Aztec code
/// type: <see cref="System.Int32" />, or <see cref="System.String" /> representation of the integer value
/// </summary>
AZTEC_LAYERS,
/// <summary>
/// Specifies the exact version of QR code to be encoded.
/// (Type <see cref="System.Int32" />, or <see cref="System.String" /> representation of the integer value).
/// </summary>
QR_VERSION,
/// <summary>
/// Specifies whether the data should be encoded to the GS1 standard
/// type: <see cref="System.Boolean" />, or "true" or "false"
/// <see cref="System.String" /> value
/// </summary>
GS1_FORMAT,
/// <summary>
/// Specifies the QR code mask pattern to be used. Allowed values are
/// 0..QRCode.NUM_MASK_PATTERNS-1. By default the code will automatically select
/// the optimal mask pattern.
/// (Type <see cref="System.Int32" />, or <see cref="System.String" /> representation of the integer value).
/// </summary>
QR_MASK_PATTERN,
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing
{
/// <summary>
/// Thrown when a barcode was successfully detected, but some aspect of
/// the content did not conform to the barcode's format rules. This could have
/// been due to a mis-detection.
/// <author>Sean Owen</author>
/// </summary>
[Serializable]
public sealed class FormatException : ReaderException
{
/// <summary>
///
/// </summary>
public FormatException()
{
}
/// <summary>
///
/// </summary>
public FormatException(String message)
: base(message)
{
}
/// <summary>
///
/// </summary>
public FormatException(Exception innerException)
: base(innerException)
{
}
/// <summary>
///
/// </summary>
public FormatException(String message, Exception innerException)
: base(message, innerException)
{
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing
{
/// <summary>
/// Interface for a smart class to decode the barcode inside a bitmap object
/// </summary>
public partial interface IBarcodeReader
{
/// <summary>
/// Decodes the specified barcode bitmap.
/// </summary>
/// <param name="barcodeBitmap">The barcode bitmap.</param>
/// <returns>the result data or null</returns>
Result Decode(System.Drawing.Bitmap barcodeBitmap);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace ZXing
{
/// <summary>
/// Interface for a smart class to decode multiple barcodes inside a bitmap object
/// </summary>
public partial interface IBarcodeReader
{
/// <summary>
/// Decodes the specified barcode bitmap which is given by a generic byte array with the order RGB24.
/// </summary>
/// <param name="rawRGB">The image as RGB24 array.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="format">The format.</param>
/// <returns>
/// the result data or null
/// </returns>
Result[] DecodeMultiple(byte[] rawRGB, int width, int height, RGBLuminanceSource.BitmapFormat format);
/// <summary>
/// Tries to decode barcodes within an image which is given by a luminance source.
/// That method gives a chance to prepare a luminance source completely before calling
/// the time consuming decoding method. On the other hand there is a chance to create
/// a luminance source which is independent from external resources (like Bitmap objects)
/// and the decoding call can be made in a background thread.
/// </summary>
/// <param name="luminanceSource">The luminance source.</param>
/// <returns></returns>
Result[] DecodeMultiple(LuminanceSource luminanceSource);
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using ZXing.Common;
namespace ZXing
{
/// <summary>
/// Interface for a smart class to decode the barcode inside a bitmap object
/// </summary>
public partial interface IBarcodeReader
{
/// <summary>
/// event is executed when a result point was found
/// </summary>
event Action<ResultPoint> ResultPointFound;
/// <summary>
/// event is executed when a result was found via decode
/// </summary>
event Action<Result> ResultFound;
/// <summary>
/// Specifies some options which influence the decoding process
/// </summary>
DecodingOptions Options { get; set; }
/// <summary>
/// Decodes the specified barcode bitmap which is given by a generic byte array with the order RGB24.
/// </summary>
/// <param name="rawRGB">The image as RGB24 array.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="format">The format.</param>
/// <returns>
/// the result data or null
/// </returns>
Result Decode(byte[] rawRGB, int width, int height, RGBLuminanceSource.BitmapFormat format);
/// <summary>
/// Tries to decode a barcode within an image which is given by a luminance source.
/// That method gives a chance to prepare a luminance source completely before calling
/// the time consuming decoding method. On the other hand there is a chance to create
/// a luminance source which is independent from external resources (like Bitmap objects)
/// and the decoding call can be made in a background thread.
/// </summary>
/// <param name="luminanceSource">The luminance source.</param>
/// <returns></returns>
Result Decode(LuminanceSource luminanceSource);
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace ZXing
{
/// <summary>
/// Interface for a smart class to decode multiple barcodes inside a bitmap object
/// </summary>
public partial interface IBarcodeReader<T>
{
/// <summary>
/// Decodes the specified barcode bitmap.
/// </summary>
/// <param name="barcodeBitmap">The barcode bitmap.</param>
/// <returns>the result data or null</returns>
Result[] DecodeMultiple(T barcodeBitmap);
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace ZXing
{
/// <summary>
/// Interface for a smart class to decode the barcode inside a bitmap object
/// </summary>
public partial interface IBarcodeReader<T>
{
/// <summary>
/// Decodes the specified barcode bitmap.
/// </summary>
/// <param name="barcodeBitmap">The barcode bitmap.</param>
/// <returns>the result data or null</returns>
Result Decode(T barcodeBitmap);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace ZXing
{
/// <summary>
/// Interface for a smart class to decode multiple barcodes inside a bitmap object
/// </summary>
public partial interface IBarcodeReaderGeneric
{
/// <summary>
/// Decodes the specified barcode bitmap which is given by a generic byte array with the order RGB24.
/// </summary>
/// <param name="rawRGB">The barcode bitmap.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="format">The format.</param>
/// <returns>
/// the result data or null
/// </returns>
Result[] DecodeMultiple(byte[] rawRGB, int width, int height, RGBLuminanceSource.BitmapFormat format);
/// <summary>
/// Tries to decode barcodes within an image which is given by a luminance source.
/// That method gives a chance to prepare a luminance source completely before calling
/// the time consuming decoding method. On the other hand there is a chance to create
/// a luminance source which is independent from external resources (like Bitmap objects)
/// and the decoding call can be made in a background thread.
/// </summary>
/// <param name="luminanceSource">The luminance source.</param>
/// <returns></returns>
Result[] DecodeMultiple(LuminanceSource luminanceSource);
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using ZXing.Common;
namespace ZXing
{
/// <summary>
/// Interface for a smart class to decode the barcode inside a bitmap object
/// </summary>
public partial interface IBarcodeReaderGeneric
{
/// <summary>
/// event is executed when a result point was found
/// </summary>
event Action<ResultPoint> ResultPointFound;
/// <summary>
/// event is executed when a result was found via decode
/// </summary>
event Action<Result> ResultFound;
/// <summary>
/// Specifies some options which influence the decoding process
/// </summary>
DecodingOptions Options { get; set; }
/// <summary>
/// Decodes the specified barcode bitmap which is given by a generic byte array.
/// </summary>
/// <param name="rawRGB">The barcode bitmap.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="format">The format.</param>
/// <returns>
/// the result data or null
/// </returns>
Result Decode(byte[] rawRGB, int width, int height, RGBLuminanceSource.BitmapFormat format);
/// <summary>
/// Tries to decode a barcode within an image which is given by a luminance source.
/// That method gives a chance to prepare a luminance source completely before calling
/// the time consuming decoding method. On the other hand there is a chance to create
/// a luminance source which is independent from external resources (like Bitmap objects)
/// and the decoding call can be made in a background thread.
/// </summary>
/// <param name="luminanceSource">The luminance source.</param>
/// <returns></returns>
Result Decode(LuminanceSource luminanceSource);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using ZXing.Common;
namespace ZXing
{
/// <summary>
/// Interface for a smart class to encode some content into a barcode
/// </summary>
public partial interface IBarcodeWriter
{
/// <summary>
/// Creates a visual representation of the contents
/// </summary>
System.Drawing.Bitmap Write(string contents);
/// <summary>
/// Returns a rendered instance of the barcode which is given by a BitMatrix.
/// </summary>
System.Drawing.Bitmap Write(BitMatrix matrix);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using ZXing.Common;
using ZXing.Rendering;
namespace ZXing
{
/// <summary>
/// Interface for a smart class to encode some content into a barcode
/// </summary>
public partial interface IBarcodeWriterPixelData
{
/// <summary>
/// Creates a visual representation of the contents
/// </summary>
PixelData Write(string contents);
/// <summary>
/// Returns a rendered instance of the barcode which is given by a BitMatrix.
/// </summary>
PixelData Write(BitMatrix matrix);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using ZXing.Common;
using ZXing.Rendering;
namespace ZXing
{
/// <summary>
/// Interface for a smart class to encode some content into a barcode
/// </summary>
public partial interface IBarcodeWriterSvg
{
/// <summary>
/// Creates a visual representation of the contents
/// </summary>
SvgRenderer.SvgImage Write(string contents);
/// <summary>
/// Returns a rendered instance of the barcode which is given by a BitMatrix.
/// </summary>
SvgRenderer.SvgImage Write(BitMatrix matrix);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using ZXing.Common;
namespace ZXing
{
/// <summary>
/// Interface for a smart class to encode some content into a barcode
/// </summary>
public partial interface IBarcodeWriter
{
/// <summary>
/// Get or sets the barcode format which should be generated
/// (only suitable if MultiFormatWriter is used for property Encoder which is the default)
/// </summary>
BarcodeFormat Format { get; set; }
/// <summary>
/// Gets or sets the options container for the encoding and renderer process.
/// </summary>
EncodingOptions Options { get; set; }
/// <summary>
/// Gets or sets the writer which encodes the content to a BitMatrix.
/// If no value is set the MultiFormatWriter is used.
/// </summary>
Writer Encoder { get; set; }
/// <summary>
/// Encodes the specified contents.
/// </summary>
/// <param name="contents">The contents.</param>
/// <returns></returns>
BitMatrix Encode(string contents);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using ZXing.Common;
namespace ZXing
{
#if !(WINDOWS_PHONE || WindowsCE)
/// <summary>
/// Interface for a smart class to encode some content into a barcode
/// </summary>
public partial interface IBarcodeWriter<out TOutput>
#else
/// <summary>
/// Interface for a smart class to encode some content into a barcode
/// </summary>
public interface IBarcodeWriter<TOutput>
#endif
{
/// <summary>
/// Creates a visual representation of the contents
/// </summary>
/// <param name="contents">The contents.</param>
/// <returns></returns>
TOutput Write(string contents);
/// <summary>
/// Returns a rendered instance of the barcode which is given by a BitMatrix.
/// </summary>
/// <param name="matrix">The matrix.</param>
/// <returns></returns>
TOutput Write(BitMatrix matrix);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using ZXing.Common;
namespace ZXing
{
/// <summary>
/// Interface for a smart class to encode some content into a barcode
/// </summary>
public interface IBarcodeWriterGeneric
{
/// <summary>
/// Get or sets the barcode format which should be generated
/// (only suitable if MultiFormatWriter is used for property Encoder which is the default)
/// </summary>
BarcodeFormat Format { get; set; }
/// <summary>
/// Gets or sets the options container for the encoding and renderer process.
/// </summary>
EncodingOptions Options { get; set; }
/// <summary>
/// Gets or sets the writer which encodes the content to a BitMatrix.
/// If no value is set the MultiFormatWriter is used.
/// </summary>
Writer Encoder { get; set; }
/// <summary>
/// Encodes the specified contents.
/// </summary>
/// <param name="contents">The contents.</param>
/// <returns></returns>
BitMatrix Encode(string contents);
}
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright 2013 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace ZXing
{
/// <summary>
/// A wrapper implementation of {@link LuminanceSource} which inverts the luminances it returns -- black becomes
/// white and vice versa, and each value becomes (255-value).
/// </summary>
/// <author>Sean Owen</author>
public sealed class InvertedLuminanceSource : LuminanceSource
{
private readonly LuminanceSource @delegate;
private byte[] invertedMatrix;
/// <summary>
/// Initializes a new instance of the <see cref="InvertedLuminanceSource"/> class.
/// </summary>
/// <param name="delegate">The @delegate.</param>
public InvertedLuminanceSource(LuminanceSource @delegate)
: base(@delegate.Width, @delegate.Height)
{
this.@delegate = @delegate;
}
/// <summary>
/// Fetches one row of luminance data from the underlying platform's bitmap. Values range from
/// 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
/// to bitwise and with 0xff for each value. It is preferable for implementations of this method
/// to only fetch this row rather than the whole image, since no 2D Readers may be installed and
/// getMatrix() may never be called.
/// </summary>
/// <param name="y">The row to fetch, 0 &lt;= y &lt; Height.</param>
/// <param name="row">An optional preallocated array. If null or too small, it will be ignored.
/// Always use the returned object, and ignore the .length of the array.</param>
/// <returns>
/// An array containing the luminance data.
/// </returns>
override public byte[] getRow(int y, byte[] row)
{
row = @delegate.getRow(y, row);
int width = Width;
for (int i = 0; i < width; i++)
{
row[i] = (byte)(255 - (row[i] & 0xFF));
}
return row;
}
/// <summary>
/// Fetches luminance data for the underlying bitmap. Values should be fetched using:
/// int luminance = array[y * width + x] &amp; 0xff;
/// </summary>
/// <returns> A row-major 2D array of luminance values. Do not use result.length as it may be
/// larger than width * height bytes on some platforms. Do not modify the contents
/// of the result.
/// </returns>
override public byte[] Matrix
{
get
{
if (invertedMatrix == null)
{
byte[] matrix = @delegate.Matrix;
int length = Width * Height;
invertedMatrix = new byte[length];
for (int i = 0; i < length; i++)
{
invertedMatrix[i] = (byte)(255 - (matrix[i] & 0xFF));
}
}
return invertedMatrix;
}
}
/// <summary>
/// </summary>
/// <returns> Whether this subclass supports cropping.</returns>
override public bool CropSupported
{
get { return @delegate.CropSupported; }
}
/// <summary>
/// Returns a new object with cropped image data. Implementations may keep a reference to the
/// original data rather than a copy. Only callable if CropSupported is true.
/// </summary>
/// <param name="left">The left coordinate, 0 &lt;= left &lt; Width.</param>
/// <param name="top">The top coordinate, 0 &lt;= top &lt;= Height.</param>
/// <param name="width">The width of the rectangle to crop.</param>
/// <param name="height">The height of the rectangle to crop.</param>
/// <returns>
/// A cropped version of this object.
/// </returns>
override public LuminanceSource crop(int left, int top, int width, int height)
{
return new InvertedLuminanceSource(@delegate.crop(left, top, width, height));
}
/// <summary>
/// </summary>
/// <returns> Whether this subclass supports counter-clockwise rotation.</returns>
override public bool RotateSupported
{
get { return @delegate.RotateSupported; }
}
/// <summary>
/// Inverts this instance.
/// </summary>
/// <returns>original delegate {@link LuminanceSource} since invert undoes itself</returns>
override public LuminanceSource invert()
{
return @delegate;
}
/// <summary>
/// Returns a new object with rotated image data by 90 degrees counterclockwise.
/// Only callable if {@link #isRotateSupported()} is true.
/// </summary>
/// <returns>
/// A rotated version of this object.
/// </returns>
override public LuminanceSource rotateCounterClockwise()
{
return new InvertedLuminanceSource(@delegate.rotateCounterClockwise());
}
/// <summary>
/// Returns a new object with rotated image data by 45 degrees counterclockwise.
/// Only callable if {@link #isRotateSupported()} is true.
/// </summary>
/// <returns>
/// A rotated version of this object.
/// </returns>
override public LuminanceSource rotateCounterClockwise45()
{
return new InvertedLuminanceSource(@delegate.rotateCounterClockwise45());
}
}
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Text;
namespace ZXing
{
/// <summary>
/// The purpose of this class hierarchy is to abstract different bitmap implementations across
/// platforms into a standard interface for requesting greyscale luminance values. The interface
/// only provides immutable methods; therefore crop and rotation create copies. This is to ensure
/// that one Reader does not modify the original luminance source and leave it in an unknown state
/// for other Readers in the chain.
/// </summary>
/// <author>dswitkin@google.com (Daniel Switkin)</author>
public abstract class LuminanceSource
{
private int width;
private int height;
/// <summary>
/// initializing constructor
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
protected LuminanceSource(int width, int height)
{
this.width = width;
this.height = height;
}
/// <summary>
/// Fetches one row of luminance data from the underlying platform's bitmap. Values range from
/// 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
/// to bitwise and with 0xff for each value. It is preferable for implementations of this method
/// to only fetch this row rather than the whole image, since no 2D Readers may be installed and
/// getMatrix() may never be called.
/// </summary>
/// <param name="y">The row to fetch, which must be in [0, bitmap height)</param>
/// <param name="row">An optional preallocated array. If null or too small, it will be ignored.
/// Always use the returned object, and ignore the .length of the array.
/// </param>
/// <returns> An array containing the luminance data.</returns>
public abstract byte[] getRow(int y, byte[] row);
/// <summary>
/// Fetches luminance data for the underlying bitmap. Values should be fetched using:
/// <code>int luminance = array[y * width + x] &amp; 0xff</code>
/// </summary>
/// <returns>
/// A row-major 2D array of luminance values. Do not use result.length as it may be
/// larger than width * height bytes on some platforms. Do not modify the contents
/// of the result.
/// </returns>
public abstract byte[] Matrix { get; }
/// <returns> The width of the bitmap.</returns>
public virtual int Width
{
get
{
return width;
}
protected set
{
width = value;
}
}
/// <returns> The height of the bitmap.</returns>
public virtual int Height
{
get
{
return height;
}
protected set
{
height = value;
}
}
/// <returns> Whether this subclass supports cropping.</returns>
public virtual bool CropSupported
{
get
{
return false;
}
}
/// <summary>
/// Returns a new object with cropped image data. Implementations may keep a reference to the
/// original data rather than a copy. Only callable if CropSupported is true.
/// </summary>
/// <param name="left">The left coordinate, which must be in [0, Width)</param>
/// <param name="top">The top coordinate, which must be in [0, Height)</param>
/// <param name="width">The width of the rectangle to crop.</param>
/// <param name="height">The height of the rectangle to crop.</param>
/// <returns> A cropped version of this object.</returns>
public virtual LuminanceSource crop(int left, int top, int width, int height)
{
throw new NotSupportedException("This luminance source does not support cropping.");
}
/// <returns> Whether this subclass supports counter-clockwise rotation.</returns>
public virtual bool RotateSupported
{
get
{
return false;
}
}
/// <summary>
/// Returns a new object with rotated image data by 90 degrees counterclockwise.
/// Only callable if <see cref="RotateSupported"/> is true.
/// </summary>
/// <returns>A rotated version of this object.</returns>
public virtual LuminanceSource rotateCounterClockwise()
{
throw new NotSupportedException("This luminance source does not support rotation.");
}
/// <summary>
/// Returns a new object with rotated image data by 45 degrees counterclockwise.
/// Only callable if <see cref="RotateSupported"/> is true.
/// </summary>
/// <returns>A rotated version of this object.</returns>
public virtual LuminanceSource rotateCounterClockwise45()
{
throw new NotSupportedException("This luminance source does not support rotation by 45 degrees.");
}
/// <summary>
/// </summary>
/// <returns>Whether this subclass supports invertion.</returns>
public virtual bool InversionSupported
{
get
{
return false;
}
}
/// <summary>
/// inverts the luminance values, not supported here. has to implemented in sub classes
/// </summary>
/// <returns></returns>
public virtual LuminanceSource invert()
{
throw new NotSupportedException("This luminance source does not support inversion.");
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override String ToString()
{
var row = new byte[width];
var result = new StringBuilder(height * (width + 1));
for (int y = 0; y < height; y++)
{
row = getRow(y, row);
for (int x = 0; x < width; x++)
{
int luminance = row[x] & 0xFF;
char c;
if (luminance < 0x40)
{
c = '#';
}
else if (luminance < 0x80)
{
c = '+';
}
else if (luminance < 0xC0)
{
c = '.';
}
else
{
c = ' ';
}
result.Append(c);
}
result.Append('\n');
}
return result.ToString();
}
}
}

View File

@@ -0,0 +1,229 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Collections.Generic;
using ZXing.Aztec;
using ZXing.Datamatrix;
using ZXing.IMB;
using ZXing.Maxicode;
using ZXing.OneD;
using ZXing.PDF417;
using ZXing.QrCode;
namespace ZXing
{
/// <summary>
/// MultiFormatReader is a convenience class and the main entry point into the library for most uses.
/// By default it attempts to decode all barcode formats that the library supports. Optionally, you
/// can provide a hints object to request different behavior, for example only decoding QR codes.
/// </summary>
/// <author>Sean Owen</author>
/// <author>dswitkin@google.com (Daniel Switkin)</author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source</author>
public sealed class MultiFormatReader : Reader
{
private IDictionary<DecodeHintType, object> hints;
private IList<Reader> readers;
/// <summary> This version of decode honors the intent of Reader.decode(BinaryBitmap) in that it
/// passes null as a hint to the decoders. However, that makes it inefficient to call repeatedly.
/// Use setHints() followed by decodeWithState() for continuous scan applications.
///
/// </summary>
/// <param name="image">The pixel data to decode
/// </param>
/// <returns> The contents of the image
/// </returns>
/// <throws> ReaderException Any errors which occurred </throws>
public Result decode(BinaryBitmap image)
{
Hints = null;
return decodeInternal(image);
}
/// <summary> Decode an image using the hints provided. Does not honor existing state.
///
/// </summary>
/// <param name="image">The pixel data to decode
/// </param>
/// <param name="hints">The hints to use, clearing the previous state.
/// </param>
/// <returns> The contents of the image
/// </returns>
/// <throws> ReaderException Any errors which occurred </throws>
public Result decode(BinaryBitmap image, IDictionary<DecodeHintType, object> hints)
{
Hints = hints;
return decodeInternal(image);
}
/// <summary> Decode an image using the state set up by calling setHints() previously. Continuous scan
/// clients will get a <b>large</b> speed increase by using this instead of decode().
///
/// </summary>
/// <param name="image">The pixel data to decode
/// </param>
/// <returns> The contents of the image
/// </returns>
/// <throws> ReaderException Any errors which occurred </throws>
public Result decodeWithState(BinaryBitmap image)
{
// Make sure to set up the default state so we don't crash
if (readers == null)
{
Hints = null;
}
return decodeInternal(image);
}
/// <summary> This method adds state to the MultiFormatReader. By setting the hints once, subsequent calls
/// to decodeWithState(image) can reuse the same set of readers without reallocating memory. This
/// is important for performance in continuous scan clients.
///
/// </summary>
public IDictionary<DecodeHintType, object> Hints
{
set
{
hints = value;
var tryHarder = value != null && value.ContainsKey(DecodeHintType.TRY_HARDER);
var formats = value == null || !value.ContainsKey(DecodeHintType.POSSIBLE_FORMATS) ? null : (IList<BarcodeFormat>)value[DecodeHintType.POSSIBLE_FORMATS];
if (formats != null)
{
bool addOneDReader =
formats.Contains(BarcodeFormat.All_1D) ||
formats.Contains(BarcodeFormat.UPC_A) ||
formats.Contains(BarcodeFormat.UPC_E) ||
formats.Contains(BarcodeFormat.EAN_13) ||
formats.Contains(BarcodeFormat.EAN_8) ||
formats.Contains(BarcodeFormat.CODABAR) ||
formats.Contains(BarcodeFormat.CODE_39) ||
formats.Contains(BarcodeFormat.CODE_93) ||
formats.Contains(BarcodeFormat.CODE_128) ||
formats.Contains(BarcodeFormat.ITF) ||
formats.Contains(BarcodeFormat.RSS_14) ||
formats.Contains(BarcodeFormat.RSS_EXPANDED);
readers = new List<Reader>();
// Put 1D readers upfront in "normal" mode
if (addOneDReader && !tryHarder)
{
readers.Add(new MultiFormatOneDReader(value));
}
if (formats.Contains(BarcodeFormat.QR_CODE))
{
readers.Add(new QRCodeReader());
}
if (formats.Contains(BarcodeFormat.DATA_MATRIX))
{
readers.Add(new DataMatrixReader());
}
if (formats.Contains(BarcodeFormat.AZTEC))
{
readers.Add(new AztecReader());
}
if (formats.Contains(BarcodeFormat.PDF_417))
{
readers.Add(new PDF417Reader());
}
if (formats.Contains(BarcodeFormat.MAXICODE))
{
readers.Add(new MaxiCodeReader());
}
if (formats.Contains(BarcodeFormat.IMB))
{
readers.Add(new IMBReader());
}
// At end in "try harder" mode
if (addOneDReader && tryHarder)
{
readers.Add(new MultiFormatOneDReader(value));
}
}
if (readers == null ||
readers.Count == 0)
{
readers = readers ?? new List<Reader>();
if (!tryHarder)
{
readers.Add(new MultiFormatOneDReader(value));
}
readers.Add(new QRCodeReader());
readers.Add(new DataMatrixReader());
readers.Add(new AztecReader());
readers.Add(new PDF417Reader());
readers.Add(new MaxiCodeReader());
if (tryHarder)
{
readers.Add(new MultiFormatOneDReader(value));
}
}
}
}
/// <summary>
/// resets all specific readers
/// </summary>
public void reset()
{
if (readers != null)
{
foreach (var reader in readers)
{
reader.reset();
}
}
}
private Result decodeInternal(BinaryBitmap image)
{
if (readers != null)
{
var rpCallback = hints != null && hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK)
? (ResultPointCallback)hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK]
: null;
for (var index = 0; index < readers.Count; index++)
{
var reader = readers[index];
reader.reset();
var result = reader.decode(image, hints);
if (result != null)
{
// found a barcode, pushing the successful reader up front
// I assume that the same type of barcode is read multiple times
// so the reordering of the readers list should speed up the next reading
// a little bit
readers.RemoveAt(index);
readers.Insert(0, reader);
return result;
}
if (rpCallback != null)
rpCallback(null);
}
}
return null;
}
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using ZXing.Aztec;
using ZXing.Common;
using ZXing.Datamatrix;
using ZXing.OneD;
using ZXing.PDF417;
using ZXing.QrCode;
namespace ZXing
{
/// <summary> This is a factory class which finds the appropriate Writer subclass for the BarcodeFormat
/// requested and encodes the barcode with the supplied contents.
///
/// </summary>
/// <author> dswitkin@google.com (Daniel Switkin)
/// </author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
/// </author>
public sealed class MultiFormatWriter : Writer
{
private static readonly IDictionary<BarcodeFormat, Func<Writer>> formatMap;
static MultiFormatWriter()
{
formatMap = new Dictionary<BarcodeFormat, Func<Writer>>
{
{BarcodeFormat.EAN_8, () => new EAN8Writer()},
{BarcodeFormat.UPC_E, () => new UPCEWriter()},
{BarcodeFormat.EAN_13, () => new EAN13Writer()},
{BarcodeFormat.UPC_A, () => new UPCAWriter()},
{BarcodeFormat.QR_CODE, () => new QRCodeWriter()},
{BarcodeFormat.CODE_39, () => new Code39Writer()},
{BarcodeFormat.CODE_93, () => new Code93Writer()},
{BarcodeFormat.CODE_128, () => new Code128Writer()},
{BarcodeFormat.ITF, () => new ITFWriter()},
{BarcodeFormat.PDF_417, () => new PDF417Writer()},
{BarcodeFormat.CODABAR, () => new CodaBarWriter()},
{BarcodeFormat.MSI, () => new MSIWriter()},
{BarcodeFormat.PLESSEY, () => new PlesseyWriter()},
{BarcodeFormat.DATA_MATRIX, () => new DataMatrixWriter()},
{BarcodeFormat.AZTEC, () => new AztecWriter()},
};
}
/// <summary>
/// Gets the collection of supported writers.
/// </summary>
public static ICollection<BarcodeFormat> SupportedWriters
{
get { return formatMap.Keys; }
}
/// <summary>
/// encode the given data
/// </summary>
/// <param name="contents"></param>
/// <param name="format"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
{
return encode(contents, format, width, height, null);
}
/// <summary>
/// encode the given data
/// </summary>
/// <param name="contents"></param>
/// <param name="format"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="hints"></param>
/// <returns></returns>
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, IDictionary<EncodeHintType, object> hints)
{
if (!formatMap.ContainsKey(format))
throw new ArgumentException("No encoder available for format " + format);
return formatMap[format]().encode(contents, format, width, height, hints);
}
}
}

View File

@@ -0,0 +1,267 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing
{
/// <summary>
/// This object extends LuminanceSource around an array of YUV data returned from the camera driver,
/// with the option to crop to a rectangle within the full data. This can be used to exclude
/// superfluous pixels around the perimeter and speed up decoding.
/// It works for any pixel format where the Y channel is planar and appears first, including
/// YCbCr_420_SP and YCbCr_422_SP.
/// @author dswitkin@google.com (Daniel Switkin)
/// </summary>
public sealed class PlanarYUVLuminanceSource : BaseLuminanceSource
{
private const int THUMBNAIL_SCALE_FACTOR = 2;
private readonly byte[] yuvData;
private readonly int dataWidth;
private readonly int dataHeight;
private readonly int left;
private readonly int top;
/// <summary>
/// Initializes a new instance of the <see cref="PlanarYUVLuminanceSource"/> class.
/// </summary>
/// <param name="yuvData">The yuv data.</param>
/// <param name="dataWidth">Width of the data.</param>
/// <param name="dataHeight">Height of the data.</param>
/// <param name="left">The left.</param>
/// <param name="top">The top.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="reverseHoriz">if set to <c>true</c> [reverse horiz].</param>
public PlanarYUVLuminanceSource(byte[] yuvData,
int dataWidth,
int dataHeight,
int left,
int top,
int width,
int height,
bool reverseHoriz)
: base(width, height)
{
if (left + width > dataWidth || top + height > dataHeight)
{
throw new ArgumentException("Crop rectangle does not fit within image data.");
}
this.yuvData = yuvData;
this.dataWidth = dataWidth;
this.dataHeight = dataHeight;
this.left = left;
this.top = top;
if (reverseHoriz)
{
reverseHorizontal(width, height);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="PlanarYUVLuminanceSource"/> class.
/// </summary>
/// <param name="luminances">The luminances.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
private PlanarYUVLuminanceSource(byte[] luminances, int width, int height)
: base(width, height)
{
yuvData = luminances;
this.luminances = luminances;
dataWidth = width;
dataHeight = height;
left = 0;
top = 0;
}
/// <summary>
/// Fetches one row of luminance data from the underlying platform's bitmap. Values range from
/// 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have
/// to bitwise and with 0xff for each value. It is preferable for implementations of this method
/// to only fetch this row rather than the whole image, since no 2D Readers may be installed and
/// getMatrix() may never be called.
/// </summary>
/// <param name="y">The row to fetch, 0 &lt;= y &lt; Height.</param>
/// <param name="row">An optional preallocated array. If null or too small, it will be ignored.
/// Always use the returned object, and ignore the .length of the array.</param>
/// <returns>
/// An array containing the luminance data.
/// </returns>
override public byte[] getRow(int y, byte[] row)
{
if (y < 0 || y >= Height)
{
throw new ArgumentException("Requested row is outside the image: " + y);
}
int width = Width;
if (row == null || row.Length < width)
{
row = new byte[width];
}
int offset = (y + top) * dataWidth + left;
Array.Copy(yuvData, offset, row, 0, width);
return row;
}
/// <summary>
///
/// </summary>
override public byte[] Matrix
{
get
{
int width = Width;
int height = Height;
// If the caller asks for the entire underlying image, save the copy and give them the
// original data. The docs specifically warn that result.length must be ignored.
if (width == dataWidth && height == dataHeight)
{
return yuvData;
}
int area = width * height;
byte[] matrix = new byte[area];
int inputOffset = top * dataWidth + left;
// If the width matches the full width of the underlying data, perform a single copy.
if (width == dataWidth)
{
Array.Copy(yuvData, inputOffset, matrix, 0, area);
return matrix;
}
// Otherwise copy one cropped row at a time.
byte[] yuv = yuvData;
for (int y = 0; y < height; y++)
{
int outputOffset = y * width;
Array.Copy(yuvData, inputOffset, matrix, outputOffset, width);
inputOffset += dataWidth;
}
return matrix;
}
}
/// <summary>
/// </summary>
/// <returns> Whether this subclass supports cropping.</returns>
override public bool CropSupported
{
get { return true; }
}
/// <summary>
/// Returns a new object with cropped image data. Implementations may keep a reference to the
/// original data rather than a copy. Only callable if CropSupported is true.
/// </summary>
/// <param name="left">The left coordinate, 0 &lt;= left &lt; Width.</param>
/// <param name="top">The top coordinate, 0 &lt;= top &lt;= Height.</param>
/// <param name="width">The width of the rectangle to crop.</param>
/// <param name="height">The height of the rectangle to crop.</param>
/// <returns>
/// A cropped version of this object.
/// </returns>
override public LuminanceSource crop(int left, int top, int width, int height)
{
return new PlanarYUVLuminanceSource(yuvData,
dataWidth,
dataHeight,
this.left + left,
this.top + top,
width,
height,
false);
}
/// <summary>
/// Renders the cropped greyscale bitmap.
/// </summary>
/// <returns></returns>
public int[] renderThumbnail()
{
int width = Width / THUMBNAIL_SCALE_FACTOR;
int height = Height / THUMBNAIL_SCALE_FACTOR;
int[] pixels = new int[width * height];
byte[] yuv = yuvData;
int inputOffset = top * dataWidth + left;
for (int y = 0; y < height; y++)
{
int outputOffset = y * width;
for (int x = 0; x < width; x++)
{
int grey = yuv[inputOffset + x * THUMBNAIL_SCALE_FACTOR] & 0xff;
pixels[outputOffset + x] = ((0x00FF0000 << 8) | (grey * 0x00010101));
}
inputOffset += dataWidth * THUMBNAIL_SCALE_FACTOR;
}
return pixels;
}
/// <summary>
/// width of image from {@link #renderThumbnail()}
/// </summary>
public int ThumbnailWidth
{
get
{
return Width / THUMBNAIL_SCALE_FACTOR;
}
}
/// <summary>
/// height of image from {@link #renderThumbnail()}
/// </summary>
public int ThumbnailHeight
{
get
{
return Height / THUMBNAIL_SCALE_FACTOR;
}
}
private void reverseHorizontal(int width, int height)
{
byte[] yuvData = this.yuvData;
for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth)
{
int middle = rowStart + width / 2;
for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--)
{
byte temp = yuvData[x1];
yuvData[x1] = yuvData[x2];
yuvData[x2] = temp;
}
}
}
/// <summary>
/// creates a new instance
/// </summary>
/// <param name="newLuminances"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
protected override LuminanceSource CreateLuminanceSource(byte[] newLuminances, int width, int height)
{
return new PlanarYUVLuminanceSource(newLuminances, width, height);
}
}
}

View File

@@ -0,0 +1,380 @@
/*
* Copyright 2012 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing
{
/// <summary>
/// Luminance source class which support different formats of images.
/// </summary>
public partial class RGBLuminanceSource : BaseLuminanceSource
{
/// <summary>
/// enumeration of supported bitmap format which the RGBLuminanceSource can process
/// </summary>
public enum BitmapFormat
{
/// <summary>
/// format of the byte[] isn't known. RGBLuminanceSource tries to determine the best possible value
/// </summary>
Unknown,
/// <summary>
/// grayscale array, the byte array is a luminance array with 1 byte per pixel
/// </summary>
Gray8,
/// <summary>
/// grayscale array, the byte array is a luminance array with 2 bytes per pixel
/// </summary>
Gray16,
/// <summary>
/// 3 bytes per pixel with the channels red, green and blue
/// </summary>
RGB24,
/// <summary>
/// 4 bytes per pixel with the channels red, green and blue
/// </summary>
RGB32,
/// <summary>
/// 4 bytes per pixel with the channels alpha, red, green and blue
/// </summary>
ARGB32,
/// <summary>
/// 3 bytes per pixel with the channels blue, green and red
/// </summary>
BGR24,
/// <summary>
/// 4 bytes per pixel with the channels blue, green and red
/// </summary>
BGR32,
/// <summary>
/// 4 bytes per pixel with the channels blue, green, red and alpha
/// </summary>
BGRA32,
/// <summary>
/// 2 bytes per pixel, 5 bit red, 6 bits green and 5 bits blue
/// </summary>
RGB565,
/// <summary>
/// 4 bytes per pixel with the channels red, green, blue and alpha
/// </summary>
RGBA32,
/// <summary>
/// 4 bytes for two pixels, UYVY formatted
/// </summary>
UYVY,
/// <summary>
/// 4 bytes for two pixels, YUYV formatted
/// </summary>
YUYV
}
/// <summary>
/// Initializes a new instance of the <see cref="RGBLuminanceSource"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
protected RGBLuminanceSource(int width, int height)
: base(width, height)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RGBLuminanceSource"/> class.
/// It supports a byte array with 3 bytes per pixel (RGB24).
/// </summary>
/// <param name="rgbRawBytes">The RGB raw bytes.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
public RGBLuminanceSource(byte[] rgbRawBytes, int width, int height)
: this(rgbRawBytes, width, height, BitmapFormat.RGB24)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RGBLuminanceSource"/> class.
/// It supports a byte array with 1 byte per pixel (Gray8).
/// That means the whole array consists of the luminance values (grayscale).
/// </summary>
/// <param name="luminanceArray">The luminance array.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="is8Bit">if set to <c>true</c> [is8 bit].</param>
[Obsolete("Use RGBLuminanceSource(luminanceArray, width, height, BitmapFormat.Gray8)")]
public RGBLuminanceSource(byte[] luminanceArray, int width, int height, bool is8Bit)
: this(luminanceArray, width, height, BitmapFormat.Gray8)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RGBLuminanceSource"/> class.
/// It supports a byte array with 3 bytes per pixel (RGB24).
/// </summary>
/// <param name="rgbRawBytes">The RGB raw bytes.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="bitmapFormat">The bitmap format.</param>
public RGBLuminanceSource(byte[] rgbRawBytes, int width, int height, BitmapFormat bitmapFormat)
: base(width, height)
{
CalculateLuminance(rgbRawBytes, bitmapFormat);
}
/// <summary>
/// Should create a new luminance source with the right class type.
/// The method is used in methods crop and rotate.
/// </summary>
/// <param name="newLuminances">The new luminances.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <returns></returns>
protected override LuminanceSource CreateLuminanceSource(byte[] newLuminances, int width, int height)
{
return new RGBLuminanceSource(width, height) { luminances = newLuminances };
}
private static BitmapFormat DetermineBitmapFormat(byte[] rgbRawBytes, int width, int height)
{
var square = width * height;
var byteperpixel = rgbRawBytes.Length / square;
switch (byteperpixel)
{
case 1:
return BitmapFormat.Gray8;
case 2:
return BitmapFormat.RGB565;
case 3:
return BitmapFormat.RGB24;
case 4:
return BitmapFormat.RGB32;
default:
throw new ArgumentException("The bitmap format could not be determined. Please specify the correct value.");
}
}
/// <summary>
/// calculates the luminance values for the given byte array and bitmap format
/// </summary>
/// <param name="rgbRawBytes"></param>
/// <param name="bitmapFormat"></param>
protected void CalculateLuminance(byte[] rgbRawBytes, BitmapFormat bitmapFormat)
{
if (bitmapFormat == BitmapFormat.Unknown)
{
bitmapFormat = DetermineBitmapFormat(rgbRawBytes, Width, Height);
}
switch (bitmapFormat)
{
case BitmapFormat.Gray8:
Buffer.BlockCopy(rgbRawBytes, 0, luminances, 0, rgbRawBytes.Length < luminances.Length ? rgbRawBytes.Length : luminances.Length);
break;
case BitmapFormat.Gray16:
CalculateLuminanceGray16(rgbRawBytes);
break;
case BitmapFormat.RGB24:
CalculateLuminanceRGB24(rgbRawBytes);
break;
case BitmapFormat.BGR24:
CalculateLuminanceBGR24(rgbRawBytes);
break;
case BitmapFormat.RGB32:
CalculateLuminanceRGB32(rgbRawBytes);
break;
case BitmapFormat.BGR32:
CalculateLuminanceBGR32(rgbRawBytes);
break;
case BitmapFormat.RGBA32:
CalculateLuminanceRGBA32(rgbRawBytes);
break;
case BitmapFormat.ARGB32:
CalculateLuminanceARGB32(rgbRawBytes);
break;
case BitmapFormat.BGRA32:
CalculateLuminanceBGRA32(rgbRawBytes);
break;
case BitmapFormat.RGB565:
CalculateLuminanceRGB565(rgbRawBytes);
break;
case BitmapFormat.UYVY:
CalculateLuminanceUYVY(rgbRawBytes);
break;
case BitmapFormat.YUYV:
CalculateLuminanceYUYV(rgbRawBytes);
break;
default:
throw new ArgumentException("The bitmap format isn't supported.", bitmapFormat.ToString());
}
}
private void CalculateLuminanceRGB565(byte[] rgb565RawData)
{
var luminanceIndex = 0;
for (var index = 0; index < rgb565RawData.Length && luminanceIndex < luminances.Length; index += 2, luminanceIndex++)
{
var byte1 = rgb565RawData[index];
var byte2 = rgb565RawData[index + 1];
var b5 = byte1 & 0x1F;
var g5 = (((byte1 & 0xE0) >> 5) | ((byte2 & 0x03) << 3)) & 0x1F;
var r5 = (byte2 >> 2) & 0x1F;
var r8 = (r5 * 527 + 23) >> 6;
var g8 = (g5 * 527 + 23) >> 6;
var b8 = (b5 * 527 + 23) >> 6;
// cheap, not fully accurate conversion
//var pixel = (byte2 << 8) | byte1;
//b8 = (((pixel) & 0x001F) << 3);
//g8 = (((pixel) & 0x07E0) >> 2) & 0xFF;
//r8 = (((pixel) & 0xF800) >> 8);
luminances[luminanceIndex] = (byte)((RChannelWeight * r8 + GChannelWeight * g8 + BChannelWeight * b8) >> ChannelWeight);
}
}
private void CalculateLuminanceRGB24(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
int r = rgbRawBytes[rgbIndex++];
int g = rgbRawBytes[rgbIndex++];
int b = rgbRawBytes[rgbIndex++];
luminances[luminanceIndex] = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
}
}
private void CalculateLuminanceBGR24(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
int b = rgbRawBytes[rgbIndex++];
int g = rgbRawBytes[rgbIndex++];
int r = rgbRawBytes[rgbIndex++];
luminances[luminanceIndex] = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
}
}
private void CalculateLuminanceRGB32(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
int r = rgbRawBytes[rgbIndex++];
int g = rgbRawBytes[rgbIndex++];
int b = rgbRawBytes[rgbIndex++];
rgbIndex++;
luminances[luminanceIndex] = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
}
}
private void CalculateLuminanceBGR32(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
int b = rgbRawBytes[rgbIndex++];
int g = rgbRawBytes[rgbIndex++];
int r = rgbRawBytes[rgbIndex++];
rgbIndex++;
luminances[luminanceIndex] = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
}
}
private void CalculateLuminanceBGRA32(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
var b = rgbRawBytes[rgbIndex++];
var g = rgbRawBytes[rgbIndex++];
var r = rgbRawBytes[rgbIndex++];
var alpha = rgbRawBytes[rgbIndex++];
var luminance = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
luminances[luminanceIndex] = (byte)(((luminance * alpha) >> 8) + (255 * (255 - alpha) >> 8));
}
}
private void CalculateLuminanceRGBA32(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
var r = rgbRawBytes[rgbIndex++];
var g = rgbRawBytes[rgbIndex++];
var b = rgbRawBytes[rgbIndex++];
var alpha = rgbRawBytes[rgbIndex++];
var luminance = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
luminances[luminanceIndex] = (byte)(((luminance * alpha) >> 8) + (255 * (255 - alpha) >> 8));
}
}
private void CalculateLuminanceARGB32(byte[] rgbRawBytes)
{
for (int rgbIndex = 0, luminanceIndex = 0; rgbIndex < rgbRawBytes.Length && luminanceIndex < luminances.Length; luminanceIndex++)
{
// Calculate luminance cheaply, favoring green.
var alpha = rgbRawBytes[rgbIndex++];
var r = rgbRawBytes[rgbIndex++];
var g = rgbRawBytes[rgbIndex++];
var b = rgbRawBytes[rgbIndex++];
var luminance = (byte)((RChannelWeight * r + GChannelWeight * g + BChannelWeight * b) >> ChannelWeight);
luminances[luminanceIndex] = (byte)(((luminance * alpha) >> 8) + (255 * (255 - alpha) >> 8));
}
}
private void CalculateLuminanceUYVY(byte[] uyvyRawBytes)
{
// start by 1, jump over first U byte
for (int uyvyIndex = 1, luminanceIndex = 0; uyvyIndex < uyvyRawBytes.Length - 3 && luminanceIndex < luminances.Length;)
{
byte y1 = uyvyRawBytes[uyvyIndex];
uyvyIndex += 2; // jump from 1 to 3 (from Y1 over to Y2)
byte y2 = uyvyRawBytes[uyvyIndex];
uyvyIndex += 2; // jump from 3 to 5
luminances[luminanceIndex++] = y1;
luminances[luminanceIndex++] = y2;
}
}
private void CalculateLuminanceYUYV(byte[] yuyvRawBytes)
{
// start by 0 not by 1 like UYUV
for (int yuyvIndex = 0, luminanceIndex = 0; yuyvIndex < yuyvRawBytes.Length - 3 && luminanceIndex < luminances.Length;)
{
byte y1 = yuyvRawBytes[yuyvIndex];
yuyvIndex += 2; // jump from 0 to 2 (from Y1 over over to Y2)
byte y2 = yuyvRawBytes[yuyvIndex];
yuyvIndex += 2; // jump from 2 to 4
luminances[luminanceIndex++] = y1;
luminances[luminanceIndex++] = y2;
}
}
private void CalculateLuminanceGray16(byte[] gray16RawBytes)
{
for (int grayIndex = 0, luminanceIndex = 0; grayIndex < gray16RawBytes.Length && luminanceIndex < luminances.Length; grayIndex += 2, luminanceIndex++)
{
byte gray8 = gray16RawBytes[grayIndex];
luminances[luminanceIndex] = gray8;
}
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Collections.Generic;
namespace ZXing
{
/// <summary>
/// Implementations of this interface can decode an image of a barcode in some format into
/// the String it encodes. For example, <see cref="ZXing.QrCode.QRCodeReader" /> can
/// decode a QR code. The decoder may optionally receive hints from the caller which may help
/// it decode more quickly or accurately.
///
/// See <see cref="MultiFormatReader" />, which attempts to determine what barcode
/// format is present within the image as well, and then decodes it accordingly.
/// </summary>
/// <author>Sean Owen</author>
/// <author>dswitkin@google.com (Daniel Switkin)</author>
public interface Reader
{
/// <summary>
/// Locates and decodes a barcode in some format within an image.
/// </summary>
/// <param name="image">image of barcode to decode</param>
/// <returns>String which the barcode encodes</returns>
Result decode(BinaryBitmap image);
/// <summary> Locates and decodes a barcode in some format within an image. This method also accepts
/// hints, each possibly associated to some data, which may help the implementation decode.
/// </summary>
/// <param name="image">image of barcode to decode</param>
/// <param name="hints">passed as a <see cref="IDictionary{TKey, TValue}" /> from <see cref="DecodeHintType" />
/// to arbitrary data. The
/// meaning of the data depends upon the hint type. The implementation may or may not do
/// anything with these hints.
/// </param>
/// <returns>String which the barcode encodes</returns>
Result decode(BinaryBitmap image, IDictionary<DecodeHintType, object> hints);
/// <summary>
/// Resets any internal state the implementation has after a decode, to prepare it
/// for reuse.
/// </summary>
void reset();
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing
{
/// <summary>
/// The general exception class throw when something goes wrong during decoding of a barcode.
/// This includes, but is not limited to, failing checksums / error correction algorithms, being
/// unable to locate finder timing patterns, and so on.
/// </summary>
/// <author>Sean Owen</author>
[Serializable]
public class ReaderException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="ReaderException"/> class.
/// </summary>
public ReaderException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ReaderException"/> class.
/// </summary>
/// <param name="message"></param>
public ReaderException(String message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ReaderException"/> class.
/// </summary>
/// <param name="innerException">The inner exception.</param>
public ReaderException(Exception innerException)
: base(innerException.Message, innerException)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ReaderException"/> class.
/// </summary>
/// <param name="innerException">The inner exception.</param>
/// <param name="message"></param>
public ReaderException(String message, Exception innerException)
: base(message, innerException)
{
}
}
}

View File

@@ -0,0 +1,198 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
namespace ZXing
{
/// <summary>
/// Encapsulates the result of decoding a barcode within an image.
/// </summary>
public sealed class Result
{
/// <returns>raw text encoded by the barcode, if applicable, otherwise <code>null</code></returns>
public String Text { get; private set; }
/// <returns>raw bytes encoded by the barcode, if applicable, otherwise <code>null</code></returns>
public byte[] RawBytes { get; private set; }
/// <returns>
/// points related to the barcode in the image. These are typically points
/// identifying finder patterns or the corners of the barcode. The exact meaning is
/// specific to the type of barcode that was decoded.
/// </returns>
public ResultPoint[] ResultPoints { get; private set; }
/// <returns>{@link BarcodeFormat} representing the format of the barcode that was decoded</returns>
public BarcodeFormat BarcodeFormat { get; private set; }
/// <returns>
/// {@link Hashtable} mapping {@link ResultMetadataType} keys to values. May be
/// <code>null</code>. This contains optional metadata about what was detected about the barcode,
/// like orientation.
/// </returns>
public IDictionary<ResultMetadataType, object> ResultMetadata { get; private set; }
/// <summary>
/// Gets the timestamp.
/// </summary>
public long Timestamp { get; private set; }
/// <summary>
/// how many bits of <see cref="RawBytes"/> are valid; typically 8 times its length
/// </summary>
public int NumBits { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="Result"/> class.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="rawBytes">The raw bytes.</param>
/// <param name="resultPoints">The result points.</param>
/// <param name="format">The format.</param>
public Result(String text,
byte[] rawBytes,
ResultPoint[] resultPoints,
BarcodeFormat format)
: this(text, rawBytes, rawBytes == null ? 0 : 8 * rawBytes.Length, resultPoints, format, DateTime.Now.Ticks)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Result"/> class.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="rawBytes">The raw bytes.</param>
/// <param name="numBits"></param>
/// <param name="resultPoints">The result points.</param>
/// <param name="format">The format.</param>
public Result(String text,
byte[] rawBytes,
int numBits,
ResultPoint[] resultPoints,
BarcodeFormat format)
: this(text, rawBytes, numBits, resultPoints, format, DateTime.Now.Ticks)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Result"/> class.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="rawBytes">The raw bytes.</param>
/// <param name="resultPoints">The result points.</param>
/// <param name="format">The format.</param>
/// <param name="timestamp">The timestamp.</param>
public Result(String text, byte[] rawBytes, ResultPoint[] resultPoints, BarcodeFormat format, long timestamp)
: this(text, rawBytes, rawBytes == null ? 0 : 8 * rawBytes.Length, resultPoints, format, timestamp)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Result"/> class.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="rawBytes">The raw bytes.</param>
/// <param name="numBits"></param>
/// <param name="resultPoints">The result points.</param>
/// <param name="format">The format.</param>
/// <param name="timestamp">The timestamp.</param>
public Result(String text, byte[] rawBytes, int numBits, ResultPoint[] resultPoints, BarcodeFormat format, long timestamp)
{
if (text == null && rawBytes == null)
{
throw new ArgumentException("Text and bytes are null");
}
Text = text;
RawBytes = rawBytes;
NumBits = numBits;
ResultPoints = resultPoints;
BarcodeFormat = format;
ResultMetadata = null;
Timestamp = timestamp;
}
/// <summary>
/// Adds one metadata to the result
/// </summary>
/// <param name="type">The type.</param>
/// <param name="value">The value.</param>
public void putMetadata(ResultMetadataType type, Object value)
{
if (ResultMetadata == null)
{
ResultMetadata = new Dictionary<ResultMetadataType, object>();
}
ResultMetadata[type] = value;
}
/// <summary>
/// Adds a list of metadata to the result
/// </summary>
/// <param name="metadata">The metadata.</param>
public void putAllMetadata(IDictionary<ResultMetadataType, object> metadata)
{
if (metadata != null)
{
if (ResultMetadata == null)
{
ResultMetadata = metadata;
}
else
{
foreach (var entry in metadata)
ResultMetadata[entry.Key] = entry.Value;
}
}
}
/// <summary>
/// Adds the result points.
/// </summary>
/// <param name="newPoints">The new points.</param>
public void addResultPoints(ResultPoint[] newPoints)
{
var oldPoints = ResultPoints;
if (oldPoints == null)
{
ResultPoints = newPoints;
}
else if (newPoints != null && newPoints.Length > 0)
{
var allPoints = new ResultPoint[oldPoints.Length + newPoints.Length];
Array.Copy(oldPoints, 0, allPoints, 0, oldPoints.Length);
Array.Copy(newPoints, 0, allPoints, oldPoints.Length, newPoints.Length);
ResultPoints = allPoints;
}
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override String ToString()
{
if (Text == null)
{
return "[" + RawBytes.Length + " bytes]";
}
return Text;
}
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace ZXing
{
/// <summary>
/// Represents some type of metadata about the result of the decoding that the decoder
/// wishes to communicate back to the caller.
/// </summary>
/// <author>Sean Owen</author>
public enum ResultMetadataType
{
/// <summary>
/// Unspecified, application-specific metadata. Maps to an unspecified {@link Object}.
/// </summary>
OTHER,
/// <summary>
/// Denotes the likely approximate orientation of the barcode in the image. This value
/// is given as degrees rotated clockwise from the normal, upright orientation.
/// For example a 1D barcode which was found by reading top-to-bottom would be
/// said to have orientation "90". This key maps to an {@link Integer} whose
/// value is in the range [0,360).
/// </summary>
ORIENTATION,
/// <summary>
/// <p>2D barcode formats typically encode text, but allow for a sort of 'byte mode'
/// which is sometimes used to encode binary data. While {@link Result} makes available
/// the complete raw bytes in the barcode for these formats, it does not offer the bytes
/// from the byte segments alone.</p>
/// <p>This maps to a {@link java.util.List} of byte arrays corresponding to the
/// raw bytes in the byte segments in the barcode, in order.</p>
/// </summary>
BYTE_SEGMENTS,
/// <summary>
/// Error correction level used, if applicable. The value type depends on the
/// format, but is typically a String.
/// </summary>
ERROR_CORRECTION_LEVEL,
/// <summary>
/// For some periodicals, indicates the issue number as an {@link Integer}.
/// </summary>
ISSUE_NUMBER,
/// <summary>
/// For some products, indicates the suggested retail price in the barcode as a
/// formatted {@link String}.
/// </summary>
SUGGESTED_PRICE,
/// <summary>
/// For some products, the possible country of manufacture as a {@link String} denoting the
/// ISO country code. Some map to multiple possible countries, like "US/CA".
/// </summary>
POSSIBLE_COUNTRY,
/// <summary>
/// For some products, the extension text
/// </summary>
UPC_EAN_EXTENSION,
/// <summary>
/// If the code format supports structured append and
/// the current scanned code is part of one then the
/// sequence number is given with it.
/// </summary>
STRUCTURED_APPEND_SEQUENCE,
/// <summary>
/// If the code format supports structured append and
/// the current scanned code is part of one then the
/// parity is given with it.
/// </summary>
STRUCTURED_APPEND_PARITY,
/// <summary>
/// PDF417-specific metadata
/// </summary>
PDF417_EXTRA_METADATA,
/// <summary>
/// Aztec-specific metadata
/// </summary>
AZTEC_EXTRA_METADATA
}
}

View File

@@ -0,0 +1,196 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using ZXing.Common.Detector;
namespace ZXing
{
/// <summary>
/// Encapsulates a point of interest in an image containing a barcode. Typically, this
/// would be the location of a finder pattern or the corner of the barcode, for example.
/// </summary>
/// <author>Sean Owen</author>
public class ResultPoint
{
private readonly float x;
private readonly float y;
private readonly byte[] bytesX;
private readonly byte[] bytesY;
private String toString;
/// <summary>
/// Initializes a new instance of the <see cref="ResultPoint"/> class.
/// </summary>
public ResultPoint()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ResultPoint"/> class.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
public ResultPoint(float x, float y)
{
this.x = x;
this.y = y;
// calculate only once for GetHashCode
bytesX = BitConverter.GetBytes(x);
bytesY = BitConverter.GetBytes(y);
}
/// <summary>
/// Gets the X.
/// </summary>
virtual public float X
{
get
{
return x;
}
}
/// <summary>
/// Gets the Y.
/// </summary>
virtual public float Y
{
get
{
return y;
}
}
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
/// </summary>
/// <param name="other">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(Object other)
{
var otherPoint = other as ResultPoint;
if (otherPoint == null)
return false;
return x == otherPoint.x && y == otherPoint.y;
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
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];
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override String ToString()
{
if (toString == null)
{
var result = new System.Text.StringBuilder(25);
result.AppendFormat(System.Globalization.CultureInfo.CurrentUICulture, "({0}, {1})", x, y);
toString = result.ToString();
}
return toString;
}
/// <summary>
/// Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC and
/// BC is less than AC and the angle between BC and BA is less than 180 degrees.
/// </summary>
/// <param name="patterns">array of three <see cref="ResultPoint" /> to order</param>
public static void orderBestPatterns(ResultPoint[] patterns)
{
// Find distances between pattern centers
float zeroOneDistance = distance(patterns[0], patterns[1]);
float oneTwoDistance = distance(patterns[1], patterns[2]);
float zeroTwoDistance = distance(patterns[0], patterns[2]);
ResultPoint pointA, pointB, pointC;
// Assume one closest to other two is B; A and C will just be guesses at first
if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance)
{
pointB = patterns[0];
pointA = patterns[1];
pointC = patterns[2];
}
else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance)
{
pointB = patterns[1];
pointA = patterns[0];
pointC = patterns[2];
}
else
{
pointB = patterns[2];
pointA = patterns[0];
pointC = patterns[1];
}
// Use cross product to figure out whether A and C are correct or flipped.
// This asks whether BC x BA has a positive z component, which is the arrangement
// we want for A, B, C. If it's negative, then we've got it flipped around and
// should swap A and C.
if (crossProductZ(pointA, pointB, pointC) < 0.0f)
{
ResultPoint temp = pointA;
pointA = pointC;
pointC = temp;
}
patterns[0] = pointA;
patterns[1] = pointB;
patterns[2] = pointC;
}
/// <summary>
/// calculates the distance between two points
/// </summary>
/// <param name="pattern1">first pattern</param>
/// <param name="pattern2">second pattern</param>
/// <returns>
/// distance between two points
/// </returns>
public static float distance(ResultPoint pattern1, ResultPoint pattern2)
{
return MathUtils.distance(pattern1.x, pattern1.y, pattern2.x, pattern2.y);
}
/// <summary>
/// Returns the z component of the cross product between vectors BC and BA.
/// </summary>
private static float crossProductZ(ResultPoint pointA, ResultPoint pointB, ResultPoint pointC)
{
float bX = pointB.x;
float bY = pointB.y;
return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX));
}
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace ZXing
{
/// <summary> Callback which is invoked when a possible result point (significant
/// point in the barcode image such as a corner) is found.
///
/// </summary>
/// <seealso cref="DecodeHintType.NEED_RESULT_POINT_CALLBACK">
/// </seealso>
public delegate void ResultPointCallback(ResultPoint point);
}

View File

@@ -0,0 +1,185 @@
//
// In order to convert some functionality to Visual C#, the Java Language Conversion Assistant
// creates "support classes" that duplicate the original functionality.
//
// Support classes replicate the functionality of the original code, but in some cases they are
// substantially different architecturally. Although every effort is made to preserve the
// original architecture of the application in the converted project, the user should be aware that
// the primary goal of these support classes is to replicate functionality, and that at times
// the architecture of the resulting solution may differ somewhat.
//
using System;
using System.Collections.Generic;
using System.Text;
namespace ZXing
{
/// <summary>
/// Contains conversion support elements such as classes, interfaces and static methods.
/// </summary>
public static class SupportClass
{
/*******************************/
/// <summary>
/// Copies an array of chars obtained from a String into a specified array of chars
/// </summary>
/// <param name="sourceString">The String to get the chars from</param>
/// <param name="sourceStart">Position of the String to start getting the chars</param>
/// <param name="sourceEnd">Position of the String to end getting the chars</param>
/// <param name="destinationArray">Array to return the chars</param>
/// <param name="destinationStart">Position of the destination array of chars to start storing the chars</param>
/// <returns>An array of chars</returns>
public static void GetCharsFromString(System.String sourceString, int sourceStart, int sourceEnd, char[] destinationArray, int destinationStart)
{
int sourceCounter = sourceStart;
int destinationCounter = destinationStart;
while (sourceCounter < sourceEnd)
{
destinationArray[destinationCounter] = (char)sourceString[sourceCounter];
sourceCounter++;
destinationCounter++;
}
}
/*******************************/
/// <summary>
/// Sets the capacity for the specified List
/// </summary>
/// <param name="vector">The List which capacity will be set</param>
/// <param name="newCapacity">The new capacity value</param>
public static void SetCapacity<T>(System.Collections.Generic.IList<T> vector, int newCapacity) where T : new()
{
while (newCapacity > vector.Count)
vector.Add(new T());
while (newCapacity < vector.Count)
vector.RemoveAt(vector.Count - 1);
}
/// <summary>
/// Converts a string-Collection to an array
/// </summary>
/// <param name="strings">The strings.</param>
/// <returns></returns>
public static String[] toStringArray(ICollection<string> strings)
{
var result = new String[strings.Count];
strings.CopyTo(result, 0);
return result;
}
/// <summary>
/// Joins all elements to one string.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="separator">The separator.</param>
/// <param name="values">The values.</param>
/// <returns></returns>
public static string Join<T>(string separator, IEnumerable<T> values)
{
var builder = new StringBuilder();
separator = separator ?? String.Empty;
if (values != null)
{
foreach (var value in values)
{
builder.Append(value);
builder.Append(separator);
}
if (builder.Length > 0)
builder.Length -= separator.Length;
}
return builder.ToString();
}
/// <summary>
/// Fills the specified array.
/// (can't use extension method because of .Net 2.0 support)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array">The array.</param>
/// <param name="value">The value.</param>
public static void Fill<T>(T[] array, T value)
{
for (int i = 0; i < array.Length; i++)
{
array[i] = value;
}
}
/// <summary>
/// Fills the specified array.
/// (can't use extension method because of .Net 2.0 support)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array">The array.</param>
/// <param name="startIndex">The start index.</param>
/// <param name="endIndex">The end index.</param>
/// <param name="value">The value.</param>
public static void Fill<T>(T[] array, int startIndex, int endIndex, T value)
{
for (int i = startIndex; i < endIndex; i++)
{
array[i] = value;
}
}
/// <summary>
///
/// </summary>
/// <param name="x"></param>
/// <returns></returns>
public static string ToBinaryString(int x)
{
char[] bits = new char[32];
int i = 0;
while (x != 0)
{
bits[i++] = (x & 1) == 1 ? '1' : '0';
x >>= 1;
}
Array.Reverse(bits, 0, i);
return new string(bits);
}
/// <summary>
///
/// </summary>
/// <param name="n"></param>
/// <returns></returns>
public static int bitCount(int n)
{
int ret = 0;
while (n != 0)
{
n &= (n - 1);
ret++;
}
return ret;
}
/// <summary>
/// Savely gets the value of a decoding hint
/// if hints is null the default is returned
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="hints">The hints.</param>
/// <param name="hintType">Type of the hint.</param>
/// <param name="default">The @default.</param>
/// <returns></returns>
public static T GetValue<T>(IDictionary<DecodeHintType, object> hints, DecodeHintType hintType, T @default)
{
// can't use extension method because of .Net 2.0 support
if (hints == null)
return @default;
if (!hints.ContainsKey(hintType))
return @default;
return (T)hints[hintType];
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using ZXing.Common;
namespace ZXing
{
/// <summary> The base class for all objects which encode/generate a barcode image.
///
/// </summary>
/// <author> dswitkin@google.com (Daniel Switkin)
/// </author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
/// </author>
public interface Writer
{
/// <summary>
/// Encode a barcode using the default settings.
/// </summary>
/// <param name="contents">The contents to encode in the barcode</param>
/// <param name="format">The barcode format to generate</param>
/// <param name="width">The preferred width in pixels</param>
/// <param name="height">The preferred height in pixels</param>
/// <returns> The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)</returns>
BitMatrix encode(System.String contents, BarcodeFormat format, int width, int height);
/// <summary> </summary>
/// <param name="contents">The contents to encode in the barcode</param>
/// <param name="format">The barcode format to generate</param>
/// <param name="width">The preferred width in pixels</param>
/// <param name="height">The preferred height in pixels</param>
/// <param name="hints">Additional parameters to supply to the encoder</param>
/// <returns> The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)</returns>
BitMatrix encode(String contents, BarcodeFormat format, int width, int height, IDictionary<EncodeHintType, object> hints);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing
{
/// <summary>
/// A base class which covers the range of exceptions which may occur when encoding a barcode using
/// the Writer framework.
/// </summary>
/// <author>dswitkin@google.com (Daniel Switkin)</author>
[Serializable]
public sealed class WriterException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="WriterException"/> class.
/// </summary>
public WriterException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WriterException"/> class.
/// </summary>
/// <param name="message">The message.</param>
public WriterException(String message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WriterException"/> class.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="innerExc">The inner exc.</param>
public WriterException(String message, Exception innerExc)
: base(message, innerExc)
{
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2010 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using ZXing.Common;
namespace ZXing.Aztec.Internal
{
/// <summary>
/// Extends <see cref="DetectorResult" /> with more information specific to the Aztec format,
/// like the number of layers and whether it's compact.
/// </summary>
public class AztecDetectorResult : DetectorResult
{
/// <summary>
/// Gets a value indicating whether this Aztec code is compact.
/// </summary>
/// <value>
/// <c>true</c> if compact; otherwise, <c>false</c>.
/// </value>
public bool Compact { get; private set; }
/// <summary>
/// Gets the nb datablocks.
/// </summary>
public int NbDatablocks { get; private set; }
/// <summary>
/// Gets the nb layers.
/// </summary>
public int NbLayers { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="AztecDetectorResult"/> class.
/// </summary>
/// <param name="bits">The bits.</param>
/// <param name="points">The points.</param>
/// <param name="compact">if set to <c>true</c> [compact].</param>
/// <param name="nbDatablocks">The nb datablocks.</param>
/// <param name="nbLayers">The nb layers.</param>
public AztecDetectorResult(BitMatrix bits,
ResultPoint[] points,
bool compact,
int nbDatablocks,
int nbLayers)
: base(bits, points)
{
Compact = compact;
NbDatablocks = nbDatablocks;
NbLayers = nbLayers;
}
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2010 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System.Collections.Generic;
using ZXing.Common;
using ZXing.Aztec.Internal;
namespace ZXing.Aztec
{
/// <summary>
/// This implementation can detect and decode Aztec codes in an image.
/// </summary>
/// <author>David Olivier</author>
public class AztecReader : Reader
{
/// <summary>
/// Locates and decodes a barcode in some format within an image.
/// </summary>
/// <param name="image">image of barcode to decode</param>
/// <returns>
/// a String representing the content encoded by the Data Matrix code
/// </returns>
public Result decode(BinaryBitmap image)
{
return decode(image, null);
}
/// <summary>
/// Locates and decodes a Data Matrix code in an image.
/// </summary>
/// <param name="image">image of barcode to decode</param>
/// <param name="hints">passed as a {@link java.util.Hashtable} from {@link com.google.zxing.DecodeHintType}
/// to arbitrary data. The
/// meaning of the data depends upon the hint type. The implementation may or may not do
/// anything with these hints.</param>
/// <returns>
/// String which the barcode encodes
/// </returns>
public Result decode(BinaryBitmap image, IDictionary<DecodeHintType, object> hints)
{
var blackmatrix = image.BlackMatrix;
if (blackmatrix == null)
return null;
Detector detector = new Detector(blackmatrix);
ResultPoint[] points = null;
DecoderResult decoderResult = null;
var detectorResult = detector.detect(false);
if (detectorResult != null)
{
points = detectorResult.Points;
decoderResult = new Decoder().decode(detectorResult);
}
if (decoderResult == null)
{
detectorResult = detector.detect(true);
if (detectorResult == null)
return null;
points = detectorResult.Points;
decoderResult = new Decoder().decode(detectorResult);
if (decoderResult == null)
return null;
}
if (hints != null &&
hints.ContainsKey(DecodeHintType.NEED_RESULT_POINT_CALLBACK))
{
var rpcb = (ResultPointCallback)hints[DecodeHintType.NEED_RESULT_POINT_CALLBACK];
if (rpcb != null)
{
foreach (var point in points)
{
rpcb(point);
}
}
}
var result = new Result(decoderResult.Text, decoderResult.RawBytes, decoderResult.NumBits, points, BarcodeFormat.AZTEC);
IList<byte[]> byteSegments = decoderResult.ByteSegments;
if (byteSegments != null)
{
result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
}
var ecLevel = decoderResult.ECLevel;
if (ecLevel != null)
{
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
}
result.putMetadata(ResultMetadataType.AZTEC_EXTRA_METADATA,
new AztecResultMetadata(detectorResult.Compact, detectorResult.NbDatablocks, detectorResult.NbLayers));
return result;
}
/// <summary>
/// Resets any internal state the implementation has after a decode, to prepare it
/// for reuse.
/// </summary>
public void reset()
{
// do nothing
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2013 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace ZXing.Aztec
{
/// <summary>
/// Aztec result meta data.
/// </summary>
public sealed class AztecResultMetadata
{
/// <summary>
/// Gets a value indicating whether this Aztec code is compact.
/// </summary>
/// <value>
/// <c>true</c> if compact; otherwise, <c>false</c>.
/// </value>
public bool Compact { get; private set; }
/// <summary>
/// Gets the nb datablocks.
/// </summary>
public int Datablocks { get; private set; }
/// <summary>
/// Gets the nb layers.
/// </summary>
public int Layers { get; private set; }
/// <summary>
///
/// </summary>
/// <param name="compact"></param>
/// <param name="datablocks"></param>
/// <param name="layers"></param>
public AztecResultMetadata(bool compact, int datablocks, int layers)
{
Compact = compact;
Datablocks = datablocks;
Layers = layers;
}
}
}

View File

@@ -0,0 +1,195 @@
/*
* Copyright 2013 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Text;
using ZXing.Aztec.Internal;
using ZXing.Common;
namespace ZXing.Aztec
{
/// <summary>
/// Renders an Aztec code as a <see cref="BitMatrix" />
/// </summary>
public sealed class AztecWriter : Writer
{
public static readonly Encoding DEFAULT_CHARSET;
static AztecWriter()
{
#if !(WindowsCE || SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || PORTABLE)
DEFAULT_CHARSET = Encoding.GetEncoding("ISO-8859-1");
#elif WindowsCE
try
{
DEFAULT_CHARSET = Encoding.GetEncoding("ISO-8859-1");
}
catch (PlatformNotSupportedException)
{
DEFAULT_CHARSET = Encoding.GetEncoding(1252);
}
#else
// not fully correct but what else
DEFAULT_CHARSET = Encoding.GetEncoding("UTF-8");
#endif
}
/// <summary>
/// Encode a barcode using the default settings.
/// </summary>
/// <param name="contents">The contents to encode in the barcode</param>
/// <param name="format">The barcode format to generate</param>
/// <param name="width">The preferred width in pixels</param>
/// <param name="height">The preferred height in pixels</param>
/// <returns>
/// The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
/// </returns>
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
{
return encode(contents, format, width, height, null);
}
/// <summary>
/// </summary>
/// <param name="contents">The contents to encode in the barcode</param>
/// <param name="format">The barcode format to generate</param>
/// <param name="width">The preferred width in pixels</param>
/// <param name="height">The preferred height in pixels</param>
/// <param name="hints">Additional parameters to supply to the encoder</param>
/// <returns>
/// The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
/// </returns>
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height, IDictionary<EncodeHintType, object> hints)
{
var charset = DEFAULT_CHARSET;
if (hints != null)
{
if (hints.ContainsKey(EncodeHintType.CHARACTER_SET))
{
object charsetname = hints[EncodeHintType.CHARACTER_SET];
if (charsetname != null)
{
charset = Encoding.GetEncoding(charsetname.ToString());
}
}
}
var byteContent = charset.GetBytes(contents);
return encode(byteContent, format, width, height, hints);
}
/// <summary>
/// </summary>
/// <param name="contents">The contents to encode in the barcode</param>
/// <param name="format">The barcode format to generate</param>
/// <param name="width">The preferred width in pixels</param>
/// <param name="height">The preferred height in pixels</param>
/// <param name="hints">Additional parameters to supply to the encoder</param>
/// <returns>
/// The generated barcode as a Matrix of unsigned bytes (0 == black, 255 == white)
/// </returns>
public BitMatrix encode(byte[] contents, BarcodeFormat format, int width, int height, IDictionary<EncodeHintType, object> hints)
{
var charset = DEFAULT_CHARSET;
int eccPercent = Internal.Encoder.DEFAULT_EC_PERCENT;
int layers = Internal.Encoder.DEFAULT_AZTEC_LAYERS;
if (hints != null)
{
if (hints.ContainsKey(EncodeHintType.CHARACTER_SET))
{
object charsetname = hints[EncodeHintType.CHARACTER_SET];
if (charsetname != null)
{
charset = Encoding.GetEncoding(charsetname.ToString());
}
}
if (hints.ContainsKey(EncodeHintType.ERROR_CORRECTION))
{
object eccPercentObject = hints[EncodeHintType.ERROR_CORRECTION];
if (eccPercentObject != null)
{
eccPercent = Convert.ToInt32(eccPercentObject);
}
}
if (hints.ContainsKey(EncodeHintType.AZTEC_LAYERS))
{
object layersObject = hints[EncodeHintType.AZTEC_LAYERS];
if (layersObject != null)
{
layers = Convert.ToInt32(layersObject);
}
}
}
return encode(contents,
format,
width,
height,
charset,
eccPercent,
layers);
}
private static BitMatrix encode(byte[] contents, BarcodeFormat format, int width, int height, Encoding charset, int eccPercent, int layers)
{
// charset stays here for later use in ECI segment
if (format != BarcodeFormat.AZTEC)
{
throw new ArgumentException("Can only encode AZTEC code, but got " + format);
}
var aztec = Internal.Encoder.encode(contents, eccPercent, layers);
return renderResult(aztec, width, height);
}
private static BitMatrix renderResult(AztecCode code, int width, int height)
{
var input = code.Matrix;
if (input == null)
{
throw new InvalidOperationException("No input code matrix");
}
int inputWidth = input.Width;
int inputHeight = input.Height;
int outputWidth = Math.Max(width, inputWidth);
int outputHeight = Math.Max(height, inputHeight);
int multiple = Math.Min(outputWidth / inputWidth, outputHeight / inputHeight);
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
var output = new BitMatrix(outputWidth, outputHeight);
for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple)
{
// Write the contents of this row of the barcode
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple)
{
if (input[inputX, inputY])
{
output.setRegion(outputX, outputY, multiple, multiple);
}
}
}
return output;
}
}
}

View File

@@ -0,0 +1,538 @@
/*
* Copyright 2010 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Text;
using ZXing.Common;
using ZXing.Common.ReedSolomon;
namespace ZXing.Aztec.Internal
{
/// <summary>
/// The main class which implements Aztec Code decoding -- as opposed to locating and extracting
/// the Aztec Code from an image.
/// </summary>
/// <author>David Olivier</author>
public sealed class Decoder
{
private enum Table
{
UPPER,
LOWER,
MIXED,
DIGIT,
PUNCT,
BINARY
}
private static readonly String[] UPPER_TABLE =
{
"CTRL_PS", " ", "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", "CTRL_LL", "CTRL_ML", "CTRL_DL", "CTRL_BS"
};
private static readonly String[] LOWER_TABLE =
{
"CTRL_PS", " ", "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", "CTRL_US", "CTRL_ML", "CTRL_DL", "CTRL_BS"
};
private static readonly String[] MIXED_TABLE =
{
"CTRL_PS", " ", "\x1", "\x2", "\x3", "\x4", "\x5", "\x6", "\x7", "\b", "\t", "\n",
"\xB", "\f", "\r", "\x1B", "\x1C", "\x1D", "\x1E", "\x1F", "@", "\\", "^", "_",
"`", "|", "~", "\x7F", "CTRL_LL", "CTRL_UL", "CTRL_PL", "CTRL_BS"
};
private static readonly String[] PUNCT_TABLE =
{
"FLG(n)", "\r", "\r\n", ". ", ", ", ": ", "!", "\"", "#", "$", "%", "&", "'", "(", ")",
"*", "+", ",", "-", ".", "/", ":", ";", "<", "=", ">", "?", "[", "]", "{", "}", "CTRL_UL"
};
private static readonly String[] DIGIT_TABLE =
{
"CTRL_PS", " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ",", ".", "CTRL_UL", "CTRL_US"
};
private static Encoding DEFAULT_ENCODING = AztecWriter.DEFAULT_CHARSET;
private static readonly IDictionary<Table, String[]> codeTables = new Dictionary<Table, String[]>
{
{Table.UPPER, UPPER_TABLE},
{Table.LOWER, LOWER_TABLE},
{Table.MIXED, MIXED_TABLE},
{Table.PUNCT, PUNCT_TABLE},
{Table.DIGIT, DIGIT_TABLE},
{Table.BINARY, null}
};
private static readonly IDictionary<char, Table> codeTableMap = new Dictionary<char, Table>
{
{'U', Table.UPPER},
{'L', Table.LOWER},
{'M', Table.MIXED},
{'P', Table.PUNCT},
{'D', Table.DIGIT},
{'B', Table.BINARY}
};
private AztecDetectorResult ddata;
/// <summary>
/// Decodes the specified detector result.
/// </summary>
/// <param name="detectorResult">The detector result.</param>
/// <returns></returns>
public DecoderResult decode(AztecDetectorResult detectorResult)
{
ddata = detectorResult;
var matrix = detectorResult.Bits;
var rawbits = extractBits(matrix);
if (rawbits == null)
return null;
var correctedBits = correctBits(rawbits);
if (correctedBits == null)
return null;
var result = getEncodedData(correctedBits.correctBits);
if (result == null)
return null;
var rawBytes = convertBoolArrayToByteArray(correctedBits.correctBits);
var decoderResult = new DecoderResult(rawBytes, correctedBits.correctBits.Length, result, null, String.Format("{0}", correctedBits.ecLevel));
return decoderResult;
}
/// <summary>
/// This method is used for testing the high-level encoder
/// </summary>
/// <param name="correctedBits"></param>
/// <returns></returns>
public static String highLevelDecode(bool[] correctedBits)
{
return getEncodedData(correctedBits);
}
/// <summary>
/// Gets the string encoded in the aztec code bits
/// </summary>
/// <param name="correctedBits">The corrected bits.</param>
/// <returns>the decoded string</returns>
private static String getEncodedData(bool[] correctedBits)
{
var endIndex = correctedBits.Length;
var latchTable = Table.UPPER; // table most recently latched to
var shiftTable = Table.UPPER; // table to use for the next read
var strTable = UPPER_TABLE;
var index = 0;
// Final decoded string result
// (correctedBits-5) / 4 is an upper bound on the size (all-digit result)
var result = new StringBuilder((correctedBits.Length - 5) / 4);
// Intermediary buffer of decoded bytes, which is decoded into a string and flushed
// when character encoding changes (ECI) or input ends.
using (var decodedBytes = new System.IO.MemoryStream())
{
var encoding = DEFAULT_ENCODING;
while (index < endIndex)
{
if (shiftTable == Table.BINARY)
{
if (endIndex - index < 5)
{
break;
}
int length = readCode(correctedBits, index, 5);
index += 5;
if (length == 0)
{
if (endIndex - index < 11)
{
break;
}
length = readCode(correctedBits, index, 11) + 31;
index += 11;
}
for (int charCount = 0; charCount < length; charCount++)
{
if (endIndex - index < 8)
{
index = endIndex; // Force outer loop to exit
break;
}
int code = readCode(correctedBits, index, 8);
decodedBytes.WriteByte((byte)code);
index += 8;
}
// Go back to whatever mode we had been in
shiftTable = latchTable;
strTable = codeTables[shiftTable];
}
else
{
int size = shiftTable == Table.DIGIT ? 4 : 5;
if (endIndex - index < size)
{
break;
}
int code = readCode(correctedBits, index, size);
index += size;
String str = getCharacter(strTable, code);
if ("FLG(n)".Equals(str))
{
if (endIndex - index < 3)
{
break;
}
int n = readCode(correctedBits, index, 3);
index += 3;
switch (n)
{
case 0:
result.Append((char)29); // translate FNC1 as ASCII 29
break;
case 7:
throw new FormatException("FLG(7) is reserved and illegal");
default:
// flush bytes before changing character set
if (decodedBytes.Length > 0)
{
var byteArray = decodedBytes.ToArray();
result.Append(encoding.GetString(byteArray, 0, byteArray.Length));
decodedBytes.SetLength(0);
}
// ECI is decimal integer encoded as 1-6 codes in DIGIT mode
int eci = 0;
if (endIndex - index < 4 * n)
{
break;
}
while (n-- > 0)
{
int nextDigit = readCode(correctedBits, index, 4);
index += 4;
if (nextDigit < 2 || nextDigit > 11)
{
throw new FormatException("Not a decimal digit");
}
eci = eci * 10 + (nextDigit - 2);
}
CharacterSetECI charsetECI = CharacterSetECI.getCharacterSetECIByValue(eci);
encoding = CharacterSetECI.getEncoding(charsetECI);
break;
}
// Go back to whatever mode we had been in
shiftTable = latchTable;
strTable = codeTables[shiftTable];
}
else if (str.StartsWith("CTRL_"))
{
// Table changes
// ISO/IEC 24778:2008 prescribes ending a shift sequence in the mode from which it was invoked.
// That's including when that mode is a shift.
// Our test case dlusbs.png for issue #642 exercises that.
latchTable = shiftTable; // Latch the current mode, so as to return to Upper after U/S B/S
shiftTable = getTable(str[5]);
strTable = codeTables[shiftTable];
if (str[6] == 'L')
{
latchTable = shiftTable;
}
}
else
{
// Though stored as a table of strings for convenience, codes actually represent 1 or 2 *bytes*.
#if (PORTABLE || NETSTANDARD1_0 || NETSTANDARD1_1)
var b = Encoding.GetEncoding(StringUtils.PLATFORM_DEFAULT_ENCODING).GetBytes(str);
#else
var b = Encoding.ASCII.GetBytes(str);
#endif
decodedBytes.Write(b, 0, b.Length);
// Go back to whatever mode we had been in
shiftTable = latchTable;
strTable = codeTables[shiftTable];
}
}
}
if (decodedBytes.Length > 0)
{
var byteArray = decodedBytes.ToArray();
result.Append(encoding.GetString(byteArray, 0, byteArray.Length));
}
}
return result.ToString();
}
/// <summary>
/// gets the table corresponding to the char passed
/// </summary>
/// <param name="t">The t.</param>
/// <returns></returns>
private static Table getTable(char t)
{
if (!codeTableMap.ContainsKey(t))
return codeTableMap['U'];
return codeTableMap[t];
}
/// <summary>
/// Gets the character (or string) corresponding to the passed code in the given table
/// </summary>
/// <param name="table">the table used</param>
/// <param name="code">the code of the character</param>
/// <returns></returns>
private static String getCharacter(String[] table, int code)
{
return table[code];
}
internal sealed class CorrectedBitsResult
{
public bool[] correctBits;
public int ecLevel;
public CorrectedBitsResult(bool[] correctBits, int ecLevel)
{
this.correctBits = correctBits;
this.ecLevel = ecLevel;
}
}
/// <summary>
///Performs RS error correction on an array of bits.
/// </summary>
/// <param name="rawbits">The rawbits.</param>
/// <returns>the corrected array</returns>
private CorrectedBitsResult correctBits(bool[] rawbits)
{
GenericGF gf;
int codewordSize;
if (ddata.NbLayers <= 2)
{
codewordSize = 6;
gf = GenericGF.AZTEC_DATA_6;
}
else if (ddata.NbLayers <= 8)
{
codewordSize = 8;
gf = GenericGF.AZTEC_DATA_8;
}
else if (ddata.NbLayers <= 22)
{
codewordSize = 10;
gf = GenericGF.AZTEC_DATA_10;
}
else
{
codewordSize = 12;
gf = GenericGF.AZTEC_DATA_12;
}
int numDataCodewords = ddata.NbDatablocks;
int numCodewords = rawbits.Length / codewordSize;
if (numCodewords < numDataCodewords)
return null;
int offset = rawbits.Length % codewordSize;
int numECCodewords = numCodewords - numDataCodewords;
int[] dataWords = new int[numCodewords];
for (int i = 0; i < numCodewords; i++, offset += codewordSize)
{
dataWords[i] = readCode(rawbits, offset, codewordSize);
}
var rsDecoder = new ReedSolomonDecoder(gf);
if (!rsDecoder.decode(dataWords, numECCodewords))
return null;
// Now perform the unstuffing operation.
// First, count how many bits are going to be thrown out as stuffing
int mask = (1 << codewordSize) - 1;
int stuffedBits = 0;
for (int i = 0; i < numDataCodewords; i++)
{
int dataWord = dataWords[i];
if (dataWord == 0 || dataWord == mask)
{
return null;
}
else if (dataWord == 1 || dataWord == mask - 1)
{
stuffedBits++;
}
}
// Now, actually unpack the bits and remove the stuffing
bool[] correctedBits = new bool[numDataCodewords * codewordSize - stuffedBits];
int index = 0;
for (int i = 0; i < numDataCodewords; i++)
{
int dataWord = dataWords[i];
if (dataWord == 1 || dataWord == mask - 1)
{
// next codewordSize-1 bits are all zeros or all ones
SupportClass.Fill(correctedBits, index, index + codewordSize - 1, dataWord > 1);
index += codewordSize - 1;
}
else
{
for (int bit = codewordSize - 1; bit >= 0; --bit)
{
correctedBits[index++] = (dataWord & (1 << bit)) != 0;
}
}
}
if (index != correctedBits.Length)
return null;
return new CorrectedBitsResult(correctedBits, 100 * (numCodewords - numDataCodewords) / numCodewords);
}
/// <summary>
/// Gets the array of bits from an Aztec Code matrix
/// </summary>
/// <param name="matrix">The matrix.</param>
/// <returns>the array of bits</returns>
private bool[] extractBits(BitMatrix matrix)
{
bool compact = ddata.Compact;
int layers = ddata.NbLayers;
int baseMatrixSize = (compact ? 11 : 14) + layers * 4; // not including alignment lines
int[] alignmentMap = new int[baseMatrixSize];
bool[] rawbits = new bool[totalBitsInLayer(layers, compact)];
if (compact)
{
for (int i = 0; i < alignmentMap.Length; i++)
{
alignmentMap[i] = i;
}
}
else
{
int matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15);
int origCenter = baseMatrixSize / 2;
int center = matrixSize / 2;
for (int i = 0; i < origCenter; i++)
{
int newOffset = i + i / 15;
alignmentMap[origCenter - i - 1] = center - newOffset - 1;
alignmentMap[origCenter + i] = center + newOffset + 1;
}
}
for (int i = 0, rowOffset = 0; i < layers; i++)
{
int rowSize = (layers - i) * 4 + (compact ? 9 : 12);
// The top-left most point of this layer is <low, low> (not including alignment lines)
int low = i * 2;
// The bottom-right most point of this layer is <high, high> (not including alignment lines)
int high = baseMatrixSize - 1 - low;
// We pull bits from the two 2 x rowSize columns and two rowSize x 2 rows
for (int j = 0; j < rowSize; j++)
{
int columnOffset = j * 2;
for (int k = 0; k < 2; k++)
{
// left column
rawbits[rowOffset + columnOffset + k] =
matrix[alignmentMap[low + k], alignmentMap[low + j]];
// bottom row
rawbits[rowOffset + 2 * rowSize + columnOffset + k] =
matrix[alignmentMap[low + j], alignmentMap[high - k]];
// right column
rawbits[rowOffset + 4 * rowSize + columnOffset + k] =
matrix[alignmentMap[high - k], alignmentMap[high - j]];
// top row
rawbits[rowOffset + 6 * rowSize + columnOffset + k] =
matrix[alignmentMap[high - j], alignmentMap[low + k]];
}
}
rowOffset += rowSize * 8;
}
return rawbits;
}
/// <summary>
/// Reads a code of given length and at given index in an array of bits
/// </summary>
/// <param name="rawbits">The rawbits.</param>
/// <param name="startIndex">The start index.</param>
/// <param name="length">The length.</param>
/// <returns></returns>
private static int readCode(bool[] rawbits, int startIndex, int length)
{
int res = 0;
for (int i = startIndex; i < startIndex + length; i++)
{
res <<= 1;
if (rawbits[i])
{
res |= 1;
}
}
return res;
}
/// <summary>
/// Reads a code of length 8 in an array of bits, padding with zeros
/// </summary>
/// <param name="rawbits"></param>
/// <param name="startIndex"></param>
/// <returns></returns>
private static byte readByte(bool[] rawbits, int startIndex)
{
int n = rawbits.Length - startIndex;
if (n >= 8)
{
return (byte)readCode(rawbits, startIndex, 8);
}
return (byte)(readCode(rawbits, startIndex, n) << (8 - n));
}
/// <summary>
/// Packs a bit array into bytes, most significant bit first
/// </summary>
/// <param name="boolArr"></param>
/// <returns></returns>
internal static byte[] convertBoolArrayToByteArray(bool[] boolArr)
{
byte[] byteArr = new byte[(boolArr.Length + 7) / 8];
for (int i = 0; i < byteArr.Length; i++)
{
byteArr[i] = readByte(boolArr, 8 * i);
}
return byteArr;
}
private static int totalBitsInLayer(int layers, bool compact)
{
return ((compact ? 88 : 112) + 16 * layers) * layers;
}
}
}

View File

@@ -0,0 +1,691 @@
/*
* Copyright 2010 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using ZXing.Common;
using ZXing.Common.Detector;
using ZXing.Common.ReedSolomon;
namespace ZXing.Aztec.Internal
{
/// <summary>
/// Encapsulates logic that can detect an Aztec Code in an image, even if the Aztec Code
/// is rotated or skewed, or partially obscured.
/// </summary>
/// <author>David Olivier</author>
public sealed class Detector
{
private static readonly int[] EXPECTED_CORNER_BITS =
{
0xee0, // 07340 XXX .XX X.. ...
0x1dc, // 00734 ... XXX .XX X..
0x83b, // 04073 X.. ... XXX .XX
0x707, // 03407 .XX X.. ... XXX
};
private readonly BitMatrix image;
private bool compact;
private int nbLayers;
private int nbDataBlocks;
private int nbCenterLayers;
private int shift;
/// <summary>
/// Initializes a new instance of the <see cref="Detector"/> class.
/// </summary>
/// <param name="image">The image.</param>
public Detector(BitMatrix image)
{
this.image = image;
}
/// <summary>
/// Detects an Aztec Code in an image.
/// </summary>
public AztecDetectorResult detect()
{
return detect(false);
}
/// <summary>
/// Detects an Aztec Code in an image.
/// </summary>
/// <param name="isMirror">if true, image is a mirror-image of original.</param>
/// <returns>
/// encapsulating results of detecting an Aztec Code
/// </returns>
public AztecDetectorResult detect(bool isMirror)
{
// 1. Get the center of the aztec matrix
var pCenter = getMatrixCenter();
if (pCenter == null)
return null;
// 2. Get the center points of the four diagonal points just outside the bull's eye
// [topRight, bottomRight, bottomLeft, topLeft]
var bullsEyeCorners = getBullsEyeCorners(pCenter);
if (bullsEyeCorners == null)
{
return null;
}
if (isMirror)
{
ResultPoint temp = bullsEyeCorners[0];
bullsEyeCorners[0] = bullsEyeCorners[2];
bullsEyeCorners[2] = temp;
}
// 3. Get the size of the matrix and other parameters from the bull's eye
if (!extractParameters(bullsEyeCorners))
{
return null;
}
// 4. Sample the grid
var bits = sampleGrid(image,
bullsEyeCorners[shift % 4],
bullsEyeCorners[(shift + 1) % 4],
bullsEyeCorners[(shift + 2) % 4],
bullsEyeCorners[(shift + 3) % 4]);
if (bits == null)
{
return null;
}
// 5. Get the corners of the matrix.
var corners = getMatrixCornerPoints(bullsEyeCorners);
if (corners == null)
{
return null;
}
return new AztecDetectorResult(bits, corners, compact, nbDataBlocks, nbLayers);
}
/// <summary>
/// Extracts the number of data layers and data blocks from the layer around the bull's eye
/// </summary>
/// <param name="bullsEyeCorners">bullEyeCornerPoints the array of bull's eye corners</param>
/// <returns></returns>
private bool extractParameters(ResultPoint[] bullsEyeCorners)
{
if (!isValid(bullsEyeCorners[0]) || !isValid(bullsEyeCorners[1]) ||
!isValid(bullsEyeCorners[2]) || !isValid(bullsEyeCorners[3]))
{
return false;
}
int length = 2 * nbCenterLayers;
// Get the bits around the bull's eye
int[] sides =
{
sampleLine(bullsEyeCorners[0], bullsEyeCorners[1], length), // Right side
sampleLine(bullsEyeCorners[1], bullsEyeCorners[2], length), // Bottom
sampleLine(bullsEyeCorners[2], bullsEyeCorners[3], length), // Left side
sampleLine(bullsEyeCorners[3], bullsEyeCorners[0], length) // Top
};
// bullsEyeCorners[shift] is the corner of the bulls'eye that has three
// orientation marks.
// sides[shift] is the row/column that goes from the corner with three
// orientation marks to the corner with two.
shift = getRotation(sides, length);
if (shift < 0)
return false;
// Flatten the parameter bits into a single 28- or 40-bit long
long parameterData = 0;
for (int i = 0; i < 4; i++)
{
int side = sides[(shift + i) % 4];
if (compact)
{
// Each side of the form ..XXXXXXX. where Xs are parameter data
parameterData <<= 7;
parameterData += (side >> 1) & 0x7F;
}
else
{
// Each side of the form ..XXXXX.XXXXX. where Xs are parameter data
parameterData <<= 10;
parameterData += ((side >> 2) & (0x1f << 5)) + ((side >> 1) & 0x1F);
}
}
// Corrects parameter data using RS. Returns just the data portion
// without the error correction.
int correctedData = getCorrectedParameterData(parameterData, compact);
if (correctedData < 0)
return false;
if (compact)
{
// 8 bits: 2 bits layers and 6 bits data blocks
nbLayers = (correctedData >> 6) + 1;
nbDataBlocks = (correctedData & 0x3F) + 1;
}
else
{
// 16 bits: 5 bits layers and 11 bits data blocks
nbLayers = (correctedData >> 11) + 1;
nbDataBlocks = (correctedData & 0x7FF) + 1;
}
return true;
}
private static int getRotation(int[] sides, int length)
{
// In a normal pattern, we expect to See
// ** .* D A
// * *
//
// . *
// .. .. C B
//
// Grab the 3 bits from each of the sides the form the locator pattern and concatenate
// into a 12-bit integer. Start with the bit at A
int cornerBits = 0;
foreach (int side in sides)
{
// XX......X where X's are orientation marks
int t = ((side >> (length - 2)) << 1) + (side & 1);
cornerBits = (cornerBits << 3) + t;
}
// Mov the bottom bit to the top, so that the three bits of the locator pattern at A are
// together. cornerBits is now:
// 3 orientation bits at A || 3 orientation bits at B || ... || 3 orientation bits at D
cornerBits = ((cornerBits & 1) << 11) + (cornerBits >> 1);
// The result shift indicates which element of BullsEyeCorners[] goes into the top-left
// corner. Since the four rotation values have a Hamming distance of 8, we
// can easily tolerate two errors.
for (int shift = 0; shift < 4; shift++)
{
if (SupportClass.bitCount(cornerBits ^ EXPECTED_CORNER_BITS[shift]) <= 2)
{
return shift;
}
}
return -1;
}
/// <summary>
/// Corrects the parameter bits using Reed-Solomon algorithm
/// </summary>
/// <param name="parameterData">paremeter bits</param>
/// <param name="compact">compact true if this is a compact Aztec code</param>
/// <returns></returns>
private static int getCorrectedParameterData(long parameterData, bool compact)
{
int numCodewords;
int numDataCodewords;
if (compact)
{
numCodewords = 7;
numDataCodewords = 2;
}
else
{
numCodewords = 10;
numDataCodewords = 4;
}
int numECCodewords = numCodewords - numDataCodewords;
int[] parameterWords = new int[numCodewords];
for (int i = numCodewords - 1; i >= 0; --i)
{
parameterWords[i] = (int)parameterData & 0xF;
parameterData >>= 4;
}
var rsDecoder = new ReedSolomonDecoder(GenericGF.AZTEC_PARAM);
if (!rsDecoder.decode(parameterWords, numECCodewords))
return -1;
// Toss the error correction. Just return the data as an integer
int result = 0;
for (int i = 0; i < numDataCodewords; i++)
{
result = (result << 4) + parameterWords[i];
}
return result;
}
/// <summary>
/// Finds the corners of a bull-eye centered on the passed point
/// This returns the centers of the diagonal points just outside the bull's eye
/// Returns [topRight, bottomRight, bottomLeft, topLeft]
/// </summary>
/// <param name="pCenter">Center point</param>
/// <returns>The corners of the bull-eye</returns>
private ResultPoint[] getBullsEyeCorners(Point pCenter)
{
Point pina = pCenter;
Point pinb = pCenter;
Point pinc = pCenter;
Point pind = pCenter;
bool color = true;
for (nbCenterLayers = 1; nbCenterLayers < 9; nbCenterLayers++)
{
Point pouta = getFirstDifferent(pina, color, 1, -1);
Point poutb = getFirstDifferent(pinb, color, 1, 1);
Point poutc = getFirstDifferent(pinc, color, -1, 1);
Point poutd = getFirstDifferent(pind, color, -1, -1);
//d a
//
//c b
if (nbCenterLayers > 2)
{
float q = distance(poutd, pouta) * nbCenterLayers / (distance(pind, pina) * (nbCenterLayers + 2));
if (q < 0.75 || q > 1.25 || !isWhiteOrBlackRectangle(pouta, poutb, poutc, poutd))
{
break;
}
}
pina = pouta;
pinb = poutb;
pinc = poutc;
pind = poutd;
color = !color;
}
if (nbCenterLayers != 5 && nbCenterLayers != 7)
{
return null;
}
compact = nbCenterLayers == 5;
// Expand the square by .5 pixel in each direction so that we're on the border
// between the white square and the black square
var pinax = new ResultPoint(pina.X + 0.5f, pina.Y - 0.5f);
var pinbx = new ResultPoint(pinb.X + 0.5f, pinb.Y + 0.5f);
var pincx = new ResultPoint(pinc.X - 0.5f, pinc.Y + 0.5f);
var pindx = new ResultPoint(pind.X - 0.5f, pind.Y - 0.5f);
// Expand the square so that its corners are the centers of the points
// just outside the bull's eye.
return expandSquare(new[] { pinax, pinbx, pincx, pindx },
2 * nbCenterLayers - 3,
2 * nbCenterLayers);
}
/// <summary>
/// Finds a candidate center point of an Aztec code from an image
/// </summary>
/// <returns>the center point</returns>
private Point getMatrixCenter()
{
ResultPoint pointA;
ResultPoint pointB;
ResultPoint pointC;
ResultPoint pointD;
int cx;
int cy;
//Get a white rectangle that can be the border of the matrix in center bull's eye or
var whiteDetector = WhiteRectangleDetector.Create(image);
if (whiteDetector == null)
return null;
ResultPoint[] cornerPoints = whiteDetector.detect();
if (cornerPoints != null)
{
pointA = cornerPoints[0];
pointB = cornerPoints[1];
pointC = cornerPoints[2];
pointD = cornerPoints[3];
}
else
{
// This exception can be in case the initial rectangle is white
// In that case, surely in the bull's eye, we try to expand the rectangle.
cx = image.Width / 2;
cy = image.Height / 2;
pointA = getFirstDifferent(new Point(cx + 7, cy - 7), false, 1, -1).toResultPoint();
pointB = getFirstDifferent(new Point(cx + 7, cy + 7), false, 1, 1).toResultPoint();
pointC = getFirstDifferent(new Point(cx - 7, cy + 7), false, -1, 1).toResultPoint();
pointD = getFirstDifferent(new Point(cx - 7, cy - 7), false, -1, -1).toResultPoint();
}
//Compute the center of the rectangle
cx = MathUtils.round((pointA.X + pointD.X + pointB.X + pointC.X) / 4.0f);
cy = MathUtils.round((pointA.Y + pointD.Y + pointB.Y + pointC.Y) / 4.0f);
// Redetermine the white rectangle starting from previously computed center.
// This will ensure that we end up with a white rectangle in center bull's eye
// in order to compute a more accurate center.
whiteDetector = WhiteRectangleDetector.Create(image, 15, cx, cy);
if (whiteDetector == null)
return null;
cornerPoints = whiteDetector.detect();
if (cornerPoints != null)
{
pointA = cornerPoints[0];
pointB = cornerPoints[1];
pointC = cornerPoints[2];
pointD = cornerPoints[3];
}
else
{
// This exception can be in case the initial rectangle is white
// In that case we try to expand the rectangle.
pointA = getFirstDifferent(new Point(cx + 7, cy - 7), false, 1, -1).toResultPoint();
pointB = getFirstDifferent(new Point(cx + 7, cy + 7), false, 1, 1).toResultPoint();
pointC = getFirstDifferent(new Point(cx - 7, cy + 7), false, -1, 1).toResultPoint();
pointD = getFirstDifferent(new Point(cx - 7, cy - 7), false, -1, -1).toResultPoint();
}
// Recompute the center of the rectangle
cx = MathUtils.round((pointA.X + pointD.X + pointB.X + pointC.X) / 4.0f);
cy = MathUtils.round((pointA.Y + pointD.Y + pointB.Y + pointC.Y) / 4.0f);
return new Point(cx, cy);
}
/// <summary>
/// Gets the Aztec code corners from the bull's eye corners and the parameters.
/// </summary>
/// <param name="bullsEyeCorners">the array of bull's eye corners</param>
/// <returns>the array of aztec code corners</returns>
private ResultPoint[] getMatrixCornerPoints(ResultPoint[] bullsEyeCorners)
{
return expandSquare(bullsEyeCorners, 2 * nbCenterLayers, getDimension());
}
/// <summary>
/// Creates a BitMatrix by sampling the provided image.
/// topLeft, topRight, bottomRight, and bottomLeft are the centers of the squares on the
/// diagonal just outside the bull's eye.
/// </summary>
/// <param name="image">The image.</param>
/// <param name="topLeft">The top left.</param>
/// <param name="bottomLeft">The bottom left.</param>
/// <param name="bottomRight">The bottom right.</param>
/// <param name="topRight">The top right.</param>
/// <returns></returns>
private BitMatrix sampleGrid(BitMatrix image,
ResultPoint topLeft,
ResultPoint topRight,
ResultPoint bottomRight,
ResultPoint bottomLeft)
{
GridSampler sampler = GridSampler.Instance;
int dimension = getDimension();
float low = dimension / 2.0f - nbCenterLayers;
float high = dimension / 2.0f + nbCenterLayers;
return sampler.sampleGrid(image,
dimension,
dimension,
low, low, // topleft
high, low, // topright
high, high, // bottomright
low, high, // bottomleft
topLeft.X, topLeft.Y,
topRight.X, topRight.Y,
bottomRight.X, bottomRight.Y,
bottomLeft.X, bottomLeft.Y);
}
/// <summary>
/// Samples a line
/// </summary>
/// <param name="p1">start point (inclusive)</param>
/// <param name="p2">end point (exclusive)</param>
/// <param name="size">number of bits</param>
/// <returns> the array of bits as an int (first bit is high-order bit of result)</returns>
private int sampleLine(ResultPoint p1, ResultPoint p2, int size)
{
int result = 0;
float d = distance(p1, p2);
float moduleSize = d / size;
float px = p1.X;
float py = p1.Y;
float dx = moduleSize * (p2.X - p1.X) / d;
float dy = moduleSize * (p2.Y - p1.Y) / d;
for (int i = 0; i < size; i++)
{
if (image[MathUtils.round(px + i * dx), MathUtils.round(py + i * dy)])
{
result |= 1 << (size - i - 1);
}
}
return result;
}
/// <summary>
/// Determines whether [is white or black rectangle] [the specified p1].
/// </summary>
/// <param name="p1">The p1.</param>
/// <param name="p2">The p2.</param>
/// <param name="p3">The p3.</param>
/// <param name="p4">The p4.</param>
/// <returns>true if the border of the rectangle passed in parameter is compound of white points only
/// or black points only</returns>
private bool isWhiteOrBlackRectangle(Point p1, Point p2, Point p3, Point p4)
{
const int corr = 3;
p1 = new Point(p1.X - corr, p1.Y + corr);
p2 = new Point(p2.X - corr, p2.Y - corr);
p3 = new Point(p3.X + corr, p3.Y - corr);
p4 = new Point(p4.X + corr, p4.Y + corr);
int cInit = getColor(p4, p1);
if (cInit == 0)
{
return false;
}
int c = getColor(p1, p2);
if (c != cInit)
{
return false;
}
c = getColor(p2, p3);
if (c != cInit)
{
return false;
}
c = getColor(p3, p4);
return c == cInit;
}
/// <summary>
/// Gets the color of a segment
/// </summary>
/// <param name="p1">The p1.</param>
/// <param name="p2">The p2.</param>
/// <returns>1 if segment more than 90% black, -1 if segment is more than 90% white, 0 else</returns>
private int getColor(Point p1, Point p2)
{
float d = distance(p1, p2);
float dx = (p2.X - p1.X) / d;
float dy = (p2.Y - p1.Y) / d;
int error = 0;
float px = p1.X;
float py = p1.Y;
bool colorModel = image[p1.X, p1.Y];
int iMax = (int)Math.Ceiling(d);
for (int i = 0; i < iMax; i++)
{
px += dx;
py += dy;
if (image[MathUtils.round(px), MathUtils.round(py)] != colorModel)
{
error++;
}
}
float errRatio = error / d;
if (errRatio > 0.1f && errRatio < 0.9f)
{
return 0;
}
return (errRatio <= 0.1f) == colorModel ? 1 : -1;
}
/// <summary>
/// Gets the coordinate of the first point with a different color in the given direction
/// </summary>
/// <param name="init">The init.</param>
/// <param name="color">if set to <c>true</c> [color].</param>
/// <param name="dx">The dx.</param>
/// <param name="dy">The dy.</param>
/// <returns></returns>
private Point getFirstDifferent(Point init, bool color, int dx, int dy)
{
int x = init.X + dx;
int y = init.Y + dy;
while (isValid(x, y) && image[x, y] == color)
{
x += dx;
y += dy;
}
x -= dx;
y -= dy;
while (isValid(x, y) && image[x, y] == color)
{
x += dx;
}
x -= dx;
while (isValid(x, y) && image[x, y] == color)
{
y += dy;
}
y -= dy;
return new Point(x, y);
}
/// <summary>
/// Expand the square represented by the corner points by pushing out equally in all directions
/// </summary>
/// <param name="cornerPoints">the corners of the square, which has the bull's eye at its center</param>
/// <param name="oldSide">the original length of the side of the square in the target bit matrix</param>
/// <param name="newSide">the new length of the size of the square in the target bit matrix</param>
/// <returns>the corners of the expanded square</returns>
private static ResultPoint[] expandSquare(ResultPoint[] cornerPoints, int oldSide, int newSide)
{
float ratio = newSide / (2.0f * oldSide);
float dx = cornerPoints[0].X - cornerPoints[2].X;
float dy = cornerPoints[0].Y - cornerPoints[2].Y;
float centerx = (cornerPoints[0].X + cornerPoints[2].X) / 2.0f;
float centery = (cornerPoints[0].Y + cornerPoints[2].Y) / 2.0f;
var result0 = new ResultPoint(centerx + ratio * dx, centery + ratio * dy);
var result2 = new ResultPoint(centerx - ratio * dx, centery - ratio * dy);
dx = cornerPoints[1].X - cornerPoints[3].X;
dy = cornerPoints[1].Y - cornerPoints[3].Y;
centerx = (cornerPoints[1].X + cornerPoints[3].X) / 2.0f;
centery = (cornerPoints[1].Y + cornerPoints[3].Y) / 2.0f;
var result1 = new ResultPoint(centerx + ratio * dx, centery + ratio * dy);
var result3 = new ResultPoint(centerx - ratio * dx, centery - ratio * dy);
return new ResultPoint[] { result0, result1, result2, result3 };
}
private bool isValid(int x, int y)
{
return x >= 0 && x < image.Width && y > 0 && y < image.Height;
}
private bool isValid(ResultPoint point)
{
int x = MathUtils.round(point.X);
int y = MathUtils.round(point.Y);
return isValid(x, y);
}
// L2 distance
private static float distance(Point a, Point b)
{
return MathUtils.distance(a.X, a.Y, b.X, b.Y);
}
private static float distance(ResultPoint a, ResultPoint b)
{
return MathUtils.distance(a.X, a.Y, b.X, b.Y);
}
private int getDimension()
{
if (compact)
{
return 4 * nbLayers + 11;
}
if (nbLayers <= 4)
{
return 4 * nbLayers + 15;
}
return 4 * nbLayers + 2 * ((nbLayers - 4) / 8 + 1) + 15;
}
internal sealed class Point
{
public int X { get; private set; }
public int Y { get; private set; }
public ResultPoint toResultPoint()
{
return new ResultPoint(X, Y);
}
internal Point(int x, int y)
{
X = x;
Y = y;
}
public override String ToString()
{
return "<" + X + ' ' + Y + '>';
}
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2013 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using ZXing.Common;
namespace ZXing.Aztec.Internal
{
/// <summary>
/// Aztec 2D code representation
/// </summary>
/// <author>Rustam Abdullaev</author>
public sealed class AztecCode
{
/// <summary>
/// Compact or full symbol indicator
/// </summary>
public bool isCompact { get; set; }
/// <summary>
/// Size in pixels (width and height)
/// </summary>
public int Size { get; set; }
/// <summary>
/// Number of levels
/// </summary>
public int Layers { get; set; }
/// <summary>
/// Number of data codewords
/// </summary>
public int CodeWords { get; set; }
/// <summary>
/// The symbol image
/// </summary>
public BitMatrix Matrix { get; set; }
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2013 ZXing.Net authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.ComponentModel;
using ZXing.Common;
namespace ZXing.Aztec
{
/// <summary>
/// The class holds the available options for the <see cref="AztecWriter" />
/// </summary>
[Serializable]
public class AztecEncodingOptions : EncodingOptions
{
/// <summary>
/// Representing the minimal percentage of error correction words.
/// Note: an Aztec symbol should have a minimum of 25% EC words.
/// </summary>
#if !NETSTANDARD && !NETFX_CORE && !WindowsCE && !SILVERLIGHT && !PORTABLE && !UNITY
[CategoryAttribute("Standard"), DescriptionAttribute("The minimal percentage of error correction words (> 25%).")]
#endif
public int? ErrorCorrection
{
get
{
if (Hints.ContainsKey(EncodeHintType.ERROR_CORRECTION))
{
return (int) Hints[EncodeHintType.ERROR_CORRECTION];
}
return null;
}
set
{
if (value == null)
{
if (Hints.ContainsKey(EncodeHintType.ERROR_CORRECTION))
Hints.Remove(EncodeHintType.ERROR_CORRECTION);
}
else
{
Hints[EncodeHintType.ERROR_CORRECTION] = value;
}
}
}
/// <summary>
/// Specifies the required number of layers for an Aztec code:
/// a negative number (-1, -2, -3, -4) specifies a compact Aztec code
/// 0 indicates to use the minimum number of layers (the default)
/// a positive number (1, 2, .. 32) specifies a normal (non-compact) Aztec code
/// </summary>
#if !NETSTANDARD && !NETFX_CORE && !WindowsCE && !SILVERLIGHT && !PORTABLE && !UNITY
[CategoryAttribute("Standard"), DescriptionAttribute("The required number of layers for an Aztec code" +
" -1 to -4 specify a compact code, 0 indicates to use the minimum number of layers and" +
" 1 to 32 specify a normal (non-compact) Aztec code.")]
#endif
public int? Layers
{
get
{
if (Hints.ContainsKey(EncodeHintType.AZTEC_LAYERS))
{
return (int) Hints[EncodeHintType.AZTEC_LAYERS];
}
return null;
}
set
{
if (value == null)
{
if (Hints.ContainsKey(EncodeHintType.AZTEC_LAYERS))
Hints.Remove(EncodeHintType.AZTEC_LAYERS);
}
else
{
Hints[EncodeHintType.AZTEC_LAYERS] = value;
}
}
}
/// <summary>
/// Specifies what character encoding to use where applicable (type <see cref="String"/>)
/// </summary>
#if !NETSTANDARD && !NETFX_CORE && !WindowsCE && !SILVERLIGHT && !PORTABLE && !UNITY
[CategoryAttribute("Standard"), DescriptionAttribute("Specifies what character encoding to use where applicable.")]
#endif
public string CharacterSet
{
get
{
if (Hints.ContainsKey(EncodeHintType.CHARACTER_SET))
{
return (string) Hints[EncodeHintType.CHARACTER_SET];
}
return null;
}
set
{
if (value == null)
{
if (Hints.ContainsKey(EncodeHintType.CHARACTER_SET))
Hints.Remove(EncodeHintType.CHARACTER_SET);
}
else
{
Hints[EncodeHintType.CHARACTER_SET] = value;
}
}
}
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2013 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using ZXing.Common;
namespace ZXing.Aztec.Internal
{
/// <summary>
/// represents a token for a binary shift
/// </summary>
public sealed class BinaryShiftToken : Token
{
private readonly short binaryShiftStart;
private readonly short binaryShiftByteCount;
/// <summary>
/// initializing constructor
/// </summary>
/// <param name="previous"></param>
/// <param name="binaryShiftStart"></param>
/// <param name="binaryShiftByteCount"></param>
public BinaryShiftToken(Token previous,
int binaryShiftStart,
int binaryShiftByteCount)
: base(previous)
{
this.binaryShiftStart = (short)binaryShiftStart;
this.binaryShiftByteCount = (short)binaryShiftByteCount;
}
/// <summary>
/// appends the byte array to the BitArray
/// </summary>
/// <param name="bitArray"></param>
/// <param name="text"></param>
public override void appendTo(BitArray bitArray, byte[] text)
{
for (int i = 0; i < binaryShiftByteCount; i++)
{
if (i == 0 || (i == 31 && binaryShiftByteCount <= 62))
{
// We need a header before the first character, and before
// character 31 when the total byte code is <= 62
bitArray.appendBits(31, 5); // BINARY_SHIFT
if (binaryShiftByteCount > 62)
{
bitArray.appendBits(binaryShiftByteCount - 31, 16);
}
else if (i == 0)
{
// 1 <= binaryShiftByteCode <= 62
bitArray.appendBits(Math.Min(binaryShiftByteCount, (short)31), 5);
}
else
{
// 32 <= binaryShiftCount <= 62 and i == 31
bitArray.appendBits(binaryShiftByteCount - 31, 5);
}
}
bitArray.appendBits(text[binaryShiftStart + i], 8);
}
}
/// <summary>
/// string representation
/// </summary>
/// <returns></returns>
public override String ToString()
{
return "<" + binaryShiftStart + "::" + (binaryShiftStart + binaryShiftByteCount - 1) + '>';
}
}
}

View File

@@ -0,0 +1,430 @@
/*
* Copyright 2013 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using ZXing.Common;
using ZXing.Common.ReedSolomon;
namespace ZXing.Aztec.Internal
{
/// <summary>
/// Generates Aztec 2D barcodes.
/// </summary>
/// <author>Rustam Abdullaev</author>
public static class Encoder
{
/// <summary>
/// default EC percent 33
/// </summary>
public const int DEFAULT_EC_PERCENT = 33; // default minimal percentage of error check words
/// <summary>
/// default layers 0
/// </summary>
public const int DEFAULT_AZTEC_LAYERS = 0;
private const int MAX_NB_BITS = 32;
private const int MAX_NB_BITS_COMPACT = 4;
private static readonly int[] WORD_SIZE = {
4, 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
};
/// <summary>
/// Encodes the given binary content as an Aztec symbol
/// </summary>
/// <param name="data">input data string</param>
/// <returns>Aztec symbol matrix with metadata</returns>
public static AztecCode encode(byte[] data)
{
return encode(data, DEFAULT_EC_PERCENT, DEFAULT_AZTEC_LAYERS);
}
/// <summary>
/// Encodes the given binary content as an Aztec symbol
/// </summary>
/// <param name="data">input data string</param>
/// <param name="minECCPercent">minimal percentage of error check words (According to ISO/IEC 24778:2008,
/// a minimum of 23% + 3 words is recommended)</param>
/// <param name="userSpecifiedLayers">if non-zero, a user-specified value for the number of layers</param>
/// <returns>
/// Aztec symbol matrix with metadata
/// </returns>
public static AztecCode encode(byte[] data, int minECCPercent, int userSpecifiedLayers)
{
// High-level encode
var bits = new HighLevelEncoder(data).encode();
// stuff bits and choose symbol size
int eccBits = bits.Size * minECCPercent / 100 + 11;
int totalSizeBits = bits.Size + eccBits;
bool compact;
int layers;
int totalBitsInLayer;
int wordSize;
BitArray stuffedBits;
if (userSpecifiedLayers != DEFAULT_AZTEC_LAYERS)
{
compact = userSpecifiedLayers < 0;
layers = Math.Abs(userSpecifiedLayers);
if (layers > (compact ? MAX_NB_BITS_COMPACT : MAX_NB_BITS))
{
throw new ArgumentException(
String.Format("Illegal value {0} for layers", userSpecifiedLayers));
}
totalBitsInLayer = TotalBitsInLayer(layers, compact);
wordSize = WORD_SIZE[layers];
int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);
stuffedBits = stuffBits(bits, wordSize);
if (stuffedBits.Size + eccBits > usableBitsInLayers)
{
throw new ArgumentException("Data to large for user specified layer");
}
if (compact && stuffedBits.Size > wordSize * 64)
{
// Compact format only allows 64 data words, though C4 can hold more words than that
throw new ArgumentException("Data to large for user specified layer");
}
}
else
{
wordSize = 0;
stuffedBits = null;
// We look at the possible table sizes in the order Compact1, Compact2, Compact3,
// Compact4, Normal4,... Normal(i) for i < 4 isn't typically used since Compact(i+1)
// is the same size, but has more data.
for (int i = 0; ; i++)
{
if (i > MAX_NB_BITS)
{
throw new ArgumentException("Data too large for an Aztec code");
}
compact = i <= 3;
layers = compact ? i + 1 : i;
totalBitsInLayer = TotalBitsInLayer(layers, compact);
if (totalSizeBits > totalBitsInLayer)
{
continue;
}
// [Re]stuff the bits if this is the first opportunity, or if the
// wordSize has changed
if (stuffedBits == null || wordSize != WORD_SIZE[layers])
{
wordSize = WORD_SIZE[layers];
stuffedBits = stuffBits(bits, wordSize);
}
int usableBitsInLayers = totalBitsInLayer - (totalBitsInLayer % wordSize);
if (compact && stuffedBits.Size > wordSize * 64)
{
// Compact format only allows 64 data words, though C4 can hold more words than that
continue;
}
if (stuffedBits.Size + eccBits <= usableBitsInLayers)
{
break;
}
}
}
BitArray messageBits = generateCheckWords(stuffedBits, totalBitsInLayer, wordSize);
// generate mode message
int messageSizeInWords = stuffedBits.Size / wordSize;
var modeMessage = generateModeMessage(compact, layers, messageSizeInWords);
// allocate symbol
int baseMatrixSize = (compact ? 11 : 14) + layers * 4; // not including alignment lines
var alignmentMap = new int[baseMatrixSize];
int matrixSize;
if (compact)
{
// no alignment marks in compact mode, alignmentMap is a no-op
matrixSize = baseMatrixSize;
for (int i = 0; i < alignmentMap.Length; i++)
{
alignmentMap[i] = i;
}
}
else
{
matrixSize = baseMatrixSize + 1 + 2 * ((baseMatrixSize / 2 - 1) / 15);
int origCenter = baseMatrixSize / 2;
int center = matrixSize / 2;
for (int i = 0; i < origCenter; i++)
{
int newOffset = i + i / 15;
alignmentMap[origCenter - i - 1] = center - newOffset - 1;
alignmentMap[origCenter + i] = center + newOffset + 1;
}
}
var matrix = new BitMatrix(matrixSize);
// draw data bits
for (int i = 0, rowOffset = 0; i < layers; i++)
{
int rowSize = (layers - i) * 4 + (compact ? 9 : 12);
for (int j = 0; j < rowSize; j++)
{
int columnOffset = j * 2;
for (int k = 0; k < 2; k++)
{
if (messageBits[rowOffset + columnOffset + k])
{
matrix[alignmentMap[i * 2 + k], alignmentMap[i * 2 + j]] = true;
}
if (messageBits[rowOffset + rowSize * 2 + columnOffset + k])
{
matrix[alignmentMap[i * 2 + j], alignmentMap[baseMatrixSize - 1 - i * 2 - k]] = true;
}
if (messageBits[rowOffset + rowSize * 4 + columnOffset + k])
{
matrix[alignmentMap[baseMatrixSize - 1 - i * 2 - k], alignmentMap[baseMatrixSize - 1 - i * 2 - j]] = true;
}
if (messageBits[rowOffset + rowSize * 6 + columnOffset + k])
{
matrix[alignmentMap[baseMatrixSize - 1 - i * 2 - j], alignmentMap[i * 2 + k]] = true;
}
}
}
rowOffset += rowSize * 8;
}
// draw mode message
drawModeMessage(matrix, compact, matrixSize, modeMessage);
// draw alignment marks
if (compact)
{
drawBullsEye(matrix, matrixSize / 2, 5);
}
else
{
drawBullsEye(matrix, matrixSize / 2, 7);
for (int i = 0, j = 0; i < baseMatrixSize / 2 - 1; i += 15, j += 16)
{
for (int k = (matrixSize / 2) & 1; k < matrixSize; k += 2)
{
matrix[matrixSize / 2 - j, k] = true;
matrix[matrixSize / 2 + j, k] = true;
matrix[k, matrixSize / 2 - j] = true;
matrix[k, matrixSize / 2 + j] = true;
}
}
}
return new AztecCode
{
isCompact = compact,
Size = matrixSize,
Layers = layers,
CodeWords = messageSizeInWords,
Matrix = matrix
};
}
private static void drawBullsEye(BitMatrix matrix, int center, int size)
{
for (var i = 0; i < size; i += 2)
{
for (var j = center - i; j <= center + i; j++)
{
matrix[j, center - i] = true;
matrix[j, center + i] = true;
matrix[center - i, j] = true;
matrix[center + i, j] = true;
}
}
matrix[center - size, center - size] = true;
matrix[center - size + 1, center - size] = true;
matrix[center - size, center - size + 1] = true;
matrix[center + size, center - size] = true;
matrix[center + size, center - size + 1] = true;
matrix[center + size, center + size - 1] = true;
}
internal static BitArray generateModeMessage(bool compact, int layers, int messageSizeInWords)
{
var modeMessage = new BitArray();
if (compact)
{
modeMessage.appendBits(layers - 1, 2);
modeMessage.appendBits(messageSizeInWords - 1, 6);
modeMessage = generateCheckWords(modeMessage, 28, 4);
}
else
{
modeMessage.appendBits(layers - 1, 5);
modeMessage.appendBits(messageSizeInWords - 1, 11);
modeMessage = generateCheckWords(modeMessage, 40, 4);
}
return modeMessage;
}
private static void drawModeMessage(BitMatrix matrix, bool compact, int matrixSize, BitArray modeMessage)
{
int center = matrixSize / 2;
if (compact)
{
for (var i = 0; i < 7; i++)
{
int offset = center - 3 + i;
if (modeMessage[i])
{
matrix[offset, center - 5] = true;
}
if (modeMessage[i + 7])
{
matrix[center + 5, offset] = true;
}
if (modeMessage[20 - i])
{
matrix[offset, center + 5] = true;
}
if (modeMessage[27 - i])
{
matrix[center - 5, offset] = true;
}
}
}
else
{
for (var i = 0; i < 10; i++)
{
int offset = center - 5 + i + i / 5;
if (modeMessage[i])
{
matrix[offset, center - 7] = true;
}
if (modeMessage[i + 10])
{
matrix[center + 7, offset] = true;
}
if (modeMessage[29 - i])
{
matrix[offset, center + 7] = true;
}
if (modeMessage[39 - i])
{
matrix[center - 7, offset] = true;
}
}
}
}
private static BitArray generateCheckWords(BitArray bitArray, int totalBits, int wordSize)
{
if (bitArray.Size % wordSize != 0)
throw new InvalidOperationException("size of bit array is not a multiple of the word size");
// bitArray is guaranteed to be a multiple of the wordSize, so no padding needed
int messageSizeInWords = bitArray.Size / wordSize;
var rs = new ReedSolomonEncoder(getGF(wordSize));
var totalWords = totalBits / wordSize;
var messageWords = bitsToWords(bitArray, wordSize, totalWords);
rs.encode(messageWords, totalWords - messageSizeInWords);
var startPad = totalBits % wordSize;
var messageBits = new BitArray();
messageBits.appendBits(0, startPad);
foreach (var messageWord in messageWords)
{
messageBits.appendBits(messageWord, wordSize);
}
return messageBits;
}
private static int[] bitsToWords(BitArray stuffedBits, int wordSize, int totalWords)
{
var message = new int[totalWords];
int i;
int n;
for (i = 0, n = stuffedBits.Size / wordSize; i < n; i++)
{
int value = 0;
for (int j = 0; j < wordSize; j++)
{
value |= stuffedBits[i * wordSize + j] ? (1 << wordSize - j - 1) : 0;
}
message[i] = value;
}
return message;
}
private static GenericGF getGF(int wordSize)
{
switch (wordSize)
{
case 4:
return GenericGF.AZTEC_PARAM;
case 6:
return GenericGF.AZTEC_DATA_6;
case 8:
return GenericGF.AZTEC_DATA_8;
case 10:
return GenericGF.AZTEC_DATA_10;
case 12:
return GenericGF.AZTEC_DATA_12;
default:
throw new ArgumentException("Unsupported word size " + wordSize);
}
}
internal static BitArray stuffBits(BitArray bits, int wordSize)
{
var @out = new BitArray();
int n = bits.Size;
int mask = (1 << wordSize) - 2;
for (int i = 0; i < n; i += wordSize)
{
int word = 0;
for (int j = 0; j < wordSize; j++)
{
if (i + j >= n || bits[i + j])
{
word |= 1 << (wordSize - 1 - j);
}
}
if ((word & mask) == mask)
{
@out.appendBits(word & mask, wordSize);
i--;
}
else if ((word & mask) == 0)
{
@out.appendBits(word | 1, wordSize);
i--;
}
else
{
@out.appendBits(word, wordSize);
}
}
return @out;
}
private static int TotalBitsInLayer(int layers, bool compact)
{
return ((compact ? 88 : 112) + 16 * layers) * layers;
}
}
}

View File

@@ -0,0 +1,383 @@
/*
* Copyright 2013 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ZXing.Common;
namespace ZXing.Aztec.Internal
{
/// <summary>
/// This produces nearly optimal encodings of text into the first-level of
/// encoding used by Aztec code.
/// It uses a dynamic algorithm. For each prefix of the string, it determines
/// a set of encodings that could lead to this prefix. We repeatedly add a
/// character and generate a new set of optimal encodings until we have read
/// through the entire input.
/// @author Frank Yellin
/// @author Rustam Abdullaev
/// </summary>
public sealed class HighLevelEncoder
{
internal static String[] MODE_NAMES = { "UPPER", "LOWER", "DIGIT", "MIXED", "PUNCT" };
internal const int MODE_UPPER = 0; // 5 bits
internal const int MODE_LOWER = 1; // 5 bits
internal const int MODE_DIGIT = 2; // 4 bits
internal const int MODE_MIXED = 3; // 5 bits
internal const int MODE_PUNCT = 4; // 5 bits
// The Latch Table shows, for each pair of Modes, the optimal method for
// getting from one mode to another. In the worst possible case, this can
// be up to 14 bits. In the best possible case, we are already there!
// The high half-word of each entry gives the number of bits.
// The low half-word of each entry are the actual bits necessary to change
internal static readonly int[][] LATCH_TABLE = new int[][]
{
new[]
{
0,
(5 << 16) + 28, // UPPER -> LOWER
(5 << 16) + 30, // UPPER -> DIGIT
(5 << 16) + 29, // UPPER -> MIXED
(10 << 16) + (29 << 5) + 30, // UPPER -> MIXED -> PUNCT
},
new[]
{
(9 << 16) + (30 << 4) + 14, // LOWER -> DIGIT -> UPPER
0,
(5 << 16) + 30, // LOWER -> DIGIT
(5 << 16) + 29, // LOWER -> MIXED
(10 << 16) + (29 << 5) + 30, // LOWER -> MIXED -> PUNCT
},
new[]
{
(4 << 16) + 14, // DIGIT -> UPPER
(9 << 16) + (14 << 5) + 28, // DIGIT -> UPPER -> LOWER
0,
(9 << 16) + (14 << 5) + 29, // DIGIT -> UPPER -> MIXED
(14 << 16) + (14 << 10) + (29 << 5) + 30,
// DIGIT -> UPPER -> MIXED -> PUNCT
},
new[]
{
(5 << 16) + 29, // MIXED -> UPPER
(5 << 16) + 28, // MIXED -> LOWER
(10 << 16) + (29 << 5) + 30, // MIXED -> UPPER -> DIGIT
0,
(5 << 16) + 30, // MIXED -> PUNCT
},
new[]
{
(5 << 16) + 31, // PUNCT -> UPPER
(10 << 16) + (31 << 5) + 28, // PUNCT -> UPPER -> LOWER
(10 << 16) + (31 << 5) + 30, // PUNCT -> UPPER -> DIGIT
(10 << 16) + (31 << 5) + 29, // PUNCT -> UPPER -> MIXED
0,
},
};
// A reverse mapping from [mode][char] to the encoding for that character
// in that mode. An entry of 0 indicates no mapping exists.
internal static readonly int[][] CHAR_MAP = new int[5][];
// A map showing the available shift codes. (The shifts to BINARY are not shown
internal static readonly int[][] SHIFT_TABLE = new int[6][]; // mode shift codes, per table
private readonly byte[] text;
static HighLevelEncoder()
{
CHAR_MAP[0] = new int[256];
CHAR_MAP[1] = new int[256];
CHAR_MAP[2] = new int[256];
CHAR_MAP[3] = new int[256];
CHAR_MAP[4] = new int[256];
SHIFT_TABLE[0] = new int[6];
SHIFT_TABLE[1] = new int[6];
SHIFT_TABLE[2] = new int[6];
SHIFT_TABLE[3] = new int[6];
SHIFT_TABLE[4] = new int[6];
SHIFT_TABLE[5] = new int[6];
CHAR_MAP[MODE_UPPER][' '] = 1;
for (int c = 'A'; c <= 'Z'; c++)
{
CHAR_MAP[MODE_UPPER][c] = c - 'A' + 2;
}
CHAR_MAP[MODE_LOWER][' '] = 1;
for (int c = 'a'; c <= 'z'; c++)
{
CHAR_MAP[MODE_LOWER][c] = c - 'a' + 2;
}
CHAR_MAP[MODE_DIGIT][' '] = 1;
for (int c = '0'; c <= '9'; c++)
{
CHAR_MAP[MODE_DIGIT][c] = c - '0' + 2;
}
CHAR_MAP[MODE_DIGIT][','] = 12;
CHAR_MAP[MODE_DIGIT]['.'] = 13;
int[] mixedTable = {
'\0', ' ', 1, 2, 3, 4, 5, 6, 7, '\b', '\t', '\n', 11, '\f', '\r',
27, 28, 29, 30, 31, '@', '\\', '^', '_', '`', '|', '~', 127
};
for (int i = 0; i < mixedTable.Length; i++)
{
CHAR_MAP[MODE_MIXED][mixedTable[i]] = i;
}
int[] punctTable =
{
'\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'',
'(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
'[', ']', '{', '}'
};
for (int i = 0; i < punctTable.Length; i++)
{
if (punctTable[i] > 0)
{
CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;
}
}
foreach (int[] table in SHIFT_TABLE)
{
SupportClass.Fill(table, -1);
}
SHIFT_TABLE[MODE_UPPER][MODE_PUNCT] = 0;
SHIFT_TABLE[MODE_LOWER][MODE_PUNCT] = 0;
SHIFT_TABLE[MODE_LOWER][MODE_UPPER] = 28;
SHIFT_TABLE[MODE_MIXED][MODE_PUNCT] = 0;
SHIFT_TABLE[MODE_DIGIT][MODE_PUNCT] = 0;
SHIFT_TABLE[MODE_DIGIT][MODE_UPPER] = 15;
}
/// <summary>
///
/// </summary>
/// <param name="text"></param>
public HighLevelEncoder(byte[] text)
{
this.text = text;
}
/// <summary>
/// Convert the text represented by this High Level Encoder into a BitArray.
/// </summary>
/// <returns>text represented by this encoder encoded as a <see cref="BitArray"/></returns>
public BitArray encode()
{
ICollection<State> states = new Collection<State>();
states.Add(State.INITIAL_STATE);
for (int index = 0; index < text.Length; index++)
{
int pairCode;
// don't remove the (int) type cast, mono compiler needs it
int nextChar = (index + 1 < text.Length) ? (int)text[index + 1] : 0;
switch (text[index])
{
case (byte)'\r':
pairCode = nextChar == '\n' ? 2 : 0;
break;
case (byte)'.':
pairCode = nextChar == ' ' ? 3 : 0;
break;
case (byte)',':
pairCode = nextChar == ' ' ? 4 : 0;
break;
case (byte)':':
pairCode = nextChar == ' ' ? 5 : 0;
break;
default:
pairCode = 0;
break;
}
if (pairCode > 0)
{
// We have one of the four special PUNCT pairs. Treat them specially.
// Get a new set of states for the two new characters.
states = updateStateListForPair(states, index, pairCode);
index++;
}
else
{
// Get a new set of states for the new character.
states = updateStateListForChar(states, index);
}
}
// We are left with a set of states. Find the shortest one.
State minState = null;
foreach (var state in states)
{
if (minState == null)
{
minState = state;
}
else
{
if (state.BitCount < minState.BitCount)
{
minState = state;
}
}
}
/*
State minState = Collections.min(states, new Comparator<State>() {
@Override
public int compare(State a, State b) {
return a.getBitCount() - b.getBitCount();
}
});
*/
// Convert it to a bit array, and return.
return minState.toBitArray(text);
}
// We update a set of states for a new character by updating each state
// for the new character, merging the results, and then removing the
// non-optimal states.
private ICollection<State> updateStateListForChar(IEnumerable<State> states, int index)
{
var result = new LinkedList<State>();
foreach (State state in states)
{
updateStateForChar(state, index, result);
}
return simplifyStates(result);
}
// Return a set of states that represent the possible ways of updating this
// state for the next character. The resulting set of states are added to
// the "result" list.
private void updateStateForChar(State state, int index, ICollection<State> result)
{
char ch = (char)(text[index] & 0xFF);
bool charInCurrentTable = CHAR_MAP[state.Mode][ch] > 0;
State stateNoBinary = null;
for (int mode = 0; mode <= MODE_PUNCT; mode++)
{
int charInMode = CHAR_MAP[mode][ch];
if (charInMode > 0)
{
if (stateNoBinary == null)
{
// Only create stateNoBinary the first time it's required.
stateNoBinary = state.endBinaryShift(index);
}
// Try generating the character by latching to its mode
if (!charInCurrentTable || mode == state.Mode || mode == MODE_DIGIT)
{
// If the character is in the current table, we don't want to latch to
// any other mode except possibly digit (which uses only 4 bits). Any
// other latch would be equally successful *after* this character, and
// so wouldn't save any bits.
var latchState = stateNoBinary.latchAndAppend(mode, charInMode);
result.Add(latchState);
}
// Try generating the character by switching to its mode.
if (!charInCurrentTable && SHIFT_TABLE[state.Mode][mode] >= 0)
{
// It never makes sense to temporarily shift to another mode if the
// character exists in the current mode. That can never save bits.
var shiftState = stateNoBinary.shiftAndAppend(mode, charInMode);
result.Add(shiftState);
}
}
}
if (state.BinaryShiftByteCount > 0 || CHAR_MAP[state.Mode][ch] == 0)
{
// It's never worthwhile to go into binary shift mode if you're not already
// in binary shift mode, and the character exists in your current mode.
// That can never save bits over just outputting the char in the current mode.
var binaryState = state.addBinaryShiftChar(index);
result.Add(binaryState);
}
}
private static ICollection<State> updateStateListForPair(IEnumerable<State> states, int index, int pairCode)
{
var result = new LinkedList<State>();
foreach (State state in states)
{
updateStateForPair(state, index, pairCode, result);
}
return simplifyStates(result);
}
private static void updateStateForPair(State state, int index, int pairCode, ICollection<State> result)
{
State stateNoBinary = state.endBinaryShift(index);
// Possibility 1. Latch to MODE_PUNCT, and then append this code
result.Add(stateNoBinary.latchAndAppend(MODE_PUNCT, pairCode));
if (state.Mode != MODE_PUNCT)
{
// Possibility 2. Shift to MODE_PUNCT, and then append this code.
// Every state except MODE_PUNCT (handled above) can shift
result.Add(stateNoBinary.shiftAndAppend(MODE_PUNCT, pairCode));
}
if (pairCode == 3 || pairCode == 4)
{
// both characters are in DIGITS. Sometimes better to just add two digits
var digitState = stateNoBinary
.latchAndAppend(MODE_DIGIT, 16 - pairCode) // period or comma in DIGIT
.latchAndAppend(MODE_DIGIT, 1); // space in DIGIT
result.Add(digitState);
}
if (state.BinaryShiftByteCount > 0)
{
// It only makes sense to do the characters as binary if we're already
// in binary mode.
State binaryState = state.addBinaryShiftChar(index).addBinaryShiftChar(index + 1);
result.Add(binaryState);
}
}
private static ICollection<State> simplifyStates(IEnumerable<State> states)
{
var result = new LinkedList<State>();
var removeList = new List<State>();
foreach (State newState in states)
{
bool add = true;
removeList.Clear();
foreach (var oldState in result)
{
if (oldState.isBetterThanOrEqualTo(newState))
{
add = false;
break;
}
if (newState.isBetterThanOrEqualTo(oldState))
{
removeList.Add(oldState);
}
}
if (add)
{
result.AddLast(newState);
}
foreach (var removeItem in removeList)
{
result.Remove(removeItem);
}
}
return result;
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2013 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using ZXing.Common;
namespace ZXing.Aztec.Internal
{
/// <summary>
///
/// </summary>
public sealed class SimpleToken : Token
{
// For normal words, indicates value and bitCount
private readonly short value;
private readonly short bitCount;
/// <summary>
/// initializing constructor
/// </summary>
/// <param name="previous"></param>
/// <param name="value"></param>
/// <param name="bitCount"></param>
public SimpleToken(Token previous, int value, int bitCount)
: base(previous)
{
this.value = (short)value;
this.bitCount = (short)bitCount;
}
/// <summary>
/// append token to bitarray
/// </summary>
/// <param name="bitArray"></param>
/// <param name="text"></param>
public override void appendTo(BitArray bitArray, byte[] text)
{
bitArray.appendBits(value, bitCount);
}
/// <summary>
/// string representation
/// </summary>
/// <returns></returns>
public override String ToString()
{
int value = this.value & ((1 << bitCount) - 1);
value |= 1 << bitCount;
return '<' + SupportClass.ToBinaryString(value | (1 << bitCount)).Substring(1) + '>';
}
}
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright 2013 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using ZXing.Common;
namespace ZXing.Aztec.Internal
{
/// <summary>
/// State represents all information about a sequence necessary to generate the current output.
/// Note that a state is immutable.
/// </summary>
internal sealed class State
{
public static readonly State INITIAL_STATE = new State(Token.EMPTY, HighLevelEncoder.MODE_UPPER, 0, 0);
// The current mode of the encoding (or the mode to which we'll return if
// we're in Binary Shift mode.
private readonly int mode;
// The list of tokens that we output. If we are in Binary Shift mode, this
// token list does *not* yet included the token for those bytes
private readonly Token token;
// If non-zero, the number of most recent bytes that should be output
// in Binary Shift mode.
private readonly int binaryShiftByteCount;
// The total number of bits generated (including Binary Shift).
private readonly int bitCount;
public State(Token token, int mode, int binaryBytes, int bitCount)
{
this.token = token;
this.mode = mode;
this.binaryShiftByteCount = binaryBytes;
this.bitCount = bitCount;
// Make sure we match the token
//int binaryShiftBitCount = (binaryShiftByteCount * 8) +
// (binaryShiftByteCount == 0 ? 0 :
// binaryShiftByteCount <= 31 ? 10 :
// binaryShiftByteCount <= 62 ? 20 : 21);
//assert this.bitCount == token.getTotalBitCount() + binaryShiftBitCount;
}
public int Mode
{
get { return mode; }
}
public Token Token
{
get { return token; }
}
public int BinaryShiftByteCount
{
get { return binaryShiftByteCount; }
}
public int BitCount
{
get { return bitCount; }
}
/// <summary>
/// Create a new state representing this state with a latch to a (not
/// necessary different) mode, and then a code.
/// </summary>
public State latchAndAppend(int mode, int value)
{
//assert binaryShiftByteCount == 0;
int bitCount = this.bitCount;
Token token = this.token;
if (mode != this.mode)
{
int latch = HighLevelEncoder.LATCH_TABLE[this.mode][mode];
token = token.add(latch & 0xFFFF, latch >> 16);
bitCount += latch >> 16;
}
int latchModeBitCount = mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5;
token = token.add(value, latchModeBitCount);
return new State(token, mode, 0, bitCount + latchModeBitCount);
}
/// <summary>
/// Create a new state representing this state, with a temporary shift
/// to a different mode to output a single value.
/// </summary>
public State shiftAndAppend(int mode, int value)
{
//assert binaryShiftByteCount == 0 && this.mode != mode;
Token token = this.token;
int thisModeBitCount = this.mode == HighLevelEncoder.MODE_DIGIT ? 4 : 5;
// Shifts exist only to UPPER and PUNCT, both with tokens size 5.
token = token.add(HighLevelEncoder.SHIFT_TABLE[this.mode][mode], thisModeBitCount);
token = token.add(value, 5);
return new State(token, this.mode, 0, this.bitCount + thisModeBitCount + 5);
}
/// <summary>
/// Create a new state representing this state, but an additional character
/// output in Binary Shift mode.
/// </summary>
public State addBinaryShiftChar(int index)
{
Token token = this.token;
int mode = this.mode;
int bitCount = this.bitCount;
if (this.mode == HighLevelEncoder.MODE_PUNCT || this.mode == HighLevelEncoder.MODE_DIGIT)
{
//assert binaryShiftByteCount == 0;
int latch = HighLevelEncoder.LATCH_TABLE[mode][HighLevelEncoder.MODE_UPPER];
token = token.add(latch & 0xFFFF, latch >> 16);
bitCount += latch >> 16;
mode = HighLevelEncoder.MODE_UPPER;
}
int deltaBitCount =
(binaryShiftByteCount == 0 || binaryShiftByteCount == 31) ? 18 :
(binaryShiftByteCount == 62) ? 9 : 8;
State result = new State(token, mode, binaryShiftByteCount + 1, bitCount + deltaBitCount);
if (result.binaryShiftByteCount == 2047 + 31)
{
// The string is as long as it's allowed to be. We should end it.
result = result.endBinaryShift(index + 1);
}
return result;
}
/// <summary>
/// Create the state identical to this one, but we are no longer in
/// Binary Shift mode.
/// </summary>
public State endBinaryShift(int index)
{
if (binaryShiftByteCount == 0)
{
return this;
}
Token token = this.token;
token = token.addBinaryShift(index - binaryShiftByteCount, binaryShiftByteCount);
//assert token.getTotalBitCount() == this.bitCount;
return new State(token, mode, 0, this.bitCount);
}
/// <summary>
/// Returns true if "this" state is better (or equal) to be in than "that"
/// state under all possible circumstances.
/// </summary>
public bool isBetterThanOrEqualTo(State other)
{
int newModeBitCount = this.bitCount + (HighLevelEncoder.LATCH_TABLE[this.mode][other.mode] >> 16);
if (this.binaryShiftByteCount < other.binaryShiftByteCount)
{
// add additional B/S encoding cost of other, if any
newModeBitCount += calculateBinaryShiftCost(other) - calculateBinaryShiftCost(this);
}
else if (this.binaryShiftByteCount > other.binaryShiftByteCount && other.binaryShiftByteCount > 0)
{
// maximum possible additional cost (we end up exceeding the 31 byte boundary and other state can stay beneath it)
newModeBitCount += 10;
}
return newModeBitCount <= other.bitCount;
}
public BitArray toBitArray(byte[] text)
{
// Reverse the tokens, so that they are in the order that they should
// be output
var symbols = new LinkedList<Token>();
for (Token token = endBinaryShift(text.Length).token; token != null; token = token.Previous)
{
symbols.AddFirst(token);
}
BitArray bitArray = new BitArray();
// Add each token to the result.
foreach (Token symbol in symbols)
{
symbol.appendTo(bitArray, text);
}
//assert bitArray.getSize() == this.bitCount;
return bitArray;
}
public override String ToString()
{
return String.Format("{0} bits={1} bytes={2}", HighLevelEncoder.MODE_NAMES[mode], bitCount, binaryShiftByteCount);
}
private static int calculateBinaryShiftCost(State state)
{
if (state.binaryShiftByteCount > 62)
{
return 21; // B/S with extended length
}
if (state.binaryShiftByteCount > 31)
{
return 20; // two B/S
}
if (state.binaryShiftByteCount > 0)
{
return 10; // one B/S
}
return 0;
}
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2013 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using ZXing.Common;
namespace ZXing.Aztec.Internal
{
/// <summary>
///
/// </summary>
public abstract class Token
{
/// <summary>
/// represents an empty token
/// </summary>
public static Token EMPTY = new SimpleToken(null, 0, 0);
private readonly Token previous;
/// <summary>
/// constructor
/// </summary>
/// <param name="previous"></param>
protected Token(Token previous)
{
this.previous = previous;
}
/// <summary>
/// previous token
/// </summary>
public Token Previous
{
get { return previous; }
}
/// <summary>
/// adds a new simple token
/// </summary>
/// <param name="value"></param>
/// <param name="bitCount"></param>
/// <returns></returns>
public Token add(int value, int bitCount)
{
return new SimpleToken(this, value, bitCount);
}
/// <summary>
///
/// </summary>
/// <param name="start"></param>
/// <param name="byteCount"></param>
/// <returns></returns>
public Token addBinaryShift(int start, int byteCount)
{
int bitCount = (byteCount * 8) + (byteCount <= 31 ? 10 : byteCount <= 62 ? 20 : 21);
return new BinaryShiftToken(this, start, byteCount);
}
/// <summary>
/// append to bitarray
/// </summary>
/// <param name="bitArray"></param>
/// <param name="text"></param>
public abstract void appendTo(BitArray bitArray, byte[] text);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing.Client.Result
{
/// <summary> <p>See
/// <a href="http://www.nttdocomo.co.jp/english/service/imode/make/content/barcode/about/s2.html">
/// DoCoMo's documentation</a> about the result types represented by subclasses of this class.</p>
///
/// <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less
/// on exception-based mechanisms during parsing.</p>
///
/// </summary>
/// <author> Sean Owen
/// </author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
/// </author>
abstract class AbstractDoCoMoResultParser : ResultParser
{
internal static String[] matchDoCoMoPrefixedField(String prefix, String rawText)
{
return matchPrefixedField(prefix, rawText, ';', true);
}
internal static String matchSingleDoCoMoPrefixedField(String prefix, String rawText, bool trim)
{
return matchSinglePrefixedField(prefix, rawText, ';', trim);
}
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
namespace ZXing.Client.Result
{
/// <summary> Implements KDDI AU's address book format. See
/// <a href="http://www.au.kddi.com/ezfactory/tec/two_dimensions/index.html">
/// http://www.au.kddi.com/ezfactory/tec/two_dimensions/index.html</a>.
/// (Thanks to Yuzo for translating!)
///
/// </summary>
/// <author> Sean Owen
/// </author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
/// </author>
sealed class AddressBookAUResultParser : ResultParser
{
override public ParsedResult parse(ZXing.Result result)
{
var rawText = result.Text;
// MEMORY is mandatory; seems like a decent indicator, as does end-of-record separator CR/LF
if (rawText == null || rawText.IndexOf("MEMORY") < 0 || rawText.IndexOf("\r\n") < 0)
{
return null;
}
// NAME1 and NAME2 have specific uses, namely written name and pronunciation, respectively.
// Therefore we treat them specially instead of as an array of names.
var name = matchSinglePrefixedField("NAME1:", rawText, '\r', true);
var pronunciation = matchSinglePrefixedField("NAME2:", rawText, '\r', true);
var phoneNumbers = matchMultipleValuePrefix("TEL", rawText);
var emails = matchMultipleValuePrefix("MAIL", rawText);
var note = matchSinglePrefixedField("MEMORY:", rawText, '\r', false);
var address = matchSinglePrefixedField("ADD:", rawText, '\r', true);
var addresses = address == null ? null : new[] { address };
return new AddressBookParsedResult(maybeWrap(name),
null,
pronunciation,
phoneNumbers,
null,
emails,
null,
null,
note,
addresses,
null,
null,
null,
null,
null,
null);
}
private static String[] matchMultipleValuePrefix(String prefix, String rawText)
{
IList<string> values = null;
// For now, always 3, and always trim
for (int i = 1; i <= 3; i++)
{
var value = matchSinglePrefixedField(prefix + i + ':', rawText, '\r', true);
if (value == null)
{
break;
}
if (values == null)
{
values = new List<string>();
}
values.Add(value);
}
if (values == null)
{
return null;
}
return SupportClass.toStringArray(values);
}
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing.Client.Result
{
/// <summary> Implements the "MECARD" address book entry format.
///
/// Supported keys: N, SOUND, TEL, EMAIL, NOTE, ADR, BDAY, URL, plus ORG
/// Unsupported keys: TEL-AV, NICKNAME
///
/// Except for TEL, multiple values for keys are also not supported;
/// the first one found takes precedence.
///
/// Our understanding of the MECARD format is based on this document:
///
/// http://www.mobicode.org.tw/files/OMIA%20Mobile%20Bar%20Code%20Standard%20v3.2.1.doc
///
/// </summary>
/// <author> Sean Owen
/// </author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
/// </author>
sealed class AddressBookDoCoMoResultParser : AbstractDoCoMoResultParser
{
override public ParsedResult parse(ZXing.Result result)
{
String rawText = result.Text;
if (rawText == null || !rawText.StartsWith("MECARD:"))
{
return null;
}
String[] rawName = matchDoCoMoPrefixedField("N:", rawText);
if (rawName == null)
{
return null;
}
String name = parseName(rawName[0]);
String pronunciation = matchSingleDoCoMoPrefixedField("SOUND:", rawText, true);
String[] phoneNumbers = matchDoCoMoPrefixedField("TEL:", rawText);
String[] emails = matchDoCoMoPrefixedField("EMAIL:", rawText);
String note = matchSingleDoCoMoPrefixedField("NOTE:", rawText, false);
String[] addresses = matchDoCoMoPrefixedField("ADR:", rawText);
String birthday = matchSingleDoCoMoPrefixedField("BDAY:", rawText, true);
if (!isStringOfDigits(birthday, 8))
{
// No reason to throw out the whole card because the birthday is formatted wrong.
birthday = null;
}
String[] urls = matchDoCoMoPrefixedField("URL:", rawText);
// Although ORG may not be strictly legal in MECARD, it does exist in VCARD and we might as well
// honor it when found in the wild.
String org = matchSingleDoCoMoPrefixedField("ORG:", rawText, true);
return new AddressBookParsedResult(maybeWrap(name),
null,
pronunciation,
phoneNumbers,
null,
emails,
null,
null,
note,
addresses,
null,
org,
birthday,
null,
urls,
null);
}
private static String parseName(String name)
{
int comma = name.IndexOf(',');
if (comma >= 0)
{
// Format may be last,first; switch it around
return name.Substring(comma + 1) + ' ' + name.Substring(0, comma);
}
return name;
}
}
}

View File

@@ -0,0 +1,293 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Text;
namespace ZXing.Client.Result
{
/// <summary>
/// Represents a parsed result that encodes contact information, like that in an address book entry.
/// </summary>
/// <author>Sean Owen</author>
public sealed class AddressBookParsedResult : ParsedResult
{
private readonly String[] names;
private readonly String[] nicknames;
private readonly String pronunciation;
private readonly String[] phoneNumbers;
private readonly String[] phoneTypes;
private readonly String[] emails;
private readonly String[] emailTypes;
private readonly String instantMessenger;
private readonly String note;
private readonly String[] addresses;
private readonly String[] addressTypes;
private readonly String org;
private readonly String birthday;
private readonly String title;
private readonly String[] urls;
private readonly String[] geo;
/// <summary>
/// initializing constructor
/// </summary>
/// <param name="names"></param>
/// <param name="phoneNumbers"></param>
/// <param name="phoneTypes"></param>
/// <param name="emails"></param>
/// <param name="emailTypes"></param>
/// <param name="addresses"></param>
/// <param name="addressTypes"></param>
public AddressBookParsedResult(String[] names,
String[] phoneNumbers,
String[] phoneTypes,
String[] emails,
String[] emailTypes,
String[] addresses,
String[] addressTypes)
: this(names,
null,
null,
phoneNumbers,
phoneTypes,
emails,
emailTypes,
null,
null,
addresses,
addressTypes,
null,
null,
null,
null,
null)
{
}
/// <summary>
/// initializing constructor
/// </summary>
/// <param name="names"></param>
/// <param name="nicknames"></param>
/// <param name="pronunciation"></param>
/// <param name="phoneNumbers"></param>
/// <param name="phoneTypes"></param>
/// <param name="emails"></param>
/// <param name="emailTypes"></param>
/// <param name="instantMessenger"></param>
/// <param name="note"></param>
/// <param name="addresses"></param>
/// <param name="addressTypes"></param>
/// <param name="org"></param>
/// <param name="birthday"></param>
/// <param name="title"></param>
/// <param name="urls"></param>
/// <param name="geo"></param>
public AddressBookParsedResult(String[] names,
String[] nicknames,
String pronunciation,
String[] phoneNumbers,
String[] phoneTypes,
String[] emails,
String[] emailTypes,
String instantMessenger,
String note,
String[] addresses,
String[] addressTypes,
String org,
String birthday,
String title,
String[] urls,
String[] geo)
: base(ParsedResultType.ADDRESSBOOK)
{
if (phoneNumbers != null && phoneTypes != null && phoneNumbers.Length != phoneTypes.Length)
{
throw new ArgumentException("Phone numbers and types lengths differ");
}
if (emails != null && emailTypes != null && emails.Length != emailTypes.Length)
{
throw new ArgumentException("Emails and types lengths differ");
}
if (addresses != null && addressTypes != null && addresses.Length != addressTypes.Length)
{
throw new ArgumentException("Addresses and types lengths differ");
}
this.names = names;
this.nicknames = nicknames;
this.pronunciation = pronunciation;
this.phoneNumbers = phoneNumbers;
this.phoneTypes = phoneTypes;
this.emails = emails;
this.emailTypes = emailTypes;
this.instantMessenger = instantMessenger;
this.note = note;
this.addresses = addresses;
this.addressTypes = addressTypes;
this.org = org;
this.birthday = birthday;
this.title = title;
this.urls = urls;
this.geo = geo;
displayResultValue = getDisplayResult();
}
/// <summary>
/// the names
/// </summary>
public String[] Names
{
get { return names; }
}
/// <summary>
/// the nicknames
/// </summary>
public String[] Nicknames
{
get { return nicknames; }
}
/// <summary>
/// In Japanese, the name is written in kanji, which can have multiple readings. Therefore a hint
/// is often provided, called furigana, which spells the name phonetically.
/// </summary>
/// <return>The pronunciation of the getNames() field, often in hiragana or katakana.</return>
public String Pronunciation
{
get { return pronunciation; }
}
/// <summary>
/// the phone numbers
/// </summary>
public String[] PhoneNumbers
{
get { return phoneNumbers; }
}
/// <return>optional descriptions of the type of each phone number. It could be like "HOME", but,
/// there is no guaranteed or standard format.</return>
public String[] PhoneTypes
{
get { return phoneTypes; }
}
/// <summary>
/// the e-mail addresses
/// </summary>
public String[] Emails
{
get { return emails; }
}
/// <return>optional descriptions of the type of each e-mail. It could be like "WORK", but,
/// there is no guaranteed or standard format.</return>
public String[] EmailTypes
{
get { return emailTypes; }
}
/// <summary>
/// the instant messenger addresses
/// </summary>
public String InstantMessenger
{
get { return instantMessenger; }
}
/// <summary>
/// the note field
/// </summary>
public String Note
{
get { return note; }
}
/// <summary>
/// the addresses
/// </summary>
public String[] Addresses
{
get { return addresses; }
}
/// <return>optional descriptions of the type of each e-mail. It could be like "WORK", but,
/// there is no guaranteed or standard format.</return>
public String[] AddressTypes
{
get { return addressTypes; }
}
/// <summary>
/// the title
/// </summary>
public String Title
{
get { return title; }
}
/// <summary>
/// the organisations
/// </summary>
public String Org
{
get { return org; }
}
/// <summary>
/// the urls
/// </summary>
public String[] URLs
{
get { return urls; }
}
/// <return>birthday formatted as yyyyMMdd (e.g. 19780917)</return>
public String Birthday
{
get { return birthday; }
}
/// <return>a location as a latitude/longitude pair</return>
public String[] Geo
{
get { return geo; }
}
private String getDisplayResult()
{
var result = new StringBuilder(100);
maybeAppend(names, result);
maybeAppend(nicknames, result);
maybeAppend(pronunciation, result);
maybeAppend(title, result);
maybeAppend(org, result);
maybeAppend(addresses, result);
maybeAppend(phoneNumbers, result);
maybeAppend(emails, result);
maybeAppend(instantMessenger, result);
maybeAppend(urls, result);
maybeAppend(birthday, result);
maybeAppend(geo, result);
maybeAppend(note, result);
return result.ToString();
}
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
namespace ZXing.Client.Result
{
/// <summary> Implements the "BIZCARD" address book entry format, though this has been
/// largely reverse-engineered from examples observed in the wild -- still
/// looking for a definitive reference.
///
/// </summary>
/// <author> Sean Owen
/// </author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
/// </author>
sealed class BizcardResultParser : AbstractDoCoMoResultParser
{
// Yes, we extend AbstractDoCoMoResultParser since the format is very much
// like the DoCoMo MECARD format, but this is not technically one of
// DoCoMo's proposed formats
override public ParsedResult parse(ZXing.Result result)
{
String rawText = result.Text;
if (rawText == null || !rawText.StartsWith("BIZCARD:"))
{
return null;
}
String firstName = matchSingleDoCoMoPrefixedField("N:", rawText, true);
String lastName = matchSingleDoCoMoPrefixedField("X:", rawText, true);
String fullName = buildName(firstName, lastName);
String title = matchSingleDoCoMoPrefixedField("T:", rawText, true);
String org = matchSingleDoCoMoPrefixedField("C:", rawText, true);
String[] addresses = matchDoCoMoPrefixedField("A:", rawText);
String phoneNumber1 = matchSingleDoCoMoPrefixedField("B:", rawText, true);
String phoneNumber2 = matchSingleDoCoMoPrefixedField("M:", rawText, true);
String phoneNumber3 = matchSingleDoCoMoPrefixedField("F:", rawText, true);
String email = matchSingleDoCoMoPrefixedField("E:", rawText, true);
return new AddressBookParsedResult(maybeWrap(fullName),
null,
null,
buildPhoneNumbers(phoneNumber1, phoneNumber2, phoneNumber3),
null,
maybeWrap(email),
null,
null,
null,
addresses,
null,
org,
null,
title,
null,
null);
}
private static String[] buildPhoneNumbers(String number1, String number2, String number3)
{
var numbers = new List<string>();
if (number1 != null)
{
numbers.Add(number1);
}
if (number2 != null)
{
numbers.Add(number2);
}
if (number3 != null)
{
numbers.Add(number3);
}
var size = numbers.Count;
if (size == 0)
{
return null;
}
return SupportClass.toStringArray(numbers);
}
private static String buildName(String firstName, String lastName)
{
if (firstName == null)
{
return lastName;
}
return lastName == null ? firstName : firstName + ' ' + lastName;
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing.Client.Result
{
/// <author> Sean Owen
/// </author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
/// </author>
sealed class BookmarkDoCoMoResultParser : AbstractDoCoMoResultParser
{
override public ParsedResult parse(ZXing.Result result)
{
String rawText = result.Text;
if (rawText == null || !rawText.StartsWith("MEBKM:"))
{
return null;
}
String title = matchSingleDoCoMoPrefixedField("TITLE:", rawText, true);
String[] rawUri = matchDoCoMoPrefixedField("URL:", rawText);
if (rawUri == null)
{
return null;
}
String uri = rawUri[0];
if (!URIResultParser.isBasicallyValidURI(uri))
{
return null;
}
return new URIParsedResult(uri, title);
}
}
}

View File

@@ -0,0 +1,300 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
namespace ZXing.Client.Result
{
/// <summary>
/// Represents a parsed result that encodes a calendar event at a certain time, optionally with attendees and a location.
/// </summary>
///<author>Sean Owen</author>
public sealed class CalendarParsedResult : ParsedResult
{
private static readonly Regex RFC2445_DURATION =
new Regex(@"\A(?:" + "P(?:(\\d+)W)?(?:(\\d+)D)?(?:T(?:(\\d+)H)?(?:(\\d+)M)?(?:(\\d+)S)?)?" + @")\z"
#if !(SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || PORTABLE || UNITY || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2)
, RegexOptions.Compiled);
#else
);
#endif
private static readonly long[] RFC2445_DURATION_FIELD_UNITS =
{
7*24*60*60*1000L, // 1 week
24*60*60*1000L, // 1 day
60*60*1000L, // 1 hour
60*1000L, // 1 minute
1000L, // 1 second
};
private static readonly Regex DATE_TIME = new Regex(@"\A(?:" + "[0-9]{8}(T[0-9]{6}Z?)?" + @")\z"
#if !(SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || PORTABLE || UNITY || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2)
, RegexOptions.Compiled);
#else
);
#endif
private readonly String summary;
private readonly DateTime start;
private readonly bool startAllDay;
private readonly DateTime? end;
private readonly bool endAllDay;
private readonly String location;
private readonly String organizer;
private readonly String[] attendees;
private readonly String description;
private readonly double latitude;
private readonly double longitude;
/// <summary>
/// initializing constructor
/// </summary>
/// <param name="summary"></param>
/// <param name="startString"></param>
/// <param name="endString"></param>
/// <param name="durationString"></param>
/// <param name="location"></param>
/// <param name="organizer"></param>
/// <param name="attendees"></param>
/// <param name="description"></param>
/// <param name="latitude"></param>
/// <param name="longitude"></param>
public CalendarParsedResult(String summary,
String startString,
String endString,
String durationString,
String location,
String organizer,
String[] attendees,
String description,
double latitude,
double longitude)
: base(ParsedResultType.CALENDAR)
{
this.summary = summary;
try
{
this.start = parseDate(startString);
}
catch (Exception pe)
{
throw new ArgumentException(pe.ToString());
}
if (endString == null)
{
long durationMS = parseDurationMS(durationString);
end = durationMS < 0L ? null : (DateTime?)start + new TimeSpan(0, 0, 0, 0, (int)durationMS);
}
else
{
try
{
this.end = parseDate(endString);
}
catch (Exception pe)
{
throw new ArgumentException(pe.ToString());
}
}
this.startAllDay = startString.Length == 8;
this.endAllDay = endString != null && endString.Length == 8;
this.location = location;
this.organizer = organizer;
this.attendees = attendees;
this.description = description;
this.latitude = latitude;
this.longitude = longitude;
var result = new StringBuilder(100);
maybeAppend(summary, result);
maybeAppend(format(startAllDay, start), result);
maybeAppend(format(endAllDay, end), result);
maybeAppend(location, result);
maybeAppend(organizer, result);
maybeAppend(attendees, result);
maybeAppend(description, result);
displayResultValue = result.ToString();
}
/// <summary>
/// summary
/// </summary>
public String Summary
{
get { return summary; }
}
/// <summary>
/// Gets the start.
/// </summary>
public DateTime Start
{
get { return start; }
}
/// <summary>
/// Determines whether [is start all day].
/// </summary>
/// <returns>if start time was specified as a whole day</returns>
public bool isStartAllDay()
{
return startAllDay;
}
/// <summary>
/// event end <see cref="DateTime"/>, or null if event has no duration
/// </summary>
public DateTime? End
{
get { return end; }
}
/// <summary>
/// Gets a value indicating whether this instance is end all day.
/// </summary>
/// <value>true if end time was specified as a whole day</value>
public bool isEndAllDay
{
get { return endAllDay; }
}
/// <summary>
/// location
/// </summary>
public String Location
{
get { return location; }
}
/// <summary>
/// organizer
/// </summary>
public String Organizer
{
get { return organizer; }
}
/// <summary>
/// attendees
/// </summary>
public String[] Attendees
{
get { return attendees; }
}
/// <summary>
/// description
/// </summary>
public String Description
{
get { return description; }
}
/// <summary>
/// latitude
/// </summary>
public double Latitude
{
get { return latitude; }
}
/// <summary>
/// longitude
/// </summary>
public double Longitude
{
get { return longitude; }
}
/// <summary>
/// Parses a string as a date. RFC 2445 allows the start and end fields to be of type DATE (e.g. 20081021)
/// or DATE-TIME (e.g. 20081021T123000 for local time, or 20081021T123000Z for UTC).
/// </summary>
/// <param name="when">The string to parse</param>
/// <returns></returns>
/// <exception cref="ArgumentException">if not a date formatted string</exception>
private static DateTime parseDate(String when)
{
if (!DATE_TIME.Match(when).Success)
{
throw new ArgumentException(String.Format("no date format: {0}", when));
}
if (when.Length == 8)
{
// Show only year/month/day
// For dates without a time, for purposes of interacting with Android, the resulting timestamp
// needs to be midnight of that day in GMT. See:
// http://code.google.com/p/android/issues/detail?id=8330
// format.setTimeZone(TimeZone.getTimeZone("GMT"));
return DateTime.ParseExact(when, "yyyyMMdd", CultureInfo.InvariantCulture);
}
// The when string can be local time, or UTC if it ends with a Z
if (when.Length == 16 && when[15] == 'Z')
{
var milliseconds = parseDateTimeString(when.Substring(0, 15));
//Calendar calendar = new GregorianCalendar();
// Account for time zone difference
//milliseconds += calendar.get(Calendar.ZONE_OFFSET);
// Might need to correct for daylight savings time, but use target time since
// now might be in DST but not then, or vice versa
//calendar.setTime(new Date(milliseconds));
//return milliseconds + calendar.get(Calendar.DST_OFFSET);
milliseconds = TimeZoneInfo.ConvertTime(milliseconds, TimeZoneInfo.Local);
return milliseconds;
}
return parseDateTimeString(when);
}
private static String format(bool allDay, DateTime? date)
{
if (date == null)
{
return null;
}
if (allDay)
return date.Value.ToString("D", CultureInfo.CurrentCulture);
return date.Value.ToString("F", CultureInfo.CurrentCulture);
}
private static long parseDurationMS(String durationString)
{
if (durationString == null)
{
return -1L;
}
var m = RFC2445_DURATION.Match(durationString);
if (!m.Success)
{
return -1L;
}
long durationMS = 0L;
for (int i = 0; i < RFC2445_DURATION_FIELD_UNITS.Length; i++)
{
String fieldValue = m.Groups[i + 1].Value;
if (!String.IsNullOrEmpty(fieldValue))
{
durationMS += RFC2445_DURATION_FIELD_UNITS[i] * Int32.Parse(fieldValue);
}
}
return durationMS;
}
private static DateTime parseDateTimeString(String dateTimeString)
{
return DateTime.ParseExact(dateTimeString, "yyyyMMdd'T'HHmmss", CultureInfo.InvariantCulture);
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Text;
namespace ZXing.Client.Result
{
/// <summary>
/// Represents a parsed result that encodes an email message including recipients, subject and body text.
/// </summary>
/// <author>Sean Owen</author>
public sealed class EmailAddressParsedResult : ParsedResult
{
/// <summary>
/// the email address
/// </summary>
public String EmailAddress
{
get
{
return Tos == null || Tos.Length == 0 ? null : Tos[0];
}
}
/// <summary>
/// the TOs
/// </summary>
public String[] Tos { get; private set; }
/// <summary>
/// the CCs
/// </summary>
public String[] CCs { get; private set; }
/// <summary>
/// the BCCs
/// </summary>
public String[] BCCs { get; private set; }
/// <summary>
/// the subject
/// </summary>
public String Subject { get; private set; }
/// <summary>
/// the body
/// </summary>
public String Body { get; private set; }
/// <summary>
/// the mailto: uri
/// </summary>
[Obsolete("deprecated without replacement")]
public String MailtoURI { get { return "mailto:"; } }
internal EmailAddressParsedResult(String to)
: this(new[] { to }, null, null, null, null)
{
}
internal EmailAddressParsedResult(String[] tos,
String[] ccs,
String[] bccs,
String subject,
String body)
: base(ParsedResultType.EMAIL_ADDRESS)
{
Tos = tos;
CCs = ccs;
BCCs = bccs;
Subject = subject;
Body = body;
var result = new StringBuilder(30);
maybeAppend(Tos, result);
maybeAppend(CCs, result);
maybeAppend(BCCs, result);
maybeAppend(Subject, result);
maybeAppend(Body, result);
displayResultValue = result.ToString();
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Text.RegularExpressions;
namespace ZXing.Client.Result
{
/// <summary>
/// Represents a result that encodes an e-mail address, either as a plain address
/// like "joe@example.org" or a mailto: URL like "mailto:joe@example.org".
/// </summary>
/// <author>Sean Owen</author>
internal sealed class EmailAddressResultParser : ResultParser
{
private static readonly Regex COMMA = new Regex(","
#if !(SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || PORTABLE || UNITY || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2)
, RegexOptions.Compiled);
#else
);
#endif
public override ParsedResult parse(ZXing.Result result)
{
String rawText = result.Text;
if (rawText == null)
{
return null;
}
if (rawText.ToLower().StartsWith("mailto:"))
{
// If it starts with mailto:, assume it is definitely trying to be an email address
String hostEmail = rawText.Substring(7);
int queryStart = hostEmail.IndexOf('?');
if (queryStart >= 0)
{
hostEmail = hostEmail.Substring(0, queryStart);
}
hostEmail = urlDecode(hostEmail);
String[] tos = null;
if (!String.IsNullOrEmpty(hostEmail))
{
tos = COMMA.Split(hostEmail);
}
var nameValues = parseNameValuePairs(rawText);
String[] ccs = null;
String[] bccs = null;
String subject = null;
String body = null;
if (nameValues != null)
{
if (tos == null)
{
String tosString;
if (nameValues.TryGetValue("to", out tosString) && tosString != null)
{
tos = COMMA.Split(tosString);
}
}
String ccString;
if (nameValues.TryGetValue("cc", out ccString) && ccString != null)
{
ccs = COMMA.Split(ccString);
}
String bccString;
if (nameValues.TryGetValue("bcc", out bccString) && bccString != null)
{
bccs = COMMA.Split(bccString);
}
nameValues.TryGetValue("subject", out subject);
nameValues.TryGetValue("body", out body);
}
return new EmailAddressParsedResult(tos, ccs, bccs, subject, body);
}
if (!EmailDoCoMoResultParser.isBasicallyValidEmailAddress(rawText))
{
return null;
}
return new EmailAddressParsedResult(rawText);
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Text.RegularExpressions;
namespace ZXing.Client.Result
{
/// <summary>
/// Implements the "MATMSG" email message entry format.
///
/// Supported keys: TO, SUB, BODY
/// </summary>
/// <author>Sean Owen</author>
internal sealed class EmailDoCoMoResultParser : AbstractDoCoMoResultParser
{
private static readonly Regex ATEXT_ALPHANUMERIC = new Regex(@"\A(?:" + "[a-zA-Z0-9@.!#$%&'*+\\-/=?^_`{|}~]+" + @")\z"
#if !(SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || PORTABLE || UNITY || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2)
, RegexOptions.Compiled);
#else
);
#endif
public override ParsedResult parse(ZXing.Result result)
{
var rawText = result.Text;
if (!rawText.StartsWith("MATMSG:"))
{
return null;
}
var tos = matchDoCoMoPrefixedField("TO:", rawText);
if (tos == null)
{
return null;
}
for (var index = 0; index < tos.Length; index++)
{
var to = tos[index];
if (!isBasicallyValidEmailAddress(to))
{
return null;
}
}
var subject = matchSingleDoCoMoPrefixedField("SUB:", rawText, false);
var body = matchSingleDoCoMoPrefixedField("BODY:", rawText, false);
return new EmailAddressParsedResult(tos, null, null, subject, body);
}
/// <summary>
/// This implements only the most basic checking for an email address's validity -- that it contains
/// an '@' and contains no characters disallowed by RFC 2822. This is an overly lenient definition of
/// validity. We want to generally be lenient here since this class is only intended to encapsulate what's
/// in a barcode, not "judge" it.
/// </summary>
/// <param name="email">The email.</param>
/// <returns>
/// <c>true</c> if it is basically a valid email address; otherwise, <c>false</c>.
/// </returns>
internal static bool isBasicallyValidEmailAddress(String email)
{
return email != null && ATEXT_ALPHANUMERIC.Match(email).Success && email.IndexOf('@') >= 0;
}
}
}

View File

@@ -0,0 +1,328 @@
/*
* Copyright (C) 2010 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* These authors would like to acknowledge the Spanish Ministry of Industry,
* Tourism and Trade, for the support in the project TSI020301-2008-2
* "PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled
* Mobile Dynamic Environments", led by Treelogic
* ( http://www.treelogic.com/ ):
*
* http://www.piramidepse.com/
*/
using System;
using System.Collections.Generic;
namespace ZXing.Client.Result
{
/// <summary>
/// Represents a parsed result that encodes extended product information as encoded by the RSS format, like weight, price, dates, etc.
/// </summary>
/// <author> Antonio Manuel Benjumea Conde, Servinform, S.A.</author>
/// <author> Agustín Delgado, Servinform, S.A.</author>
public class ExpandedProductParsedResult : ParsedResult
{
/// <summary>
/// extension for kilogram weight type
/// </summary>
public static String KILOGRAM = "KG";
/// <summary>
/// extension for pounds weight type
/// </summary>
public static String POUND = "LB";
private readonly String rawText;
private readonly String productID;
private readonly String sscc;
private readonly String lotNumber;
private readonly String productionDate;
private readonly String packagingDate;
private readonly String bestBeforeDate;
private readonly String expirationDate;
private readonly String weight;
private readonly String weightType;
private readonly String weightIncrement;
private readonly String price;
private readonly String priceIncrement;
private readonly String priceCurrency;
// For AIS that not exist in this object
private readonly IDictionary<String, String> uncommonAIs;
/// <summary>
/// initializing constructor
/// </summary>
/// <param name="rawText"></param>
/// <param name="productID"></param>
/// <param name="sscc"></param>
/// <param name="lotNumber"></param>
/// <param name="productionDate"></param>
/// <param name="packagingDate"></param>
/// <param name="bestBeforeDate"></param>
/// <param name="expirationDate"></param>
/// <param name="weight"></param>
/// <param name="weightType"></param>
/// <param name="weightIncrement"></param>
/// <param name="price"></param>
/// <param name="priceIncrement"></param>
/// <param name="priceCurrency"></param>
/// <param name="uncommonAIs"></param>
public ExpandedProductParsedResult(String rawText,
String productID,
String sscc,
String lotNumber,
String productionDate,
String packagingDate,
String bestBeforeDate,
String expirationDate,
String weight,
String weightType,
String weightIncrement,
String price,
String priceIncrement,
String priceCurrency,
IDictionary<String, String> uncommonAIs)
: base(ParsedResultType.PRODUCT)
{
this.rawText = rawText;
this.productID = productID;
this.sscc = sscc;
this.lotNumber = lotNumber;
this.productionDate = productionDate;
this.packagingDate = packagingDate;
this.bestBeforeDate = bestBeforeDate;
this.expirationDate = expirationDate;
this.weight = weight;
this.weightType = weightType;
this.weightIncrement = weightIncrement;
this.price = price;
this.priceIncrement = priceIncrement;
this.priceCurrency = priceCurrency;
this.uncommonAIs = uncommonAIs;
displayResultValue = productID;
}
/// <summary>
///
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
public override bool Equals(Object o)
{
if (!(o is ExpandedProductParsedResult))
{
return false;
}
var other = (ExpandedProductParsedResult)o;
return equalsOrNull(productID, other.productID)
&& equalsOrNull(sscc, other.sscc)
&& equalsOrNull(lotNumber, other.lotNumber)
&& equalsOrNull(productionDate, other.productionDate)
&& equalsOrNull(bestBeforeDate, other.bestBeforeDate)
&& equalsOrNull(expirationDate, other.expirationDate)
&& equalsOrNull(weight, other.weight)
&& equalsOrNull(weightType, other.weightType)
&& equalsOrNull(weightIncrement, other.weightIncrement)
&& equalsOrNull(price, other.price)
&& equalsOrNull(priceIncrement, other.priceIncrement)
&& equalsOrNull(priceCurrency, other.priceCurrency)
&& equalsOrNull(uncommonAIs, other.uncommonAIs);
}
private static bool equalsOrNull(Object o1, Object o2)
{
return o1 == null ? o2 == null : o1.Equals(o2);
}
private static bool equalsOrNull(IDictionary<String, String> o1, IDictionary<String, String> o2)
{
if (o1 == null)
return o2 == null;
if (o1.Count != o2.Count)
return false;
foreach (var entry in o1)
{
if (!o2.ContainsKey(entry.Key))
return false;
if (!entry.Value.Equals(o2[entry.Key]))
return false;
}
return true;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
int hash = 0;
hash ^= hashNotNull(productID);
hash ^= hashNotNull(sscc);
hash ^= hashNotNull(lotNumber);
hash ^= hashNotNull(productionDate);
hash ^= hashNotNull(bestBeforeDate);
hash ^= hashNotNull(expirationDate);
hash ^= hashNotNull(weight);
hash ^= hashNotNull(weightType);
hash ^= hashNotNull(weightIncrement);
hash ^= hashNotNull(price);
hash ^= hashNotNull(priceIncrement);
hash ^= hashNotNull(priceCurrency);
hash ^= hashNotNull(uncommonAIs);
return hash;
}
private static int hashNotNull(Object o)
{
return o == null ? 0 : o.GetHashCode();
}
/// <summary>
/// the raw text
/// </summary>
public String RawText
{
get { return rawText; }
}
/// <summary>
/// the product id
/// </summary>
public String ProductID
{
get { return productID; }
}
/// <summary>
/// the sscc
/// </summary>
public String Sscc
{
get { return sscc; }
}
/// <summary>
/// the lot number
/// </summary>
public String LotNumber
{
get { return lotNumber; }
}
/// <summary>
/// the production date
/// </summary>
public String ProductionDate
{
get { return productionDate; }
}
/// <summary>
/// the packaging date
/// </summary>
public String PackagingDate
{
get { return packagingDate; }
}
/// <summary>
/// the best before date
/// </summary>
public String BestBeforeDate
{
get { return bestBeforeDate; }
}
/// <summary>
/// the expiration date
/// </summary>
public String ExpirationDate
{
get { return expirationDate; }
}
/// <summary>
/// the weight
/// </summary>
public String Weight
{
get { return weight; }
}
/// <summary>
/// the weight type
/// </summary>
public String WeightType
{
get { return weightType; }
}
/// <summary>
/// the weight increment
/// </summary>
public String WeightIncrement
{
get { return weightIncrement; }
}
/// <summary>
/// the price
/// </summary>
public String Price
{
get { return price; }
}
/// <summary>
/// the price increment
/// </summary>
public String PriceIncrement
{
get { return priceIncrement; }
}
/// <summary>
/// the price currency
/// </summary>
public String PriceCurrency
{
get { return priceCurrency; }
}
/// <summary>
/// the uncommon AIs
/// </summary>
public IDictionary<String, String> UncommonAIs
{
get { return uncommonAIs; }
}
/// <summary>
/// the display representation (raw text)
/// </summary>
public override string DisplayResult
{
get
{
return rawText;
}
}
}
}

View File

@@ -0,0 +1,231 @@
/*
* Copyright (C) 2010 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* These authors would like to acknowledge the Spanish Ministry of Industry,
* Tourism and Trade, for the support in the project TSI020301-2008-2
* "PIRAmIDE: Personalizable Interactions with Resources on AmI-enabled
* Mobile Dynamic Environments", led by Treelogic
* ( http://www.treelogic.com/ ):
*
* http://www.piramidepse.com/
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace ZXing.Client.Result
{
/// <summary>
/// Parses strings of digits that represent a RSS Extended code.
/// </summary>
/// <author>Antonio Manuel Benjumea Conde, Servinform, S.A.</author>
/// <author>Agustín Delgado, Servinform, S.A.</author>
public class ExpandedProductResultParser : ResultParser
{
/// <summary>
/// tries to parse a text representation to a specific result object
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
public override ParsedResult parse(ZXing.Result result)
{
BarcodeFormat format = result.BarcodeFormat;
if (format != BarcodeFormat.RSS_EXPANDED)
{
// ExtendedProductParsedResult NOT created. Not a RSS Expanded barcode
return null;
}
String rawText = result.Text;
String productID = null;
String sscc = null;
String lotNumber = null;
String productionDate = null;
String packagingDate = null;
String bestBeforeDate = null;
String expirationDate = null;
String weight = null;
String weightType = null;
String weightIncrement = null;
String price = null;
String priceIncrement = null;
String priceCurrency = null;
var uncommonAIs = new Dictionary<String, String>();
int i = 0;
while (i < rawText.Length)
{
String ai = findAIvalue(i, rawText);
if (ai == null)
{
// Error. Code doesn't match with RSS expanded pattern
// ExtendedProductParsedResult NOT created. Not match with RSS Expanded pattern
return null;
}
i += ai.Length + 2;
String value = findValue(i, rawText);
i += value.Length;
if ("00".Equals(ai))
{
sscc = value;
}
else if ("01".Equals(ai))
{
productID = value;
}
else if ("10".Equals(ai))
{
lotNumber = value;
}
else if ("11".Equals(ai))
{
productionDate = value;
}
else if ("13".Equals(ai))
{
packagingDate = value;
}
else if ("15".Equals(ai))
{
bestBeforeDate = value;
}
else if ("17".Equals(ai))
{
expirationDate = value;
}
else if ("3100".Equals(ai) || "3101".Equals(ai)
|| "3102".Equals(ai) || "3103".Equals(ai)
|| "3104".Equals(ai) || "3105".Equals(ai)
|| "3106".Equals(ai) || "3107".Equals(ai)
|| "3108".Equals(ai) || "3109".Equals(ai))
{
weight = value;
weightType = ExpandedProductParsedResult.KILOGRAM;
weightIncrement = ai.Substring(3);
}
else if ("3200".Equals(ai) || "3201".Equals(ai)
|| "3202".Equals(ai) || "3203".Equals(ai)
|| "3204".Equals(ai) || "3205".Equals(ai)
|| "3206".Equals(ai) || "3207".Equals(ai)
|| "3208".Equals(ai) || "3209".Equals(ai))
{
weight = value;
weightType = ExpandedProductParsedResult.POUND;
weightIncrement = ai.Substring(3);
}
else if ("3920".Equals(ai) || "3921".Equals(ai)
|| "3922".Equals(ai) || "3923".Equals(ai))
{
price = value;
priceIncrement = ai.Substring(3);
}
else if ("3930".Equals(ai) || "3931".Equals(ai)
|| "3932".Equals(ai) || "3933".Equals(ai))
{
if (value.Length < 4)
{
// The value must have more of 3 symbols (3 for currency and
// 1 at least for the price)
// ExtendedProductParsedResult NOT created. Not match with RSS Expanded pattern
return null;
}
price = value.Substring(3);
priceCurrency = value.Substring(0, 3);
priceIncrement = ai.Substring(3);
}
else
{
// No match with common AIs
uncommonAIs[ai] = value;
}
}
return new ExpandedProductParsedResult(rawText,
productID,
sscc,
lotNumber,
productionDate,
packagingDate,
bestBeforeDate,
expirationDate,
weight,
weightType,
weightIncrement,
price,
priceIncrement,
priceCurrency,
uncommonAIs);
}
private static String findAIvalue(int i, String rawText)
{
char c = rawText[i];
// First character must be a open parenthesis.If not, ERROR
if (c != '(')
{
return null;
}
var rawTextAux = rawText.Substring(i + 1);
var buf = new StringBuilder();
for (int index = 0; index < rawTextAux.Length; index++)
{
char currentChar = rawTextAux[index];
if (currentChar == ')')
{
return buf.ToString();
}
if (currentChar < '0' || currentChar > '9')
{
return null;
}
buf.Append(currentChar);
}
return buf.ToString();
}
private static String findValue(int i, String rawText)
{
var buf = new StringBuilder();
var rawTextAux = rawText.Substring(i);
for (int index = 0; index < rawTextAux.Length; index++)
{
char c = rawTextAux[index];
if (c == '(')
{
// We look for a new AI. If it doesn't exist (ERROR), we continue
// with the iteration
if (findAIvalue(index, rawTextAux) != null)
{
break;
}
buf.Append('(');
}
else
{
buf.Append(c);
}
}
return buf.ToString();
}
}
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Globalization;
using System.Text;
namespace ZXing.Client.Result
{
/// <summary>
/// Represents a parsed result that encodes a geographic coordinate, with latitude, longitude and altitude.
/// </summary>
/// <author>Sean Owen</author>
public sealed class GeoParsedResult : ParsedResult
{
internal GeoParsedResult(double latitude, double longitude, double altitude, String query)
: base(ParsedResultType.GEO)
{
Latitude = latitude;
Longitude = longitude;
Altitude = altitude;
Query = query;
GeoURI = getGeoURI();
GoogleMapsURI = getGoogleMapsURI();
displayResultValue = getDisplayResult();
}
/// <returns> latitude in degrees
/// </returns>
public double Latitude { get; private set; }
/// <returns> longitude in degrees
/// </returns>
public double Longitude { get; private set; }
/// <returns> altitude in meters. If not specified, in the geo URI, returns 0.0
/// </returns>
public double Altitude { get; private set; }
/// <return> query string associated with geo URI or null if none exists</return>
public String Query { get; private set; }
/// <summary>
/// the geo URI
/// </summary>
public String GeoURI { get; private set; }
/// <returns> a URI link to Google Maps which display the point on the Earth described
/// by this instance, and sets the zoom level in a way that roughly reflects the
/// altitude, if specified
/// </returns>
public String GoogleMapsURI { get; private set; }
private String getDisplayResult()
{
var result = new StringBuilder(20);
result.AppendFormat(CultureInfo.InvariantCulture, "{0:0.0###########}", Latitude);
result.Append(", ");
result.AppendFormat(CultureInfo.InvariantCulture, "{0:0.0###########}", Longitude);
if (Altitude > 0.0)
{
result.Append(", ");
result.AppendFormat(CultureInfo.InvariantCulture, "{0:0.0###########}", Altitude);
result.Append('m');
}
if (Query != null)
{
result.Append(" (");
result.Append(Query);
result.Append(')');
}
return result.ToString();
}
private String getGeoURI()
{
var result = new StringBuilder();
result.Append("geo:");
result.AppendFormat(CultureInfo.InvariantCulture, "{0:0.0###########}", Latitude);
result.Append(',');
result.AppendFormat(CultureInfo.InvariantCulture, "{0:0.0###########}", Longitude);
if (Altitude > 0)
{
result.Append(',');
result.AppendFormat(CultureInfo.InvariantCulture, "{0:0.0###########}", Altitude);
}
if (Query != null)
{
result.Append('?');
result.Append(Query);
}
return result.ToString();
}
private String getGoogleMapsURI()
{
var result = new StringBuilder(50);
result.Append("http://maps.google.com/?ll=");
result.AppendFormat(CultureInfo.InvariantCulture, "{0:0.0###########}", Latitude);
result.Append(',');
result.AppendFormat(CultureInfo.InvariantCulture, "{0:0.0###########}", Longitude);
if (Altitude > 0.0f)
{
// Map altitude to zoom level, cleverly. Roughly, zoom level 19 is like a
// view from 1000ft, 18 is like 2000ft, 17 like 4000ft, and so on.
double altitudeInFeet = Altitude * 3.28;
int altitudeInKFeet = (int)(altitudeInFeet / 1000.0);
// No Math.log() available here, so compute log base 2 the old fashioned way
// Here logBaseTwo will take on a value between 0 and 18 actually
int logBaseTwo = 0;
while (altitudeInKFeet > 1 && logBaseTwo < 18)
{
altitudeInKFeet >>= 1;
logBaseTwo++;
}
int zoom = 19 - logBaseTwo;
result.Append("&z=");
result.Append(zoom);
}
return result.ToString();
}
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Globalization;
using System.Text.RegularExpressions;
namespace ZXing.Client.Result
{
/// <summary> Parses a "geo:" URI result, which specifies a location on the surface of
/// the Earth as well as an optional altitude above the surface. See
/// <a href="http://tools.ietf.org/html/draft-mayrhofer-geo-uri-00">
/// http://tools.ietf.org/html/draft-mayrhofer-geo-uri-00</a>.
///
/// </summary>
/// <author> Sean Owen
/// </author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
/// </author>
sealed class GeoResultParser : ResultParser
{
private static readonly Regex GEO_URL_PATTERN = new Regex(@"\A(?:" + "geo:([\\-0-9.]+),([\\-0-9.]+)(?:,([\\-0-9.]+))?(?:\\?(.*))?" + @")\z"
#if !(SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || PORTABLE || UNITY || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2)
, RegexOptions.Compiled | RegexOptions.IgnoreCase);
#else
, RegexOptions.IgnoreCase);
#endif
override public ParsedResult parse(ZXing.Result result)
{
String rawText = result.Text;
if (rawText == null)
{
return null;
}
var matcher = GEO_URL_PATTERN.Match(rawText);
if (!matcher.Success)
{
return null;
}
String query = matcher.Groups[4].Value;
if (String.IsNullOrEmpty(query))
query = null;
double latitude;
double longitude;
double altitude = 0.0;
#if WindowsCE
try { latitude = Double.Parse(matcher.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture); }
catch { return null; }
#else
if (!Double.TryParse(matcher.Groups[1].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out latitude))
return null;
#endif
if (latitude > 90.0 || latitude < -90.0)
{
return null;
}
#if WindowsCE
try { longitude = Double.Parse(matcher.Groups[2].Value, NumberStyles.Float, CultureInfo.InvariantCulture); }
catch { return null; }
#else
if (!Double.TryParse(matcher.Groups[2].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out longitude))
return null;
#endif
if (longitude > 180.0 || longitude < -180.0)
{
return null;
}
if (!String.IsNullOrEmpty(matcher.Groups[3].Value))
{
#if WindowsCE
try { altitude = Double.Parse(matcher.Groups[3].Value, NumberStyles.Float, CultureInfo.InvariantCulture); }
catch { return null; }
#else
if (!Double.TryParse(matcher.Groups[3].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out altitude))
return null;
#endif
if (altitude < 0.0)
{
return null;
}
}
return new GeoParsedResult(latitude, longitude, altitude, query);
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing.Client.Result
{
/// <summary>
/// Represents a parsed result that encodes a product ISBN number.
/// </summary>
/// <author>jbreiden@google.com (Jeff Breidenbach)</author>
public sealed class ISBNParsedResult : ParsedResult
{
internal ISBNParsedResult(String isbn)
: base(ParsedResultType.ISBN)
{
ISBN = isbn;
displayResultValue = isbn;
}
/// <summary>
/// the ISBN number
/// </summary>
public String ISBN { get; private set; }
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing.Client.Result
{
/// <summary> Parses strings of digits that represent a ISBN.
///
/// </summary>
/// <author> jbreiden@google.com (Jeff Breidenbach)
/// </author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
/// </author>
public class ISBNResultParser : ResultParser
{
/// <summary>
/// See <a href="http://www.bisg.org/isbn-13/for.dummies.html">ISBN-13 For Dummies</a>
/// </summary>
/// <param name="result">The result.</param>
/// <returns></returns>
override public ParsedResult parse(ZXing.Result result)
{
BarcodeFormat format = result.BarcodeFormat;
if (format != BarcodeFormat.EAN_13)
{
return null;
}
String rawText = result.Text;
int length = rawText.Length;
if (length != 13)
{
return null;
}
if (!rawText.StartsWith("978") && !rawText.StartsWith("979"))
{
return null;
}
return new ISBNParsedResult(rawText);
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Text;
namespace ZXing.Client.Result
{
/// <summary> <p>Abstract class representing the result of decoding a barcode, as more than
/// a String -- as some type of structured data. This might be a subclass which represents
/// a URL, or an e-mail address. {@link ResultParser#parseResult(Result)} will turn a raw
/// decoded string into the most appropriate type of structured representation.</p>
///
/// <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less
/// on exception-based mechanisms during parsing.</p>
/// </summary>
/// <author>Sean Owen</author>
public abstract class ParsedResult
{
/// <summary>
/// user friendly value
/// </summary>
protected string displayResultValue;
/// <summary>
/// gets the type of the parsed result
/// </summary>
public virtual ParsedResultType Type { get; private set; }
/// <summary>
/// user friendly value
/// </summary>
public virtual String DisplayResult { get { return displayResultValue; } }
/// <summary>
/// constructor
/// </summary>
/// <param name="type"></param>
protected ParsedResult(ParsedResultType type)
{
Type = type;
}
/// <summary>
/// gets a user friendly value
/// </summary>
/// <returns></returns>
public override String ToString()
{
return DisplayResult;
}
/// <summary>
/// compare two objects
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object obj)
{
var other = obj as ParsedResult;
if (other == null)
return false;
return other.Type.Equals(Type) && other.DisplayResult.Equals(DisplayResult);
}
/// <summary>
/// gets the hashcode
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return Type.GetHashCode() + DisplayResult.GetHashCode();
}
/// <summary>
/// append to result if not null or empty
/// </summary>
/// <param name="value"></param>
/// <param name="result"></param>
public static void maybeAppend(String value, StringBuilder result)
{
if (String.IsNullOrEmpty(value))
return;
// Don't add a newline before the first value
if (result.Length > 0)
{
result.Append('\n');
}
result.Append(value);
}
/// <summary>
/// append to result if not null or empty
/// </summary>
/// <param name="values"></param>
/// <param name="result"></param>
public static void maybeAppend(String[] values, StringBuilder result)
{
if (values != null)
{
foreach (String value in values)
{
maybeAppend(value, result);
}
}
}
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace ZXing.Client.Result
{
/// <summary>
/// Represents the type of data encoded by a barcode -- from plain text, to a
/// URI, to an e-mail address, etc.
/// </summary>
/// <author>Sean Owen</author>
public enum ParsedResultType
{
/// <summary>
/// address book
/// </summary>
ADDRESSBOOK,
/// <summary>
/// email address
/// </summary>
EMAIL_ADDRESS,
/// <summary>
/// product
/// </summary>
PRODUCT,
/// <summary>
/// URI
/// </summary>
URI,
/// <summary>
/// Text
/// </summary>
TEXT,
/// <summary>
/// geo coordinates
/// </summary>
GEO,
/// <summary>
/// telefon
/// </summary>
TEL,
/// <summary>
/// sms
/// </summary>
SMS,
/// <summary>
/// calendar
/// </summary>
CALENDAR,
/// <summary>
/// wifi
/// </summary>
WIFI,
/// <summary>
/// ISBN
/// </summary>
ISBN,
/// <summary>
/// VIN
/// </summary>
VIN
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing.Client.Result
{
/// <summary>
/// Represents a parsed result that encodes a product by an identifier of some kind.
/// </summary>
/// <author>dswitkin@google.com (Daniel Switkin)</author>
public sealed class ProductParsedResult : ParsedResult
{
internal ProductParsedResult(String productID)
: this(productID, productID)
{
}
internal ProductParsedResult(String productID, String normalizedProductID)
: base(ParsedResultType.PRODUCT)
{
ProductID = productID;
NormalizedProductID = normalizedProductID;
displayResultValue = productID;
}
/// <summary>
/// product id
/// </summary>
public String ProductID { get; private set; }
/// <summary>
/// normalized product id
/// </summary>
public String NormalizedProductID { get; private set; }
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using ZXing.OneD;
namespace ZXing.Client.Result
{
/// <summary>
/// Parses strings of digits that represent a UPC code.
/// </summary>
/// <author>dswitkin@google.com (Daniel Switkin)</author>
internal sealed class ProductResultParser : ResultParser
{
// Treat all UPC and EAN variants as UPCs, in the sense that they are all product barcodes.
public override ParsedResult parse(ZXing.Result result)
{
BarcodeFormat format = result.BarcodeFormat;
if (!(format == BarcodeFormat.UPC_A || format == BarcodeFormat.UPC_E ||
format == BarcodeFormat.EAN_8 || format == BarcodeFormat.EAN_13))
{
return null;
}
// Really neither of these should happen:
String rawText = result.Text;
if (rawText == null)
{
return null;
}
if (!isStringOfDigits(rawText, rawText.Length))
{
return null;
}
// Not actually checking the checksum again here
String normalizedProductID;
// Expand UPC-E for purposes of searching
if (format == BarcodeFormat.UPC_E && rawText.Length == 8)
{
normalizedProductID = UPCEReader.convertUPCEtoUPCA(rawText);
}
else
{
normalizedProductID = rawText;
}
return new ProductParsedResult(rawText, normalizedProductID);
}
}
}

View File

@@ -0,0 +1,408 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace ZXing.Client.Result
{
/// <summary> <p>Abstract class representing the result of decoding a barcode, as more than
/// a String -- as some type of structured data. This might be a subclass which represents
/// a URL, or an e-mail address. {@link #parseResult(com.google.zxing.Result)} will turn a raw
/// decoded string into the most appropriate type of structured representation.</p>
///
/// <p>Thanks to Jeff Griffin for proposing rewrite of these classes that relies less
/// on exception-based mechanisms during parsing.</p>
/// </summary>
/// <author>Sean Owen</author>
public abstract class ResultParser
{
private static readonly ResultParser[] PARSERS =
{
new BookmarkDoCoMoResultParser(),
new AddressBookDoCoMoResultParser(),
new EmailDoCoMoResultParser(),
new AddressBookAUResultParser(),
new VCardResultParser(),
new BizcardResultParser(),
new VEventResultParser(),
new EmailAddressResultParser(),
new SMTPResultParser(),
new TelResultParser(),
new SMSMMSResultParser(),
new SMSTOMMSTOResultParser(),
new GeoResultParser(),
new WifiResultParser(),
new URLTOResultParser(),
new URIResultParser(),
new ISBNResultParser(),
new ProductResultParser(),
new ExpandedProductResultParser(),
new VINResultParser(),
};
#if SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || PORTABLE || UNITY || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2
private static readonly Regex DIGITS = new Regex(@"\A(?:" + "\\d+" + @")\z");
private static readonly Regex AMPERSAND = new Regex("&");
private static readonly Regex EQUALS = new Regex("=");
#else
private static readonly Regex DIGITS = new Regex(@"\A(?:" + "\\d+" + @")\z", RegexOptions.Compiled);
private static readonly Regex AMPERSAND = new Regex("&", RegexOptions.Compiled);
private static readonly Regex EQUALS = new Regex("=", RegexOptions.Compiled);
#endif
/// <summary>
/// Attempts to parse the raw {@link Result}'s contents as a particular type
/// of information (email, URL, etc.) and return a {@link ParsedResult} encapsulating
/// the result of parsing.
/// </summary>
/// <param name="theResult">the raw <see cref="Result"/> to parse</param>
/// <returns><see cref="ParsedResult" /> encapsulating the parsing result</returns>
public abstract ParsedResult parse(ZXing.Result theResult);
/// <summary>
/// Parses the result.
/// </summary>
/// <param name="theResult">The result.</param>
/// <returns></returns>
public static ParsedResult parseResult(ZXing.Result theResult)
{
foreach (var parser in PARSERS)
{
var result = parser.parse(theResult);
if (result != null)
{
return result;
}
}
return new TextParsedResult(theResult.Text, null);
}
/// <summary>
/// append value to result, if not null
/// </summary>
/// <param name="value"></param>
/// <param name="result"></param>
protected static void maybeAppend(String value, System.Text.StringBuilder result)
{
if (value != null)
{
result.Append('\n');
result.Append(value);
}
}
/// <summary>
/// append value to result, if not null
/// </summary>
/// <param name="value"></param>
/// <param name="result"></param>
protected static void maybeAppend(String[] value, System.Text.StringBuilder result)
{
if (value != null)
{
for (int i = 0; i < value.Length; i++)
{
result.Append('\n');
result.Append(value[i]);
}
}
}
/// <summary>
/// wrap, if not null
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected static String[] maybeWrap(String value)
{
return value == null ? null : new[] { value };
}
/// <summary>
/// unescape backslash
/// </summary>
/// <param name="escaped"></param>
/// <returns></returns>
protected static String unescapeBackslash(String escaped)
{
if (escaped != null)
{
int backslash = escaped.IndexOf('\\');
if (backslash >= 0)
{
int max = escaped.Length;
var unescaped = new System.Text.StringBuilder(max - 1);
unescaped.Append(escaped.ToCharArray(), 0, backslash);
bool nextIsEscaped = false;
for (int i = backslash; i < max; i++)
{
char c = escaped[i];
if (nextIsEscaped || c != '\\')
{
unescaped.Append(c);
nextIsEscaped = false;
}
else
{
nextIsEscaped = true;
}
}
return unescaped.ToString();
}
}
return escaped;
}
/// <summary>
/// parse hex digit
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
protected static int parseHexDigit(char c)
{
if (c >= 'a')
{
if (c <= 'f')
{
return 10 + (c - 'a');
}
}
else if (c >= 'A')
{
if (c <= 'F')
{
return 10 + (c - 'A');
}
}
else if (c >= '0')
{
if (c <= '9')
{
return c - '0';
}
}
return -1;
}
internal static bool isStringOfDigits(String value, int length)
{
return value != null && length > 0 && length == value.Length && DIGITS.Match(value).Success;
}
internal static bool isSubstringOfDigits(String value, int offset, int length)
{
if (value == null || length <= 0)
{
return false;
}
int max = offset + length;
return value.Length >= max && DIGITS.Match(value, offset, length).Success;
}
internal static IDictionary<string, string> parseNameValuePairs(String uri)
{
int paramStart = uri.IndexOf('?');
if (paramStart < 0)
{
return null;
}
var result = new Dictionary<String, String>(3);
foreach (var keyValue in AMPERSAND.Split(uri.Substring(paramStart + 1)))
{
appendKeyValue(keyValue, result);
}
return result;
}
private static void appendKeyValue(String keyValue, IDictionary<String, String> result)
{
String[] keyValueTokens = EQUALS.Split(keyValue, 2);
if (keyValueTokens.Length == 2)
{
String key = keyValueTokens[0];
String value = keyValueTokens[1];
try
{
//value = URLDecoder.decode(value, "UTF-8");
value = urlDecode(value);
result[key] = value;
}
catch (Exception uee)
{
throw new InvalidOperationException("url decoding failed", uee); // can't happen
}
result[key] = value;
}
}
internal static String[] matchPrefixedField(String prefix, String rawText, char endChar, bool trim)
{
IList<string> matches = null;
int i = 0;
int max = rawText.Length;
while (i < max)
{
i = rawText.IndexOf(prefix, i);
if (i < 0)
{
break;
}
i += prefix.Length; // Skip past this prefix we found to start
int start = i; // Found the start of a match here
bool done = false;
while (!done)
{
i = rawText.IndexOf(endChar, i);
if (i < 0)
{
// No terminating end character? uh, done. Set i such that loop terminates and break
i = rawText.Length;
done = true;
}
else if (countPrecedingBackslashes(rawText, i) % 2 != 0)
{
// semicolon was escaped (odd count of preceding backslashes) so continue
i++;
}
else
{
// found a match
if (matches == null)
{
matches = new List<string>();
}
String element = unescapeBackslash(rawText.Substring(start, (i) - (start)));
if (trim)
{
element = element.Trim();
}
if (!String.IsNullOrEmpty(element))
{
matches.Add(element);
}
i++;
done = true;
}
}
}
if (matches == null || (matches.Count == 0))
{
return null;
}
return SupportClass.toStringArray(matches);
}
private static int countPrecedingBackslashes(String s, int pos)
{
int count = 0;
for (int i = pos - 1; i >= 0; i--)
{
if (s[i] == '\\')
{
count++;
}
else
{
break;
}
}
return count;
}
internal static String matchSinglePrefixedField(String prefix, String rawText, char endChar, bool trim)
{
String[] matches = matchPrefixedField(prefix, rawText, endChar, trim);
return matches == null ? null : matches[0];
}
/// <summary>
/// decodes url
/// </summary>
/// <param name="escaped"></param>
/// <returns></returns>
protected static String urlDecode(String escaped)
{
// Should we better use HttpUtility.UrlDecode?
// Is HttpUtility.UrlDecode available for all platforms?
// What about encoding like UTF8?
if (escaped == null)
{
return null;
}
char[] escapedArray = escaped.ToCharArray();
int first = findFirstEscape(escapedArray);
if (first < 0)
{
return escaped;
}
int max = escapedArray.Length;
// final length is at most 2 less than original due to at least 1 unescaping
var unescaped = new System.Text.StringBuilder(max - 2);
// Can append everything up to first escape character
unescaped.Append(escapedArray, 0, first);
for (int i = first; i < max; i++)
{
char c = escapedArray[i];
if (c == '+')
{
// + is translated directly into a space
unescaped.Append(' ');
}
else if (c == '%')
{
// Are there even two more chars? if not we will just copy the escaped sequence and be done
if (i >= max - 2)
{
unescaped.Append('%'); // append that % and move on
}
else
{
int firstDigitValue = parseHexDigit(escapedArray[++i]);
int secondDigitValue = parseHexDigit(escapedArray[++i]);
if (firstDigitValue < 0 || secondDigitValue < 0)
{
// bad digit, just move on
unescaped.Append('%');
unescaped.Append(escapedArray[i - 1]);
unescaped.Append(escapedArray[i]);
}
unescaped.Append((char)((firstDigitValue << 4) + secondDigitValue));
}
}
else
{
unescaped.Append(c);
}
}
return unescaped.ToString();
}
private static int findFirstEscape(char[] escapedArray)
{
int max = escapedArray.Length;
for (int i = 0; i < max; i++)
{
char c = escapedArray[i];
if (c == '+' || c == '%')
{
return i;
}
}
return -1;
}
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
namespace ZXing.Client.Result
{
/// <summary> <p>Parses an "sms:" URI result, which specifies a number to SMS and optional
/// "via" number. See <a href="http://gbiv.com/protocols/uri/drafts/draft-antti-gsm-sms-url-04.txt">
/// the IETF draft</a> on this.</p>
///
/// <p>This actually also parses URIs starting with "mms:", "smsto:", "mmsto:", "SMSTO:", and
/// "MMSTO:", and treats them all the same way, and effectively converts them to an "sms:" URI
/// for purposes of forwarding to the platform.</p>
///
/// </summary>
/// <author> Sean Owen
/// </author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
/// </author>
sealed class SMSMMSResultParser : ResultParser
{
override public ParsedResult parse(ZXing.Result result)
{
String rawText = result.Text;
if (rawText == null ||
!(rawText.StartsWith("sms:") || rawText.StartsWith("SMS:") ||
rawText.StartsWith("mms:") || rawText.StartsWith("MMS:")))
{
return null;
}
// Check up front if this is a URI syntax string with query arguments
var nameValuePairs = parseNameValuePairs(rawText);
String subject = null;
String body = null;
var querySyntax = false;
if (nameValuePairs != null && nameValuePairs.Count != 0)
{
subject = nameValuePairs["subject"];
body = nameValuePairs["body"];
querySyntax = true;
}
// Drop sms, query portion
//UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'"
var queryStart = rawText.IndexOf('?', 4);
String smsURIWithoutQuery;
// If it's not query syntax, the question mark is part of the subject or message
if (queryStart < 0 || !querySyntax)
{
smsURIWithoutQuery = rawText.Substring(4);
}
else
{
smsURIWithoutQuery = rawText.Substring(4, (queryStart) - (4));
}
int lastComma = -1;
int comma;
var numbers = new List<String>(1);
var vias = new List<String>(1);
while ((comma = smsURIWithoutQuery.IndexOf(',', lastComma + 1)) > lastComma)
{
String numberPart = smsURIWithoutQuery.Substring(lastComma + 1, comma);
addNumberVia(numbers, vias, numberPart);
lastComma = comma;
}
addNumberVia(numbers, vias, smsURIWithoutQuery.Substring(lastComma + 1));
return new SMSParsedResult(SupportClass.toStringArray(numbers),
SupportClass.toStringArray(vias),
subject,
body);
}
private static void addNumberVia(ICollection<String> numbers,
ICollection<String> vias,
String numberPart)
{
int numberEnd = numberPart.IndexOf(';');
if (numberEnd < 0)
{
numbers.Add(numberPart);
vias.Add(null);
}
else
{
numbers.Add(numberPart.Substring(0, numberEnd));
String maybeVia = numberPart.Substring(numberEnd + 1);
String via;
if (maybeVia.StartsWith("via="))
{
via = maybeVia.Substring(4);
}
else
{
via = null;
}
vias.Add(via);
}
}
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Text;
namespace ZXing.Client.Result
{
/// <summary>
/// Represents a parsed result that encodes an SMS message, including recipients, subject and body text.
/// </summary>
/// <author>Sean Owen</author>
public sealed class SMSParsedResult : ParsedResult
{
/// <summary>
/// initializing constructor
/// </summary>
/// <param name="number"></param>
/// <param name="via"></param>
/// <param name="subject"></param>
/// <param name="body"></param>
public SMSParsedResult(String number,
String via,
String subject,
String body)
: this(new[] { number }, new[] { via }, subject, body)
{
}
/// <summary>
/// initializing constructor
/// </summary>
/// <param name="numbers"></param>
/// <param name="vias"></param>
/// <param name="subject"></param>
/// <param name="body"></param>
public SMSParsedResult(String[] numbers,
String[] vias,
String subject,
String body)
: base(ParsedResultType.SMS)
{
Numbers = numbers;
Vias = vias;
Subject = subject;
Body = body;
SMSURI = getSMSURI();
var result = new StringBuilder(100);
maybeAppend(Numbers, result);
maybeAppend(Subject, result);
maybeAppend(Body, result);
displayResultValue = result.ToString();
}
private String getSMSURI()
{
var result = new StringBuilder();
result.Append("sms:");
bool first = true;
for (int i = 0; i < Numbers.Length; i++)
{
if (first)
{
first = false;
}
else
{
result.Append(',');
}
result.Append(Numbers[i]);
if (Vias != null && Vias[i] != null)
{
result.Append(";via=");
result.Append(Vias[i]);
}
}
bool hasBody = Body != null;
bool hasSubject = Subject != null;
if (hasBody || hasSubject)
{
result.Append('?');
if (hasBody)
{
result.Append("body=");
result.Append(Body);
}
if (hasSubject)
{
if (hasBody)
{
result.Append('&');
}
result.Append("subject=");
result.Append(Subject);
}
}
return result.ToString();
}
/// <summary>
/// numbers
/// </summary>
public String[] Numbers { get; private set; }
/// <summary>
/// vias
/// </summary>
public String[] Vias { get; private set; }
/// <summary>
/// subject
/// </summary>
public String Subject { get; private set; }
/// <summary>
/// body
/// </summary>
public String Body { get; private set; }
/// <summary>
/// sms uri
/// </summary>
public String SMSURI { get; private set; }
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing.Client.Result
{
/// <summary>
/// <p>Parses an "smsto:" URI result, whose format is not standardized but appears to be like:
/// {@code smsto:number(:body)}.</p>
/// <p>This actually also parses URIs starting with "smsto:", "mmsto:", "SMSTO:", and
/// "MMSTO:", and treats them all the same way, and effectively converts them to an "sms:" URI
/// for purposes of forwarding to the platform.</p>
/// </summary>
/// <author>Sean Owen</author>
public class SMSTOMMSTOResultParser : ResultParser
{
/// <summary>
/// attempt to parse the raw result to the specific type
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
public override ParsedResult parse(ZXing.Result result)
{
String rawText = result.Text;
if (!(rawText.StartsWith("smsto:") || rawText.StartsWith("SMSTO:") ||
rawText.StartsWith("mmsto:") || rawText.StartsWith("MMSTO:")))
{
return null;
}
// Thanks to dominik.wild for suggesting this enhancement to support
// smsto:number:body URIs
String number = rawText.Substring(6);
String body = null;
int bodyStart = number.IndexOf(':');
if (bodyStart >= 0)
{
body = number.Substring(bodyStart + 1);
number = number.Substring(0, bodyStart);
}
return new SMSParsedResult(number, null, null, body);
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2010 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing.Client.Result
{
/// <summary>
/// <p>Parses an "smtp:" URI result, whose format is not standardized but appears to be like:
/// <code>smtp[:subject[:body]]}</code>.</p>
/// <p>See http://code.google.com/p/zxing/issues/detail?id=536</p>
/// </summary>
/// <author>Sean Owen</author>
public class SMTPResultParser : ResultParser
{
/// <summary>
/// attempt to parse the raw result to the specific type
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
public override ParsedResult parse(ZXing.Result result)
{
String rawText = result.Text;
if (!(rawText.StartsWith("smtp:") || rawText.StartsWith("SMTP:")))
{
return null;
}
String emailAddress = rawText.Substring(5);
String subject = null;
String body = null;
int colon = emailAddress.IndexOf(':');
if (colon >= 0)
{
subject = emailAddress.Substring(colon + 1);
emailAddress = emailAddress.Substring(0, colon);
colon = subject.IndexOf(':');
if (colon >= 0)
{
body = subject.Substring(colon + 1);
subject = subject.Substring(0, colon);
}
}
return new EmailAddressParsedResult(new[] { emailAddress },
null,
null,
subject,
body);
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing.Client.Result
{
/// <summary>
/// Represents a parsed result that encodes a telephone number.
/// </summary>
/// <author>Sean Owen</author>
public sealed class TelParsedResult : ParsedResult
{
/// <summary>
/// initializing constructor
/// </summary>
/// <param name="number"></param>
/// <param name="telURI"></param>
/// <param name="title"></param>
public TelParsedResult(String number, String telURI, String title)
: base(ParsedResultType.TEL)
{
Number = number;
TelURI = telURI;
Title = title;
var result = new System.Text.StringBuilder(20);
maybeAppend(number, result);
maybeAppend(title, result);
displayResultValue = result.ToString();
}
/// <summary>
/// number
/// </summary>
public String Number { get; private set; }
/// <summary>
/// URI
/// </summary>
public String TelURI { get; private set; }
/// <summary>
/// title
/// </summary>
public String Title { get; private set; }
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing.Client.Result
{
/// <summary>
/// Parses a "tel:" URI result, which specifies a phone number.
/// </summary>
/// <author> Sean Owen
/// </author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
/// </author>
sealed class TelResultParser : ResultParser
{
override public ParsedResult parse(ZXing.Result result)
{
String rawText = result.Text;
if (rawText == null ||
(!rawText.StartsWith("tel:") && !rawText.StartsWith("TEL:")))
{
return null;
}
// Normalize "TEL:" to "tel:"
String telURI = rawText.StartsWith("TEL:") ? "tel:" + rawText.Substring(4) : rawText;
// Drop tel, query portion
//UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'"
int queryStart = rawText.IndexOf('?', 4);
String number = queryStart < 0 ? rawText.Substring(4) : rawText.Substring(4, (queryStart) - (4));
return new TelParsedResult(number, telURI, null);
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing.Client.Result
{
/// <summary>
/// A simple result type encapsulating a string that has no further interpretation.
/// </summary>
/// <author>Sean Owen</author>
public sealed class TextParsedResult : ParsedResult
{
/// <summary>
/// initializing constructor
/// </summary>
/// <param name="text"></param>
/// <param name="language"></param>
public TextParsedResult(String text, String language)
: base(ParsedResultType.TEXT)
{
Text = text;
Language = language;
displayResultValue = text;
}
/// <summary>
/// text
/// </summary>
public String Text { get; private set; }
/// <summary>
/// language
/// </summary>
public String Language { get; private set; }
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing.Client.Result
{
/// <summary>
/// A simple result type encapsulating a URI that has no further interpretation.
/// </summary>
/// <author>Sean Owen</author>
public sealed class URIParsedResult : ParsedResult
{
/// <summary>
/// URI
/// </summary>
public String URI { get; private set; }
/// <summary>
/// title
/// </summary>
public String Title { get; private set; }
/// <returns> true if the URI contains suspicious patterns that may suggest it intends to
/// mislead the user about its true nature. At the moment this looks for the presence
/// of user/password syntax in the host/authority portion of a URI which may be used
/// in attempts to make the URI's host appear to be other than it is. Example:
/// http://yourbank.com@phisher.com This URI connects to phisher.com but may appear
/// to connect to yourbank.com at first glance.
/// </returns>
[Obsolete("deprecated, see {@link URIResultParser#isPossiblyMaliciousURI(String)")]
public bool PossiblyMaliciousURI { get; private set; }
/// <summary>
/// initializing constructor
/// </summary>
/// <param name="uri"></param>
/// <param name="title"></param>
public URIParsedResult(String uri, String title)
: base(ParsedResultType.URI)
{
URI = massageURI(uri);
Title = title;
PossiblyMaliciousURI = URIResultParser.isPossiblyMaliciousURI(uri);
var result = new System.Text.StringBuilder(30);
maybeAppend(Title, result);
maybeAppend(URI, result);
displayResultValue = result.ToString();
}
/// <summary> Transforms a string that represents a URI into something more proper, by adding or canonicalizing
/// the protocol.
/// </summary>
private static String massageURI(String uri)
{
int protocolEnd = uri.IndexOf(':');
if (protocolEnd < 0 || isColonFollowedByPortNumber(uri, protocolEnd))
{
// No protocol, or found a colon, but it looks like it is after the host, so the protocol is still missing,
// so assume http
uri = "http://" + uri;
}
return uri;
}
private static bool isColonFollowedByPortNumber(String uri, int protocolEnd)
{
int start = protocolEnd + 1;
int nextSlash = uri.IndexOf('/', start);
if (nextSlash < 0)
{
nextSlash = uri.Length;
}
return ResultParser.isSubstringOfDigits(uri, start, nextSlash - start);
}
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Text.RegularExpressions;
namespace ZXing.Client.Result
{
/// <summary>
/// Tries to parse results that are a URI of some kind.
/// </summary>
/// <author>Sean Owen</author>
sealed class URIResultParser : ResultParser
{
private static readonly Regex ALLOWED_URI_CHARS_PATTERN = new Regex("^[-._~:/?#\\[\\]@!$&'()*+,;=%A-Za-z0-9]+$"
#if !(SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || PORTABLE || UNITY || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2)
, RegexOptions.Compiled);
#else
);
#endif
private static readonly Regex USER_IN_HOST = new Regex(":/*([^/@]+)@[^/]+"
#if !(SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || PORTABLE || UNITY || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2)
, RegexOptions.Compiled);
#else
);
#endif
// See http://www.ietf.org/rfc/rfc2396.txt
private static readonly Regex URL_WITH_PROTOCOL_PATTERN = new Regex("[a-zA-Z][a-zA-Z0-9+-.]+:"
#if !(SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || PORTABLE || UNITY || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2)
, RegexOptions.Compiled);
#else
);
#endif
private static readonly Regex URL_WITHOUT_PROTOCOL_PATTERN = new Regex(
"([a-zA-Z0-9\\-]+\\.){1,6}[a-zA-Z]{2,}" + // host name elements
"(:\\d{1,5})?" + // maybe port
"(/|\\?|$)" // query, path or nothing
#if !(SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || PORTABLE || UNITY || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2)
, RegexOptions.Compiled);
#else
);
#endif
override public ParsedResult parse(ZXing.Result result)
{
String rawText = result.Text;
// We specifically handle the odd "URL" scheme here for simplicity and add "URI" for fun
// Assume anything starting this way really means to be a URI
if (rawText.StartsWith("URL:") || rawText.StartsWith("URI:"))
{
return new URIParsedResult(rawText.Substring(4).Trim(), null);
}
rawText = rawText.Trim();
if (!isBasicallyValidURI(rawText) || isPossiblyMaliciousURI(rawText))
{
return null;
}
return new URIParsedResult(rawText, null);
}
/**
* @return true if the URI contains suspicious patterns that may suggest it intends to
* mislead the user about its true nature. At the moment this looks for the presence
* of user/password syntax in the host/authority portion of a URI which may be used
* in attempts to make the URI's host appear to be other than it is. Example:
* http://yourbank.com@phisher.com This URI connects to phisher.com but may appear
* to connect to yourbank.com at first glance.
*/
internal static bool isPossiblyMaliciousURI(String uri)
{
return !ALLOWED_URI_CHARS_PATTERN.Match(uri).Success || USER_IN_HOST.Match(uri).Success;
}
internal static bool isBasicallyValidURI(String uri)
{
if (uri.IndexOf(" ") >= 0)
{
// Quick hack check for a common case
return false;
}
var m = URL_WITH_PROTOCOL_PATTERN.Match(uri);
if (m.Success && m.Index == 0)
{
// match at start only
return true;
}
m = URL_WITHOUT_PROTOCOL_PATTERN.Match(uri);
return m.Success && m.Index == 0;
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
namespace ZXing.Client.Result
{
/// <summary> Parses the "URLTO" result format, which is of the form "URLTO:[title]:[url]".
/// This seems to be used sometimes, but I am not able to find documentation
/// on its origin or official format?
///
/// </summary>
/// <author> Sean Owen
/// </author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
/// </author>
sealed class URLTOResultParser : ResultParser
{
override public ParsedResult parse(ZXing.Result result)
{
var rawText = result.Text;
if (rawText == null ||
(!rawText.StartsWith("urlto:") && !rawText.StartsWith("URLTO:")))
{
return null;
}
//UPGRADE_WARNING: Method 'java.lang.String.indexOf' was converted to 'System.String.IndexOf' which may throw an exception. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1101'"
int titleEnd = rawText.IndexOf(':', 6);
if (titleEnd < 0)
{
return null;
}
var title = titleEnd <= 6 ? null : rawText.Substring(6, (titleEnd) - (6));
var uri = rawText.Substring(titleEnd + 1);
return new URIParsedResult(uri, title);
}
}
}

View File

@@ -0,0 +1,490 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
namespace ZXing.Client.Result
{
/// <summary>
/// Parses contact information formatted according to the VCard (2.1) format. This is not a complete
/// implementation but should parse information as commonly encoded in 2D barcodes.
/// </summary>
/// <author>Sean Owen</author>
sealed class VCardResultParser : ResultParser
{
#if SILVERLIGHT4 || SILVERLIGHT5 || NETFX_CORE || PORTABLE || UNITY || NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2
private static readonly Regex BEGIN_VCARD = new Regex("BEGIN:VCARD", RegexOptions.IgnoreCase);
private static readonly Regex VCARD_LIKE_DATE = new Regex(@"\A(?:" + "\\d{4}-?\\d{2}-?\\d{2}" + @")\z");
private static readonly Regex CR_LF_SPACE_TAB = new Regex("\r\n[ \t]");
private static readonly Regex NEWLINE_ESCAPE = new Regex("\\\\[nN]");
private static readonly Regex VCARD_ESCAPES = new Regex("\\\\([,;\\\\])");
private static readonly Regex EQUALS = new Regex("=");
private static readonly Regex SEMICOLON = new Regex(";");
private static readonly Regex UNESCAPED_SEMICOLONS = new Regex("(?<!\\\\);+");
private static readonly Regex COMMA = new Regex(",");
private static readonly Regex SEMICOLON_OR_COMMA = new Regex("[;,]");
#else
private static readonly Regex BEGIN_VCARD = new Regex("BEGIN:VCARD", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex VCARD_LIKE_DATE = new Regex(@"\A(?:" + "\\d{4}-?\\d{2}-?\\d{2}" + @")\z", RegexOptions.Compiled);
private static readonly Regex CR_LF_SPACE_TAB = new Regex("\r\n[ \t]", RegexOptions.Compiled);
private static readonly Regex NEWLINE_ESCAPE = new Regex("\\\\[nN]", RegexOptions.Compiled);
private static readonly Regex VCARD_ESCAPES = new Regex("\\\\([,;\\\\])", RegexOptions.Compiled);
private static readonly Regex EQUALS = new Regex("=", RegexOptions.Compiled);
private static readonly Regex SEMICOLON = new Regex(";", RegexOptions.Compiled);
private static readonly Regex UNESCAPED_SEMICOLONS = new Regex("(?<!\\\\);+", RegexOptions.Compiled);
private static readonly Regex COMMA = new Regex(",", RegexOptions.Compiled);
private static readonly Regex SEMICOLON_OR_COMMA = new Regex("[;,]", RegexOptions.Compiled);
#endif
override public ParsedResult parse(ZXing.Result result)
{
// Although we should insist on the raw text ending with "END:VCARD", there's no reason
// to throw out everything else we parsed just because this was omitted. In fact, Eclair
// is doing just that, and we can't parse its contacts without this leniency.
String rawText = result.Text;
var m = BEGIN_VCARD.Match(rawText);
if (!m.Success || m.Index != 0)
{
return null;
}
List<List<String>> names = matchVCardPrefixedField("FN", rawText, true, false);
if (names == null)
{
// If no display names found, look for regular name fields and format them
names = matchVCardPrefixedField("N", rawText, true, false);
formatNames(names);
}
List<String> nicknameString = matchSingleVCardPrefixedField("NICKNAME", rawText, true, false);
String[] nicknames = nicknameString == null ? null : COMMA.Split(nicknameString[0]);
List<List<String>> phoneNumbers = matchVCardPrefixedField("TEL", rawText, true, false);
List<List<String>> emails = matchVCardPrefixedField("EMAIL", rawText, true, false);
List<String> note = matchSingleVCardPrefixedField("NOTE", rawText, false, false);
List<List<String>> addresses = matchVCardPrefixedField("ADR", rawText, true, true);
List<String> org = matchSingleVCardPrefixedField("ORG", rawText, true, true);
List<String> birthday = matchSingleVCardPrefixedField("BDAY", rawText, true, false);
if (birthday != null && !isLikeVCardDate(birthday[0]))
{
birthday = null;
}
List<String> title = matchSingleVCardPrefixedField("TITLE", rawText, true, false);
List<List<String>> urls = matchVCardPrefixedField("URL", rawText, true, false);
List<String> instantMessenger = matchSingleVCardPrefixedField("IMPP", rawText, true, false);
List<String> geoString = matchSingleVCardPrefixedField("GEO", rawText, true, false);
String[] geo = geoString == null ? null : SEMICOLON_OR_COMMA.Split(geoString[0]);
if (geo != null && geo.Length != 2)
{
geo = null;
}
return new AddressBookParsedResult(toPrimaryValues(names),
nicknames,
null,
toPrimaryValues(phoneNumbers),
toTypes(phoneNumbers),
toPrimaryValues(emails),
toTypes(emails),
toPrimaryValue(instantMessenger),
toPrimaryValue(note),
toPrimaryValues(addresses),
toTypes(addresses),
toPrimaryValue(org),
toPrimaryValue(birthday),
toPrimaryValue(title),
toPrimaryValues(urls),
geo);
}
public static List<List<String>> matchVCardPrefixedField(String prefix,
String rawText,
bool trim,
bool parseFieldDivider)
{
List<List<String>> matches = null;
int i = 0;
int max = rawText.Length;
while (i < max)
{
// At start or after newline, match prefix, followed by optional metadata
// (led by ;) ultimately ending in colon
var matcher = new Regex("(?:^|\n)" + prefix + "(?:;([^:]*))?:", RegexOptions.IgnoreCase);
if (i > 0)
{
i--; // Find from i-1 not i since looking at the preceding character
}
var match = matcher.Match(rawText, i);
if (!match.Success)
{
break;
}
i = match.Index + match.Length;
String metadataString = match.Groups[1].Value; // group 1 = metadata substring
List<String> metadata = null;
bool quotedPrintable = false;
String quotedPrintableCharset = null;
String valueType = null;
if (metadataString != null)
{
foreach (String metadatum in SEMICOLON.Split(metadataString))
{
if (metadata == null)
{
metadata = new List<String>(1);
}
metadata.Add(metadatum);
String[] metadatumTokens = EQUALS.Split(metadatum, 2);
if (metadatumTokens.Length > 1)
{
String key = metadatumTokens[0];
String value = metadatumTokens[1];
if (String.Compare("ENCODING", key, StringComparison.OrdinalIgnoreCase) == 0 &&
String.Compare("QUOTED-PRINTABLE", value, StringComparison.OrdinalIgnoreCase) == 0)
{
quotedPrintable = true;
}
else if (String.Compare("CHARSET", key, StringComparison.OrdinalIgnoreCase) == 0)
{
quotedPrintableCharset = value;
}
else if (String.Compare("VALUE", key, StringComparison.OrdinalIgnoreCase) == 0)
{
valueType = value;
}
}
}
}
int matchStart = i; // Found the start of a match here
while ((i = rawText.IndexOf('\n', i)) >= 0)
{
// Really, end in \r\n
if (i < rawText.Length - 1 && // But if followed by tab or space,
(rawText[i + 1] == ' ' || // this is only a continuation
rawText[i + 1] == '\t'))
{
i += 2; // Skip \n and continutation whitespace
}
else if (quotedPrintable && // If preceded by = in quoted printable
((i >= 1 && rawText[i - 1] == '=') || // this is a continuation
(i >= 2 && rawText[i - 2] == '=')))
{
i++; // Skip \n
}
else
{
break;
}
}
if (i < 0)
{
// No terminating end character? uh, done. Set i such that loop terminates and break
i = max;
}
else if (i > matchStart)
{
// found a match
if (matches == null)
{
matches = new List<List<String>>(1); // lazy init
}
if (i >= 1 && rawText[i - 1] == '\r')
{
i--; // Back up over \r, which really should be there
}
String element = rawText.Substring(matchStart, i - matchStart);
if (trim)
{
element = element.Trim();
}
if (quotedPrintable)
{
element = decodeQuotedPrintable(element, quotedPrintableCharset);
if (parseFieldDivider)
{
element = UNESCAPED_SEMICOLONS.Replace(element, "\n").Trim();
}
}
else
{
if (parseFieldDivider)
{
element = UNESCAPED_SEMICOLONS.Replace(element, "\n").Trim();
}
element = CR_LF_SPACE_TAB.Replace(element, "");
element = NEWLINE_ESCAPE.Replace(element, "\n");
element = VCARD_ESCAPES.Replace(element, "$1");
}
// Only handle VALUE=uri specially
if ("uri".Equals(valueType))
{
// Don't actually support dereferencing URIs, but use scheme-specific part not URI
// as value, to support tel: and mailto:
try
{
Uri uri;
if (Uri.TryCreate(element, UriKind.RelativeOrAbsolute, out uri))
{
element = uri.AbsoluteUri.Replace(uri.Scheme + ':', "");
}
}
catch (Exception)
{
// ignore
}
}
if (metadata == null)
{
var matched = new List<String>(1);
matched.Add(element);
matches.Add(matched);
}
else
{
metadata.Insert(0, element);
matches.Add(metadata);
}
i++;
}
else
{
i++;
}
}
return matches;
}
private static String decodeQuotedPrintable(String value, String charset)
{
int length = value.Length;
var result = new StringBuilder(length);
var fragmentBuffer = new MemoryStream();
for (int i = 0; i < length; i++)
{
char c = value[i];
switch (c)
{
case '\r':
case '\n':
break;
case '=':
if (i < length - 2)
{
char nextChar = value[i + 1];
if (nextChar == '\r' || nextChar == '\n')
{
// Ignore, it's just a continuation symbol
}
else
{
char nextNextChar = value[i + 2];
int firstDigit = parseHexDigit(nextChar);
int secondDigit = parseHexDigit(nextNextChar);
if (firstDigit >= 0 && secondDigit >= 0)
{
fragmentBuffer.WriteByte((byte)((firstDigit << 4) | secondDigit));
} // else ignore it, assume it was incorrectly encoded
i += 2;
}
}
break;
default:
maybeAppendFragment(fragmentBuffer, charset, result);
result.Append(c);
break;
}
}
maybeAppendFragment(fragmentBuffer, charset, result);
return result.ToString();
}
private static void maybeAppendFragment(MemoryStream fragmentBuffer,
String charset,
StringBuilder result)
{
if (fragmentBuffer.Length > 0)
{
byte[] fragmentBytes = fragmentBuffer.ToArray();
String fragment;
if (charset == null)
{
#if WindowsCE
fragment = Encoding.Default.GetString(fragmentBytes, 0, fragmentBytes.Length);
#else
fragment = Encoding.UTF8.GetString(fragmentBytes, 0, fragmentBytes.Length);
#endif
}
else
{
try
{
fragment = Encoding.GetEncoding(charset).GetString(fragmentBytes, 0, fragmentBytes.Length);
}
catch (Exception)
{
#if WindowsCE
// WindowsCE doesn't support all encodings. But it is device depended.
// So we try here the some different ones
if (charset == "ISO-8859-1")
{
fragment = Encoding.GetEncoding(1252).GetString(fragmentBytes, 0, fragmentBytes.Length);
}
else
{
fragment = Encoding.Default.GetString(fragmentBytes, 0, fragmentBytes.Length);
}
fragment = Encoding.Default.GetString(fragmentBytes, 0, fragmentBytes.Length);
#else
fragment = Encoding.UTF8.GetString(fragmentBytes, 0, fragmentBytes.Length);
#endif
}
}
fragmentBuffer.Seek(0, SeekOrigin.Begin);
fragmentBuffer.SetLength(0);
result.Append(fragment);
}
}
internal static List<String> matchSingleVCardPrefixedField(String prefix,
String rawText,
bool trim,
bool parseFieldDivider)
{
List<List<String>> values = matchVCardPrefixedField(prefix, rawText, trim, parseFieldDivider);
return values == null || values.Count == 0 ? null : values[0];
}
private static String toPrimaryValue(List<String> list)
{
return list == null || list.Count == 0 ? null : list[0];
}
private static String[] toPrimaryValues(ICollection<List<String>> lists)
{
if (lists == null || lists.Count == 0)
{
return null;
}
var result = new List<String>(lists.Count);
foreach (var list in lists)
{
String value = list[0];
if (!String.IsNullOrEmpty(value))
{
result.Add(value);
}
}
return SupportClass.toStringArray(result);
}
private static String[] toTypes(ICollection<List<String>> lists)
{
if (lists == null || lists.Count == 0)
{
return null;
}
List<String> result = new List<String>(lists.Count);
foreach (var list in lists)
{
String value = list[0];
if (!String.IsNullOrEmpty(value))
{
String type = null;
for (int i = 1; i < list.Count; i++)
{
String metadatum = list[i];
int equals = metadatum.IndexOf('=');
if (equals < 0)
{
// take the whole thing as a usable label
type = metadatum;
break;
}
if (String.Compare("TYPE", metadatum.Substring(0, equals), StringComparison.OrdinalIgnoreCase) == 0)
{
type = metadatum.Substring(equals + 1);
break;
}
}
result.Add(type);
}
}
return SupportClass.toStringArray(result);
}
private static bool isLikeVCardDate(String value)
{
return value == null || VCARD_LIKE_DATE.Match(value).Success;
}
/**
* Formats name fields of the form "Public;John;Q.;Reverend;III" into a form like
* "Reverend John Q. Public III".
*
* @param names name values to format, in place
*/
private static void formatNames(IEnumerable<List<String>> names)
{
if (names != null)
{
foreach (var list in names)
{
String name = list[0];
String[] components = new String[5];
int start = 0;
int end;
int componentIndex = 0;
while (componentIndex < components.Length - 1 && (end = name.IndexOf(';', start)) >= 0)
{
components[componentIndex] = name.Substring(start, end - start);
componentIndex++;
start = end + 1;
}
components[componentIndex] = name.Substring(start);
StringBuilder newName = new StringBuilder(100);
maybeAppendComponent(components, 3, newName);
maybeAppendComponent(components, 1, newName);
maybeAppendComponent(components, 2, newName);
maybeAppendComponent(components, 0, newName);
maybeAppendComponent(components, 4, newName);
list.Insert(0, newName.ToString().Trim());
}
}
}
private static void maybeAppendComponent(String[] components, int i, StringBuilder newName)
{
if (!String.IsNullOrEmpty(components[i]))
{
if (newName.Length > 0)
{
newName.Append(' ');
}
newName.Append(components[i]);
}
}
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Globalization;
namespace ZXing.Client.Result
{
/// <summary>
/// Partially implements the iCalendar format's "VEVENT" format for specifying a
/// calendar event. See RFC 2445. This supports SUMMARY, DTSTART and DTEND fields.
/// </summary>
/// <author> Sean Owen
/// </author>
/// <author>www.Redivivus.in (suraj.supekar@redivivus.in) - Ported from ZXING Java Source
/// </author>
sealed class VEventResultParser : ResultParser
{
override public ParsedResult parse(ZXing.Result result)
{
String rawText = result.Text;
if (rawText == null)
{
return null;
}
int vEventStart = rawText.IndexOf("BEGIN:VEVENT");
if (vEventStart < 0)
{
return null;
}
String summary = matchSingleVCardPrefixedField("SUMMARY", rawText);
String start = matchSingleVCardPrefixedField("DTSTART", rawText);
if (start == null)
{
return null;
}
String end = matchSingleVCardPrefixedField("DTEND", rawText);
String duration = matchSingleVCardPrefixedField("DURATION", rawText);
String location = matchSingleVCardPrefixedField("LOCATION", rawText);
String organizer = stripMailto(matchSingleVCardPrefixedField("ORGANIZER", rawText));
String[] attendees = matchVCardPrefixedField("ATTENDEE", rawText);
if (attendees != null)
{
for (int i = 0; i < attendees.Length; i++)
{
attendees[i] = stripMailto(attendees[i]);
}
}
String description = matchSingleVCardPrefixedField("DESCRIPTION", rawText);
String geoString = matchSingleVCardPrefixedField("GEO", rawText);
double latitude;
double longitude;
if (geoString == null)
{
latitude = Double.NaN;
longitude = Double.NaN;
}
else
{
int semicolon = geoString.IndexOf(';');
if (semicolon < 0)
{
return null;
}
#if WindowsCE
try { latitude = Double.Parse(geoString.Substring(0, semicolon), NumberStyles.Float, CultureInfo.InvariantCulture); }
catch { return null; }
try { longitude = Double.Parse(geoString.Substring(semicolon + 1), NumberStyles.Float, CultureInfo.InvariantCulture); }
catch { return null; }
#else
if (!Double.TryParse(geoString.Substring(0, semicolon), NumberStyles.Float, CultureInfo.InvariantCulture, out latitude))
return null;
if (!Double.TryParse(geoString.Substring(semicolon + 1), NumberStyles.Float, CultureInfo.InvariantCulture, out longitude))
return null;
#endif
}
try
{
return new CalendarParsedResult(summary,
start,
end,
duration,
location,
organizer,
attendees,
description,
latitude,
longitude);
}
catch (ArgumentException)
{
return null;
}
}
private static String matchSingleVCardPrefixedField(String prefix,
String rawText)
{
var values = VCardResultParser.matchSingleVCardPrefixedField(prefix, rawText, true, false);
return values == null || values.Count == 0 ? null : values[0];
}
private static String[] matchVCardPrefixedField(String prefix, String rawText)
{
List<List<String>> values = VCardResultParser.matchVCardPrefixedField(prefix, rawText, true, false);
if (values == null || values.Count == 0)
{
return null;
}
int size = values.Count;
String[] result = new String[size];
for (int i = 0; i < size; i++)
{
result[i] = values[i][0];
}
return result;
}
private static String stripMailto(String s)
{
if (s != null && (s.StartsWith("mailto:") || s.StartsWith("MAILTO:")))
{
s = s.Substring(7);
}
return s;
}
}
}

Some files were not shown because too many files have changed in this diff Show More